概述
图片查看是很常见的功能,点击图片之后跳转到另一个界面查看大图,看起来是非常简单,不过自己动手尝试了一下之后并没有想象中的那么顺利,其中还是有很多需要注意的地方。
好了,先看一下效果
![这里写图片描述](http://7xr3nz.com1.z0.glb.clouddn.com/mi_20160220_232817.png)
对,就是这么简单!显示一张图片,图片可以保存到本地,当然,现在的app基本上都有手势缩放图片的功能,这里我们也要添加这个功能。
好了,功能基本就是这样,下面看代码,首先得界面布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#c0000000"
tools:context="me.masteryi.gankio.PhotoActivity">
<ImageView
android:id="@+id/photo_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"/>
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
</FrameLayout>
布局很简单,一个FrameLayout,上面是Appbar,下面是一个ImageView。这里为什么用FrameLayout而不用RelativeLayout呢?主要是为了能够让图片能够全屏显示,可以参考网易新闻的图片界面,这里就不贴图了,你要是喜欢,改成别的布局也不影响。
接下来就是Activity了。这里先提一下用到的第三方库:
- 图片加载:Picasso
- 图片缩放:PhotoView
- 控件注入:ButterKnife
- 异步操作:RxJava/RxAndroid
其实Picasso和Butterknife是Android Studio自带的,Picasso是Square家的图片加载库,是主流的图片加载库之一,可以搭配同样是Square出品的OkHttp使用。Butterknife配合Butterknife Zelezny插件可以非常方便的完成控件的注入,妈妈再也不用担心我写findViewById了。RxJava/RxAndroid最近非常火,为了跟上时代潮流,我也在努力学习RxJava的用法。PhotoView是一个非常好的图片控制库,可以手势控制图片移动和缩放,并支持双击放大。其实图片缩放本来是想自己实现的,后来发现有这么一个牛逼的库,就偷个懒先用着,以后再补上。
首先是加载图片,这里我们用Picasso实现
Picasso.with(this)
.load(url)
.into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
photoImageView.setImageBitmap(bitmap);
if (attacher == null) {
attacher = new PhotoViewAttacher(photoImageView);
} else {
attacher.update();
}
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
Toast.makeText(PhotoActivity.this, "加载图片出错", Toast.LENGTH_SHORT).show();
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
我这里into方法中用的是一个Target而没有直接用imageView,因为通过Target我们可以得到Bitmap对象,至于为什么要得到这个bitmap对象,我们后面再说。
好了,就这样,一个简单的图片详情页面就做好了。当然,这是最基本的功能,这么可爱的妹纸,当然要保存起来了,so,一般的App都会有保存图片,收藏或者分享功能,这里我们只做下载功能,收藏跟分享以后再补上。
我们新建一个menu
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_more"
android:icon="@drawable/ic_more_vert_white_24dp"
android:title="@string/more"
app:showAsAction="always"
>
<menu>
<item
android:id="@+id/menu_download"
android:icon="@drawable/ic_file_download_black_24dp"
android:title="@string/download"
/>
</menu>
</item>
</menu>
菜单包括溢出菜单,溢出菜单中有一个下载菜单。好,就是这么简单。接下来我们做下载功能。
我们先准备一个保存图片的工具类FileUtil
/**
* Created by Lee
* Date 2016/2/20
* Email jon_ly@163.com
* Blog http://masteryi.me
*/
public class FileUtil {
public static final String TAG = "FileUtil";
public static final String IMAGE_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
.getPath() + File.separator + "Gank";
public static boolean saveImage(String imageName, Bitmap image) {
// Log.d(TAG, "image path:" + IMAGE_PATH);
File file = new File(IMAGE_PATH);
if (!file.exists()) {
file.mkdirs();
}
File imageFile = new File(file, imageName);
try {
if (imageFile.createNewFile()) {
FileOutputStream fos = new FileOutputStream(imageFile);
image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 通过url获得文件名
*
* @param url 图片url
* @return 图片文件名
*/
public static String url2ImageName(String url) {
String imageName = url.substring(url.lastIndexOf("/") + 1);
return imageName;
}
}
FileUtil很简单,只有两个方法,一个是通过url找到文件名XXX.jpg,一个是保存图片的方法,应该没什么难度。这里有一点要注意的是保存路径我这里用了Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)这个方法,这个其实就是android用来存放公共图片的文件夹,打开这个目录,我们可以看见还有知乎,网易等目录在下面,我们新建一个Gank的目录来存放图片。关于Environment.getExternalStoragePublicDirectory其实不止DIRECTORY_PICTURES一种,还有很多别的类型,具体可以看官方文档(自备梯子)。
接下来我们添加保存图片的方法
private void downloadImage() {
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Observable.create((Observable.OnSubscribe<Boolean>) subscriber -> {
// Log.d(TAG, "thread1:" + Thread.currentThread().getName());
String imageName = FileUtil.url2ImageName(url);
subscriber.onNext(FileUtil.saveImage(imageName, bitmap));
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aBoolean -> {
// Log.d(TAG, "thread2:" + Thread.currentThread().getName());
if (aBoolean) {
Toast.makeText(PhotoActivity.this, "保存图片成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(PhotoActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
}
}, throwable -> {
Toast.makeText(PhotoActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
Log.d(TAG, throwable.getMessage());
throwable.printStackTrace();
});
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
Toast.makeText(PhotoActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(this)
.load(url)
.into(target);
}
我们来看一下downloadImage方法
这里Picasso的into方法中也用了Target,具体原因刚才介绍过了,为了获得Bitmap对象。拿到bitmap之后接下来就是要保存到本地,考虑到图片可能很大,保存操作可能是个耗时操作,为了提升界面流畅度,我们需要在线程中进行。这里我们用RxJava来进行异步操作。RxJava教程可以参考这里。RxJava有一个很好的地方就是可以很方便的切换线程,这里我们让图片保存的操作在Schedulers.io()线程也就是IO线程中进行,回调显示在AndroidSchedulers.mainThread()也就是android的主线程中进行,这样,就可以很方便的进行耗时操作并显示结果了。我们可以把线程打印出来
02-21 14:12:44.976 29015-29074/me.masteryi.gankio D/PhotoActivity: thread1:RxCachedThreadScheduler-2
02-21 14:12:45.116 29015-29015/me.masteryi.gankio D/PhotoActivity: thread2:main
可以看出来保存图片的操作发生在RxCachedThreadScheduler这个线程,而显示结果的线程发生在main线程,也就是主线程。
关于subscribeOn和observeOn这两个方法我也看了很久,根据官方文档
![]()
To specify on which Scheduler the Observable should invoke its observers’ onNext, onCompleted, and onError methods, use the observeOn operator, passing it the appropriate Scheduler.
![]()
To specify on which Scheduler the Observable should operate, use the subscribeOn operator, passing it the appropriate Scheduler.
我的理解是subscribeOn指定Observable所运行的线程,也就是这里我们Observable.creat()运行的线程,obserOn指定onNext,onError等方法运行的线程,所以我们可以在create中运行耗时方法,在onNext中调用改变UI的方法。我不知道这样理解有没有问题,如果有错,希望大家能够指出来。
好了,最基本的功能都已经实现了,后面我们会加入一些扩展的功能。
最后
以上就是威武金鱼为你收集整理的android图片查看(1)的全部内容,希望文章能够帮你解决android图片查看(1)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复