Android相册功能技术实现细节

来源:互联网 发布:youtube翻墙软件下载 编辑:程序博客网 时间:2024/06/06 14:10

最近的一个demo是模拟手Q中发送图片界面,它实际上是一个由缩略图组成的预览界面,在开发过程中,遇到两个问题:

  1. 因为该界面是由缩略图组成,如果直接将原始图片加载到内存中来处理,有可能导致OOM(一张图片是很大的,况且有很多张图片),如何有效的获取原始图片的缩略图呢?

  2. 在demo的第一版中,图片能正确加载到界面,可是当我去滑动gridView时,会非常卡,这是什么原因导致,又该如何处理呢?

又是各种度娘,google,问题最终还是解决了,这里总结一下解决方案。

问题一:图片缩放


一般来说,取缩略图的方法是是使用BitmapFactory的decodeFile方法,通过BitmapFactory.Options的inSampleSize属性来控制缩放比例。属性值inSampleSize表示缩略图大小为原始的N分之几。然而,我们不可能将所有图片都加载到内存中来进行缩放操作,因为有些图片很大,而手机中内存又是很宝贵的,该怎么办呢?经研究发现,BitmapFactory.Options中有个inJustDecodeBounds属性,如果该值设置为true,,那么将不返回实际的bitmap,不给其分配实际的内存空间,而仅仅是一些图片大小信息。

有了这个属性,相应的方法就很简单了,可以先设置Options中有个inJustDecodeBounds为true,使用decodeFile获取图片的大小信息,计算出一个缩放比例(inSampleSize)后,再设置inJustDecodeBounds为false,将缩放后的图片加载到内存中。 具体代码如下:

private Bitmap getBitmapFromUrl(String path){    Bitmap bitmap = null;    //先从缓存中读取    bitmap = gridviewBitmapCaches.get(path);    if (bitmap != null) {        return bitmap;    }    BitmapFactory.Options options = new BitmapFactory.Options();    //设置inJustDecodeBounds为ture,来加载图片的边界信息    ptions.inJustDecodeBounds = true;    bitmap = BitmapFactory.decodeFile(path, options);    //计算出实际的缩放比例    int be = Math.min((int)(options.outHeight/(float)mImageHeight), (int)(options.outWidth/(float)mImageWidth));    if (be <= 0) {        be = 1;    }    options.inSampleSize = be;    //现在讲inJustDecodeBounds重新设置为false,获取实际的缩放后的图片    ptions.inJustDecodeBounds = false;    bitmap=BitmapFactory.decodeFile(path,options);    return bitmap;}

问题二:GridView滑动的时候卡


在demo刚成型的时候,发现在滑动GridView的时候是很卡的,仔细想了想,终于发现问题所在,因为我是在主线程来加载图片,并更新UI,这样,IO本身就是一个耗时的操作,若将图片加载放到主线程来执行,必然会阻塞IO。知道了问题所在,解决问题的方法也就有了,即异步来加载图片。 Android规定只有主线程能更新UI,并提供了一些在新线程中来更新UI的方式,其中最简单,最轻量的莫过于使用AsyncTask了,AsyncTask的具体用法可以查阅相关的文档,这里给出具体的实现代码:

//这个是在GridViewAdapter中@Overridepublic View getView(int position, View convertView, ViewGroup parent) {    View view = converView;    if (view == null) {        view = getLayoutInflater().inflate(R.layout.qq_photo_select_item, null);    }    view.setLayoutParams(new GridView.LayoutParams(mImageWidth,            mImageHeight));    PhotoInfo imageInfo = getItem(position);    ImageView imageView = (ImageView) view            .findViewById(R.id.photo_select_item_photo_iv);    imageView.setAdjustViewBounds(false);    String path = imageInfo.getPath();    AsyncLoadImageTask task = new AsyncLoadImageTask(imageView);    task.execute(path);}//这里就是异步任务private class AsyncLoadImageTask extends AsyncTask<String, Void, Bitmap> {    private String url = null;    private final WeakReference<ImageView> imageViewReference;    public AsyncLoadImageTask(ImageView imageview) {        super();        // TODO Auto-generated constructor stub        imageViewReference = new WeakReference<ImageView>(imageview);    }    @Override    protected Bitmap doInBackground(String... params) {        Bitmap bitmap = null;        this.url = params[0];        bitmap = getBitmapFromUrl(url);        MainActivity.gridviewBitmapCaches.put(url, bitmap);        return bitmap;    }    @Override    protected void onPostExecute(Bitmap resultBitmap) {        if (imageViewReference != null) {            ImageView imageview = imageViewReference.get();            imageview.setImageBitmap(resultBitmap);        }    }}   

问题解决,当然,这里还有很多优化的时候,比如在滑动GridView的时候,在它的滑动事件里,去recycle(对于那些不可见的convertView)相应的bitmap。

原创粉丝点击