【轉載】Android 使用開源庫StickyGridHeaders來實現帶sections和headers的GridView顯示本地圖片效果

我是轉載的,原作地址:http://blog.csdn.net/xiaanming/article/details/20481185


轉載請註明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/20481185),請尊重他人的辛勤勞動成果,謝謝!


StickyGridHeaders是一個自定義GridView帶sections和headers的Android庫,sections就是GridView item之間的分隔,headers就是固定在GridView頂部的標題,類似一些Android手機聯繫人的效果,StickyGridHeaders的介紹在https://github.com/TonicArtos/StickyGridHeaders,與此對應也有一個相同效果的自定義ListView帶sections和headers的開源庫https://github.com/emilsjolander/StickyListHeaders,大家有興趣的可以去看下,我這裏介紹的是StickyGridHeaders的使用,我在Android應用方面看到使用StickyGridHeaders的不是很多,而是在Iphone上看到相冊採用的是這種效果,於是我就使用StickyGridHeaders來仿照Iphone按照日期分隔顯示本地圖片

我們先新建一個Android項目StickyHeaderGridView,去https://github.com/TonicArtos/StickyGridHeaders下載開源庫,爲了方便瀏覽源碼我直接將源碼拷到我的工程中了


com.tonicartos.widget.stickygridheaders這個包就是我放StickyGridHeaders開源庫的源碼,com.example.stickyheadergridview這個包是我實現此功能的代碼,類看起來還蠻多的,下面我就一一來介紹了

GridItem用來封裝StickyGridHeadersGridView 每個Item的數據,裏面有本地圖片的路徑,圖片加入手機系統的時間和headerId

[java] view plain copy
  1. package com.example.stickyheadergridview;  
  2. /** 
  3.  * @blog http://blog.csdn.net/xiaanming 
  4.  *  
  5.  * @author xiaanming 
  6.  * 
  7.  */  
  8. public class GridItem {  
  9.     /** 
  10.      * 圖片的路徑 
  11.      */  
  12.     private String path;  
  13.     /** 
  14.      * 圖片加入手機中的時間,只取了年月日 
  15.      */  
  16.     private String time;  
  17.     /** 
  18.      * 每個Item對應的HeaderId 
  19.      */  
  20.     private int headerId;  
  21.   
  22.     public GridItem(String path, String time) {  
  23.         super();  
  24.         this.path = path;  
  25.         this.time = time;  
  26.     }  
  27.       
  28.     public String getPath() {  
  29.         return path;  
  30.     }  
  31.     public void setPath(String path) {  
  32.         this.path = path;  
  33.     }  
  34.     public String getTime() {  
  35.         return time;  
  36.     }  
  37.     public void setTime(String time) {  
  38.         this.time = time;  
  39.     }  
  40.   
  41.     public int getHeaderId() {  
  42.         return headerId;  
  43.     }  
  44.   
  45.     public void setHeaderId(int headerId) {  
  46.         this.headerId = headerId;  
  47.     }  
  48.       
  49.   
  50. }  
 圖片的路徑path和圖片加入的時間time 我們直接可以通過ContentProvider獲取,但是headerId需要我們根據邏輯來生成。

[java] view plain copy
  1. package com.example.stickyheadergridview;  
  2.   
  3. import android.content.ContentResolver;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.database.Cursor;  
  7. import android.net.Uri;  
  8. import android.os.Environment;  
  9. import android.os.Handler;  
  10. import android.os.Message;  
  11. import android.provider.MediaStore;  
  12. /** 
  13.  * 圖片掃描器 
  14.  *  
  15.  * @author xiaanming 
  16.  * 
  17.  */  
  18. public class ImageScanner {  
  19.     private Context mContext;  
  20.       
  21.     public ImageScanner(Context context){  
  22.         this.mContext = context;  
  23.     }  
  24.       
  25.     /** 
  26.      * 利用ContentProvider掃描手機中的圖片,將掃描的Cursor回調到ScanCompleteCallBack 
  27.      * 接口的scanComplete方法中,此方法在運行在子線程中 
  28.      */  
  29.     public void scanImages(final ScanCompleteCallBack callback) {  
  30.         final Handler mHandler = new Handler() {  
  31.   
  32.             @Override  
  33.             public void handleMessage(Message msg) {  
  34.                 super.handleMessage(msg);  
  35.                 callback.scanComplete((Cursor)msg.obj);  
  36.             }  
  37.         };  
  38.           
  39.         new Thread(new Runnable() {  
  40.   
  41.             @Override  
  42.             public void run() {  
  43.                 //先發送廣播掃描下整個sd卡  
  44.                 mContext.sendBroadcast(new Intent(   
  45.                             Intent.ACTION_MEDIA_MOUNTED,   
  46.                             Uri.parse("file://" + Environment.getExternalStorageDirectory())));  
  47.                   
  48.                 Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
  49.                 ContentResolver mContentResolver = mContext.getContentResolver();  
  50.                   
  51.                 Cursor mCursor = mContentResolver.query(mImageUri, nullnullnull, MediaStore.Images.Media.DATE_ADDED);  
  52.                   
  53.                 //利用Handler通知調用線程  
  54.                 Message msg = mHandler.obtainMessage();  
  55.                 msg.obj = mCursor;  
  56.                 mHandler.sendMessage(msg);  
  57.             }  
  58.         }).start();  
  59.   
  60.     }  
  61.       
  62.     /** 
  63.      * 掃描完成之後的回調接口 
  64.      * 
  65.      */  
  66.     public static interface ScanCompleteCallBack{  
  67.         public void scanComplete(Cursor cursor);  
  68.     }  
  69.   
  70.   
  71. }  
ImageScanner是一個圖片的掃描器類,該類使用ContentProvider掃描手機中的圖片,我們通過調用scanImages()方法就能對手機中的圖片進行掃描,將掃描的Cursor回調到ScanCompleteCallBack 接口的scanComplete方法中,由於考慮到掃描圖片屬於耗時操作,所以該操作運行在子線程中,在我們掃描圖片之前我們需要先發送廣播來掃描外部媒體庫,爲什麼要這麼做呢,假如我們新增加一張圖片到sd卡,圖片確實已經添加了進去,但是我們此時的媒體庫還沒有同步更新,若不同步媒體庫我們就看不到新增加的圖片,當然我們可以通過重新啓動系統來更新媒體庫,但是這樣不可取,所以我們直接發送廣播就可以同步媒體庫了。

[java] view plain copy
  1. package com.example.stickyheadergridview;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4.   
  5. import android.graphics.Bitmap;  
  6. import android.graphics.BitmapFactory;  
  7. import android.graphics.Point;  
  8. import android.os.Handler;  
  9. import android.os.Message;  
  10. import android.support.v4.util.LruCache;  
  11. import android.util.Log;  
  12.   
  13. /** 
  14.  * 本地圖片加載器,採用的是異步解析本地圖片,單例模式利用getInstance()獲取NativeImageLoader實例 
  15.  * 調用loadNativeImage()方法加載本地圖片,此類可作爲一個加載本地圖片的工具類 
  16.  *  
  17.  * @blog http://blog.csdn.net/xiaanming 
  18.  *  
  19.  * @author xiaanming 
  20.  * 
  21.  */  
  22. public class NativeImageLoader {  
  23.     private static final String TAG = NativeImageLoader.class.getSimpleName();  
  24.     private static NativeImageLoader mInstance = new NativeImageLoader();  
  25.     private static LruCache<String, Bitmap> mMemoryCache;  
  26.     private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1);  
  27.       
  28.       
  29.     private NativeImageLoader(){  
  30.         //獲取應用程序的最大內存  
  31.         final int maxMemory = (int) (Runtime.getRuntime().maxMemory());  
  32.   
  33.         //用最大內存的1/8來存儲圖片  
  34.         final int cacheSize = maxMemory / 8;  
  35.         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  36.               
  37.             //獲取每張圖片的bytes  
  38.             @Override  
  39.             protected int sizeOf(String key, Bitmap bitmap) {  
  40.                 return bitmap.getRowBytes() * bitmap.getHeight();  
  41.             }  
  42.               
  43.         };  
  44.     }  
  45.       
  46.     /** 
  47.      * 通過此方法來獲取NativeImageLoader的實例 
  48.      * @return 
  49.      */  
  50.     public static NativeImageLoader getInstance(){  
  51.         return mInstance;  
  52.     }  
  53.       
  54.       
  55.     /** 
  56.      * 加載本地圖片,對圖片不進行裁剪 
  57.      * @param path 
  58.      * @param mCallBack 
  59.      * @return 
  60.      */  
  61.     public Bitmap loadNativeImage(final String path, final NativeImageCallBack mCallBack){  
  62.         return this.loadNativeImage(path, null, mCallBack);  
  63.     }  
  64.       
  65.     /** 
  66.      * 此方法來加載本地圖片,這裏的mPoint是用來封裝ImageView的寬和高,我們會根據ImageView控件的大小來裁剪Bitmap 
  67.      * 如果你不想裁剪圖片,調用loadNativeImage(final String path, final NativeImageCallBack mCallBack)來加載 
  68.      * @param path 
  69.      * @param mPoint 
  70.      * @param mCallBack 
  71.      * @return 
  72.      */  
  73.     public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){  
  74.         //先獲取內存中的Bitmap  
  75.         Bitmap bitmap = getBitmapFromMemCache(path);  
  76.           
  77.         final Handler mHander = new Handler(){  
  78.   
  79.             @Override  
  80.             public void handleMessage(Message msg) {  
  81.                 super.handleMessage(msg);  
  82.                 mCallBack.onImageLoader((Bitmap)msg.obj, path);  
  83.             }  
  84.               
  85.         };  
  86.           
  87.         //若該Bitmap不在內存緩存中,則啓用線程去加載本地的圖片,並將Bitmap加入到mMemoryCache中  
  88.         if(bitmap == null){  
  89.             mImageThreadPool.execute(new Runnable() {  
  90.                   
  91.                 @Override  
  92.                 public void run() {  
  93.                     //先獲取圖片的縮略圖  
  94.                     Bitmap mBitmap = decodeThumbBitmapForFile(path, mPoint == null ? 0: mPoint.x, mPoint == null ? 0: mPoint.y);  
  95.                     Message msg = mHander.obtainMessage();  
  96.                     msg.obj = mBitmap;  
  97.                     mHander.sendMessage(msg);  
  98.                       
  99.                     //將圖片加入到內存緩存  
  100.                     addBitmapToMemoryCache(path, mBitmap);  
  101.                 }  
  102.             });  
  103.         }  
  104.         return bitmap;  
  105.           
  106.     }  
  107.   
  108.       
  109.       
  110.     /** 
  111.      * 往內存緩存中添加Bitmap 
  112.      *  
  113.      * @param key 
  114.      * @param bitmap 
  115.      */  
  116.     private void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  117.         if (getBitmapFromMemCache(key) == null && bitmap != null) {  
  118.             mMemoryCache.put(key, bitmap);  
  119.         }  
  120.     }  
  121.   
  122.     /** 
  123.      * 根據key來獲取內存中的圖片 
  124.      * @param key 
  125.      * @return 
  126.      */  
  127.     private Bitmap getBitmapFromMemCache(String key) {  
  128.         Bitmap bitmap = mMemoryCache.get(key);  
  129.           
  130.         if(bitmap != null){  
  131.             Log.i(TAG, "get image for LRUCache , path = " + key);  
  132.         }  
  133.         return bitmap;  
  134.     }  
  135.       
  136.     /** 
  137.      * 清除LruCache中的bitmap 
  138.      */  
  139.     public void trimMemCache(){  
  140.         mMemoryCache.evictAll();  
  141.     }  
  142.       
  143.       
  144.     /** 
  145.      * 根據View(主要是ImageView)的寬和高來獲取圖片的縮略圖 
  146.      * @param path 
  147.      * @param viewWidth 
  148.      * @param viewHeight 
  149.      * @return 
  150.      */  
  151.     private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight){  
  152.         BitmapFactory.Options options = new BitmapFactory.Options();  
  153.         //設置爲true,表示解析Bitmap對象,該對象不佔內存  
  154.         options.inJustDecodeBounds = true;  
  155.         BitmapFactory.decodeFile(path, options);  
  156.         //設置縮放比例  
  157.         options.inSampleSize = computeScale(options, viewWidth, viewHeight);  
  158.           
  159.         //設置爲false,解析Bitmap對象加入到內存中  
  160.         options.inJustDecodeBounds = false;  
  161.           
  162.           
  163.         Log.e(TAG, "get Iamge form file,  path = " + path);  
  164.           
  165.         return BitmapFactory.decodeFile(path, options);  
  166.     }  
  167.       
  168.       
  169.     /** 
  170.      * 根據View(主要是ImageView)的寬和高來計算Bitmap縮放比例。默認不縮放 
  171.      * @param options 
  172.      * @param width 
  173.      * @param height 
  174.      */  
  175.     private int computeScale(BitmapFactory.Options options, int viewWidth, int viewHeight){  
  176.         int inSampleSize = 1;  
  177.         if(viewWidth == 0 || viewWidth == 0){  
  178.             return inSampleSize;  
  179.         }  
  180.         int bitmapWidth = options.outWidth;  
  181.         int bitmapHeight = options.outHeight;  
  182.           
  183.         //假如Bitmap的寬度或高度大於我們設定圖片的View的寬高,則計算縮放比例  
  184.         if(bitmapWidth > viewWidth || bitmapHeight > viewWidth){  
  185.             int widthScale = Math.round((float) bitmapWidth / (float) viewWidth);  
  186.             int heightScale = Math.round((float) bitmapHeight / (float) viewWidth);  
  187.               
  188.             //爲了保證圖片不縮放變形,我們取寬高比例最小的那個  
  189.             inSampleSize = widthScale < heightScale ? widthScale : heightScale;  
  190.         }  
  191.         return inSampleSize;  
  192.     }  
  193.       
  194.       
  195.     /** 
  196.      * 加載本地圖片的回調接口 
  197.      *  
  198.      * @author xiaanming 
  199.      * 
  200.      */  
  201.     public interface NativeImageCallBack{  
  202.         /** 
  203.          * 當子線程加載完了本地的圖片,將Bitmap和圖片路徑回調在此方法中 
  204.          * @param bitmap 
  205.          * @param path 
  206.          */  
  207.         public void onImageLoader(Bitmap bitmap, String path);  
  208.     }  
  209. }  
NativeImageLoader該類是一個單例類,提供了本地圖片加載,內存緩存,裁剪等邏輯,該類在加載本地圖片的時候採用的是異步加載的方式,對於大圖片的加載也是比較耗時的,所以採用子線程的方式去加載,對於圖片的緩存機制使用的是LruCache,我們使用手機分配給應用程序內存的1/8用來緩存圖片,給圖片緩存的內存不宜太大,太大也可能會發生OOM,該類是用我之前寫的文章Android 使用ContentProvider掃描手機中的圖片,仿微信顯示本地圖片效果,在這裏我就不做過多的介紹,有興趣的可以去看看那篇文章,不過這裏新增了一個方法trimMemCache(),,用來清空LruCache使用的內存

我們看主界面的佈局代碼,裏面只有一個自定義的StickyGridHeadersGridView控件

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <com.tonicartos.widget.stickygridheaders.StickyGridHeadersGridView xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:id="@+id/asset_grid"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:clipToPadding="false"  
  8.     android:columnWidth="90dip"  
  9.     android:horizontalSpacing="3dip"  
  10.     android:numColumns="auto_fit"  
  11.     android:verticalSpacing="3dip" />  

在看主界面的代碼之前我們先看StickyGridAdapter的代碼

[java] view plain copy
  1. package com.example.stickyheadergridview;  
  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.stickyheadergridview.MyImageView.OnMeasureListener;  
  17. import com.example.stickyheadergridview.NativeImageLoader.NativeImageCallBack;  
  18. import com.tonicartos.widget.stickygridheaders.StickyGridHeadersSimpleAdapter;  
  19. /** 
  20.  * StickyHeaderGridView的適配器,除了要繼承BaseAdapter之外還需要 
  21.  * 實現StickyGridHeadersSimpleAdapter接口 
  22.  *  
  23.  * @blog http://blog.csdn.net/xiaanming 
  24.  *  
  25.  * @author xiaanming 
  26.  * 
  27.  */  
  28. public class StickyGridAdapter extends BaseAdapter implements  
  29.         StickyGridHeadersSimpleAdapter {  
  30.   
  31.     private List<GridItem> hasHeaderIdList;  
  32.     private LayoutInflater mInflater;  
  33.     private GridView mGridView;  
  34.     private Point mPoint = new Point(00);//用來封裝ImageView的寬和高的對象   
  35.   
  36.     public StickyGridAdapter(Context context, List<GridItem> hasHeaderIdList,  
  37.             GridView mGridView) {  
  38.         mInflater = LayoutInflater.from(context);  
  39.         this.mGridView = mGridView;  
  40.         this.hasHeaderIdList = hasHeaderIdList;  
  41.     }  
  42.   
  43.   
  44.     @Override  
  45.     public int getCount() {  
  46.         return hasHeaderIdList.size();  
  47.     }  
  48.   
  49.     @Override  
  50.     public Object getItem(int position) {  
  51.         return hasHeaderIdList.get(position);  
  52.     }  
  53.   
  54.     @Override  
  55.     public long getItemId(int position) {  
  56.         return position;  
  57.     }  
  58.   
  59.     @Override  
  60.     public View getView(int position, View convertView, ViewGroup parent) {  
  61.         ViewHolder mViewHolder;  
  62.         if (convertView == null) {  
  63.             mViewHolder = new ViewHolder();  
  64.             convertView = mInflater.inflate(R.layout.grid_item, parent, false);  
  65.             mViewHolder.mImageView = (MyImageView) convertView  
  66.                     .findViewById(R.id.grid_item);  
  67.             convertView.setTag(mViewHolder);  
  68.               
  69.              //用來監聽ImageView的寬和高    
  70.             mViewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() {    
  71.                     
  72.                 @Override    
  73.                 public void onMeasureSize(int width, int height) {    
  74.                     mPoint.set(width, height);    
  75.                 }    
  76.             });   
  77.               
  78.         } else {  
  79.             mViewHolder = (ViewHolder) convertView.getTag();  
  80.         }  
  81.   
  82.         String path = hasHeaderIdList.get(position).getPath();  
  83.         mViewHolder.mImageView.setTag(path);  
  84.   
  85.         Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint,  
  86.                 new NativeImageCallBack() {  
  87.   
  88.                     @Override  
  89.                     public void onImageLoader(Bitmap bitmap, String path) {  
  90.                         ImageView mImageView = (ImageView) mGridView  
  91.                                 .findViewWithTag(path);  
  92.                         if (bitmap != null && mImageView != null) {  
  93.                             mImageView.setImageBitmap(bitmap);  
  94.                         }  
  95.                     }  
  96.                 });  
  97.   
  98.         if (bitmap != null) {  
  99.             mViewHolder.mImageView.setImageBitmap(bitmap);  
  100.         } else {  
  101.             mViewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);  
  102.         }  
  103.   
  104.         return convertView;  
  105.     }  
  106.       
  107.   
  108.     @Override  
  109.     public View getHeaderView(int position, View convertView, ViewGroup parent) {  
  110.         HeaderViewHolder mHeaderHolder;  
  111.           
  112.         if (convertView == null) {  
  113.             mHeaderHolder = new HeaderViewHolder();  
  114.             convertView = mInflater.inflate(R.layout.header, parent, false);  
  115.             mHeaderHolder.mTextView = (TextView) convertView  
  116.                     .findViewById(R.id.header);  
  117.             convertView.setTag(mHeaderHolder);  
  118.         } else {  
  119.             mHeaderHolder = (HeaderViewHolder) convertView.getTag();  
  120.         }  
  121.         mHeaderHolder.mTextView.setText(hasHeaderIdList.get(position).getTime());  
  122.           
  123.         return convertView;  
  124.     }  
  125.       
  126.     /** 
  127.      * 獲取HeaderId, 只要HeaderId不相等就添加一個Header 
  128.      */  
  129.     @Override  
  130.     public long getHeaderId(int position) {  
  131.         return hasHeaderIdList.get(position).getHeaderId();  
  132.     }  
  133.   
  134.       
  135.     public static class ViewHolder {  
  136.         public MyImageView mImageView;  
  137.     }  
  138.   
  139.     public static class HeaderViewHolder {  
  140.         public TextView mTextView;  
  141.     }  
  142.   
  143.   
  144.   
  145. }  
除了要繼承BaseAdapter之外還需要實現StickyGridHeadersSimpleAdapter接口,繼承BaseAdapter需要實現getCount(),getItem(int position), getItemId(int position),getView(int position, View convertView, ViewGroup parent)這四個方法,這幾個方法的實現跟我們平常實現的方式一樣,主要是看一下getView()方法,我們將每個item的圖片路徑設置Tag到該ImageView上面,然後利用NativeImageLoader來加載本地圖片,在這裏使用的ImageView依然是自定義的MyImageView,該自定義ImageView主要實現當MyImageView測量完畢之後,就會將測量的寬和高回調到onMeasureSize()中,然後我們可以根據MyImageView的大小來裁剪圖片

另外我們需要實現StickyGridHeadersSimpleAdapter接口的getHeaderId(int position)和getHeaderView(int position, View convertView, ViewGroup parent),getHeaderId(int position)方法返回每個Item的headerId,getHeaderView()方法是生成sections和headers的,如果某個item的headerId跟他下一個item的HeaderId不同,則會調用getHeaderView方法生成一個sections用來區分不同的組,還會根據firstVisibleItem的headerId來生成一個位於頂部的headers,所以如何生成每個Item的headerId纔是關鍵,生成headerId的方法在MainActivity中

[java] view plain copy
  1. package com.example.stickyheadergridview;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.ArrayList;  
  5. import java.util.Collections;  
  6. import java.util.Date;  
  7. import java.util.HashMap;  
  8. import java.util.List;  
  9. import java.util.ListIterator;  
  10. import java.util.Map;  
  11. import java.util.TimeZone;  
  12.   
  13. import android.app.Activity;  
  14. import android.app.ProgressDialog;  
  15. import android.database.Cursor;  
  16. import android.os.Bundle;  
  17. import android.provider.MediaStore;  
  18. import android.widget.GridView;  
  19.   
  20. import com.example.stickyheadergridview.ImageScanner.ScanCompleteCallBack;  
  21.   
  22. public class MainActivity extends Activity {  
  23.     private ProgressDialog mProgressDialog;  
  24.     /** 
  25.      * 圖片掃描器 
  26.      */  
  27.     private ImageScanner mScanner;  
  28.     private GridView mGridView;  
  29.     /** 
  30.      * 沒有HeaderId的List 
  31.      */  
  32.     private List<GridItem> nonHeaderIdList = new ArrayList<GridItem>();  
  33.   
  34.   
  35.     @Override  
  36.     protected void onCreate(Bundle savedInstanceState) {  
  37.         super.onCreate(savedInstanceState);  
  38.         setContentView(R.layout.activity_main);  
  39.           
  40.         mGridView = (GridView) findViewById(R.id.asset_grid);  
  41.         mScanner = new ImageScanner(this);  
  42.           
  43.         mScanner.scanImages(new ScanCompleteCallBack() {  
  44.             {  
  45.                 mProgressDialog = ProgressDialog.show(MainActivity.thisnull"正在加載...");  
  46.             }  
  47.               
  48.             @Override  
  49.             public void scanComplete(Cursor cursor) {  
  50.                 // 關閉進度條  
  51.                 mProgressDialog.dismiss();  
  52.                   
  53.                 if(cursor == null){  
  54.                     return;  
  55.                 }  
  56.                   
  57.                 while (cursor.moveToNext()) {  
  58.                     // 獲取圖片的路徑  
  59.                     String path = cursor.getString(cursor  
  60.                             .getColumnIndex(MediaStore.Images.Media.DATA));  
  61.                     //獲取圖片的添加到系統的毫秒數  
  62.                     long times = cursor.getLong(cursor  
  63.                             .getColumnIndex(MediaStore.Images.Media.DATE_ADDED));  
  64.                       
  65.                     GridItem mGridItem = new GridItem(path, paserTimeToYMD(times, "yyyy年MM月dd日"));  
  66.                     nonHeaderIdList.add(mGridItem);  
  67.   
  68.                 }  
  69.                 cursor.close();  
  70.                   
  71.                 //給GridView的item的數據生成HeaderId  
  72.                 List<GridItem> hasHeaderIdList = generateHeaderId(nonHeaderIdList);  
  73.                 //排序  
  74.                 Collections.sort(hasHeaderIdList, new YMDComparator());  
  75.                 mGridView.setAdapter(new StickyGridAdapter(MainActivity.this, hasHeaderIdList, mGridView));  
  76.                   
  77.             }  
  78.         });  
  79.     }  
  80.       
  81.       
  82.     /** 
  83.      * 對GridView的Item生成HeaderId, 根據圖片的添加時間的年、月、日來生成HeaderId 
  84.      * 年、月、日相等HeaderId就相同 
  85.      * @param nonHeaderIdList 
  86.      * @return 
  87.      */  
  88.     private List<GridItem> generateHeaderId(List<GridItem> nonHeaderIdList) {  
  89.         Map<String, Integer> mHeaderIdMap = new HashMap<String, Integer>();  
  90.         int mHeaderId = 1;  
  91.         List<GridItem> hasHeaderIdList;  
  92.           
  93.         for(ListIterator<GridItem> it = nonHeaderIdList.listIterator(); it.hasNext();){  
  94.             GridItem mGridItem = it.next();  
  95.             String ymd = mGridItem.getTime();  
  96.             if(!mHeaderIdMap.containsKey(ymd)){  
  97.                 mGridItem.setHeaderId(mHeaderId);  
  98.                 mHeaderIdMap.put(ymd, mHeaderId);  
  99.                 mHeaderId ++;  
  100.             }else{  
  101.                 mGridItem.setHeaderId(mHeaderIdMap.get(ymd));  
  102.             }  
  103.         }  
  104.         hasHeaderIdList = nonHeaderIdList;  
  105.           
  106.         return hasHeaderIdList;  
  107.     }  
  108.   
  109.       
  110.     @Override  
  111.     protected void onDestroy() {  
  112.         super.onDestroy();  
  113.         //退出頁面清除LRUCache中的Bitmap佔用的內存  
  114.         NativeImageLoader.getInstance().trimMemCache();  
  115.     }  
  116.   
  117.   
  118.     /** 
  119.      * 將毫秒數裝換成pattern這個格式,我這裏是轉換成年月日 
  120.      * @param time 
  121.      * @param pattern 
  122.      * @return 
  123.      */  
  124.     public static String paserTimeToYMD(long time, String pattern ) {  
  125.         System.setProperty("user.timezone""Asia/Shanghai");  
  126.         TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");  
  127.         TimeZone.setDefault(tz);  
  128.         SimpleDateFormat format = new SimpleDateFormat(pattern);  
  129.         return format.format(new Date(time * 1000L));  
  130.     }  
  131.   
  132. }  

主界面的代碼主要是組裝StickyGridHeadersGridView的數據,我們將掃描出來的圖片的路徑,時間的毫秒數解析成年月日的格式封裝到GridItem中,然後將GridItem加入到List中,此時每個Item還沒有生成headerId,我們需要調用generateHeaderId(),該方法主要是將同一天加入的系統的圖片生成相同的HeaderId,這樣子同一天加入的圖片就在一個組中,當然你要改成同一個月的圖片在一起,修改paserTimeToYMD()方法的第二個參數就行了,當Activity finish之後,我們利用NativeImageLoader.getInstance().trimMemCache()釋放內存,當然我們還需要對GridView的數據進行排序,比如說headerId相同的item不連續,headerId相同的item就會生成多個sections(即多個分組),所以我們要利用YMDComparator使得在同一天加入的圖片在一起,YMDComparator的代碼如下

[java] view plain copy
  1. package com.example.stickyheadergridview;  
  2.   
  3. import java.util.Comparator;  
  4.   
  5. public class YMDComparator implements Comparator<GridItem> {  
  6.   
  7.     @Override  
  8.     public int compare(GridItem o1, GridItem o2) {  
  9.         return o1.getTime().compareTo(o2.getTime());  
  10.     }  
  11.   
  12. }  
當然這篇文章不使用YMDComparator也是可以的,因爲我在利用ContentProvider獲取圖片的時候,就是根據加入系統的時間排序的,排序只是針對一般的數據來說的。

接下來我們運行下程序看看效果如何


今天的文章就到這裏結束了,感謝大家的觀看,上面還有一個類和一些資源文件沒有貼出來,大家有興趣研究下就直接下載項目源碼,記住採用LruCache緩存圖片的時候,cacheSize不要設置得過大,不然產生OOM的概率就更大些,我利用上面的程序測試顯示600多張圖片來回滑動,沒有產生OOM,有問題不明白的同學可以在下面留言!

項目源碼,點擊下載


發佈了8 篇原創文章 · 獲贊 19 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章