转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/18730223),请尊重他人的辛勤劳动成果,谢谢!

写这篇文章之前,先简单说几句,首先是先恭喜下自己获得了2013年的博客之星称号,很意外也很开心,自己是从2013年开始写博客,那时候也不知道怎么写,我从小就不喜欢写日记,作文什么的,所以刚开始都是贴代码,也没有人看,后面慢慢的,写的文章被推荐博客首页和CSDN首页(这里也要小小的感谢下小编MM),访问量逐渐的多了起来,有更多的人看我的文章,这也使自己有了继续写文章的动力,也希望我写的东西对大家有点帮助吧,在2014年我会继续在CSDN上面写博客,然后是感谢博客之星给我投票支持我的朋友们,谢谢你们支持我的每一票,最后就是2014春节马上就到了,提前祝福大家新年快乐,工作顺利,事事顺心!

回到主题,之前群里面有朋友问我,有没有关于本地图片选择的Demo,类似微信的效果,他说网上没有这方面的Demo,问我能不能写一篇关于这个效果的Demo,于是我研究了下微信的本地图片选择的Demo,自己仿照的写了下分享给大家,希望对以后有这样子需求的朋友有一点帮助吧,主要使用的是ContentProvider扫描手机中的图片,并用GridView将图片显示出来,关于GridView和ListView显示图片的问题,一直是一个很头疼的问题,因为我们手机的内存有限,手机给每个应用程序分配的内存也有限,所以图片多的情况下很容易伴随着OOM的发生,不过现在也有很多的开源的图片显示框架,对显示很多图片进行了优化,大家有兴趣的可以去了解了解,今天我的这篇文章使用的是LruCache这个类(之前写了一篇使用LruCache加载网络图片的Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅)以及对图片进行相对应的裁剪,这样也可以尽量的避免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" ><GridViewandroid: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" ><FrameLayoutandroid:id="@+id/framelayout"android:layout_width="fill_parent"android:layout_height="wrap_content" ><com.example.imagescan.MyImageViewandroid: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" /><TextViewandroid: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><TextViewandroid: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;
/*** @blog http://blog.csdn.net/xiaanming* * @author xiaanming* **/
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(){@Overridepublic 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;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mGroupGridView = (GridView) findViewById(R.id.main_grid);getImages();mGroupGridView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic 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() {@Overridepublic 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()方法加载本地图片,此类可作为一个加载本地图片的工具类* * @blog http://blog.csdn.net/xiaanming* * @author xiaanming**/
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) {//获取每张图片的大小@Overrideprotected 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){//先获取内存中的BitmapBitmap bitmap = getBitmapFromMemCache(path);final Handler mHander = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);mCallBack.onImageLoader((Bitmap)msg.obj, path);}};//若该Bitmap不在内存缓存中,则启用线程去加载本地的图片,并将Bitmap加入到mMemoryCache中if(bitmap == null){mImageThreadPool.execute(new Runnable() {@Overridepublic 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滑动的流畅度,介绍里面几个比较重要的方法

  1. computeScale()计算图片需要裁剪的比例,根据控件的大小和图片的大小确定比例,如果图片比控件大,我们就进行裁剪,否则不需要。
  2. decodeThumbBitmapForFile()方法是根据计算好了图片裁剪的比例之后从文件中加载图片,我们先设置options.inJustDecodeBounds = true表示解析不占用内存,但是我们能获取图片的具体大小,利用computeScale()计算好比例,在将options.inJustDecodeBounds=false,再次解析Bitmap,这样子就对图片进行了裁剪。
  3. loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack)我们在客户端只需要调用该方法就能获取到Bitmap对象,里面的具体逻辑是先判断内存缓存LruCache中是否存在该Bitmap,不存在就开启子线程去读取,为了方便管理加载本地图片线程,这里使用了线程池,池中只能容纳一个线程,读取完了本地图片先将Bitmap加入到LruCache中,保存的Key为图片路径,然后再使用Handler通知主线程图片加载好了,之后将Bitmap和路径回调到方法onImageLoader(Bitmap bitmap, String path)中,该方法的mPoint是用来封装控件的宽和高的对象
  4. 如果不对图片进行裁剪直接这个方法的重载方法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;@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic 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);}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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()等操作,必须等到retrueconvertView的时候,表示该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);}@Overrideprotected 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;@Overrideprotected 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);}@Overridepublic 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);}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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的产生,工程中有些东西还没有贴完全,有兴趣的朋友可以下载Demo来运行一下,好了,今天的讲解到这里结束了,感谢大家观看,有疑问的朋友可以在下面留言,我会为大家解答的!

项目源码,点击下载

Android 使用ContentProvider扫描手机中的图片,仿微信显示本地图片效果相关推荐

  1. android 在 ListView 的 item 中插入 GridView 仿微信朋友圈图片显示。

    转载请声明出处(http://www.cnblogs.com/linguanh/) 先上张效果图: 1,思路简述 这个肯定是要重写 baseAdapter的了,这里我分了两个数据适配器,一个是自定义的 ...

  2. android 仿微信聊天气泡显示图片,仿微信聊天气泡 图片尖角 按下变暗

    实现微信气泡图片尖角 //-------------gen corner bitmap flow------------------------ //load the bg: .9.png which ...

  3. Android 使用开源库StickyGridHeaders来实现带sections和headers的GridView显示本地图片效果...

    转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/20481185),请尊重他人的辛勤劳动成果,谢谢! 大家好! ...

  4. fastreport打印指定路径图片显示不出来_报表工具中图片文件怎么展示---本地图片--网络图片--数据库图片...

    用于报表展示的图片来源有多种,如:数据库的图片字段.服务器本地图片.网络图片等,因此,报表工具也针对多种图片来源提供了多种多样的处理方式. 下面从不同的图片来源角度,举例介绍报表工具如何设置呈现. 图 ...

  5. 判断手机中是否安装了 微信 app

    判断手机中是否安装了微信, 有两个方法 1.获得android中,所有安装的app的列表 然后比对微信的包名进行比较,然后得出结论2.通过 微信提供的 api 方法进行判断是否有 微信安装 .// 是 ...

  6. 三星手机实现airtest自动化屏幕显示比例小或测试报告中操作的位置未显示在图片上

    三星手机实现airtest自动化屏幕显示比例小或测试报告中操作的位置未显示在图片上 1.三星手机连接到airtestIDE中只显示了很小一部分,如图所示: 2.测试报告中操作的位置未显示在图片上,如图 ...

  7. 在InternetExplorer.Application中显示本地图片

    忘记了,喜欢一个人的感觉 Demon's Blog  »  程序设计  »  在InternetExplorer.Application中显示本地图片 « 对VBS效率的再思考--处理二进制数据 Wo ...

  8. android微信点赞ui,Android中使用PopupWindow 仿微信点赞和评论弹出

    微信朋友圈的点赞和评论功能,有2个组成部分:左下角的"更多"按钮:点击该按钮后弹出的对话框: PopupWindow,弹出框使用PopupWindow实现,这是点赞和评论的载体,具 ...

  9. 成功解决wps文档的论文中插入图片时只显示一半图片(两步教程完美搞定!)

    成功解决wps文档的论文中插入图片时只显示一半图片(两步教程完美搞定!) 目录 解决问题 解决思路 解决方法 解决问题 解决wps文档的论文中插入图片时只显示一半图片,如图所示, 解决

最新文章

  1. Ember.js系列文章
  2. windows系统下的python环境的搭建
  3. 鸿蒙上海开发者日直播,华为鸿蒙 OS 开发者日于 4月17 日上海举行
  4. POJ-1067取石子游戏,威佐夫博弈范例题/NYOJ-161,主要在于这个黄金公式~~
  5. [js开源组件开发]图片放大镜
  6. android o 跨进程广播,[Android] Android O 广播限制
  7. python列表用来有序存放一组_python入门第二课------列表
  8. 甘蔗是怎么变成白糖的?
  9. 计算机网络-VRRP
  10. 软件加license的一种实现方法
  11. OpenCV-Python实战(番外篇)——OpenCV、NumPy和Matplotlib直方图比较
  12. 河北省应用计算机模拟考试,河北省职称计算机考试模拟系统
  13. github1s 油猴插件
  14. 数学建模三十六计——线性模型
  15. mysql压缩修复数据库_压缩修复Access数据库
  16. 2022 -7-18 第八小组 顾宇佳 学习笔记
  17. 中国证券金融股份有限公司的成立加速融资融券的发展
  18. 【echarts地图制作】下钻到乡镇/街道级别的
  19. 推荐一个好用的在线pdf压缩工具
  20. 一个软件公司需要多少前端_自己开发一个app需要多少钱,多少时间

热门文章

  1. vue.js的学习中的简单案例
  2. 深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer
  3. 教你动手做一个 iOS 越狱 app
  4. 2016年5月心情吧 233
  5. 利用第三方工具上传文件
  6. linux命令(44):sed,vim;去掉文件中的^M 符号,去掉行首空格和制表符
  7. asp.net生产环境和开发环境的错误日志包装策略
  8. Java千百问_01基本概念(003)_J2EE里面的2是什么意思
  9. Java字符串格式化记录
  10. ConcurrentHashMap(转)