Android获取本地图片缩略图终极解决方案

Posted 化作孤岛的瓜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android获取本地图片缩略图终极解决方案相关的知识,希望对你有一定的参考价值。

QAQ学android真的还是要在项目中获得锻炼,脱离实际一切都是耍流氓哼唧~!

花了一下午时间搞定了项目中要实现的:获取本地图片缩略图并显示在ListView上的,并且点击要能获得该图片文件路径功能,下面先上效果图:


作为一个新手,大概碰到这种需求的思路就是:

首先,递归遍历本地所有文件,然后按文件后缀名找出所有的图片文件,更好的方式是在媒体库里查找所有的图片(系统已经帮你过滤好了所有的图片文件直接去调用就阔以了),再通过得到的文件对象file显示图像。

当然这种处理结果就是大概1~2张图片就直接OOM了。(反正我手机里图片都是至少上MB的...)。

所以呢,必须对图片进行压缩,于是我又在网上找到了一个比较好的图片压缩方法(这里没有引用转载地址了,1是因为是昨天找到的现在已经找不到网址了,2是因为很多篇博客都是相同的方法相同的注释,我也不知道到底谁才是原作...):

/*    *//**
     * 根据指定的图像路径和大小来获取缩略图
     * 此方法有两点好处:
     *     1. 使用较小的内存空间,第一次获取的bitmap实际上为null,只是为了读取宽度和高度,
     *        第二次读取的bitmap是根据比例压缩过的图像,第三次读取的bitmap是所要的缩略图。
     *     2. 缩略图对于原图像来讲没有拉伸,这里使用了2.2版本的新工具ThumbnailUtils,使
     *        用这个工具生成的图像不会被拉伸。
     * @param imagePath 图像的路径
     * @param width 指定输出图像的宽度
     * @param height 指定输出图像的高度
     * @return 生成的缩略图
     *//*
    private Bitmap getImageThumbnail(String imagePath, int width, int height) 
        Bitmap bitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        // 获取这个图片的宽和高,注意此处的bitmap为null
        bitmap = BitmapFactory.decodeFile(imagePath, options);
        options.inJustDecodeBounds = false; // 设为 false
        // 计算缩放比
        int h = options.outHeight;
        int w = options.outWidth;
        int beWidth = w / width;
        int beHeight = h / height;
        int be = 1;
        if (beWidth < beHeight) 
            be = beWidth;
         else 
            be = beHeight;
        
        if (be <= 0) 
            be = 1;
        
        options.inSampleSize = be;
        // 重新读入图片,读取缩放后的bitmap,注意这次要把options.inJustDecodeBounds 设为 false
        bitmap = BitmapFactory.decodeFile(imagePath, options);
        // 利用ThumbnailUtils来创建缩略图,这里要指定要缩放哪个Bitmap对象
        bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
                ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
        return bitmap;
    */
方法很简单,然而可以发现这个方法其实是做了这么几件事情:

1、根据传入的图片路径构造bitmap,得到原图的宽高.

2、计算图片合适的缩放比.

3、利用ThumbnailUtils来创建缩略图,然后返回这个最终缩放以后的bitmap.

尽管像方法中说的那样:使用较小的内存空间,第一次获取的bitmap实际上为null,只是为了读取宽度和高度, 第二次读取的bitmap是根据比例压缩过的图像,第三次读取的bitmap是所要的缩略图。

然而整个方法还是开辟了3个bitmap对象的内存区域,这还不考虑ThumbnailUtils.extractThumbnail()方法所耗费的时空间。

所以当我使用了这个方法实现了在ListView中遍历本地图片的时候,上下滑起来是灰常卡滴(这里就不贴gif图了有兴趣想知道的朋友可以自己尝试一下)


Finaly,在踏破铁鞋无觅处之后,终于找到了最终解决方法,也是一开始忽略的方法:

原来一直不知道的Thumbnails类,才是解决问题的关键。

在Android系统中也有对应的thumbnails文件,下面是百度百科对它的描述:


然后在MediaStore媒体库类中,也是有Thumbnails这么一个内部类的:

 /**
         * This class allows developers to query and get two kinds of thumbnails:
         * MINI_KIND: 512 x 384 thumbnail
         * MICRO_KIND: 96 x 96 thumbnail
         */
        public static class Thumbnails implements BaseColumns 
            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) 
                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
            

            public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
                    String[] projection) 
                return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
            

            public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
                    String[] projection) 
                return cr.query(EXTERNAL_CONTENT_URI, projection,
                        IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
                        kind, null, null);
            

            /**
             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
             * interrupted and return immediately. Only the original process which made the getThumbnail
             * requests can cancel their own requests.
             *
             * @param cr ContentResolver
             * @param origId original image id
             */
            public static void cancelThumbnailRequest(ContentResolver cr, long origId) 
                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
                        InternalThumbnails.DEFAULT_GROUP_ID);
            

            /**
             * This method checks if the thumbnails of the specified image (origId) has been created.
             * It will be blocked until the thumbnails are generated.
             *
             * @param cr ContentResolver used to dispatch queries to MediaProvider.
             * @param origId Original image id associated with thumbnail of interest.
             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
             * @param options this is only used for MINI_KIND when decoding the Bitmap
             * @return A Bitmap instance. It could be null if the original image
             *         associated with origId doesn't exist or memory is not enough.
             */
            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
                    BitmapFactory.Options options) 
                return InternalThumbnails.getThumbnail(cr, origId,
                        InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
                        EXTERNAL_CONTENT_URI, false);
            

            /**
             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
             * interrupted and return immediately. Only the original process which made the getThumbnail
             * requests can cancel their own requests.
             *
             * @param cr ContentResolver
             * @param origId original image id
             * @param groupId the same groupId used in getThumbnail.
             */
            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) 
                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
            

            /**
             * This method checks if the thumbnails of the specified image (origId) has been created.
             * It will be blocked until the thumbnails are generated.
             *
             * @param cr ContentResolver used to dispatch queries to MediaProvider.
             * @param origId Original image id associated with thumbnail of interest.
             * @param groupId the id of group to which this request belongs
             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
             * @param options this is only used for MINI_KIND when decoding the Bitmap
             * @return A Bitmap instance. It could be null if the original image
             *         associated with origId doesn't exist or memory is not enough.
             */
            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
                    int kind, BitmapFactory.Options options) 
                return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
                        EXTERNAL_CONTENT_URI, false);
            

            /**
             * Get the content:// style URI for the image media table on the
             * given volume.
             *
             * @param volumeName the name of the volume to get the URI for
             * @return the URI to the image media table on the given volume
             */
            public static Uri getContentUri(String volumeName) 
                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
                        "/images/thumbnails");
            

            /**
             * The content:// style URI for the internal storage.
             */
            public static final Uri INTERNAL_CONTENT_URI =
                    getContentUri("internal");

            /**
             * The content:// style URI for the "primary" external storage
             * volume.
             */
            public static final Uri EXTERNAL_CONTENT_URI =
                    getContentUri("external");

            /**
             * The default sort order for this table
             */
            public static final String DEFAULT_SORT_ORDER = "image_id ASC";

            /**
             * The data stream for the thumbnail
             * <P>Type: DATA STREAM</P>
             */
            public static final String DATA = "_data";

            /**
             * The original image for the thumbnal
             * <P>Type: INTEGER (ID from Images table)</P>
             */
            public static final String IMAGE_ID = "image_id";

            /**
             * The kind of the thumbnail
             * <P>Type: INTEGER (One of the values below)</P>
             */
            public static final String KIND = "kind";

            public static final int MINI_KIND = 1;
            public static final int FULL_SCREEN_KIND = 2;
            public static final int MICRO_KIND = 3;
            /**
             * The blob raw data of thumbnail
             * <P>Type: DATA STREAM</P>
             */
            public static final String THUMB_DATA = "thumb_data";

            /**
             * The width of the thumbnal
             * <P>Type: INTEGER (long)</P>
             */
            public static final String WIDTH = "width";

            /**
             * The height of the thumbnail
             * <P>Type: INTEGER (long)</P>
             */
            public static final String HEIGHT = "height";
        
    
我们可以从中找到几个可用于Cursor查找的重要参数:

/**
 * The data stream for the thumbnail
 * <P>Type: DATA STREAM</P>
 */
public static final String DATA = "_data";

/**
 * The original image for the thumbnal
 * <P>Type: INTEGER (ID from Images table)</P>
 */
public static final String IMAGE_ID = "image_id";
/**
 * The content:// style URI for the "primary" external storage
 * volume.
 */
public static final Uri EXTERNAL_CONTENT_URI =
        getContentUri("external");
有了这三个参数,我们就可以很轻松从本地媒体库中获得图片缩略图的ID和路径。

        //先得到缩略图的URL和对应的图片id
        Cursor cursor = cr.query(
                Thumbnails.EXTERNAL_CONTENT_URI,
                new String[]
                        Thumbnails.IMAGE_ID,
                        Thumbnails.DATA
                ,
                null,
                null,
                null);

这里的缩略图ID有什么用呢?我们从它的注释中可以很明显地得到:

      /**
             * The original image for the thumbnal
             * <P>Type: INTEGER (ID from Images table)</P>

ID from Images table!!!这个ID是跟多媒体库中的images表的ID相对应的,由此,我们可以通过这个id来设置cursor的查找条件,从而找出images表中对应的真正的图片文件的路径!

从而完美地实现了文章开头的功能需求。

下面是完整的代码:

获得一个HashMap参数的ArrayList,HashMap项的键"thumbnail_path"对应真实图片路径值,键"image_id_path"对应缩略图路径值,有了这两个路径,想干嘛干嘛了2333

/**
     * 得到本地图片文件
     * @param context
     * @return
     */
    public static ArrayList<HashMap<String,String>> getAllPictures(Context context) 
        ArrayList<HashMap<String,String>> picturemaps = new ArrayList<>();
        HashMap<String,String> picturemap;
        ContentResolver cr = context.getContentResolver();
        //先得到缩略图的URL和对应的图片id
        Cursor cursor = cr.query(
                Thumbnails.EXTERNAL_CONTENT_URI,
                new String[]
                        Thumbnails.IMAGE_ID,
                        Thumbnails.DATA
                ,
                null,
                null,
                null);
        if (cursor.moveToFirst()) 
            do 
                picturemap = new HashMap<>();
                picturemap.put("image_id_path",cursor.getInt(0)+"");
                picturemap.put("thumbnail_path",cursor.getString(1));
                picturemaps.add(picturemap);
             while (cursor.moveToNext());
            cursor.close();
        
        //再得到正常图片的path
        for (int i = 0;i<picturemaps.size();i++) 
            picturemap = picturemaps.get(i);
            String media_id = picturemap.get("image_id_path");
            cursor = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    new String[]
                            MediaStore.Images.Media.DATA
                    ,
                    MediaStore.Audio.Media._ID+"="+media_id,
                    null,
                    null
            );
            if (cursor.moveToFirst()) 
                do 
                    picturemap.put("image_id",cursor.getString(0));
                    picturemaps.set(i,picturemap);
                 while (cursor.moveToNext());
                cursor.close();
            
        
        return picturemaps;
    





以上是关于Android获取本地图片缩略图终极解决方案的主要内容,如果未能解决你的问题,请参考以下文章

Android获取(网络和本地)视频缩略图

Android向系统相册中插入图片,相册中会出现两张 一样的图片(只是图片大小不一致)

如何获取图片网络链接啊?

Android 调用系统相机拍照后 添加文字水印

融云发送不显示缩略图解决方法

高仿QQ微信效果的图片浏览器(支持原图和缩略图多种手势CocoaPods)