Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android仿微信顯示本地圖片效果

Android仿微信顯示本地圖片效果

編輯:Android開發實例

 

我研究了下微信的本地圖片選擇的Demo,自己仿照的寫了下分享給大家,希望對以後有這樣子需求的朋友有一點幫助吧,主要使用的是ContentProvider掃描手機中的圖片,並用GridView將圖片顯示出來,關於GridView和ListView顯示圖片的問題,一直是一個很頭疼的問題,因為我們手機的內存有限,手機給每個應用程序分配的內存也有限,所以圖片多的情況下很容易伴隨著OOM的發生,不過現在也有很多的開源的圖片顯示框架,大家有興趣的可以去了解了解,今天我的這篇文章使用的是LruCache這個類(http://www.fengfly.com/plus/view-214548-1.html)以及對圖片進行相對應的裁剪,這樣也可以盡量的避免OOM的發生,我們先看下微信的效果吧

 

 

接下來我們就來實現這些效果吧,首先我們新建一個項目,取名ImageScan
首先我們先看第一個界面吧,使用將手機中的圖片掃描出來,然後根據圖片的所在的文件夾將其分類出來,並顯示所在文件夾裡面的一張圖片和文件夾中圖片個數,我們根據界面元素(文件夾名, 文件夾圖片個數,文件夾中的一張圖片)使用一個實體對象ImageBean來封裝這三個屬性

  1. package com.example.imagescan; 
  2.  
  3. /** 
  4.  * GridView的每個item的數據對象 
  5.  *  
  6.  * @author len 
  7.  * 
  8.  */ 
  9. public class ImageBean{ 
  10.     /** 
  11.      * 文件夾的第一張圖片路徑 
  12.      */ 
  13.     private String topImagePath; 
  14.     /** 
  15.      * 文件夾名 
  16.      */ 
  17.     private String folderName;  
  18.     /** 
  19.      * 文件夾中的圖片數 
  20.      */ 
  21.     private int imageCounts; 
  22.      
  23.     public String getTopImagePath() { 
  24.         return topImagePath; 
  25.     } 
  26.     public void setTopImagePath(String topImagePath) { 
  27.         this.topImagePath = topImagePath; 
  28.     } 
  29.     public String getFolderName() { 
  30.         return folderName; 
  31.     } 
  32.     public void setFolderName(String folderName) { 
  33.         this.folderName = folderName; 
  34.     } 
  35.     public int getImageCounts() { 
  36.         return imageCounts; 
  37.     } 
  38.     public void setImageCounts(int imageCounts) { 
  39.         this.imageCounts = imageCounts; 
  40.     } 
  41.      

接下來就是主界面的布局啦,上面的導航欄我沒有加進去,只有下面的GridView,所以說主界面布局中只有一個GridView

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     xmlns:tools="http://schemas.android.com/tools" 
  3.     android:layout_width="match_parent" 
  4.     android:layout_height="match_parent" > 
  5.  
  6.     <GridView 
  7.         android:id="@+id/main_grid" 
  8.         android:layout_width="match_parent" 
  9.         android:layout_height="match_parent" 
  10.         android:listSelector="@android:color/transparent" 
  11.         android:cacheColorHint="@android:color/transparent" 
  12.         android:stretchMode="columnWidth" 
  13.         android:horizontalSpacing="20dip" 
  14.         android:gravity="center" 
  15.         android:verticalSpacing="20dip" 
  16.         android:columnWidth="90dip" 
  17.         android:numColumns="2" > 
  18.     </GridView> 
  19.  
  20. </RelativeLayout> 

接下來就是GridView的Item的布局,看上面的圖也行你會認為他的效果是2張圖片添加的效果,其實不是,後面的疊加效果只是一張背景圖片而已,代碼先貼上來

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:layout_width="fill_parent" 
  4.     android:layout_height="wrap_content" > 
  5.  
  6.     <FrameLayout 
  7.         android:id="@+id/framelayout" 
  8.         android:layout_width="fill_parent" 
  9.         android:layout_height="wrap_content" > 
  10.  
  11.         <com.example.imagescan.MyImageView 
  12.             android:id="@+id/group_image" 
  13.             android:background="@drawable/albums_bg" 
  14.             android:src="@drawable/friends_sends_pictures_no" 
  15.             android:paddingLeft="20dip" 
  16.             android:paddingRight="20dip" 
  17.             android:paddingTop="18dip" 
  18.             android:paddingBottom="30dip" 
  19.             android:scaleType="fitXY" 
  20.             android:layout_width="fill_parent" 
  21.             android:layout_height="150dip" /> 
  22.  
  23.         <TextView 
  24.             android:id="@+id/group_count" 
  25.             android:layout_width="wrap_content" 
  26.             android:layout_height="wrap_content" 
  27.             android:background="@drawable/albums_icon_bg" 
  28.             android:gravity="center" 
  29.             android:layout_marginBottom="10dip" 
  30.             android:text="5" 
  31.             android:layout_gravity="bottom|center_horizontal" /> 
  32.     </FrameLayout> 
  33.  
  34.     <TextView 
  35.         android:id="@+id/group_title" 
  36.         android:layout_width="fill_parent" 
  37.         android:layout_height="wrap_content" 
  38.         android:gravity="center" 
  39.         android:layout_below="@id/framelayout" 
  40.         android:layout_centerHorizontal="true" 
  41.         android:ellipsize="end" 
  42.         android:singleLine="true" 
  43.         android:text="Camera" 
  44.         android:textSize="16sp" /> 
  45.  
  46. </RelativeLayout> 

看到上面的布局代碼,也行你已經發現了,上面使用的是自定義的MyImageView,我先不說這個自定義MyImageView的作用,待會再給大家說,我們繼續看代碼
第一個界面的主要代碼

  1. package com.example.imagescan; 
  2.  
  3. import java.io.File; 
  4. import java.util.ArrayList; 
  5. import java.util.HashMap; 
  6. import java.util.Iterator; 
  7. import java.util.List; 
  8. import java.util.Map; 
  9.  
  10. import android.app.Activity; 
  11. import android.app.ProgressDialog; 
  12. import android.content.ContentResolver; 
  13. import android.content.Intent; 
  14. import android.database.Cursor; 
  15. import android.net.Uri; 
  16. import android.os.Bundle; 
  17. import android.os.Handler; 
  18. import android.os.Message; 
  19. import android.provider.MediaStore; 
  20. import android.view.View; 
  21. import android.widget.AdapterView; 
  22. import android.widget.AdapterView.OnItemClickListener; 
  23. import android.widget.GridView; 
  24. /** 
  25.  *  
  26.  * @author xiaanming 
  27.  *  
  28.  * 
  29.  */ 
  30. public class MainActivity extends Activity { 
  31.     private HashMap<String, List<String>> mGruopMap = new HashMap<String, List<String>>(); 
  32.     private List<ImageBean> list = new ArrayList<ImageBean>(); 
  33.     private final static int SCAN_OK = 1; 
  34.     private ProgressDialog mProgressDialog; 
  35.     private GroupAdapter adapter; 
  36.     private GridView mGroupGridView; 
  37.      
  38.     private Handler mHandler = new Handler(){ 
  39.  
  40.         @Override 
  41.         public void handleMessage(Message msg) { 
  42.             super.handleMessage(msg); 
  43.             switch (msg.what) { 
  44.             case SCAN_OK: 
  45.                 //關閉進度條 
  46.                 mProgressDialog.dismiss(); 
  47.                  
  48.                 adapter = new GroupAdapter(MainActivity.this, list = subGroupOfImage(mGruopMap), mGroupGridView); 
  49.                 mGroupGridView.setAdapter(adapter); 
  50.                 break; 
  51.             } 
  52.         } 
  53.          
  54.     }; 
  55.  
  56.     @Override 
  57.     protected void onCreate(Bundle savedInstanceState) { 
  58.         super.onCreate(savedInstanceState); 
  59.         setContentView(R.layout.activity_main); 
  60.          
  61.         mGroupGridView = (GridView) findViewById(R.id.main_grid); 
  62.          
  63.         getImages(); 
  64.          
  65.         mGroupGridView.setOnItemClickListener(new OnItemClickListener() { 
  66.  
  67.             @Override 
  68.             public void onItemClick(AdapterView<?> parent, View view, 
  69.                     int position, long id) { 
  70.                 List<String> childList = mGruopMap.get(list.get(position).getFolderName()); 
  71.                  
  72.                 Intent mIntent = new Intent(MainActivity.this, ShowImageActivity.class); 
  73.                 mIntent.putStringArrayListExtra("data", (ArrayList<String>)childList); 
  74.                 startActivity(mIntent); 
  75.                  
  76.             } 
  77.         }); 
  78.          
  79.     } 
  80.  
  81.  
  82.     /** 
  83.      * 利用ContentProvider掃描手機中的圖片,此方法在運行在子線程中 
  84.      */ 
  85.     private void getImages() { 
  86.         //顯示進度條 
  87.         mProgressDialog = ProgressDialog.show(this, null, "正在加載..."); 
  88.          
  89.         new Thread(new Runnable() { 
  90.              
  91.             @Override 
  92.             public void run() { 
  93.                 Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 
  94.                 ContentResolver mContentResolver = MainActivity.this.getContentResolver(); 
  95.  
  96.                 //只查詢jpeg和png的圖片 
  97.                 Cursor mCursor = mContentResolver.query(mImageUri, null, 
  98.                         MediaStore.Images.Media.MIME_TYPE + "=? or " 
  99.                                 + MediaStore.Images.Media.MIME_TYPE + "=?", 
  100.                         new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED); 
  101.                  
  102.                 if(mCursor == null){ 
  103.                     return; 
  104.                 } 
  105.                  
  106.                 while (mCursor.moveToNext()) { 
  107.                     //獲取圖片的路徑 
  108.                     String path = mCursor.getString(mCursor 
  109.                             .getColumnIndex(MediaStore.Images.Media.DATA)); 
  110.                      
  111.                     //獲取該圖片的父路徑名 
  112.                     String parentName = new File(path).getParentFile().getName(); 
  113.  
  114.                      
  115.                     //根據父路徑名將圖片放入到mGruopMap中 
  116.                     if (!mGruopMap.containsKey(parentName)) { 
  117.                         List<String> chileList = new ArrayList<String>(); 
  118.                         chileList.add(path); 
  119.                         mGruopMap.put(parentName, chileList); 
  120.                     } else { 
  121.                         mGruopMap.get(parentName).add(path); 
  122.                     } 
  123.                 } 
  124.                  
  125.                 //通知Handler掃描圖片完成 
  126.                 mHandler.sendEmptyMessage(SCAN_OK); 
  127.                 mCursor.close(); 
  128.             } 
  129.         }).start(); 
  130.          
  131.     } 
  132.      
  133.      
  134.     /** 
  135.      * 組裝分組界面GridView的數據源,因為我們掃描手機的時候將圖片信息放在HashMap中 
  136.      * 所以需要遍歷HashMap將數據組裝成List 
  137.      *  
  138.      * @param mGruopMap 
  139.      * @return 
  140.      */ 
  141.     private List<ImageBean> subGroupOfImage(HashMap<String, List<String>> mGruopMap){ 
  142.         if(mGruopMap.size() == 0){ 
  143.             return null; 
  144.         } 
  145.         List<ImageBean> list = new ArrayList<ImageBean>(); 
  146.          
  147.         Iterator<Map.Entry<String, List<String>>> it = mGruopMap.entrySet().iterator(); 
  148.         while (it.hasNext()) { 
  149.             Map.Entry<String, List<String>> entry = it.next(); 
  150.             ImageBean mImageBean = new ImageBean(); 
  151.             String key = entry.getKey(); 
  152.             List<String> value = entry.getValue(); 
  153.              
  154.             mImageBean.setFolderName(key); 
  155.             mImageBean.setImageCounts(value.size()); 
  156.             mImageBean.setTopImagePath(value.get(0));//獲取該組的第一張圖片 
  157.              
  158.             list.add(mImageBean); 
  159.         } 
  160.          
  161.         return list; 
  162.          
  163.     } 
  164.  
  165.  

首先看getImages()這個方法,該方法是使用ContentProvider將手機中的圖片掃描出來,我這裡只掃描了手機的外部存儲中的圖片,由於手機中可能存在很多的圖片,掃描圖片又比較耗時,所以我們在這裡開啟了子線程去獲取圖片,掃描的圖片都存放在Cursor中,我們先要將圖片按照文件夾進行分類,我們使用了HashMap來進行分類並將結果存儲到mGruopMap(Key是文件夾名,Value是文件夾中的圖片路徑的List)中,分類完了關閉Cursor並利用Handler來通知主線程
然後是subGroupOfImage()方法,改方法是將mGruopMap的數據組裝到List中,在List中存放GridView中的每個item的數據對象ImageBean, 遍歷HashMap對象,具體的邏輯看代碼,之後就是給GridView設置Adapter。
設置item點擊事件,點擊文件夾跳轉到展示文件夾圖片的Activity, 我們需要傳遞每個文件夾中的圖片的路徑的集合
看GroupAdapter的代碼之前,我們先看一個比較重要的類,本地圖片加載器NativeImageLoader

  1. package com.example.imagescan; 
  2.  
  3. import java.util.concurrent.ExecutorService; 
  4. import java.util.concurrent.Executors; 
  5.  
  6. import android.graphics.Bitmap; 
  7. import android.graphics.BitmapFactory; 
  8. import android.graphics.Point; 
  9. import android.os.Handler; 
  10. import android.os.Message; 
  11. import android.support.v4.util.LruCache; 
  12.  
  13. /** 
  14.  * 本地圖片加載器,采用的是異步解析本地圖片,單例模式利用getInstance()獲取NativeImageLoader實例 
  15.  * 調用loadNativeImage()方法加載本地圖片,此類可作為一個加載本地圖片的工具類 
  16.  *  
  17.  *  
  18.  * @author xiaanming 
  19.  * 
  20.  */ 
  21. public class NativeImageLoader { 
  22.     private LruCache<String, Bitmap> mMemoryCache; 
  23.     private static NativeImageLoader mInstance = new NativeImageLoader(); 
  24.     private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1); 
  25.      
  26.      
  27.     private NativeImageLoader(){ 
  28.         //獲取應用程序的最大內存 
  29.         final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 
  30.  
  31.         //用最大內存的1/4來存儲圖片 
  32.         final int cacheSize = maxMemory / 4; 
  33.         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { 
  34.              
  35.             //獲取每張圖片的大小 
  36.             @Override 
  37.             protected int sizeOf(String key, Bitmap bitmap) { 
  38.                 return bitmap.getRowBytes() * bitmap.getHeight() / 1024; 
  39.             } 
  40.         }; 
  41.     } 
  42.      
  43.     /** 
  44.      * 通過此方法來獲取NativeImageLoader的實例 
  45.      * @return 
  46.      */ 
  47.     public static NativeImageLoader getInstance(){ 
  48.         return mInstance; 
  49.     } 
  50.      
  51.      
  52.     /** 
  53.      * 加載本地圖片,對圖片不進行裁剪 
  54.      * @param path 
  55.      * @param mCallBack 
  56.      * @return 
  57.      */ 
  58.     public Bitmap loadNativeImage(final String path, final NativeImageCallBack mCallBack){ 
  59.         return this.loadNativeImage(path, null, mCallBack); 
  60.     } 
  61.      
  62.     /** 
  63.      * 此方法來加載本地圖片,這裡的mPoint是用來封裝ImageView的寬和高,我們會根據ImageView控件的大小來裁剪Bitmap 
  64.      * 如果你不想裁剪圖片,調用loadNativeImage(final String path, final NativeImageCallBack mCallBack)來加載 
  65.      * @param path 
  66.      * @param mPoint 
  67.      * @param mCallBack 
  68.      * @return 
  69.      */ 
  70.     public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){ 
  71.         //先獲取內存中的Bitmap 
  72.         Bitmap bitmap = getBitmapFromMemCache(path); 
  73.          
  74.         final Handler mHander = new Handler(){ 
  75.  
  76.             @Override 
  77.             public void handleMessage(Message msg) { 
  78.                 super.handleMessage(msg); 
  79.                 mCallBack.onImageLoader((Bitmap)msg.obj, path); 
  80.             } 
  81.              
  82.         }; 
  83.          
  84.         //若該Bitmap不在內存緩存中,則啟用線程去加載本地的圖片,並將Bitmap加入到mMemoryCache中 
  85.         if(bitmap == null){ 
  86.             mImageThreadPool.execute(new Runnable() { 
  87.                  
  88.                 @Override 
  89.                 public void run() { 
  90.                     //先獲取圖片的縮略圖 
  91.                     Bitmap mBitmap = decodeThumbBitmapForFile(path, mPoint == null ? 0: mPoint.x, mPoint == null ? 0: mPoint.y); 
  92.                     Message msg = mHander.obtainMessage(); 
  93.                     msg.obj = mBitmap; 
  94.                     mHander.sendMessage(msg); 
  95.                      
  96.                     //將圖片加入到內存緩存 
  97.                     addBitmapToMemoryCache(path, mBitmap); 
  98.                 } 
  99.             }); 
  100.         } 
  101.         return bitmap; 
  102.          
  103.     } 
  104.  
  105.      
  106.      
  107.     /** 
  108.      * 往內存緩存中添加Bitmap 
  109.      *  
  110.      * @param key 
  111.      * @param bitmap 
  112.      */ 
  113.     private void addBitmapToMemoryCache(String key, Bitmap bitmap) { 
  114.         if (getBitmapFromMemCache(key) == null && bitmap != null) { 
  115.             mMemoryCache.put(key, bitmap); 
  116.         } 
  117.     } 
  118.  
  119.     /** 
  120.      * 根據key來獲取內存中的圖片 
  121.      * @param key 
  122.      * @return 
  123.      */ 
  124.     private Bitmap getBitmapFromMemCache(String key) { 
  125.         return mMemoryCache.get(key); 
  126.     } 
  127.      
  128.      
  129.     /** 
  130.      * 根據View(主要是ImageView)的寬和高來獲取圖片的縮略圖 
  131.      * @param path 
  132.      * @param viewWidth 
  133.      * @param viewHeight 
  134.      * @return 
  135.      */ 
  136.     private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight){ 
  137.         BitmapFactory.Options options = new BitmapFactory.Options(); 
  138.         //設置為true,表示解析Bitmap對象,該對象不占內存 
  139.         options.inJustDecodeBounds = true; 
  140.         BitmapFactory.decodeFile(path, options); 
  141.         //設置縮放比例 
  142.         options.inSampleSize = computeScale(options, viewWidth, viewHeight); 
  143.          
  144.         //設置為false,解析Bitmap對象加入到內存中 
  145.         options.inJustDecodeBounds = false; 
  146.          
  147.         return BitmapFactory.decodeFile(path, options); 
  148.     } 
  149.      
  150.      
  151.     /** 
  152.      * 根據View(主要是ImageView)的寬和高來計算Bitmap縮放比例。默認不縮放 
  153.      * @param options 
  154.      * @param width 
  155.      * @param height 
  156.      */ 
  157.     private int computeScale(BitmapFactory.Options options, int viewWidth, int viewHeight){ 
  158.         int inSampleSize = 1; 
  159.         if(viewWidth == 0 || viewWidth == 0){ 
  160.             return inSampleSize; 
  161.         } 
  162.         int bitmapWidth = options.outWidth; 
  163.         int bitmapHeight = options.outHeight; 
  164.          
  165.         //假如Bitmap的寬度或高度大於我們設定圖片的View的寬高,則計算縮放比例 
  166.         if(bitmapWidth > viewWidth || bitmapHeight > viewWidth){ 
  167.             int widthScale = Math.round((float) bitmapWidth / (float) viewWidth); 
  168.             int heightScale = Math.round((float) bitmapHeight / (float) viewWidth); 
  169.              
  170.             //為了保證圖片不縮放變形,我們取寬高比例最小的那個 
  171.             inSampleSize = widthScale < heightScale ? widthScale : heightScale; 
  172.         } 
  173.         return inSampleSize; 
  174.     } 
  175.      
  176.      
  177.     /** 
  178.      * 加載本地圖片的回調接口 
  179.      *  
  180.      * @author xiaanming 
  181.      * 
  182.      */ 
  183.     public interface NativeImageCallBack{ 
  184.         /** 
  185.          * 當子線程加載完了本地的圖片,將Bitmap和圖片路徑回調在此方法中 
  186.          * @param bitmap 
  187.          * @param path 
  188.          */ 
  189.         public void onImageLoader(Bitmap bitmap, String path); 
  190.     } 

該類是一個單例類,提供了本地圖片加載,內存緩存,裁剪等邏輯,該類在加載本地圖片的時候采用的是異步加載的方式,對於大圖片的加載也是比較耗時的,所以采用子線程的方式去加載,對於圖片的緩存機制使用的是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類的代碼

  1. package com.example.imagescan; 
  2.  
  3. import java.util.List; 
  4.  
  5. import android.content.Context; 
  6. import android.graphics.Bitmap; 
  7. import android.graphics.Point; 
  8. import android.view.LayoutInflater; 
  9. import android.view.View; 
  10. import android.view.ViewGroup; 
  11. import android.widget.BaseAdapter; 
  12. import android.widget.GridView; 
  13. import android.widget.ImageView; 
  14. import android.widget.TextView; 
  15.  
  16. import com.example.imagescan.MyImageView.OnMeasureListener; 
  17. import com.example.imagescan.NativeImageLoader.NativeImageCallBack; 
  18.  
  19. public class GroupAdapter extends BaseAdapter{ 
  20.     private List<ImageBean> list; 
  21.     private Point mPoint = new Point(0, 0);//用來封裝ImageView的寬和高的對象 
  22.     private GridView mGridView; 
  23.     protected LayoutInflater mInflater; 
  24.      
  25.     @Override 
  26.     public int getCount() { 
  27.         return list.size(); 
  28.     } 
  29.  
  30.     @Override 
  31.     public Object getItem(int position) { 
  32.         return list.get(position); 
  33.     } 
  34.  
  35.  
  36.     @Override 
  37.     public long getItemId(int position) { 
  38.         return position; 
  39.     } 
  40.      
  41.     public GroupAdapter(Context context, List<ImageBean> list, GridView mGridView){ 
  42.         this.list = list; 
  43.         this.mGridView = mGridView; 
  44.         mInflater = LayoutInflater.from(context); 
  45.     } 
  46.      
  47.  
  48.     @Override 
  49.     public View getView(int position, View convertView, ViewGroup parent) { 
  50.         final ViewHolder viewHolder; 
  51.         ImageBean mImageBean = list.get(position); 
  52.         String path = mImageBean.getTopImagePath(); 
  53.         if(convertView == null){ 
  54.             viewHolder = new ViewHolder(); 
  55.             convertView = mInflater.inflate(R.layout.grid_group_item, null); 
  56.             viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.group_image); 
  57.             viewHolder.mTextViewTitle = (TextView) convertView.findViewById(R.id.group_title); 
  58.             viewHolder.mTextViewCounts = (TextView) convertView.findViewById(R.id.group_count); 
  59.              
  60.             //用來監聽ImageView的寬和高 
  61.             viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() { 
  62.                  
  63.                 @Override 
  64.                 public void onMeasureSize(int width, int height) { 
  65.                     mPoint.set(width, height); 
  66.                 } 
  67.             }); 
  68.              
  69.             convertView.setTag(viewHolder); 
  70.         }else{ 
  71.             viewHolder = (ViewHolder) convertView.getTag(); 
  72.             viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no); 
  73.         } 
  74.          
  75.         viewHolder.mTextViewTitle.setText(mImageBean.getFolderName()); 
  76.         viewHolder.mTextViewCounts.setText(Integer.toString(mImageBean.getImageCounts())); 
  77.         //給ImageView設置路徑Tag,這是異步加載圖片的小技巧 
  78.         viewHolder.mImageView.setTag(path); 
  79.          
  80.          
  81.         //利用NativeImageLoader類加載本地圖片 
  82.         Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() { 
  83.              
  84.             @Override 
  85.             public void onImageLoader(Bitmap bitmap, String path) { 
  86.                 ImageView mImageView = (ImageView) mGridView.findViewWithTag(path); 
  87.                 if(bitmap != null && mImageView != null){ 
  88.                     mImageView.setImageBitmap(bitmap); 
  89.                 } 
  90.             } 
  91.         }); 
  92.          
  93.         if(bitmap != null){ 
  94.             viewHolder.mImageView.setImageBitmap(bitmap); 
  95.         }else{ 
  96.             viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no); 
  97.         } 
  98.          
  99.          
  100.         return convertView; 
  101.     } 
  102.      
  103.      
  104.      
  105.     public static class ViewHolder{ 
  106.         public MyImageView mImageView; 
  107.         public TextView mTextViewTitle; 
  108.         public TextView mTextViewCounts; 
  109.     } 
  110.  
  111.      

首先我們將每個item的圖片路徑設置到該ImageView上面,然後利用NativeImageLoader來加載本地圖片,很普通,但是我們想在getView()中獲取ImageView的寬和高存在問題,在getView()裡面剛開始顯示item的時候利用ImageView.getWidth()獲取的都是0,這個比較郁悶,但是我們想要獲取ImageView的寬和高怎麼辦呢?於是我想到了自定義Image View,在onMeasure()中利用回調的模式主動通知我ImageView測量的寬和高,但是這有一個小小的問題,就是顯示GridView的第一個item的時候,獲取的寬和高還是0,第二個就能正常獲取了,第一個寬和高為0,我們不對第一張圖片進行裁剪而已,在效率上也沒啥問題,不知道大家有沒有好的方法,可以在getView()中獲取Item中某個控件的寬和高。
自定義MyImageView的代碼,我們只需要設置OnMeasureListener監聽,當MyImageView測量完畢之後,就會將測量的寬和高回調到onMeasureSize()中,然後我們可以根據MyImageView的大小來裁剪圖片

  1. package com.example.imagescan; 
  2.  
  3. import android.content.Context; 
  4. import android.util.AttributeSet; 
  5. import android.widget.ImageView; 
  6.  
  7. public class MyImageView extends ImageView { 
  8.     private OnMeasureListener onMeasureListener; 
  9.      
  10.     public void setOnMeasureListener(OnMeasureListener onMeasureListener) { 
  11.         this.onMeasureListener = onMeasureListener; 
  12.     } 
  13.  
  14.     public MyImageView(Context context, AttributeSet attrs) { 
  15.         super(context, attrs); 
  16.     } 
  17.  
  18.     public MyImageView(Context context, AttributeSet attrs, int defStyle) { 
  19.         super(context, attrs, defStyle); 
  20.     } 
  21.  
  22.     @Override 
  23.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  24.         super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
  25.          
  26.         //將圖片測量的大小回調到onMeasureSize()方法中 
  27.         if(onMeasureListener != null){ 
  28.             onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight()); 
  29.         } 
  30.     } 
  31.  
  32.     public interface OnMeasureListener{ 
  33.         public void onMeasureSize(int width, int height); 
  34.     } 
  35.      

 

上面這些代碼就完成了第一個界面的功能了,接下來就是點擊GridView的item跳轉另一個界面來顯示該文件夾下面的所有圖片,功能跟第一個界面差不多,也是使用GridView來顯示圖片,第二個界面的布局代碼我就不貼了,直接貼上界面的代碼

  1. package com.example.imagescan; 
  2.  
  3. import java.util.List; 
  4.  
  5. import android.app.Activity; 
  6. import android.os.Bundle; 
  7. import android.widget.GridView; 
  8. import android.widget.Toast; 
  9.  
  10. public class ShowImageActivity extends Activity { 
  11.     private GridView mGridView; 
  12.     private List<String> list; 
  13.     private ChildAdapter adapter; 
  14.  
  15.     @Override 
  16.     protected void onCreate(Bundle savedInstanceState) { 
  17.         super.onCreate(savedInstanceState); 
  18.         setContentView(R.layout.show_image_activity); 
  19.          
  20.         mGridView = (GridView) findViewById(R.id.child_grid); 
  21.         list = getIntent().getStringArrayListExtra("data"); 
  22.          
  23.         adapter = new ChildAdapter(this, list, mGridView); 
  24.         mGridView.setAdapter(adapter); 
  25.          
  26.     } 
  27.  
  28.     @Override 
  29.     public void onBackPressed() { 
  30.         Toast.makeText(this, "選中 " + adapter.getSelectItems().size() + " item", Toast.LENGTH_LONG).show(); 
  31.         super.onBackPressed(); 
  32.     } 
  33.      
  34.      

GridView的item上面一個我們自定義的MyImageView用來顯示圖片,另外還有一個CheckBox來記錄我們選中情況,Adapter的代碼如下

  1. package com.example.imagescan; 
  2.  
  3. import java.util.ArrayList; 
  4. import java.util.HashMap; 
  5. import java.util.Iterator; 
  6. import java.util.List; 
  7. import java.util.Map; 
  8.  
  9. import android.content.Context; 
  10. import android.graphics.Bitmap; 
  11. import android.graphics.Point; 
  12. import android.view.LayoutInflater; 
  13. import android.view.View; 
  14. import android.view.ViewGroup; 
  15. import android.widget.BaseAdapter; 
  16. import android.widget.CheckBox; 
  17. import android.widget.CompoundButton; 
  18. import android.widget.ImageView; 
  19. import android.widget.CompoundButton.OnCheckedChangeListener; 
  20. import android.widget.GridView; 
  21.  
  22. import com.example.imagescan.MyImageView.OnMeasureListener; 
  23. import com.example.imagescan.NativeImageLoader.NativeImageCallBack; 
  24. import com.nineoldandroids.animation.AnimatorSet; 
  25. import com.nineoldandroids.animation.ObjectAnimator; 
  26.  
  27. public class ChildAdapter extends BaseAdapter { 
  28.     private Point mPoint = new Point(0, 0);//用來封裝ImageView的寬和高的對象 
  29.     /** 
  30.      * 用來存儲圖片的選中情況 
  31.      */ 
  32.     private HashMap<Integer, Boolean> mSelectMap = new HashMap<Integer, Boolean>(); 
  33.     private GridView mGridView; 
  34.     private List<String> list; 
  35.     protected LayoutInflater mInflater; 
  36.  
  37.     public ChildAdapter(Context context, List<String> list, GridView mGridView) { 
  38.         this.list = list; 
  39.         this.mGridView = mGridView; 
  40.         mInflater = LayoutInflater.from(context); 
  41.     } 
  42.      
  43.     @Override 
  44.     public int getCount() { 
  45.         return list.size(); 
  46.     } 
  47.  
  48.     @Override 
  49.     public Object getItem(int position) { 
  50.         return list.get(position); 
  51.     } 
  52.  
  53.  
  54.     @Override 
  55.     public long getItemId(int position) { 
  56.         return position; 
  57.     } 
  58.      
  59.     @Override 
  60.     public View getView(final int position, View convertView, ViewGroup parent) { 
  61.         final ViewHolder viewHolder; 
  62.         String path = list.get(position); 
  63.          
  64.         if(convertView == null){ 
  65.             convertView = mInflater.inflate(R.layout.grid_child_item, null); 
  66.             viewHolder = new ViewHolder(); 
  67.             viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.child_image); 
  68.             viewHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.child_checkbox); 
  69.              
  70.             //用來監聽ImageView的寬和高 
  71.             viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() { 
  72.                  
  73.                 @Override 
  74.                 public void onMeasureSize(int width, int height) { 
  75.                     mPoint.set(width, height); 
  76.                 } 
  77.             }); 
  78.              
  79.             convertView.setTag(viewHolder); 
  80.         }else{ 
  81.             viewHolder = (ViewHolder) convertView.getTag(); 
  82.             viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no); 
  83.         } 
  84.         viewHolder.mImageView.setTag(path); 
  85.         viewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 
  86.              
  87.             @Override 
  88.             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
  89.                 //如果是未選中的CheckBox,則添加動畫 
  90.                 if(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){ 
  91.                     addAnimation(viewHolder.mCheckBox); 
  92.                 } 
  93.                 mSelectMap.put(position, isChecked); 
  94.             } 
  95.         }); 
  96.          
  97.         viewHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : false); 
  98.          
  99.         //利用NativeImageLoader類加載本地圖片 
  100.         Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() { 
  101.              
  102.             @Override 
  103.             public void onImageLoader(Bitmap bitmap, String path) { 
  104.                 ImageView mImageView = (ImageView) mGridView.findViewWithTag(path); 
  105.                 if(bitmap != null && mImageView != null){ 
  106.                     mImageView.setImageBitmap(bitmap); 
  107.                 } 
  108.             } 
  109.         }); 
  110.          
  111.         if(bitmap != null){ 
  112.             viewHolder.mImageView.setImageBitmap(bitmap); 
  113.         }else{ 
  114.             viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no); 
  115.         } 
  116.          
  117.         return convertView; 
  118.     } 
  119.      
  120.     /** 
  121.      * 給CheckBox加點擊動畫,利用開源庫nineoldandroids設置動畫  
  122.      * @param view 
  123.      */ 
  124.     private void addAnimation(View view){ 
  125.         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}; 
  126.         AnimatorSet set = new AnimatorSet(); 
  127.         set.playTogether(ObjectAnimator.ofFloat(view, "scaleX", vaules),  
  128.                 ObjectAnimator.ofFloat(view, "scaleY", vaules)); 
  129.                 set.setDuration(150); 
  130.         set.start(); 
  131.     } 
  132.      
  133.      
  134.     /** 
  135.      * 獲取選中的Item的position 
  136.      * @return 
  137.      */ 
  138.     public List<Integer> getSelectItems(){ 
  139.         List<Integer> list = new ArrayList<Integer>(); 
  140.         for(Iterator<Map.Entry<Integer, Boolean>> it = mSelectMap.entrySet().iterator(); it.hasNext();){ 
  141.             Map.Entry<Integer, Boolean> entry = it.next(); 
  142.             if(entry.getValue()){ 
  143.                 list.add(entry.getKey()); 
  144.             } 
  145.         } 
  146.          
  147.         return list; 
  148.     } 
  149.      
  150.      
  151.     public static class ViewHolder{ 
  152.         public MyImageView mImageView; 
  153.         public CheckBox mCheckBox; 
  154.     } 
  155.  
  156.  

 

第二個界面的Adapter跟第一個界面差不多,無非多了一個CheckBox用來記錄圖片選擇情況,我們只需要對CheckBox設置setOnCheckedChangeListener監聽,微信的選中之後CheckBox有一個動畫效果,所以我利用nineoldandroids動畫庫也給CheckBox加了一個動畫效果,直接調用addAnimation()方法就能添加了,getSelectItems()方法就能獲取我們選中的item的position了,知道了選中的position,其他的信息就都知道了,微信有對圖片進行預覽的功能,我這裡就不添加了,如果有這個需求可以自行添加,給大家推薦一個https://github.com/chrisbanes/PhotoView

運行項目,效果如下

 

起來還不錯吧,采用的是異步讀取圖片,對圖片進行了緩存和裁剪,使得在顯示本地圖片方面比較流暢,GridView滑動也挺流暢的,也有效的避免OOM的產生,工程中有些東西還沒有貼完全,有興趣的朋友可以下載Demo來運行一下。

源碼下載

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved