android 获取本地图片(二)

链接上一篇博文

本篇博文接着讲述本地图片的获取。

下面给出获取本地图片的异步线程类LoadLoacalPhotoCursorTask的代码:

/**
* 获取本地图片的异步线程类
*/
public class LoadLoacalPhotoCursorTask extends AsyncTask<Object, Object, Object> {
    private Context mContext;
    private final ContentResolver mContentResolver;
    private boolean mExitTasksEarly = false;//退出任务线程的标志位
    private OnLoadPhotoCursor onLoadPhotoCursor;//定义回调接口,获取解析到的数据

    private ArrayList<Uri> uriArray = new ArrayList<Uri>();//存放图片URI
    private ArrayList<Long> origIdArray = new ArrayList<Long>();//存放图片ID

    public LoadLoacalPhotoCursorTask(Context mContext) {
        this.mContext = mContext;
        mContentResolver = mContext.getContentResolver();
    }

    @Override
    protected Object doInBackground(Object... params) {
        String[] projection = {
                MediaStore.Images.Media._ID
        };
        Uri ext_uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String where = MediaStore.Images.Media.SIZE + ">=?";
        /**
        * 这个查询操作完成图片大小大于100K的图片的ID查询。
        * 大家可能疑惑为什么不查询得到图片DATA呢?
        * 这样是为了节省内存。通过图片的ID可以查询得到指定的图片
        * 如果这里就把图片数据查询得到,手机中的图片大量的情况下
        * 内存消耗严重。那么,什么时候查询图片呢?应该是在Adapter
        * 中完成指定的ID的图片的查询,并不一次性加载全部图片数据
        */
        Cursor c = MediaStore.Images.Media.query(
                mContentResolver,
                ext_uri,
                projection,
                where,
                new String[]{1 * 100 * 1024 + ""},
                MediaStore.Images.Media.DATE_ADDED+" desc");

        int columnIndex = c.getColumnIndexOrThrow(MediaStore.Images.Media._ID);

        int i = 0;
        while (c.moveToNext() && i < c.getCount() && !mExitTasksEarly) {   //移到指定的位置,遍历数据库
            long origId = c.getLong(columnIndex);
            uriArray.add(
                    Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            origId + "")
            );

            origIdArray.add(origId);
            c.moveToPosition(i);
            i++;
        }
        c.close();//关闭数据库
        if (mExitTasksEarly) {
            uriArray = new ArrayList<Uri>();
            origIdArray = new ArrayList<Long>();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Object o) {
        if (onLoadPhotoCursor != null && !mExitTasksEarly) {
        /**
        * 查询完成之后,设置回调接口中的数据,把数据传递到Activity中
        */          onLoadPhotoCursor.onLoadPhotoSursorResult(uriArray, origIdArray);
        }
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();    //To change body of overridden methods use File | Settings | File Templates.
        mExitTasksEarly = true;
    }

    public void setExitTasksEarly(boolean exitTasksEarly) {
        this.mExitTasksEarly = exitTasksEarly;
    }

    public void setOnLoadPhotoCursor(OnLoadPhotoCursor onLoadPhotoCursor) {
        this.onLoadPhotoCursor = onLoadPhotoCursor;
    }

    public interface OnLoadPhotoCursor {
        public void onLoadPhotoSursorResult(ArrayList<Uri> uriArray, ArrayList<Long> origIdArray);
    }
}

下面给出加载图片的类ImageWorker的代码:

/**
 * 手机本地图片异步加载处理类
 * 图片的加载性能影响很大,使用弱引用和软引用
 * 缓存图片,加快响应的速度,提高性能。
 */
public class ImageWorker {

    //这个值设置的是加载图片的动画效果的间隔时间,达到渐隐渐显的效果
    private static final int FADE_IN_TIME = 200;

    private boolean mExitTasksEarly = false;//判断图片加载任务是否提前退出
    private boolean mPauseWork = false;//加载图片线程是否挂起
    private final Object mPauseWorkLock = new Object();//锁对象,这个锁对象是为了判断是否进行图片的加载

    protected final Resources mResources;
    private final ContentResolver mContentResolver;//内容解析者
    private final BitmapFactory.Options mOptions;

    private final HashMap<Long, SoftReference<BitmapDrawable>> bitmapCache = new HashMap<Long, SoftReference<BitmapDrawable>>();//用于缓存图片,每一个缓存的图片对应一个Long类型的id值,SoftReference对应该图片的软引用

    private Bitmap mLoadBitmap;//GridView中默认的背景图片
    //构造器
    public ImageWorker(Context context) {
        this.mResources = context.getResources();
        this.mContentResolver = context.getContentResolver();
        mOptions = new BitmapFactory.Options();
        mOptions.inSampleSize = 3;//缩放图片为原来的1/9。一般应用中加载图片都会进行图片的缩放,防止内存溢出的问题。这部分的内容有关Bitmap,请参考我的博文(http://blog.csdn.net/u010156024/article/details/44103557)
    }

    /**
     * 加载图片
     * @param origId 每个本地图片对应一个id值
     * @param imageView
     */
    public void loadImage(long origId, ImageView imageView) {
        BitmapDrawable bitmapDrawable = null;
        //先从缓存中加载图片,如果缓存中有,加载图片即可。
        //如果缓存中没有,首先判断当前任务是否暂停,没有暂停则使用loadBitmapTask异步任务线程加载图片
        if (bitmapCache.containsKey(origId)) {
            bitmapDrawable = bitmapCache.get(origId).get();
        }
        if (bitmapDrawable != null) {
            imageView.setImageDrawable(bitmapDrawable);
        } else if (cancelPotentialWork(origId, imageView)) {
            final LoadBitmapTask loadBitmapTask = new LoadBitmapTask(imageView);
            final AsyncDrawable asyncDrawable =
                    new AsyncDrawable(mResources, mLoadBitmap, loadBitmapTask);
            imageView.setImageDrawable(asyncDrawable);
            //SERIAL_EXECUTOR 启动线程,保证线程顺序依次执行
            loadBitmapTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, origId);
        }
    }

/**
* 该类提供这个方法设置GridView中每个item默认的图片
*/
    public void setLoadBitmap(Bitmap mLoadBitmap) {
        this.mLoadBitmap = mLoadBitmap;
    }

    /**
     * 设置图片动画  加载图片渐隐渐显的效果
     *
     * @param imageView
     * @param drawable
     */
    private void setImageDrawable(ImageView imageView, Drawable drawable) {
        final TransitionDrawable td =
                new TransitionDrawable(new Drawable[]{
                        new ColorDrawable(android.R.color.transparent),
                        drawable
                });
        imageView.setImageDrawable(td);
        td.startTransition(FADE_IN_TIME);
    }

    /**
     * 取消可能在运行并且暂停的任务。
     *
     * @param origId
     * @param imageView
     * @return
     */
    private static boolean cancelPotentialWork(long origId, ImageView imageView) {
        final LoadBitmapTask loadBitmapTask = getBitmapWorkerTask(imageView);

        if (loadBitmapTask != null) {
            final long bitmapOrigId = loadBitmapTask.origId;
            if (bitmapOrigId == origId) {
                loadBitmapTask.cancel(true);
            } else {
                // The same work is already in progress.
                return false;
            }
        }
        return true;
    }

    /**
     * 图片异步加载线程类-任务线程
     */
    private class LoadBitmapTask extends AsyncTask<Long, Void, BitmapDrawable> {
        private long origId;
        private WeakReference<ImageView> imageViewReference;//指向Imageview的弱引用,把图片缓存在HashMap<Long, SoftReference<BitmapDrawable>> bitmapCache中。

        public LoadBitmapTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        @Override
        protected BitmapDrawable doInBackground(Long... params) {
            origId = params[0];
            Bitmap bitmap = null;
            BitmapDrawable drawable = null;

            // Wait here if work is paused and the task is not cancelled
            synchronized (mPauseWorkLock) {
                while (mPauseWork && !isCancelled()) {
                    try {
                        mPauseWorkLock.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }

            if (bitmapCache != null && !isCancelled() && getAttachedImageView() != null
                    && !mExitTasksEarly) {
                //这里是根据图片的id值查询手机本地的图片,获取图片的缩略图,MICRO_KIND 代表96*96大小的图片
                bitmap = MediaStore.Images.Thumbnails.getThumbnail(
                        mContentResolver,
                        origId,
                    MediaStore.Images.Thumbnails.MICRO_KIND,
                        mOptions
                );
            }

            if (bitmap != null) {
                drawable = new BitmapDrawable(mResources, bitmap);
                bitmapCache.put(origId,new SoftReference<BitmapDrawable>(drawable));
            }
            return drawable;
        }

        @Override
        protected void onPostExecute(BitmapDrawable drawable) {
            if (isCancelled() || mExitTasksEarly) {
                drawable = null;
            }

            final ImageView imageView = getAttachedImageView();
            if (drawable != null && imageView != null) {
                setImageDrawable(imageView, drawable);
            }
        }

       @Override
        protected void onCancelled(BitmapDrawable drawable) {
            super.onCancelled(drawable);
            synchronized (mPauseWorkLock) {
                mPauseWorkLock.notifyAll();
            }
        }

        /**
         * 返回与此任务相关的ImageView,
         * 如果ImageView 内的任务是当前任务,
         * 则返回当前ImageView,否则返回null。
         * @return
         */
        private ImageView getAttachedImageView() {
            final ImageView imageView = imageViewReference.get();
            final LoadBitmapTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask) {
                return imageView;
            }
            return null;
        }
    }

    /**
     * 存储异步信息图片资源类
     */
    private static class AsyncDrawable extends BitmapDrawable {
        private final WeakReference<LoadBitmapTask> bitmapWorkerTaskReference;//虚引用
        public AsyncDrawable(Resources res, Bitmap bitmap, LoadBitmapTask bitmapWorkerTask) {
            super(res, bitmap);
            bitmapWorkerTaskReference =
                    new WeakReference<LoadBitmapTask>(bitmapWorkerTask);
        }
        public LoadBitmapTask getLoadBitmapTask() {
            return bitmapWorkerTaskReference.get();
        }
    }

    /**
     * 返回图片资源内存放的异步线程,如果存在,则返回,不存在,返回null。
     *
     * @param imageView 当前存放异步资源图片的ImageView
     * @return
     */
    private static LoadBitmapTask getBitmapWorkerTask(ImageView imageView) {
        if (imageView != null) {
            final Drawable drawable = imageView.getDrawable();
            if (drawable instanceof AsyncDrawable) {
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                return asyncDrawable.getLoadBitmapTask();
            }
        }
        return null;
    }

    /**
     * 设置异步任务是否暂停,false为启动,true为暂停。
     * @param pauseWork
     */
    public void setPauseWork(boolean pauseWork) {
        synchronized (mPauseWorkLock) {
            mPauseWork = pauseWork;
            if (!mPauseWork) {
                mPauseWorkLock.notifyAll();
            }
        }
    }

    /**
     * 退出线程
     * @param exitTasksEarly
     */
    public void setExitTasksEarly(boolean exitTasksEarly) {
        mExitTasksEarly = exitTasksEarly;
        setPauseWork(false);//这个设置为false,使得退出任务优雅。这个设置为true也是可行的,也没有问题,可以达到同样的效果。但是可以比较设置为true或false的区别。
    }
}

至此 全部讲述了android手机中获取本地图片的全部内容。这个项目代码在Github中开放学习,非常值得大家深入研究学习之后。

总结:

整个项目使用到了ContentResolver、软引用、弱引用、缓存、异步线程、节省内存技巧-滚动不加载、锁机制、图片缩放、线程通信、动画等非常实用的方法、技巧。

源码下载

时间: 2024-10-03 13:27:21

android 获取本地图片(二)的相关文章

android获取本地图片并显示图片

import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.util.

android 获取本地图片(一)

我的前面几个博文中已经介绍过了如何获取本地图片和Bitmap.软引用.弱引用的使用方法.在这两个博文当中针对一个完整的Demo示例给大家讲解获取本地图片的非常有效.也是安卓官方推荐的方法. **源代码在博文最后可以下载**. 不知道怎么回事,近来csdn写博文上传图片错误,不能上传图片,所以给不了大家效果图.我就文字介绍好了.整个Demo只有一个Activity中完成,加载本地图片在GridView中进行显示,同时点击每个图片可以对图片进行选中的操作,每个item图片右上角会给出对号的提示,表示

Android 获取本地图片

MainActivity.java public class RegisterActivity extends AppCompatActivity { private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_register); iv = (Ima

android获取拍照图片、本地图片简单实现!

在安卓应用开发中经常会用到调用系统相机拍照跟获取本地图片功能,下面就是对这一常用功能的简单实现Demo! 在获取拍照图片功能中要加上这两权限. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> 布局文件 1 <Re

android选取本地图片及关于图片压缩上传问题

<span style="font-size:18px;">在一个项目里面,有一个需求是让用户自己选择图片,然后上传到服务器.看似一个很简单的需求,就是选择图片,把图片装好,然后通过网络请求上传到后台,OK.但是事实并非如此,因为我们可以android项目,他是open的,他有更多的可能性,当然你也会遇到更多古灵精怪的问题.</span> 获取图片有3种方法,一是自己用surface控件,利用镜头来获取图片:二是调用系统相机,并且返回拍到的图片:三是直接在利用图

Android -- 打开本地图片且显示路径

背景                                                                                          代码                                                                                           先上布局文件: <LinearLayout xmlns:android="http://schemas.android.co

Android之获取本地图片并压缩方法

这两天在做项目时,做到上传图片功能一块时,碰到两个问题,一个是如何获取所选图片的路径,一个是如何压缩图片,在查了一些资料和看了别人写的后总算折腾出来了,在此记录一下. 首先既然要选择图片,我们就先要获取本地所有的图片,Android已经为我们封装好了该意图. 1 Intent intent = new Intent(Intent.ACTION_PICK, null);//从列表中选择某项并返回所有数据 2 intent.setDataAndType( 3 MediaStore.Images.Me

Android实现本地图片选择及预览缩放效果仿春雨医生

在做项目时经常会遇到选择本地图片的需求,以前都是懒得写直接调用系统方法来选择图片,但是这样并不能实现多选效果,最近又遇到了,所以还是写一个demo好了,以后也方便使用.还是首先来看看效果 显示的图片使用RecyclerView实现的,利用Glide来加载:下面弹出的图片文件夹效果是采用PopupWindow实现,这里比采用PopupWindow更方便,弹出显示的左边图片是这个文件夹里的第一张图片:选中的图片可以进行预览,使用网上一个大神写的来实现的:至于图片的获取是用ContentProvide

Android选择本地图片并裁剪工具类

先说用法 1.开始选图(相机.相册) //这句代码启动相机拍照 SelectPicUtil.getByCamera(this); //这句代码跳转到相册选图  SelectPicUtil.getByAlbum(this); 2.处理图片,在onActivityResult中处理 protected void onActivityResult(int requestCode, int resultCode, Intent data) {   //输出图片800*400大小,选择图片时的裁剪比例是2