当前位置: 移动技术网 > 移动技术>移动开发>Android > Android应用中使用ContentProvider扫描本地图片并显示

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

2019年07月24日  | 移动技术网移动技术  | 我要评论

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

201645151626071.png (480×854)

201645151655089.png (480×854)

接下来我们就来实现这些效果吧,首先我们新建一个项目,取名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, null, 
            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, null, 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 viewwidth, 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 point mpoint, 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 point mpoint, 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(), onlayout(), 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, view convertview, 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, int height) { 
          mpoint.set(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, 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; 
  } 
   
  /** 
   * 给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.2f, 1.15f, 1.1f, 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的产生。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网