以上是自己尝试的一个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大小的话,貌似自动会被缩放。于是呼小图也被拉伸了……
最后,这个查看图片的感觉是自己第一个貌似可以用用的小应用。感觉在安卓上开发反馈很快,只是知识比较散,还需要多学习。