[android]图片加载应用


android-picture-viewer

以上是自己尝试的一个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大小的话,貌似自动会被缩放。于是呼小图也被拉伸了……

最后,这个查看图片的感觉是自己第一个貌似可以用用的小应用。感觉在安卓上开发反馈很快,只是知识比较散,还需要多学习。