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


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

    大家好!过完年回来到现在差不多一个月没写文章了,一是觉得不知道写哪些方面的文章,没有好的题材来写,二是因为自己的一些私事给耽误了,所以过完年的第一篇文章到现在才发表出来,2014年我还是会继续在CSDN上面更新我的博客,欢迎大家关注一下,今天这篇文章主要的是介绍下开源库StickyGridHeaders的使用,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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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, null, null, null, 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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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(0, 0);//用来封装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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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.this, null, "正在加载...");  
    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 plaincopy在CODE上查看代码片派生到我的代码片
     
    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,有问题不明白的同学可以在下面留言!

  • 相关阅读:
    Html5新标签解释及用法
    时间线效果
    NHibernate数据访问层核心代码
    C#开源资源项目
    安卓开发环境搭建之最新版(So Easy!)
    WIN8相关资源汇总
    HTML5 Shiv – 让该死的IE系列支持HTML5吧
    WCF 提高传输数据性能方法之二 (数据分割)
    支付宝集成问题(.net篇)
    WCF 提高传输数据性能方法之一 (使用压缩)
  • 原文地址:https://www.cnblogs.com/android-blogs/p/4844588.html
Copyright © 2020-2023  润新知