转载: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来封装这三个属性
<div>
<embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="comment">/**</span>
- <span class="comment"> * GridView的每个item的数据对象</span>
- <span class="comment"> * </span>
- <span class="comment"> * @author len</span>
- <span class="comment"> *</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> <span class="keyword">class</span> ImageBean{
- <span class="comment">/**</span>
- <span class="comment"> * 文件夹的第一张图片路径</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> String topImagePath;
- <span class="comment">/**</span>
- <span class="comment"> * 文件夹名</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> String folderName;
- <span class="comment">/**</span>
- <span class="comment"> * 文件夹中的图片数</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> <span class="keyword">int</span> imageCounts;
-
- <span class="keyword">public</span> String getTopImagePath() {
- <span class="keyword">return</span> topImagePath;
- }
- <span class="keyword">public</span> <span class="keyword">void</span> setTopImagePath(String topImagePath) {
- <span class="keyword">this</span>.topImagePath = topImagePath;
- }
- <span class="keyword">public</span> String getFolderName() {
- <span class="keyword">return</span> folderName;
- }
- <span class="keyword">public</span> <span class="keyword">void</span> setFolderName(String folderName) {
- <span class="keyword">this</span>.folderName = folderName;
- }
- <span class="keyword">public</span> <span class="keyword">int</span> getImageCounts() {
- <span class="keyword">return</span> imageCounts;
- }
- <span class="keyword">public</span> <span class="keyword">void</span> setImageCounts(<span class="keyword">int</span> imageCounts) {
- <span class="keyword">this</span>.imageCounts = imageCounts;
- }
-
- }
接下来就是主界面的布局啦,上面的导航栏我没有加进去,只有下面的GridView,所以说主界面布局中只有一个GridView
<div>
<embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2">
</embed>
</div>
</div>
- <span class="tag"><</span><span class="tag-name">RelativeLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">“http://schemas.android.com/apk/res/android”</span>
- <span class="attribute">xmlns:tools</span>=<span class="attribute-value">“http://schemas.android.com/tools”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“match_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“match_parent”</span> <span class="tag">></span>
-
- <span class="tag"><</span><span class="tag-name">GridView</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/main_grid”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“match_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“match_parent”</span>
- <span class="attribute">android:listSelector</span>=<span class="attribute-value">“@android:color/transparent”</span>
- <span class="attribute">android:cacheColorHint</span>=<span class="attribute-value">“@android:color/transparent”</span>
- <span class="attribute">android:stretchMode</span>=<span class="attribute-value">“columnWidth”</span>
- <span class="attribute">android:horizontalSpacing</span>=<span class="attribute-value">“20dip”</span>
- <span class="attribute">android:gravity</span>=<span class="attribute-value">“center”</span>
- <span class="attribute">android:verticalSpacing</span>=<span class="attribute-value">“20dip”</span>
- <span class="attribute">android:columnWidth</span>=<span class="attribute-value">“90dip”</span>
- <span class="attribute">android:numColumns</span>=<span class="attribute-value">“2”</span> <span class="tag">></span>
- <span class="tag"></</span><span class="tag-name">GridView</span><span class="tag">></span>
-
- <span class="tag"></</span><span class="tag-name">RelativeLayout</span><span class="tag">></span>
接下来就是GridView的Item的布局,看上面的图也行你会认为他的效果是2张图片添加的效果,其实不是,后面的叠加效果只是一张背景图片而已,代码先贴上来
<div>
<embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3">
</embed>
</div>
</div>
- <span class="tag"><?</span><span class="tag-name">xml</span> <span class="attribute">version</span>=<span class="attribute-value">“1.0”</span> <span class="attribute">encoding</span>=<span class="attribute-value">“UTF-8”</span><span class="tag">?></span>
- <span class="tag"><</span><span class="tag-name">RelativeLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">“http://schemas.android.com/apk/res/android”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“fill_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span> <span class="tag">></span>
-
- <span class="tag"><</span><span class="tag-name">FrameLayout</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/framelayout”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“fill_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span> <span class="tag">></span>
-
- <span class="tag"><</span><span class="tag-name">com.example.imagescan.MyImageView</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/group_image”</span>
- <span class="attribute">android:background</span>=<span class="attribute-value">“@drawable/albums_bg”</span>
- <span class="attribute">android:src</span>=<span class="attribute-value">“@drawable/friends_sends_pictures_no”</span>
- <span class="attribute">android:paddingLeft</span>=<span class="attribute-value">“20dip”</span>
- <span class="attribute">android:paddingRight</span>=<span class="attribute-value">“20dip”</span>
- <span class="attribute">android:paddingTop</span>=<span class="attribute-value">“18dip”</span>
- <span class="attribute">android:paddingBottom</span>=<span class="attribute-value">“30dip”</span>
- <span class="attribute">android:scaleType</span>=<span class="attribute-value">“fitXY”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“fill_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“150dip”</span> <span class="tag">/></span>
-
- <span class="tag"><</span><span class="tag-name">TextView</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/group_count”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:background</span>=<span class="attribute-value">“@drawable/albums_icon_bg”</span>
- <span class="attribute">android:gravity</span>=<span class="attribute-value">“center”</span>
- <span class="attribute">android:layout_marginBottom</span>=<span class="attribute-value">“10dip”</span>
- <span class="attribute">android:text</span>=<span class="attribute-value">“5”</span>
- <span class="attribute">android:layout_gravity</span>=<span class="attribute-value">“bottom|center_horizontal”</span> <span class="tag">/></span>
- <span class="tag"></</span><span class="tag-name">FrameLayout</span><span class="tag">></span>
-
- <span class="tag"><</span><span class="tag-name">TextView</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/group_title”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“fill_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:gravity</span>=<span class="attribute-value">“center”</span>
- <span class="attribute">android:layout_below</span>=<span class="attribute-value">“@id/framelayout”</span>
- <span class="attribute">android:layout_centerHorizontal</span>=<span class="attribute-value">“true”</span>
- <span class="attribute">android:ellipsize</span>=<span class="attribute-value">“end”</span>
- <span class="attribute">android:singleLine</span>=<span class="attribute-value">“true”</span>
- <span class="attribute">android:text</span>=<span class="attribute-value">“Camera”</span>
- <span class="attribute">android:textSize</span>=<span class="attribute-value">“16sp”</span> <span class="tag">/></span>
-
- <span class="tag"></</span><span class="tag-name">RelativeLayout</span><span class="tag">></span>
看到上面的布局代码,也行你已经发现了,上面使用的是自定义的MyImageView,我先不说这个自定义MyImageView的作用,待会再给大家说,我们继续看代码
第一个界面的主要代码
<div>
<embed id="ZeroClipboardMovie_4" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_4">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="keyword">import</span> java.io.File;
- <span class="keyword">import</span> java.util.ArrayList;
- <span class="keyword">import</span> java.util.HashMap;
- <span class="keyword">import</span> java.util.Iterator;
- <span class="keyword">import</span> java.util.List;
- <span class="keyword">import</span> java.util.Map;
-
- <span class="keyword">import</span> android.app.Activity;
- <span class="keyword">import</span> android.app.ProgressDialog;
- <span class="keyword">import</span> android.content.ContentResolver;
- <span class="keyword">import</span> android.content.Intent;
- <span class="keyword">import</span> android.database.Cursor;
- <span class="keyword">import</span> android.net.Uri;
- <span class="keyword">import</span> android.os.Bundle;
- <span class="keyword">import</span> android.os.Handler;
- <span class="keyword">import</span> android.os.Message;
- <span class="keyword">import</span> android.provider.MediaStore;
- <span class="keyword">import</span> android.view.View;
- <span class="keyword">import</span> android.widget.AdapterView;
- <span class="keyword">import</span> android.widget.AdapterView.OnItemClickListener;
- <span class="keyword">import</span> android.widget.GridView;
- <span class="comment">/**</span>
- <span class="comment"> * @blog http://blog.csdn.net/xiaanming</span>
- <span class="comment"> * </span>
- <span class="comment"> * @author xiaanming</span>
- <span class="comment"> * </span>
- <span class="comment"> *</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> <span class="keyword">class</span> MainActivity <span class="keyword">extends</span> Activity {
- <span class="keyword">private</span> HashMap<String, List<String>> mGruopMap = <span class="keyword">new</span> HashMap<String, List<String>>();
- <span class="keyword">private</span> List<ImageBean> list = <span class="keyword">new</span> ArrayList<ImageBean>();
- <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="keyword">int</span> SCAN_OK = <span class="number">1</span>;
- <span class="keyword">private</span> ProgressDialog mProgressDialog;
- <span class="keyword">private</span> GroupAdapter adapter;
- <span class="keyword">private</span> GridView mGroupGridView;
-
- <span class="keyword">private</span> Handler mHandler = <span class="keyword">new</span> Handler(){
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> handleMessage(Message msg) {
- <span class="keyword">super</span>.handleMessage(msg);
- <span class="keyword">switch</span> (msg.what) {
- <span class="keyword">case</span> SCAN_OK:
- <span class="comment">//关闭进度条</span>
- mProgressDialog.dismiss();
-
- adapter = <span class="keyword">new</span> GroupAdapter(MainActivity.<span class="keyword">this</span>, list = subGroupOfImage(mGruopMap), mGroupGridView);
- mGroupGridView.setAdapter(adapter);
- <span class="keyword">break</span>;
- }
- }
-
- };
-
- <span class="annotation">@Override</span>
- <span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) {
- <span class="keyword">super</span>.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- mGroupGridView = (GridView) findViewById(R.id.main_grid);
-
- getImages();
-
- mGroupGridView.setOnItemClickListener(<span class="keyword">new</span> OnItemClickListener() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onItemClick(AdapterView<?> parent, View view,
- <span class="keyword">int</span> position, <span class="keyword">long</span> id) {
- List<String> childList = mGruopMap.get(list.get(position).getFolderName());
-
- Intent mIntent = <span class="keyword">new</span> Intent(MainActivity.<span class="keyword">this</span>, ShowImageActivity.<span class="keyword">class</span>);
- mIntent.putStringArrayListExtra(<span class="string">“data”</span>, (ArrayList<String>)childList);
- startActivity(mIntent);
-
- }
- });
-
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 利用ContentProvider扫描手机中的图片,此方法在运行在子线程中</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> <span class="keyword">void</span> getImages() {
- <span class="comment">//显示进度条</span>
- mProgressDialog = ProgressDialog.show(<span class="keyword">this</span>, <span class="keyword">null</span>, <span class="string">“正在加载…”</span>);
-
- <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> run() {
- Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
- ContentResolver mContentResolver = MainActivity.<span class="keyword">this</span>.getContentResolver();
-
- <span class="comment">//只查询jpeg和png的图片</span>
- Cursor mCursor = mContentResolver.query(mImageUri, <span class="keyword">null</span>,
- MediaStore.Images.Media.MIME_TYPE + <span class="string">“=? or “</span>
- + MediaStore.Images.Media.MIME_TYPE + <span class="string">“=?”</span>,
- <span class="keyword">new</span> String[] { <span class="string">“image/jpeg”</span>, <span class="string">“image/png”</span> }, MediaStore.Images.Media.DATE_MODIFIED);
-
- <span class="keyword">if</span>(mCursor == <span class="keyword">null</span>){
- <span class="keyword">return</span>;
- }
-
- <span class="keyword">while</span> (mCursor.moveToNext()) {
- <span class="comment">//获取图片的路径</span>
- String path = mCursor.getString(mCursor
- .getColumnIndex(MediaStore.Images.Media.DATA));
-
- <span class="comment">//获取该图片的父路径名</span>
- String parentName = <span class="keyword">new</span> File(path).getParentFile().getName();
-
-
- <span class="comment">//根据父路径名将图片放入到mGruopMap中</span>
- <span class="keyword">if</span> (!mGruopMap.containsKey(parentName)) {
- List<String> chileList = <span class="keyword">new</span> ArrayList<String>();
- chileList.add(path);
- mGruopMap.put(parentName, chileList);
- } <span class="keyword">else</span> {
- mGruopMap.get(parentName).add(path);
- }
- }
-
- <span class="comment">//通知Handler扫描图片完成</span>
- mHandler.sendEmptyMessage(SCAN_OK);
- mCursor.close();
- }
- }).start();
-
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 组装分组界面GridView的数据源,因为我们扫描手机的时候将图片信息放在HashMap中</span>
- <span class="comment"> * 所以需要遍历HashMap将数据组装成List</span>
- <span class="comment"> * </span>
- <span class="comment"> * @param mGruopMap</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> List<ImageBean> subGroupOfImage(HashMap<String, List<String>> mGruopMap){
- <span class="keyword">if</span>(mGruopMap.size() == <span class="number"></span>){
- <span class="keyword">return</span> <span class="keyword">null</span>;
- }
- List<ImageBean> list = <span class="keyword">new</span> ArrayList<ImageBean>();
-
- Iterator<Map.Entry<String, List<String>>> it = mGruopMap.entrySet().iterator();
- <span class="keyword">while</span> (it.hasNext()) {
- Map.Entry<String, List<String>> entry = it.next();
- ImageBean mImageBean = <span class="keyword">new</span> ImageBean();
- String key = entry.getKey();
- List<String> value = entry.getValue();
-
- mImageBean.setFolderName(key);
- mImageBean.setImageCounts(value.size());
- mImageBean.setTopImagePath(value.get(<span class="number"></span>));<span class="comment">//获取该组的第一张图片</span>
-
- list.add(mImageBean);
- }
-
- <span class="keyword">return</span> 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
<div>
<embed id="ZeroClipboardMovie_5" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_5">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="keyword">import</span> java.util.concurrent.ExecutorService;
- <span class="keyword">import</span> java.util.concurrent.Executors;
-
- <span class="keyword">import</span> android.graphics.Bitmap;
- <span class="keyword">import</span> android.graphics.BitmapFactory;
- <span class="keyword">import</span> android.graphics.Point;
- <span class="keyword">import</span> android.os.Handler;
- <span class="keyword">import</span> android.os.Message;
- <span class="keyword">import</span> android.support.v4.util.LruCache;
-
- <span class="comment">/**</span>
- <span class="comment"> * 本地图片加载器,采用的是异步解析本地图片,单例模式利用getInstance()获取NativeImageLoader实例</span>
- <span class="comment"> * 调用loadNativeImage()方法加载本地图片,此类可作为一个加载本地图片的工具类</span>
- <span class="comment"> * </span>
- <span class="comment"> * @blog http://blog.csdn.net/xiaanming</span>
- <span class="comment"> * </span>
- <span class="comment"> * @author xiaanming</span>
- <span class="comment"> *</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> <span class="keyword">class</span> NativeImageLoader {
- <span class="keyword">private</span> LruCache<String, Bitmap> mMemoryCache;
- <span class="keyword">private</span> <span class="keyword">static</span> NativeImageLoader mInstance = <span class="keyword">new</span> NativeImageLoader();
- <span class="keyword">private</span> ExecutorService mImageThreadPool = Executors.newFixedThreadPool(<span class="number">1</span>);
-
-
- <span class="keyword">private</span> NativeImageLoader(){
- <span class="comment">//获取应用程序的最大内存</span>
- <span class="keyword">final</span> <span class="keyword">int</span> maxMemory = (<span class="keyword">int</span>) (Runtime.getRuntime().maxMemory() / <span class="number">1024</span>);
-
- <span class="comment">//用最大内存的1/4来存储图片</span>
- <span class="keyword">final</span> <span class="keyword">int</span> cacheSize = maxMemory / <span class="number">4</span>;
- mMemoryCache = <span class="keyword">new</span> LruCache<String, Bitmap>(cacheSize) {
-
- <span class="comment">//获取每张图片的大小</span>
- <span class="annotation">@Override</span>
- <span class="keyword">protected</span> <span class="keyword">int</span> sizeOf(String key, Bitmap bitmap) {
- <span class="keyword">return</span> bitmap.getRowBytes() * bitmap.getHeight() / <span class="number">1024</span>;
- }
- };
- }
-
- <span class="comment">/**</span>
- <span class="comment"> * 通过此方法来获取NativeImageLoader的实例</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> <span class="keyword">static</span> NativeImageLoader getInstance(){
- <span class="keyword">return</span> mInstance;
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 加载本地图片,对图片不进行裁剪</span>
- <span class="comment"> * @param path</span>
- <span class="comment"> * @param mCallBack</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> Bitmap loadNativeImage(<span class="keyword">final</span> String path, <span class="keyword">final</span> NativeImageCallBack mCallBack){
- <span class="keyword">return</span> <span class="keyword">this</span>.loadNativeImage(path, <span class="keyword">null</span>, mCallBack);
- }
-
- <span class="comment">/**</span>
- <span class="comment"> * 此方法来加载本地图片,这里的mPoint是用来封装ImageView的宽和高,我们会根据ImageView控件的大小来裁剪Bitmap</span>
- <span class="comment"> * 如果你不想裁剪图片,调用loadNativeImage(final String path, final NativeImageCallBack mCallBack)来加载</span>
- <span class="comment"> * @param path</span>
- <span class="comment"> * @param mPoint</span>
- <span class="comment"> * @param mCallBack</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> Bitmap loadNativeImage(<span class="keyword">final</span> String path, <span class="keyword">final</span> Point mPoint, <span class="keyword">final</span> NativeImageCallBack mCallBack){
- <span class="comment">//先获取内存中的Bitmap</span>
- Bitmap bitmap = getBitmapFromMemCache(path);
-
- <span class="keyword">final</span> Handler mHander = <span class="keyword">new</span> Handler(){
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> handleMessage(Message msg) {
- <span class="keyword">super</span>.handleMessage(msg);
- mCallBack.onImageLoader((Bitmap)msg.obj, path);
- }
-
- };
-
- <span class="comment">//若该Bitmap不在内存缓存中,则启用线程去加载本地的图片,并将Bitmap加入到mMemoryCache中</span>
- <span class="keyword">if</span>(bitmap == <span class="keyword">null</span>){
- mImageThreadPool.execute(<span class="keyword">new</span> Runnable() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> run() {
- <span class="comment">//先获取图片的缩略图</span>
- Bitmap mBitmap = decodeThumbBitmapForFile(path, mPoint == <span class="keyword">null</span> ? <span class="number"></span>: mPoint.x, mPoint == <span class="keyword">null</span> ? <span class="number"></span>: mPoint.y);
- Message msg = mHander.obtainMessage();
- msg.obj = mBitmap;
- mHander.sendMessage(msg);
-
- <span class="comment">//将图片加入到内存缓存</span>
- addBitmapToMemoryCache(path, mBitmap);
- }
- });
- }
- <span class="keyword">return</span> bitmap;
-
- }
-
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 往内存缓存中添加Bitmap</span>
- <span class="comment"> * </span>
- <span class="comment"> * @param key</span>
- <span class="comment"> * @param bitmap</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> <span class="keyword">void</span> addBitmapToMemoryCache(String key, Bitmap bitmap) {
- <span class="keyword">if</span> (getBitmapFromMemCache(key) == <span class="keyword">null</span> && bitmap != <span class="keyword">null</span>) {
- mMemoryCache.put(key, bitmap);
- }
- }
-
- <span class="comment">/**</span>
- <span class="comment"> * 根据key来获取内存中的图片</span>
- <span class="comment"> * @param key</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> Bitmap getBitmapFromMemCache(String key) {
- <span class="keyword">return</span> mMemoryCache.get(key);
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 根据View(主要是ImageView)的宽和高来获取图片的缩略图</span>
- <span class="comment"> * @param path</span>
- <span class="comment"> * @param viewWidth</span>
- <span class="comment"> * @param viewHeight</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> Bitmap decodeThumbBitmapForFile(String path, <span class="keyword">int</span> viewWidth, <span class="keyword">int</span> viewHeight){
- BitmapFactory.Options options = <span class="keyword">new</span> BitmapFactory.Options();
- <span class="comment">//设置为true,表示解析Bitmap对象,该对象不占内存</span>
- options.inJustDecodeBounds = <span class="keyword">true</span>;
- BitmapFactory.decodeFile(path, options);
- <span class="comment">//设置缩放比例</span>
- options.inSampleSize = computeScale(options, viewWidth, viewHeight);
-
- <span class="comment">//设置为false,解析Bitmap对象加入到内存中</span>
- options.inJustDecodeBounds = <span class="keyword">false</span>;
-
- <span class="keyword">return</span> BitmapFactory.decodeFile(path, options);
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 根据View(主要是ImageView)的宽和高来计算Bitmap缩放比例。默认不缩放</span>
- <span class="comment"> * @param options</span>
- <span class="comment"> * @param width</span>
- <span class="comment"> * @param height</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> <span class="keyword">int</span> computeScale(BitmapFactory.Options options, <span class="keyword">int</span> viewWidth, <span class="keyword">int</span> viewHeight){
- <span class="keyword">int</span> inSampleSize = <span class="number">1</span>;
- <span class="keyword">if</span>(viewWidth == <span class="number"></span> || viewWidth == <span class="number"></span>){
- <span class="keyword">return</span> inSampleSize;
- }
- <span class="keyword">int</span> bitmapWidth = options.outWidth;
- <span class="keyword">int</span> bitmapHeight = options.outHeight;
-
- <span class="comment">//假如Bitmap的宽度或高度大于我们设定图片的View的宽高,则计算缩放比例</span>
- <span class="keyword">if</span>(bitmapWidth > viewWidth || bitmapHeight > viewWidth){
- <span class="keyword">int</span> widthScale = Math.round((<span class="keyword">float</span>) bitmapWidth / (<span class="keyword">float</span>) viewWidth);
- <span class="keyword">int</span> heightScale = Math.round((<span class="keyword">float</span>) bitmapHeight / (<span class="keyword">float</span>) viewWidth);
-
- <span class="comment">//为了保证图片不缩放变形,我们取宽高比例最小的那个</span>
- inSampleSize = widthScale < heightScale ? widthScale : heightScale;
- }
- <span class="keyword">return</span> inSampleSize;
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 加载本地图片的回调接口</span>
- <span class="comment"> * </span>
- <span class="comment"> * @author xiaanming</span>
- <span class="comment"> *</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> <span class="keyword">interface</span> NativeImageCallBack{
- <span class="comment">/**</span>
- <span class="comment"> * 当子线程加载完了本地的图片,将Bitmap和图片路径回调在此方法中</span>
- <span class="comment"> * @param bitmap</span>
- <span class="comment"> * @param path</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> <span class="keyword">void</span> 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类的代码
<div>
<embed id="ZeroClipboardMovie_6" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_6">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="keyword">import</span> java.util.List;
-
- <span class="keyword">import</span> android.content.Context;
- <span class="keyword">import</span> android.graphics.Bitmap;
- <span class="keyword">import</span> android.graphics.Point;
- <span class="keyword">import</span> android.view.LayoutInflater;
- <span class="keyword">import</span> android.view.View;
- <span class="keyword">import</span> android.view.ViewGroup;
- <span class="keyword">import</span> android.widget.BaseAdapter;
- <span class="keyword">import</span> android.widget.GridView;
- <span class="keyword">import</span> android.widget.ImageView;
- <span class="keyword">import</span> android.widget.TextView;
-
- <span class="keyword">import</span> com.example.imagescan.MyImageView.OnMeasureListener;
- <span class="keyword">import</span> com.example.imagescan.NativeImageLoader.NativeImageCallBack;
-
- <span class="keyword">public</span> <span class="keyword">class</span> GroupAdapter <span class="keyword">extends</span> BaseAdapter{
- <span class="keyword">private</span> List<ImageBean> list;
- <span class="keyword">private</span> Point mPoint = <span class="keyword">new</span> Point(<span class="number"></span>, <span class="number"></span>);<span class="comment">//用来封装ImageView的宽和高的对象</span>
- <span class="keyword">private</span> GridView mGridView;
- <span class="keyword">protected</span> LayoutInflater mInflater;
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">int</span> getCount() {
- <span class="keyword">return</span> list.size();
- }
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> Object getItem(<span class="keyword">int</span> position) {
- <span class="keyword">return</span> list.get(position);
- }
-
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">long</span> getItemId(<span class="keyword">int</span> position) {
- <span class="keyword">return</span> position;
- }
-
- <span class="keyword">public</span> GroupAdapter(Context context, List<ImageBean> list, GridView mGridView){
- <span class="keyword">this</span>.list = list;
- <span class="keyword">this</span>.mGridView = mGridView;
- mInflater = LayoutInflater.from(context);
- }
-
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> View getView(<span class="keyword">int</span> position, View convertView, ViewGroup parent) {
- <span class="keyword">final</span> ViewHolder viewHolder;
- ImageBean mImageBean = list.get(position);
- String path = mImageBean.getTopImagePath();
- <span class="keyword">if</span>(convertView == <span class="keyword">null</span>){
- viewHolder = <span class="keyword">new</span> ViewHolder();
- convertView = mInflater.inflate(R.layout.grid_group_item, <span class="keyword">null</span>);
- 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);
-
- <span class="comment">//用来监听ImageView的宽和高</span>
- viewHolder.mImageView.setOnMeasureListener(<span class="keyword">new</span> OnMeasureListener() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onMeasureSize(<span class="keyword">int</span> width, <span class="keyword">int</span> height) {
- mPoint.set(width, height);
- }
- });
-
- convertView.setTag(viewHolder);
- }<span class="keyword">else</span>{
- 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()));
- <span class="comment">//给ImageView设置路径Tag,这是异步加载图片的小技巧</span>
- viewHolder.mImageView.setTag(path);
-
-
- <span class="comment">//利用NativeImageLoader类加载本地图片</span>
- Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, <span class="keyword">new</span> NativeImageCallBack() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onImageLoader(Bitmap bitmap, String path) {
- ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);
- <span class="keyword">if</span>(bitmap != <span class="keyword">null</span> && mImageView != <span class="keyword">null</span>){
- mImageView.setImageBitmap(bitmap);
- }
- }
- });
-
- <span class="keyword">if</span>(bitmap != <span class="keyword">null</span>){
- viewHolder.mImageView.setImageBitmap(bitmap);
- }<span class="keyword">else</span>{
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
-
-
- <span class="keyword">return</span> convertView;
- }
-
-
-
- <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> ViewHolder{
- <span class="keyword">public</span> MyImageView mImageView;
- <span class="keyword">public</span> TextView mTextViewTitle;
- <span class="keyword">public</span> 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的大小来裁剪图片
<div>
<embed id="ZeroClipboardMovie_7" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_7">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="keyword">import</span> android.content.Context;
- <span class="keyword">import</span> android.util.AttributeSet;
- <span class="keyword">import</span> android.widget.ImageView;
-
- <span class="keyword">public</span> <span class="keyword">class</span> MyImageView <span class="keyword">extends</span> ImageView {
- <span class="keyword">private</span> OnMeasureListener onMeasureListener;
-
- <span class="keyword">public</span> <span class="keyword">void</span> setOnMeasureListener(OnMeasureListener onMeasureListener) {
- <span class="keyword">this</span>.onMeasureListener = onMeasureListener;
- }
-
- <span class="keyword">public</span> MyImageView(Context context, AttributeSet attrs) {
- <span class="keyword">super</span>(context, attrs);
- }
-
- <span class="keyword">public</span> MyImageView(Context context, AttributeSet attrs, <span class="keyword">int</span> defStyle) {
- <span class="keyword">super</span>(context, attrs, defStyle);
- }
-
- <span class="annotation">@Override</span>
- <span class="keyword">protected</span> <span class="keyword">void</span> onMeasure(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec) {
- <span class="keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- <span class="comment">//将图片测量的大小回调到onMeasureSize()方法中</span>
- <span class="keyword">if</span>(onMeasureListener != <span class="keyword">null</span>){
- onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight());
- }
- }
-
- <span class="keyword">public</span> <span class="keyword">interface</span> OnMeasureListener{
- <span class="keyword">public</span> <span class="keyword">void</span> onMeasureSize(<span class="keyword">int</span> width, <span class="keyword">int</span> height);
- }
-
- }
上面这些代码就完成了第一个界面的功能了,接下来就是点击GridView的item跳转另一个界面来显示该文件夹下面的所有图片,功能跟第一个界面差不多,也是使用GridView来显示图片,第二个界面的布局代码我就不贴了,直接贴上界面的代码
<div>
<embed id="ZeroClipboardMovie_8" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_8">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="keyword">import</span> java.util.List;
-
- <span class="keyword">import</span> android.app.Activity;
- <span class="keyword">import</span> android.os.Bundle;
- <span class="keyword">import</span> android.widget.GridView;
- <span class="keyword">import</span> android.widget.Toast;
-
- <span class="keyword">public</span> <span class="keyword">class</span> ShowImageActivity <span class="keyword">extends</span> Activity {
- <span class="keyword">private</span> GridView mGridView;
- <span class="keyword">private</span> List<String> list;
- <span class="keyword">private</span> ChildAdapter adapter;
-
- <span class="annotation">@Override</span>
- <span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) {
- <span class="keyword">super</span>.onCreate(savedInstanceState);
- setContentView(R.layout.show_image_activity);
-
- mGridView = (GridView) findViewById(R.id.child_grid);
- list = getIntent().getStringArrayListExtra(<span class="string">“data”</span>);
-
- adapter = <span class="keyword">new</span> ChildAdapter(<span class="keyword">this</span>, list, mGridView);
- mGridView.setAdapter(adapter);
-
- }
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onBackPressed() {
- Toast.makeText(<span class="keyword">this</span>, <span class="string">“选中 “</span> + adapter.getSelectItems().size() + <span class="string">” item”</span>, Toast.LENGTH_LONG).show();
- <span class="keyword">super</span>.onBackPressed();
- }
-
-
- }
GridView的item上面一个我们自定义的MyImageView用来显示图片,另外还有一个CheckBox来记录我们选中情况,Adapter的代码如下
<div>
<embed id="ZeroClipboardMovie_9" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_9">
</embed>
</div>
</div>
- <span class="keyword">package</span> com.example.imagescan;
-
- <span class="keyword">import</span> java.util.ArrayList;
- <span class="keyword">import</span> java.util.HashMap;
- <span class="keyword">import</span> java.util.Iterator;
- <span class="keyword">import</span> java.util.List;
- <span class="keyword">import</span> java.util.Map;
-
- <span class="keyword">import</span> android.content.Context;
- <span class="keyword">import</span> android.graphics.Bitmap;
- <span class="keyword">import</span> android.graphics.Point;
- <span class="keyword">import</span> android.view.LayoutInflater;
- <span class="keyword">import</span> android.view.View;
- <span class="keyword">import</span> android.view.ViewGroup;
- <span class="keyword">import</span> android.widget.BaseAdapter;
- <span class="keyword">import</span> android.widget.CheckBox;
- <span class="keyword">import</span> android.widget.CompoundButton;
- <span class="keyword">import</span> android.widget.ImageView;
- <span class="keyword">import</span> android.widget.CompoundButton.OnCheckedChangeListener;
- <span class="keyword">import</span> android.widget.GridView;
-
- <span class="keyword">import</span> com.example.imagescan.MyImageView.OnMeasureListener;
- <span class="keyword">import</span> com.example.imagescan.NativeImageLoader.NativeImageCallBack;
- <span class="keyword">import</span> com.nineoldandroids.animation.AnimatorSet;
- <span class="keyword">import</span> com.nineoldandroids.animation.ObjectAnimator;
-
- <span class="keyword">public</span> <span class="keyword">class</span> ChildAdapter <span class="keyword">extends</span> BaseAdapter {
- <span class="keyword">private</span> Point mPoint = <span class="keyword">new</span> Point(<span class="number"></span>, <span class="number"></span>);<span class="comment">//用来封装ImageView的宽和高的对象</span>
- <span class="comment">/**</span>
- <span class="comment"> * 用来存储图片的选中情况</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> HashMap<Integer, Boolean> mSelectMap = <span class="keyword">new</span> HashMap<Integer, Boolean>();
- <span class="keyword">private</span> GridView mGridView;
- <span class="keyword">private</span> List<String> list;
- <span class="keyword">protected</span> LayoutInflater mInflater;
-
- <span class="keyword">public</span> ChildAdapter(Context context, List<String> list, GridView mGridView) {
- <span class="keyword">this</span>.list = list;
- <span class="keyword">this</span>.mGridView = mGridView;
- mInflater = LayoutInflater.from(context);
- }
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">int</span> getCount() {
- <span class="keyword">return</span> list.size();
- }
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> Object getItem(<span class="keyword">int</span> position) {
- <span class="keyword">return</span> list.get(position);
- }
-
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">long</span> getItemId(<span class="keyword">int</span> position) {
- <span class="keyword">return</span> position;
- }
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> View getView(<span class="keyword">final</span> <span class="keyword">int</span> position, View convertView, ViewGroup parent) {
- <span class="keyword">final</span> ViewHolder viewHolder;
- String path = list.get(position);
-
- <span class="keyword">if</span>(convertView == <span class="keyword">null</span>){
- convertView = mInflater.inflate(R.layout.grid_child_item, <span class="keyword">null</span>);
- viewHolder = <span class="keyword">new</span> ViewHolder();
- viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.child_image);
- viewHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.child_checkbox);
-
- <span class="comment">//用来监听ImageView的宽和高</span>
- viewHolder.mImageView.setOnMeasureListener(<span class="keyword">new</span> OnMeasureListener() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onMeasureSize(<span class="keyword">int</span> width, <span class="keyword">int</span> height) {
- mPoint.set(width, height);
- }
- });
-
- convertView.setTag(viewHolder);
- }<span class="keyword">else</span>{
- viewHolder = (ViewHolder) convertView.getTag();
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
- viewHolder.mImageView.setTag(path);
- viewHolder.mCheckBox.setOnCheckedChangeListener(<span class="keyword">new</span> OnCheckedChangeListener() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onCheckedChanged(CompoundButton buttonView, <span class="keyword">boolean</span> isChecked) {
- <span class="comment">//如果是未选中的CheckBox,则添加动画</span>
- <span class="keyword">if</span>(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){
- addAnimation(viewHolder.mCheckBox);
- }
- mSelectMap.put(position, isChecked);
- }
- });
-
- viewHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : <span class="keyword">false</span>);
-
- <span class="comment">//利用NativeImageLoader类加载本地图片</span>
- Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, <span class="keyword">new</span> NativeImageCallBack() {
-
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onImageLoader(Bitmap bitmap, String path) {
- ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);
- <span class="keyword">if</span>(bitmap != <span class="keyword">null</span> && mImageView != <span class="keyword">null</span>){
- mImageView.setImageBitmap(bitmap);
- }
- }
- });
-
- <span class="keyword">if</span>(bitmap != <span class="keyword">null</span>){
- viewHolder.mImageView.setImageBitmap(bitmap);
- }<span class="keyword">else</span>{
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
-
- <span class="keyword">return</span> convertView;
- }
-
- <span class="comment">/**</span>
- <span class="comment"> * 给CheckBox加点击动画,利用开源库nineoldandroids设置动画 </span>
- <span class="comment"> * @param view</span>
- <span class="comment"> */</span>
- <span class="keyword">private</span> <span class="keyword">void</span> addAnimation(View view){
- <span class="keyword">float</span> [] vaules = <span class="keyword">new</span> <span class="keyword">float</span>[]{<span class="number"></span>.5f, <span class="number"></span>.6f, <span class="number"></span>.7f, <span class="number"></span>.8f, <span class="number"></span>.9f, <span class="number">1</span>.0f, <span class="number">1</span>.1f, <span class="number">1</span>.2f, <span class="number">1</span>.3f, <span class="number">1</span>.25f, <span class="number">1</span>.2f, <span class="number">1</span>.15f, <span class="number">1</span>.1f, <span class="number">1</span>.0f};
- AnimatorSet set = <span class="keyword">new</span> AnimatorSet();
- set.playTogether(ObjectAnimator.ofFloat(view, <span class="string">“scaleX”</span>, vaules),
- ObjectAnimator.ofFloat(view, <span class="string">“scaleY”</span>, vaules));
- set.setDuration(<span class="number">150</span>);
- set.start();
- }
-
-
- <span class="comment">/**</span>
- <span class="comment"> * 获取选中的Item的position</span>
- <span class="comment"> * @return</span>
- <span class="comment"> */</span>
- <span class="keyword">public</span> List<Integer> getSelectItems(){
- List<Integer> list = <span class="keyword">new</span> ArrayList<Integer>();
- <span class="keyword">for</span>(Iterator<Map.Entry<Integer, Boolean>> it = mSelectMap.entrySet().iterator(); it.hasNext();){
- Map.Entry<Integer, Boolean> entry = it.next();
- <span class="keyword">if</span>(entry.getValue()){
- list.add(entry.getKey());
- }
- }
-
- <span class="keyword">return</span> list;
- }
-
-
- <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> ViewHolder{
- <span class="keyword">public</span> MyImageView mImageView;
- <span class="keyword">public</span> CheckBox mCheckBox;
- }
-
-
- }
第二个界面的Adapter跟第一个界面差不多,无非多了一个CheckBox用来记录图片选择情况,我们只需要对CheckBox设置setOnCheckedChangeListener监听,微信的选中之后CheckBox有一个动画效果,所以我利用nineoldandroids动画库也给CheckBox加了一个动画效果,直接调用addAnimation()方法就能添加了,getSelectItems()方法就能获取我们选中的item的position了,知道了选中的position,其他的信息就都知道了,微信有对图片进行预览的功能,我这里就不添加了,如果有这个需求可以自行添加,给大家推荐一个https://github.com/chrisbanes/PhotoView
运行项目,效果如下
看起来还不错吧,采用的是异步读取图片,对图片进行了缓存和裁剪,使得在显示本地图片方面比较流畅,GridView滑动也挺流畅的,也有效的避免OOM的产生,工程中有些东西还没有贴完全,有兴趣的朋友可以下载Demo来运行一下,好了,今天的讲解到这里结束了,感谢大家观看,有疑问的朋友可以在下面留言,我会为大家解答的!
💬 评论