以上是自己尝试的一个Android图像加载应用。支持摄像头拍摄,从asset目录加载(hardcode的一个图片),使用gallery选择一张图片显示。实现要点如下:
- 支持不同来源的图片的解码
- 异步解码
- 大图片加载
支持不同来源的图片的解密
摄像头的来自SDCard内的文件,asset下的图片需要用AssetManager加载,gallery选择时返回的是URI。我的统一方式是抽象图片,创建一个接口:
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public interface BitmapDecodable {
/**
* Decode to bitmap.
*
* @param options options
* @return bitmap, null if failed to decode
*/
Bitmap decode(BitmapFactory.Options options);
}
传入解码用的选项,得到解码结果。这实际是把解析委托给来源执行,以基于文件的来源为例:
public Bitmap decode(Options options) {
InputStream stream = null;
try {
stream = open(); // new FileInputStream(this.file)
return BitmapFactory.decodeStream(stream, null, options);
} catch (IOException e) {
Log.w(TAG, "failed to decode, nested exception is " + e);
return null;
} finally {
IOUtils.closeQuietly(stream);
}
}
三种来源其实只要实现open方法返回一个InputStream即可。
异步加载
如果要在加载图片时显示一个ProgressDialog,必须将加载图片的逻辑异步化,否则ProgressDialog不会显示。异步化最简单的是把加载图片做成一个任务,即继承AsyncTask,这部分不是很难,以下是主界面调用的代码。可以看到,抽象化的图片来源加异步化,使得图片加载代码很清晰:
private void loadImage(ImageView to, BitmapDecodable image) {
new ImageViewLoadTask(to) {
private ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMessage("Loading");
progressDialog.show();
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
progressDialog.dismiss();
}
}.execute(image);
}
注意这里没有考虑用户在加载时点击Back或者Home的情况。
大图片加载
参考这篇文章。
大图片加载最重要的是decode时设置inSampleSize。为了设置这个值,需要先“解码”一次获取宽高,计算出inSampleSize再实际解码一次。实际代码如下:
private Bitmap loadImage(BitmapDecodable image) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// decode for bounds
image.decode(options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateInSampleSize(options);
// decode real image
return image.decode(options);
}
public int calculateInSampleSize(BitmapFactory.Options options) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > requestHeight || width > requestWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps
// both height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > requestHeight
&& (halfWidth / inSampleSize) > requestWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
计算inSampleSize的时候直接用了developer.android.com上那篇文章中的代码。
这里使用2的倍数的原因个人认为和decode带inSampleSize背后的实现有关,实际你图片大小在容器大小2×2即4倍大小以内的画貌似直接返回比例1,即不缩放。如果不放心的话这里你用float型的精确比例也是可行的,重点是大图肯定要设置采样比。
另外提一句,你解码得到的图片大小如果大于ImageView大小的话,貌似自动会被缩放。于是呼小图也被拉伸了……
最后,这个查看图片的感觉是自己第一个貌似可以用用的小应用。感觉在安卓上开发反馈很快,只是知识比较散,还需要多学习。
