仿微信查看系统图片缩略图选择多张图片

        最近做的项目有用到查看系统图片并选择多张图片的功能,网上搜索了一些源码运行起来,拖动一下缩略图列表就卡住了,都是没有考虑到OOM的原因。结合网上的一些LRUCACHE和ASYNCTASK资料,自己写了个小组件。最开始通过读取系统thumbnail(缩略图)表获取缩略图,但是有的缩略图图片是旋转过的,又读不到旋转的角度,而原始图片可以读到旋转角度,所以只有先读取原始图片,裁剪成缩略图。此处读的是Camera文件夹的图片,还可以像微信图片那样扩展,按文件夹分类(图片、视频、截屏等),在此抛砖引玉,欢迎各位雅正。

         先获取所有原始图片路径。

/* 获取本地所有图片路径 */
	private void getAllPaths() {
		if (!DeviceUtil.isSDCardMounted()) {
			Toast.makeText(this, "SD卡没有正常挂载,无法读取图片!", Toast.LENGTH_SHORT)
					.show();
			return;
		}
		mProgressDialog = ProgressDialog.show(this, null, "正在扫描图片,请稍候...");
		new Thread(new Runnable() {

			@Override
			public void run() {
				Cursor cursor = null;
				try {
					String[] proj = { MediaStore.Images.Media.DATA };
					String where = "mime_type in ('image/png','image/jpeg')";
					String sortOrder = Media.DATE_MODIFIED;
					cursor = mContentResolver.query(
							MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj,
							where, null, sortOrder + " DESC");
					if (cursor == null || !cursor.moveToFirst()) {
						return;
					}
					do {
						int column_index = cursor
								.getColumnIndexOrThrow(proj[0]);
						String imagePath = cursor.getString(column_index);
						imagePaths.add(imagePath);// 图片路径
					} while (cursor.moveToNext());
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					if (cursor != null) {
						cursor.close();
					}
					mHandler.sendEmptyMessage(SCAN_OK);
				}
			}

		}).start();

	}
       LruCaheUtil类

public class LruCacheUtil {
	/**
	 * 缓存Image的类,当存储Image的大小大于LruCache设定的值,系统自动释放内存
	 */
	private LruCache<String, Bitmap> mMemoryCache;

	public LruCacheUtil(Context context) {
		// 获取系统分配给每个应用程序的最大内存,每个应用系统分配32M
		int maxMemory = (int) Runtime.getRuntime().maxMemory();	
		int mCacheSize = maxMemory / 8;
		// 给LruCache分配1/8 4M
		mMemoryCache = new LruCache<String, Bitmap>(mCacheSize) {

			// 必须重写此方法,来测量Bitmap的大小
			@Override
			protected int sizeOf(String key, Bitmap value) {
				return value.getRowBytes() * value.getHeight();
			}
		};
	}

	/**
	 * 移除图片缓存
	 * 
	 * @param key
	 */
	public synchronized void removeImageCache(String key) {
		if (key != null) {
			if (mMemoryCache != null) {
				Log.i("LruCacheUtils", "移除的图片名称" + key);
				Bitmap bm = mMemoryCache.remove(key);
				if (bm != null)
					bm.recycle();
			}
		}
	}

	/**
	 * 清除缓存
	 */
	public void clearCache() {
		if (mMemoryCache != null) {
			if (mMemoryCache.size() > 0) {

				Log.d("LruCacheUtils", "缓存的大小为:mMemoryCache.size() "
						+ mMemoryCache.size());
				mMemoryCache.evictAll();
				Log.d("LruCacheUtils", "清空后缓存的大小为:mMemoryCache.size()"
						+ mMemoryCache.size());
			}
			mMemoryCache = null;
		}
	}

	/**
	 * 添加Bitmap到内存缓存
	 * 
	 * @param key
	 * @param bitmap
	 */
	public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
		if (getBitmapFromMemCache(key) == null && bitmap != null) {
			mMemoryCache.put(key, bitmap);
		}
	}

	/**
	 * 从内存缓存中获取一个Bitmap
	 * 
	 * @param key
	 * @return
	 */
	public Bitmap getBitmapFromMemCache(String key) {
		return mMemoryCache.get(key);
	}
}

GridView的Adapter实现呢了OnScrollListener接口,这样可以控制每次只加载屏幕可见部分的缩略图。缩略图的加载是通过ASYNCTASK来实现的。内存缓存中有,取内存缓存;内存缓存中没有,取文件缓存,并将图片存入内存缓存;文件缓存中没有,再读取原图处理,并将图片存入内存缓存和文件缓存。这样可以浏览海量图片时发生OOM。
public class GridAdapter extends BaseAdapter implements OnScrollListener {
	
	private static final String TAG="GridAdapter";
	/**
	 * 获取选中图片数量监听
	 */
	public OnSelectedListener onSelectedListener;
	/**
	 * 异步图片处理器
	 */
	private AsyncLoadedImage asyncLoadedImage;

	/**
	 * LruCache缓存工具
	 */
	private LruCacheUtil mLruCacheUtils;

	/**
	 * 获取每个ITEM大小,作为缩放图片的宽度和高度
	 */
	private Point mPoint = new Point(0, 0);

	private LayoutInflater laytouInflater;

	/**
	 * 本地所有图片路径列表
	 */
	private List<String> mImagePaths;

	/**
	 * 记录全部缩略图选中状态
	 */
	private HashMap<Integer, Boolean> mSelectedMap = new HashMap<Integer, Boolean>();

	/**
	 * 记录已选中缩略图路径
	 */
	@SuppressLint("UseSparseArrays")
	private HashMap<Integer, String> mPathSelectedMap = new HashMap<Integer, String>();

	/**
	 * 是否首次进入界面
	 */
	private boolean mIsFirstEnter = true;

	/**
	 * 一屏中第一个item的位置
	 */
	private int mFirstVisibleItem;

	/**
	 * 一屏中所有item的个数
	 */
	private int mVisibleItemCount;

	/**
	 * 父容器
	 */
	private GridView mGridView;

	private Context mContext;

	/* 一次最多可选的图片数量 */
	private int mMaxSelectedCount = 0;
	
	/**
	 * 设置选中项目后的监听
	 * 
	 * @param onSelectedCountListener
	 */
	public void setOnSelectedListener(OnSelectedListener onSelectedListener) {
		this.onSelectedListener = onSelectedListener;
	}

	public GridAdapter(Context context, GridView gridView,
			List<String> imagePaths, int maxSelectedCount) {
		mContext = context;
		laytouInflater = LayoutInflater.from(context);
		mGridView = gridView;
		mGridView.setOnScrollListener(this);
		mImagePaths = imagePaths;
		asyncLoadedImage = new AsyncLoadedImage();
		mLruCacheUtils = new LruCacheUtil(context);
		mMaxSelectedCount = maxSelectedCount;
	}

	@Override
	public int getCount() {
		
		return mImagePaths.size();
	}

	@Override
	public Object getItem(int position) {		
		return mImagePaths.get(position);
	}

	@Override
	public long getItemId(int position) {		
		return position;
	}

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = null;
		final String imagePath = mImagePaths.get(position);
		if (convertView == null) {
			viewHolder = new ViewHolder();
			convertView = laytouInflater.inflate(R.layout.grid_view_item, null);
			viewHolder.mCheckBox = (CheckBox) convertView
					.findViewById(R.id.cb_select);
			viewHolder.mImageView = (MyImageView) convertView
					.findViewById(R.id.iv_thumbnail);
			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);// 先设置每个ITEM的默认图片

		}
		viewHolder.mImageView.setTag(imagePath);
		// 图片选中监听
		viewHolder.mCheckBox
				.setOnCheckedChangeListener(new OnCheckedChangeListener() {
					@Override
					public void onCheckedChanged(CompoundButton buttonView,
							boolean isChecked) {
						mSelectedMap.put(position, isChecked);
						// 统计已选择的图片
						if (isChecked) {
							mPathSelectedMap.put(position, imagePath);

						} else {
							mPathSelectedMap.remove(position);
						}
						// 限制每次选择图片的数量
						if (mPathSelectedMap.size() > mMaxSelectedCount) {
							Toast.makeText(
									mContext,
									String.format(
											mContext.getResources()
													.getString(
															R.string.max_selected_cousts),
											mMaxSelectedCount),
									Toast.LENGTH_SHORT).show();
							buttonView.setChecked(false);
							mSelectedMap.put(position, false);
							mPathSelectedMap.remove(position);
						} else {
							if (onSelectedListener != null) {
								onSelectedListener
										.setSelectResult(mPathSelectedMap);// 设置选中数量
							}
						}
					}
				});
		viewHolder.mCheckBox
				.setChecked(mSelectedMap.containsKey(position) ? mSelectedMap
						.get(position) : false);

		return convertView;
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
			asyncLoadedImage = new AsyncLoadedImage();
			asyncLoadedImage.execute();
		} else {
			if (asyncLoadedImage != null)
				asyncLoadedImage.cancel(true);
		}

	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		mFirstVisibleItem = firstVisibleItem;
		mVisibleItemCount = visibleItemCount;
		// 因此在这里为首次进入程序开启下载任务。
		if (mIsFirstEnter && visibleItemCount > 0) {
			asyncLoadedImage.execute();
			mIsFirstEnter = false;
		}

	}
	
	
	class AsyncLoadedImage extends AsyncTask<Object,GridViewItem,Object>{

		@Override
		protected void onPostExecute(Object result) {
			// TODO Auto-generated method stub
			super.onPostExecute(result);
		}

		@Override
		protected void onProgressUpdate(GridViewItem... values) {
			if (isCancelled())
				return;
			for (GridViewItem gridViewItemBean : values) {
				ImageView mImageView = (ImageView) mGridView
						.findViewWithTag(gridViewItemBean.getmPathName());
				if (mImageView != null) {
					mImageView.setImageBitmap(gridViewItemBean.getmBitmap());
				}
			}
		}

		@Override
		protected Object doInBackground(Object... params) {
			if (isCancelled())
				return null;
			for (int i = mFirstVisibleItem; i < mFirstVisibleItem
					+ mVisibleItemCount; ++i) {
				try {
					String imagePath = mImagePaths.get(i);
					GridViewItem gridViewItemBean;
					String fileName = FileUtil.getInstance().getFileName(
							imagePath);
					// 首先内存缓存中取
					Bitmap bitmapLruCache = mLruCacheUtils
							.getBitmapFromMemCache(fileName);
					if (bitmapLruCache != null) {
						gridViewItemBean = new GridViewItem();
						gridViewItemBean.setmBitmap(bitmapLruCache);
						gridViewItemBean.setmPathName(imagePath);
					} else {// 内存缓存没有,再从文件缓存获取
						Bitmap bitmapCache = FileUtil.getInstance().getBitmap(
								fileName);// 获取文件缓存
						if (bitmapCache != null) {
							gridViewItemBean = new GridViewItem();
							gridViewItemBean.setmBitmap(bitmapCache);
							gridViewItemBean.setmPathName(imagePath);
							mLruCacheUtils.addBitmapToMemoryCache(fileName,
									bitmapCache);
						} else {// 文件缓存没有,再读取原图处理
							Bitmap bitmap;
							Bitmap rotateBitmap;
							Bitmap newBitmap;
							BitmapFactory.Options options = new BitmapFactory.Options();
							options.inSampleSize = 10;
							bitmap = BitmapFactory.decodeFile(imagePath,
									options);
							// 获取图片旋转的角度
							int angle = BitmapUtil
									.readPictureDegree(imagePath);
							rotateBitmap = angle != 0 ? BitmapUtil
									.rotaingImageView(angle, bitmap) : bitmap;
							newBitmap = ThumbnailUtils.extractThumbnail(
									rotateBitmap,
									mPoint == null ? 0 : mPoint.x,
									mPoint == null ? 0 : mPoint.y);// 获取缩略图
							bitmap.recycle();
							rotateBitmap.recycle();
							if (newBitmap == null)
								continue;
							gridViewItemBean = new GridViewItem();
							gridViewItemBean.setmBitmap(newBitmap);
							gridViewItemBean.setmPathName(imagePath);
							// 将图片存到文件缓存
							FileUtil.getInstance().savaBitmap(fileName,
									newBitmap);
							// 将图片加入LruCache缓存
							mLruCacheUtils.addBitmapToMemoryCache(fileName,
									newBitmap);
						}
					}
					publishProgress(gridViewItemBean);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return null;
		}
		
	}
	
	class ViewHolder {
		MyImageView mImageView;
		CheckBox mCheckBox;
	}
	
	/**
	 * 获取选中项目后监听
	 * 
	 * @author SuttonSun
	 * 
	 */
	public interface OnSelectedListener {
		public void setSelectResult(HashMap<Integer, String> map);
	}
	
	
	/**
	 * 通知更新
	 * 
	 * @param list
	 *            图片路径列表
	 */
	public void setDatasetChanged(List<String> list) {
		mImagePaths = list;
		mIsFirstEnter = true;
		asyncLoadedImage = new AsyncLoadedImage();
		this.notifyDataSetChanged();
	}

	/**
	 * 取消加载
	 */
	public void cancelTask() {
		if (asyncLoadedImage != null) {
			Log.i(TAG, "停止同步任务!");
			asyncLoadedImage.cancel(true);
		}
		if (mLruCacheUtils != null) {			
			// 清除缓存
			mLruCacheUtils.clearCache();
		}
	}



}
以上是关键部分代码。

效果图:


源码下载地址:http://download.csdn.net/detail/qq_33819047/9414437



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值