Android应用中使用ContentProvider扫描本地图片并显示

Android应用中使用ContentProvider扫描本地图片并显示,第1张

概述之前群里面有朋友问我,有没有关于本地图片选择的Demo,类似微信的效果,他说网上没有这方面的Demo,问我能不能写一篇关于这个效果的Demo,于是我研究了下微信的本地图片选择的Demo,自己仿照的写了下分享给大家,希

之前群里面有朋友问我,有没有关于本地图片选择的Demo,类似微信的效果,他说网上没有这方面的Demo,问我能不能写一篇关于这个效果的Demo,于是我研究了下微信的本地图片选择的Demo,自己仿照的写了下分享给大家,希望对以后有这样子需求的朋友有一点帮助吧,主要使用的是ContentProvIDer扫描手机中的图片,并用GrIDVIEw将图片显示出来,关于GrIDVIEw和ListVIEw显示图片的问题,一直是一个很头疼的问题,因为我们手机的内存有限,手机给每个应用程序分配的内存也有限,所以图片多的情况下很容易伴随着OOM的发生,不过现在也有很多的开源的图片显示框架,对显示很多图片进行了优化,大家有兴趣的可以去了解了解,今天我的这篇文章使用的是LruCache这个类以及对图片进行相对应的裁剪,这样也可以尽量的避免OOM的发生,我们先看下微信的效果吧

接下来我们就来实现这些效果吧,首先我们新建一个项目,取名ImageScan
首先我们先看第一个界面吧,使用将手机中的图片扫描出来,然后根据图片的所在的文件夹将其分类出来,并显示所在文件夹里面的一张图片和文件夹中图片个数,我们根据界面元素(文件夹名, 文件夹图片个数,文件夹中的一张图片)使用一个实体对象ImageBean来封装这三个属性

package com.example.imagescan;  /**  * GrIDVIEw的每个item的数据对象  *  * @author len  *  */ public class ImageBean{   /**    * 文件夹的第一张图片路径    */   private String topImagePath;   /**    * 文件夹名    */   private String foldername;    /**    * 文件夹中的图片数    */   private int imageCounts;      public String gettopImagePath() {     return topImagePath;   }   public voID settopImagePath(String topImagePath) {     this.topImagePath = topImagePath;   }   public String getFoldername() {     return foldername;   }   public voID setFoldername(String foldername) {     this.foldername = foldername;   }   public int getimageCounts() {     return imageCounts;   }   public voID setimageCounts(int imageCounts) {     this.imageCounts = imageCounts;   }    } 

接下来就是主界面的布局啦,上面的导航栏我没有加进去,只有下面的GrIDVIEw,所以说主界面布局中只有一个GrIDVIEw

<relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"   xmlns:tools="http://schemas.androID.com/tools"   androID:layout_wIDth="match_parent"   androID:layout_height="match_parent" >    <GrIDVIEw     androID:ID="@+ID/main_grID"     androID:layout_wIDth="match_parent"     androID:layout_height="match_parent"     androID:ListSelector="@androID:color/transparent"     androID:cachecolorHint="@androID:color/transparent"     androID:stretchMode="columnWIDth"     androID:horizontalSpacing="20dip"     androID:gravity="center"     androID:verticalSpacing="20dip"     androID:columnWIDth="90dip"     androID:numColumns="2" >   </GrIDVIEw>  </relativeLayout> 

接下来就是GrIDVIEw的Item的布局,看上面的图也行你会认为他的效果是2张图片添加的效果,其实不是,后面的叠加效果只是一张背景图片而已,代码先贴上来

<?xml version="1.0" enCoding="UTF-8"?> <relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"   androID:layout_wIDth="fill_parent"   androID:layout_height="wrap_content" >    <FrameLayout     androID:ID="@+ID/framelayout"     androID:layout_wIDth="fill_parent"     androID:layout_height="wrap_content" >      <com.example.imagescan.MyImageVIEw       androID:ID="@+ID/group_image"       androID:background="@drawable/albums_bg"       androID:src="@drawable/frIEnds_sends_pictures_no"       androID:paddingleft="20dip"       androID:paddingRight="20dip"       androID:paddingtop="18dip"       androID:paddingBottom="30dip"       androID:scaleType="fitXY"       androID:layout_wIDth="fill_parent"       androID:layout_height="150dip" />      <TextVIEw       androID:ID="@+ID/group_count"       androID:layout_wIDth="wrap_content"       androID:layout_height="wrap_content"       androID:background="@drawable/albums_icon_bg"       androID:gravity="center"       androID:layout_marginBottom="10dip"       androID:text="5"       androID:layout_gravity="bottom|center_horizontal" />   </FrameLayout>    <TextVIEw     androID:ID="@+ID/group_Title"     androID:layout_wIDth="fill_parent"     androID:layout_height="wrap_content"     androID:gravity="center"     androID:layout_below="@ID/framelayout"     androID:layout_centerHorizontal="true"     androID:ellipsize="end"     androID:singleline="true"     androID:text="Camera"     androID:textSize="16sp" />  </relativeLayout> 

看到上面的布局代码,也行你已经发现了,上面使用的是自定义的MyImageVIEw,我先不说这个自定义MyImageVIEw的作用,待会再给大家说,我们继续看代码
第一个界面的主要代码

package com.example.imagescan;  import java.io.file; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map;  import androID.app.Activity; import androID.app.ProgressDialog; import androID.content.ContentResolver; import androID.content.Intent; import androID.database.Cursor; import androID.net.Uri; import androID.os.Bundle; import androID.os.Handler; import androID.os.Message; import androID.provIDer.MediaStore; import androID.vIEw.VIEw; import androID.Widget.AdapterVIEw; import androID.Widget.AdapterVIEw.OnItemClickListener; import androID.Widget.GrIDVIEw; public class MainActivity extends Activity {   private HashMap<String,List<String>> mGruopMap = new HashMap<String,List<String>>();   private List<ImageBean> List = new ArrayList<ImageBean>();   private final static int SCAN_OK = 1;   private ProgressDialog mProgressDialog;   private GroupAdapter adapter;   private GrIDVIEw mGroupGrIDVIEw;      private Handler mHandler = new Handler(){      @OverrIDe     public voID handleMessage(Message msg) {       super.handleMessage(msg);       switch (msg.what) {       case SCAN_OK:         //关闭进度条         mProgressDialog.dismiss();                  adapter = new GroupAdapter(MainActivity.this,List = subGroupOfImage(mGruopMap),mGroupGrIDVIEw);         mGroupGrIDVIEw.setAdapter(adapter);         break;       }     }        };    @OverrIDe   protected voID onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentVIEw(R.layout.activity_main);          mGroupGrIDVIEw = (GrIDVIEw) findVIEwByID(R.ID.main_grID);          getimages();          mGroupGrIDVIEw.setonItemClickListener(new OnItemClickListener() {        @OverrIDe       public voID onItemClick(AdapterVIEw<?> parent,VIEw vIEw,int position,long ID) {         List<String> childList = mGruopMap.get(List.get(position).getFoldername());                  Intent mIntent = new Intent(MainActivity.this,ShowImageActivity.class);         mIntent.putStringArrayListExtra("data",(ArrayList<String>)childList);         startActivity(mIntent);                }     });        } 

  /**    * 利用ContentProvIDer扫描手机中的图片,此方法在运行在子线程中    */   private voID getimages() {     //显示进度条     mProgressDialog = ProgressDialog.show(this,null,"正在加载...");          new Thread(new Runnable() {              @OverrIDe       public voID run() {         Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;         ContentResolver mContentResolver = MainActivity.this.getContentResolver();          //只查询jpeg和png的图片         Cursor mCursor = mContentResolver.query(mImageUri,MediaStore.Images.Media.MIME_TYPE + "=? or "                 + MediaStore.Images.Media.MIME_TYPE + "=?",new String[] { "image/jpeg","image/png" },MediaStore.Images.Media.DATE_MODIFIED);                  if(mCursor == null){           return;         }                  while (mCursor.movetoNext()) {           //获取图片的路径           String path = mCursor.getString(mCursor               .getColumnIndex(MediaStore.Images.Media.DATA));                      //获取该图片的父路径名           String parentname = new file(path).getParentfile().getname();                       //根据父路径名将图片放入到mGruopMap中           if (!mGruopMap.containsKey(parentname)) {             List<String> chileList = new ArrayList<String>();             chileList.add(path);             mGruopMap.put(parentname,chileList);           } else {             mGruopMap.get(parentname).add(path);           }         }                  //通知Handler扫描图片完成         mHandler.sendEmptyMessage(SCAN_OK);         mCursor.close();       }     }).start();        } 
      
  /**    * 组装分组界面GrIDVIEw的数据源,因为我们扫描手机的时候将图片信息放在HashMap中    * 所以需要遍历HashMap将数据组装成List    *    * @param mGruopMap    * @return    */   private List<ImageBean> subGroupOfImage(HashMap<String,List<String>> mGruopMap){     if(mGruopMap.size() == 0){       return null;     }     List<ImageBean> List = new ArrayList<ImageBean>();          Iterator<Map.Entry<String,List<String>>> it = mGruopMap.entrySet().iterator();     while (it.hasNext()) {       Map.Entry<String,List<String>> entry = it.next();       ImageBean mImageBean = new ImageBean();       String key = entry.getKey();       List<String> value = entry.getValue();              mImageBean.setFoldername(key);       mImageBean.setimageCounts(value.size());       mImageBean.settopImagePath(value.get(0));//获取该组的第一张图片              List.add(mImageBean);     }          return List;        }   } 
首先看getimages()这个方法,该方法是使用ContentProvIDer将手机中的图片扫描出来,我这里只扫描了手机的外部存储中的图片,由于手机中可能存在很多的图片,扫描图片又比较耗时,所以我们在这里开启了子线程去获取图片,扫描的图片都存放在Cursor中,我们先要将图片按照文件夹进行分类,我们使用了HashMap来进行分类并将结果存储到mGruopMap(Key是文件夹名,Value是文件夹中的图片路径的List)中,分类完了关闭Cursor并利用Handler来通知主线程
然后是subGroupOfImage()方法,改方法是将mGruopMap的数据组装到List中,在List中存放GrIDVIEw中的每个item的数据对象ImageBean,遍历HashMap对象,具体的逻辑看代码,之后就是给GrIDVIEw设置Adapter。
设置item点击事件,点击文件夹跳转到展示文件夹图片的Activity,我们需要传递每个文件夹中的图片的路径的集合
看GroupAdapter的代码之前,我们先看一个比较重要的类,本地图片加载器NativeImageLoader
package com.example.imagescan;  import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;  import androID.graphics.Bitmap; import androID.graphics.BitmapFactory; import androID.graphics.Point; import androID.os.Handler; import androID.os.Message; import androID.support.v4.util.LruCache;  /**  * 本地图片加载器,采用的是异步解析本地图片,单例模式利用getInstance()获取NativeImageLoader实例  * 调用loadNativeImage()方法加载本地图片,此类可作为一个加载本地图片的工具类  */ public class NativeImageLoader {   private LruCache<String,Bitmap> mMemoryCache;   private static NativeImageLoader mInstance = new NativeImageLoader();   private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1);         private NativeImageLoader(){     //获取应用程序的最大内存     final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);      //用最大内存的1/4来存储图片     final int cacheSize = maxMemory / 4;     mMemoryCache = new LruCache<String,Bitmap>(cacheSize) {              //获取每张图片的大小       @OverrIDe       protected int sizeOf(String key,Bitmap bitmap) {         return bitmap.getRowBytes() * bitmap.getHeight() / 1024;       }     };   }      /**    * 通过此方法来获取NativeImageLoader的实例    * @return    */   public static NativeImageLoader getInstance(){     return mInstance;   } 
      
      
  /**    * 加载本地图片,对图片不进行裁剪    * @param path    * @param mCallBack    * @return    */   public Bitmap loadNativeImage(final String path,final NativeImageCallBack mCallBack){     return this.loadNativeImage(path,mCallBack);   }      /**    * 此方法来加载本地图片,这里的mPoint是用来封装ImageVIEw的宽和高,我们会根据ImageVIEw控件的大小来裁剪Bitmap    * 如果你不想裁剪图片,调用loadNativeImage(final String path,final NativeImageCallBack mCallBack)来加载    * @param path    * @param mPoint    * @param mCallBack    * @return    */   public Bitmap loadNativeImage(final String path,final Point mPoint,final NativeImageCallBack mCallBack){     //先获取内存中的Bitmap     Bitmap bitmap = getBitmapFromMemCache(path);          final Handler mHander = new Handler(){        @OverrIDe       public voID handleMessage(Message msg) {         super.handleMessage(msg);         mCallBack.onImageLoader((Bitmap)msg.obj,path);       }            };          //若该Bitmap不在内存缓存中,则启用线程去加载本地的图片,并将Bitmap加入到mMemoryCache中     if(bitmap == null){       mImageThreadPool.execute(new Runnable() {                  @OverrIDe         public voID run() {           //先获取图片的缩略图           Bitmap mBitmap = decodeThumbBitmapForfile(path,mPoint == null ? 0: mPoint.x,mPoint == null ? 0: mPoint.y);           Message msg = mHander.obtainMessage();           msg.obj = mBitmap;           mHander.sendMessage(msg);                      //将图片加入到内存缓存           addBitmapToMemoryCache(path,mBitmap);         }       });     }     return bitmap;        }          /**    * 往内存缓存中添加Bitmap    *    * @param key    * @param bitmap    */   private voID addBitmapToMemoryCache(String key,Bitmap bitmap) {     if (getBitmapFromMemCache(key) == null && bitmap != null) {       mMemoryCache.put(key,bitmap);     }   }    /**    * 根据key来获取内存中的图片    * @param key    * @return    */   private Bitmap getBitmapFromMemCache(String key) {     return mMemoryCache.get(key);   }         /**    * 根据VIEw(主要是ImageVIEw)的宽和高来获取图片的缩略图    * @param path    * @param vIEwWIDth    * @param vIEwHeight    * @return    */   private Bitmap decodeThumbBitmapForfile(String path,int vIEwWIDth,int vIEwHeight){     BitmapFactory.Options options = new BitmapFactory.Options();     //设置为true,表示解析Bitmap对象,该对象不占内存     options.inJustDecodeBounds = true;     BitmapFactory.decodefile(path,options);     //设置缩放比例     options.inSampleSize = computeScale(options,vIEwWIDth,vIEwHeight);          //设置为false,解析Bitmap对象加入到内存中     options.inJustDecodeBounds = false;          return BitmapFactory.decodefile(path,options);   }         /**    * 根据VIEw(主要是ImageVIEw)的宽和高来计算Bitmap缩放比例。默认不缩放    * @param options    * @param wIDth    * @param height    */   private int computeScale(BitmapFactory.Options options,int vIEwHeight){     int inSampleSize = 1;     if(vIEwWIDth == 0 || vIEwWIDth == 0){       return inSampleSize;     }     int bitmapWIDth = options.outWIDth;     int bitmapHeight = options.outHeight;          //假如Bitmap的宽度或高度大于我们设定图片的VIEw的宽高,则计算缩放比例     if(bitmapWIDth > vIEwWIDth || bitmapHeight > vIEwWIDth){       int wIDthScale = Math.round((float) bitmapWIDth / (float) vIEwWIDth);       int heightScale = Math.round((float) bitmapHeight / (float) vIEwWIDth);              //为了保证图片不缩放变形,我们取宽高比例最小的那个       inSampleSize = wIDthScale < heightScale ? wIDthScale : heightScale;     }     return inSampleSize;   }         /**    * 加载本地图片的回调接口    *    * @author xiaanming    *    */   public interface NativeImageCallBack{     /**      * 当子线程加载完了本地的图片,将Bitmap和图片路径回调在此方法中      * @param bitmap      * @param path      */     public voID onImageLoader(Bitmap bitmap,String path);   } } 
该类是一个单例类,提供了本地图片加载,内存缓存,裁剪等逻辑,该类在加载本地图片的时候采用的是异步加载的方式,对于大图片的加载也是比较耗时的,所以采用子线程的方式去加载,对于图片的缓存机制使用的是LruCache,使用手机分配给应用程序内存的1/4用来缓存图片,除了使用LruCache缓存图片之外,还对图片进行了裁剪,举个很简单的例子,假如我们的控件大小是100 * 100, 而我们的图片是400*400,我们加载这么大的图片需要很多的内存,所以我们采用了图片裁剪,根据控件的大小来确定图片的裁剪比例,从而减小内存的消耗,提高GrIDVIEw滑动的流畅度,介绍里面几个比较重要的方法
computeScale()计算图片需要裁剪的比例,根据控件的大小和图片的大小确定比例,如果图片比控件大,我们就进行裁剪,否则不需要。
decodeThumbBitmapForfile()方法是根据计算好了图片裁剪的比例之后从文件中加载图片,我们先设置options.inJustDecodeBounds = true表示解析不占用内存,但是我们能获取图片的具体大小,利用computeScale()计算好比例,在将options.inJustDecodeBounds=false,再次解析Bitmap,这样子就对图片进行了裁剪。
loadNativeImage(final String path,final NativeImageCallBack mCallBack)我们在客户端只需要调用该方法就能获取到Bitmap对象,里面的具体逻辑是先判断内存缓存LruCache中是否存在该Bitmap,不存在就开启子线程去读取,为了方便管理加载本地图片线程,这里使用了线程池,池中只能容纳一个线程,读取完了本地图片先将Bitmap加入到LruCache中,保存的Key为图片路径,然后再使用Handler通知主线程图片加载好了,之后将Bitmap和路径回调到方法onImageLoader(Bitmap bitmap,String path)中,该方法的mPoint是用来封装控件的宽和高的对象
如果不对图片进行裁剪直接这个方法的重载方法loadNativeImage(final String path,final NativeImageCallBack mCallBack) 就行了,逻辑是一样的,只是这个方法不对图片进行裁剪
接下来就是GrIDVIEw的Adapter类的代码
package com.example.imagescan;  import java.util.List;  import androID.content.Context; import androID.graphics.Bitmap; import androID.graphics.Point; import androID.vIEw.LayoutInflater; import androID.vIEw.VIEw; import androID.vIEw.VIEwGroup; import androID.Widget.BaseAdapter; import androID.Widget.GrIDVIEw; import androID.Widget.ImageVIEw; import androID.Widget.TextVIEw;  import com.example.imagescan.MyImageVIEw.OnMeasureListener; import com.example.imagescan.NativeImageLoader.NativeImageCallBack;  public class GroupAdapter extends BaseAdapter{   private List<ImageBean> List;   private Point mPoint = new Point(0,0);//用来封装ImageVIEw的宽和高的对象   private GrIDVIEw mGrIDVIEw;   protected LayoutInflater mInflater;      @OverrIDe   public int getCount() {     return List.size();   }    @OverrIDe   public Object getItem(int position) {     return List.get(position);   }     @OverrIDe   public long getItemID(int position) {     return position;   }      public GroupAdapter(Context context,List<ImageBean> List,GrIDVIEw mGrIDVIEw){     this.List = List;     this.mGrIDVIEw = mGrIDVIEw;     mInflater = LayoutInflater.from(context);   }       @OverrIDe   public VIEw getVIEw(int position,VIEw convertVIEw,VIEwGroup parent) {     final VIEwHolder vIEwHolder;     ImageBean mImageBean = List.get(position);     String path = mImageBean.gettopImagePath();     if(convertVIEw == null){       vIEwHolder = new VIEwHolder();       convertVIEw = mInflater.inflate(R.layout.grID_group_item,null);       vIEwHolder.mImageVIEw = (MyImageVIEw) convertVIEw.findVIEwByID(R.ID.group_image);       vIEwHolder.mTextVIEwTitle = (TextVIEw) convertVIEw.findVIEwByID(R.ID.group_Title);       vIEwHolder.mTextVIEwCounts = (TextVIEw) convertVIEw.findVIEwByID(R.ID.group_count);              //用来监听ImageVIEw的宽和高       vIEwHolder.mImageVIEw.setonMeasureListener(new OnMeasureListener() {                  @OverrIDe         public voID onMeasureSize(int wIDth,int height) {           mPoint.set(wIDth,height);         }       });              convertVIEw.setTag(vIEwHolder);     }else{       vIEwHolder = (VIEwHolder) convertVIEw.getTag();       vIEwHolder.mImageVIEw.setimageResource(R.drawable.frIEnds_sends_pictures_no);     }          vIEwHolder.mTextVIEwTitle.setText(mImageBean.getFoldername());     vIEwHolder.mTextVIEwCounts.setText(Integer.toString(mImageBean.getimageCounts()));     //给ImageVIEw设置路径Tag,这是异步加载图片的小技巧     vIEwHolder.mImageVIEw.setTag(path);               //利用NativeImageLoader类加载本地图片     Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path,mPoint,new NativeImageCallBack() {              @OverrIDe       public voID onImageLoader(Bitmap bitmap,String path) {         ImageVIEw mImageVIEw = (ImageVIEw) mGrIDVIEw.findVIEwWithTag(path);         if(bitmap != null && mImageVIEw != null){           mImageVIEw.setimageBitmap(bitmap);         }       }     });          if(bitmap != null){       vIEwHolder.mImageVIEw.setimageBitmap(bitmap);     }else{       vIEwHolder.mImageVIEw.setimageResource(R.drawable.frIEnds_sends_pictures_no);     }               return convertVIEw;   }            public static class VIEwHolder{     public MyImageVIEw mImageVIEw;     public TextVIEw mTextVIEwTitle;     public TextVIEw mTextVIEwCounts;   }     } 
首先我们将每个item的图片路径设置Tag到该ImageVIEw上面,然后利用NativeImageLoader来加载本地图片,但是我们显示的图片的宽和高可能远大于GirdVIEw item中ImageVIEw的大小,于是为了节省内存,我们需要对图片进行裁剪,需要对图片裁剪我们利用loadNativeImage(final String path,final NativeImageCallBack mCallBack)方法,我们就必须要获取ImageVIEw的宽和高了
但是我们想在getVIEw()中获取ImageVIEw的宽和高存在问题,在getVIEw()里面刚开始显示item的时候利用ImageVIEw.getWIDth() 获取的都是0,为什么刚开始获取不到宽和高呢,因为我们使用LayoutInflater来将XML布局文件Inflater()成VIEw的时候,VIEw并没有显示在界面上面,表明并没有对VIEw进行onMeasure(),onLayout(),onDraw()等 *** 作,必须等到retrue convertVIEw的时候,表示该item对应的VIEw已经绘制在ListVIEw的位置上了, 此时才对item对应的VIEw进行onMeasure(),onDraw()等 *** 作,这时候才能获取到Item的宽和高,于是我想到了自定义ImageVIEw,在onMeasure()中利用回调的模式主动通知我ImageVIEw测量的宽和高,但是这有一个小小的问题,就是显示GrIDVIEw的第一个item的时候,获取的宽和高还是0,第二个就能正常获取了,第一个宽和高为0,表示我们不对第一张图片进行裁剪而已,在效率上也没啥问题,不知道大家有没有好的方法,可以在getVIEw()中获取Item中某个控件的宽和高。

自定义MyImageVIEw的代码,我们只需要设置OnMeasureListener监听,当MyImageVIEw测量完毕之后,就会将测量的宽和高回调到onMeasureSize()中,然后我们可以根据MyImageVIEw的大小来裁剪图片

package com.example.imagescan;  import androID.content.Context; import androID.util.AttributeSet; import androID.Widget.ImageVIEw;  public class MyImageVIEw extends ImageVIEw {   private OnMeasureListener onMeasureListener;      public voID setonMeasureListener(OnMeasureListener onMeasureListener) {     this.onMeasureListener = onMeasureListener;   }    public MyImageVIEw(Context context,AttributeSet attrs) {     super(context,attrs);   }    public MyImageVIEw(Context context,AttributeSet attrs,int defStyle) {     super(context,attrs,defStyle);   }    @OverrIDe   protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec) {     super.onMeasure(wIDthMeasureSpec,heightmeasureSpec);          //将图片测量的大小回调到onMeasureSize()方法中     if(onMeasureListener != null){       onMeasureListener.onMeasureSize(getMeasureDWIDth(),getMeasuredHeight());     }   }    public interface OnMeasureListener{     public voID onMeasureSize(int wIDth,int height);   }    } 

上面这些代码就完成了第一个界面的功能了,接下来就是点击GrIDVIEw的item跳转另一个界面来显示该文件夹下面的所有图片,功能跟第一个界面差不多,也是使用GrIDVIEw来显示图片,第二个界面的布局代码我就不贴了,直接贴上界面的代码

package com.example.imagescan;  import java.util.List;  import androID.app.Activity; import androID.os.Bundle; import androID.Widget.GrIDVIEw; import androID.Widget.Toast;  public class ShowImageActivity extends Activity {   private GrIDVIEw mGrIDVIEw;   private List<String> List;   private ChildAdapter adapter;    @OverrIDe   protected voID onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentVIEw(R.layout.show_image_activity);          mGrIDVIEw = (GrIDVIEw) findVIEwByID(R.ID.child_grID);     List = getIntent().getStringArrayListExtra("data");          adapter = new ChildAdapter(this,List,mGrIDVIEw);     mGrIDVIEw.setAdapter(adapter);        }    @OverrIDe   public voID onBackpressed() {     Toast.makeText(this,"选中 " + adapter.getSelectItems().size() + " item",Toast.LENGTH_LONG).show();     super.onBackpressed();   }       } 

GrIDVIEw的item上面一个我们自定义的MyImageVIEw用来显示图片,另外还有一个CheckBox来记录我们选中情况,Adapter的代码如下

package com.example.imagescan;  import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map;  import androID.content.Context; import androID.graphics.Bitmap; import androID.graphics.Point; import androID.vIEw.LayoutInflater; import androID.vIEw.VIEw; import androID.vIEw.VIEwGroup; import androID.Widget.BaseAdapter; import androID.Widget.CheckBox; import androID.Widget.Compoundbutton; import androID.Widget.ImageVIEw; import androID.Widget.Compoundbutton.OnCheckedchangelistener; import androID.Widget.GrIDVIEw;  import com.example.imagescan.MyImageVIEw.OnMeasureListener; import com.example.imagescan.NativeImageLoader.NativeImageCallBack; import com.nineoldandroIDs.animation.AnimatorSet; import com.nineoldandroIDs.animation.ObjectAnimator;  public class ChildAdapter extends BaseAdapter {   private Point mPoint = new Point(0,0);//用来封装ImageVIEw的宽和高的对象   /**    * 用来存储图片的选中情况    */   private HashMap<Integer,Boolean> mSelectMap = new HashMap<Integer,Boolean>();   private GrIDVIEw mGrIDVIEw;   private List<String> List;   protected LayoutInflater mInflater;    public ChildAdapter(Context context,List<String> List,GrIDVIEw mGrIDVIEw) {     this.List = List;     this.mGrIDVIEw = mGrIDVIEw;     mInflater = LayoutInflater.from(context);   }      @OverrIDe   public int getCount() {     return List.size();   }    @OverrIDe   public Object getItem(int position) {     return List.get(position);   }     @OverrIDe   public long getItemID(int position) {     return position;   }      @OverrIDe   public VIEw getVIEw(final int position,VIEwGroup parent) {     final VIEwHolder vIEwHolder;     String path = List.get(position);          if(convertVIEw == null){       convertVIEw = mInflater.inflate(R.layout.grID_child_item,null);       vIEwHolder = new VIEwHolder();       vIEwHolder.mImageVIEw = (MyImageVIEw) convertVIEw.findVIEwByID(R.ID.child_image);       vIEwHolder.mCheckBox = (CheckBox) convertVIEw.findVIEwByID(R.ID.child_checkBox);              //用来监听ImageVIEw的宽和高       vIEwHolder.mImageVIEw.setonMeasureListener(new OnMeasureListener() {                  @OverrIDe         public voID onMeasureSize(int wIDth,height);         }       });              convertVIEw.setTag(vIEwHolder);     }else{       vIEwHolder = (VIEwHolder) convertVIEw.getTag();       vIEwHolder.mImageVIEw.setimageResource(R.drawable.frIEnds_sends_pictures_no);     }     vIEwHolder.mImageVIEw.setTag(path);     vIEwHolder.mCheckBox.setonCheckedchangelistener(new OnCheckedchangelistener() {              @OverrIDe       public voID onCheckedChanged(Compoundbutton buttonVIEw,boolean isChecked) {         //如果是未选中的CheckBox,则添加动画         if(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){           addAnimation(vIEwHolder.mCheckBox);         }         mSelectMap.put(position,isChecked);       }     });          vIEwHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : false);          //利用NativeImageLoader类加载本地图片     Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path,String path) {         ImageVIEw mImageVIEw = (ImageVIEw) mGrIDVIEw.findVIEwWithTag(path);         if(bitmap != null && mImageVIEw != null){           mImageVIEw.setimageBitmap(bitmap);         }       }     });          if(bitmap != null){       vIEwHolder.mImageVIEw.setimageBitmap(bitmap);     }else{       vIEwHolder.mImageVIEw.setimageResource(R.drawable.frIEnds_sends_pictures_no);     }          return convertVIEw;   }      /**    * 给CheckBox加点击动画,利用开源库nineoldandroIDs设置动画    * @param vIEw    */   private voID addAnimation(VIEw vIEw){     float [] vaules = new float[]{0.5f,0.6f,0.7f,0.8f,0.9f,1.0f,1.1f,1.2f,1.3f,1.25f,1.15f,1.0f};     AnimatorSet set = new AnimatorSet();     set.playTogether(ObjectAnimator.offloat(vIEw,"scaleX",vaules),ObjectAnimator.offloat(vIEw,"scaleY",vaules));         set.setDuration(150);     set.start();   }         /**    * 获取选中的Item的position    * @return    */   public List<Integer> getSelectItems(){     List<Integer> List = new ArrayList<Integer>();     for(Iterator<Map.Entry<Integer,Boolean>> it = mSelectMap.entrySet().iterator(); it.hasNext();){       Map.Entry<Integer,Boolean> entry = it.next();       if(entry.getValue()){         List.add(entry.getKey());       }     }          return List;   }         public static class VIEwHolder{     public MyImageVIEw mImageVIEw;     public CheckBox mCheckBox;   }   } 

第二个界面的Adapter跟第一个界面差不多,无非多了一个CheckBox用来记录图片选择情况,我们只需要对CheckBox设置setonCheckedchangelistener监听,微信的选中之后CheckBox有一个动画效果,所以我利用nineoldandroIDs动画库也给CheckBox加了一个动画效果,直接调用addAnimation()方法就能添加了,getSelectItems()方法就能获取我们选中的item的position了,知道了选中的position,其他的信息就都知道了,微信有对图片进行预览的功能,我这里就不添加了,如果有这个需求可以自行添加,给大家推荐一个https://github.com/chrisbanes/PhotoVIEw

运行项目,效果如下

看起来还不错吧,采用的是异步读取图片,对图片进行了缓存和裁剪,使得在显示本地图片方面比较流畅,GrIDVIEw滑动也挺流畅的,也有效的避免OOM的产生。

总结

以上是内存溢出为你收集整理的Android应用中使用ContentProvider扫描本地图片并显示全部内容,希望文章能够帮你解决Android应用中使用ContentProvider扫描本地图片并显示所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1141666.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-31
下一篇2022-05-31

发表评论

登录后才能评论

评论列表(0条)

    保存