天天看點

Android 使用ContentProvider掃描手機中的圖檔,仿微信顯示本地圖檔效果

寫這篇文章之前,先簡單說幾句,首先是先恭喜下自己獲得了2013年的部落格之星稱号,很意外也很開心,自己是從2013年開始寫部落格,那時候也不知道怎麼寫,我從小就不喜歡寫日記,作文什麼的,是以剛開始都是貼代碼,也沒有人看,後面慢慢的,寫的文章被推薦部落格首頁和CSDN首頁(這裡也要小小的感謝下小編MM),通路量逐漸的多了起來,有更多的人看我的文章,這也使自己有了繼續寫文章的動力,也希望我寫的東西對大家有點幫助吧,在2014年我會繼續在CSDN上面寫部落格,然後是感謝部落格之星給我投票支援我的朋友們,謝謝你們支援我的每一票,最後就是2014春節馬上就到了,提前祝福大家新年快樂,工作順利,事事順心!

Android 使用ContentProvider掃描手機中的圖檔,仿微信顯示本地圖檔效果
Android 使用ContentProvider掃描手機中的圖檔,仿微信顯示本地圖檔效果

接下來我們就來實作這些效果吧,首先我們建立一個項目,取名ImageScan

首先我們先看第一個界面吧,使用将手機中的圖檔掃描出來,然後根據圖檔的所在的檔案夾将其分類出來,并顯示所在檔案夾裡面的一張圖檔和檔案夾中圖檔個數,我們根據界面元素(檔案夾名, 檔案夾圖檔個數,檔案夾中的一張圖檔)使用一個實體對象ImageBean來封裝這三個屬性

package com.example.imagescan;  

/** 

 * GridView的每個item的資料對象 

 *  

 * @author len 

 * 

 */  

public class ImageBean{  

    /** 

     * 檔案夾的第一張圖檔路徑 

     */  

    private String topImagePath;  

     * 檔案夾名 

    private String folderName;   

     * 檔案夾中的圖檔數 

    private int imageCounts;  

    public String getTopImagePath() {  

        return topImagePath;  

    }  

    public void setTopImagePath(String topImagePath) {  

        this.topImagePath = topImagePath;  

    public String getFolderName() {  

        return folderName;  

    public void setFolderName(String folderName) {  

        this.folderName = folderName;  

    public int getImageCounts() {  

        return imageCounts;  

    public void setImageCounts(int imageCounts) {  

        this.imageCounts = imageCounts;  

}  

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  

    xmlns:tools="http://schemas.android.com/tools"  

    android:layout_width="match_parent"  

    android:layout_height="match_parent" >  

    <GridView  

        android:id="@+id/main_grid"  

        android:layout_width="match_parent"  

        android:layout_height="match_parent"  

        android:listSelector="@android:color/transparent"  

        android:cacheColorHint="@android:color/transparent"  

        android:stretchMode="columnWidth"  

        android:horizontalSpacing="20dip"  

        android:gravity="center"  

        android:verticalSpacing="20dip"  

        android:columnWidth="90dip"  

        android:numColumns="2" >  

    </GridView>  

</RelativeLayout>  

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

<?xml version="1.0" encoding="UTF-8"?>  

    android:layout_width="fill_parent"  

    android:layout_height="wrap_content" >  

    <FrameLayout  

        android:id="@+id/framelayout"  

        android:layout_width="fill_parent"  

        android:layout_height="wrap_content" >  

        <com.example.imagescan.MyImageView  

            android:id="@+id/group_image"  

            android:background="@drawable/albums_bg"  

            android:src="@drawable/friends_sends_pictures_no"  

            android:paddingLeft="20dip"  

            android:paddingRight="20dip"  

            android:paddingTop="18dip"  

            android:paddingBottom="30dip"  

            android:scaleType="fitXY"  

            android:layout_width="fill_parent"  

            android:layout_height="150dip" />  

        <TextView  

            android:id="@+id/group_count"  

            android:layout_width="wrap_content"  

            android:layout_height="wrap_content"  

            android:background="@drawable/albums_icon_bg"  

            android:gravity="center"  

            android:layout_marginBottom="10dip"  

            android:text="5"  

            android:layout_gravity="bottom|center_horizontal" />  

    </FrameLayout>  

    <TextView  

        android:id="@+id/group_title"  

        android:layout_height="wrap_content"  

        android:layout_below="@id/framelayout"  

        android:layout_centerHorizontal="true"  

        android:ellipsize="end"  

        android:singleLine="true"  

        android:text="Camera"  

        android:textSize="16sp" />  

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

第一個界面的主要代碼

import java.io.File;  

import java.util.ArrayList;  

import java.util.HashMap;  

import java.util.Iterator;  

import java.util.List;  

import java.util.Map;  

import android.app.Activity;  

import android.app.ProgressDialog;  

import android.content.ContentResolver;  

import android.content.Intent;  

import android.database.Cursor;  

import android.net.Uri;  

import android.os.Bundle;  

import android.os.Handler;  

import android.os.Message;  

import android.provider.MediaStore;  

import android.view.View;  

import android.widget.AdapterView;  

import android.widget.AdapterView.OnItemClickListener;  

import android.widget.GridView;  

 * @blog http://blog.csdn.net/xiaanming 

 * @author xiaanming 

public class MainActivity extends Activity {  

    private HashMap<String, List<String>> mGruopMap = new HashMap<String, List<String>>();  

    private List<ImageBean> list = new ArrayList<ImageBean>();  

    private final static int SCAN_OK = 1;  

    private ProgressDialog mProgressDialog;  

    private GroupAdapter adapter;  

    private GridView mGroupGridView;  

    private Handler mHandler = new Handler(){  

        @Override  

        public void handleMessage(Message msg) {  

            super.handleMessage(msg);  

            switch (msg.what) {  

            case SCAN_OK:  

                //關閉進度條  

                mProgressDialog.dismiss();  

                adapter = new GroupAdapter(MainActivity.this, list = subGroupOfImage(mGruopMap), mGroupGridView);  

                mGroupGridView.setAdapter(adapter);  

                break;  

            }  

        }  

    };  

    @Override  

    protected void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.activity_main);  

        mGroupGridView = (GridView) findViewById(R.id.main_grid);  

        getImages();  

        mGroupGridView.setOnItemClickListener(new OnItemClickListener() {  

            @Override  

            public void onItemClick(AdapterView<?> parent, View view,  

                    int position, long id) {  

                List<String> childList = mGruopMap.get(list.get(position).getFolderName());  

                Intent mIntent = new Intent(MainActivity.this, ShowImageActivity.class);  

                mIntent.putStringArrayListExtra("data", (ArrayList<String>)childList);  

                startActivity(mIntent);  

        });  

     * 利用ContentProvider掃描手機中的圖檔,此方法在運作在子線程中 

    private void getImages() {  

        //顯示進度條  

        mProgressDialog = ProgressDialog.show(this, null, "正在加載...");  

        new Thread(new Runnable() {  

            public void run() {  

                Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  

                ContentResolver mContentResolver = MainActivity.this.getContentResolver();  

                //隻查詢jpeg和png的圖檔  

                Cursor mCursor = mContentResolver.query(mImageUri, null,  

                        MediaStore.Images.Media.MIME_TYPE + "=? or "  

                                + MediaStore.Images.Media.MIME_TYPE + "=?",  

                        new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);  

                if(mCursor == null){  

                    return;  

                }  

                while (mCursor.moveToNext()) {  

                    //擷取圖檔的路徑  

                    String path = mCursor.getString(mCursor  

                            .getColumnIndex(MediaStore.Images.Media.DATA));  

                    //擷取該圖檔的父路徑名  

                    String parentName = new File(path).getParentFile().getName();  

                    //根據父路徑名将圖檔放入到mGruopMap中  

                    if (!mGruopMap.containsKey(parentName)) {  

                        List<String> chileList = new ArrayList<String>();  

                        chileList.add(path);  

                        mGruopMap.put(parentName, chileList);  

                    } else {  

                        mGruopMap.get(parentName).add(path);  

                    }  

                //通知Handler掃描圖檔完成  

                mHandler.sendEmptyMessage(SCAN_OK);  

                mCursor.close();  

        }).start();  

     * 組裝分組界面GridView的資料源,因為我們掃描手機的時候将圖檔資訊放在HashMap中 

     * 是以需要周遊HashMap将資料組裝成List 

     *  

     * @param mGruopMap 

     * @return 

    private List<ImageBean> subGroupOfImage(HashMap<String, List<String>> mGruopMap){  

        if(mGruopMap.size() == 0){  

            return null;  

        List<ImageBean> list = new ArrayList<ImageBean>();  

        Iterator<Map.Entry<String, List<String>>> it = mGruopMap.entrySet().iterator();  

        while (it.hasNext()) {  

            Map.Entry<String, List<String>> entry = it.next();  

            ImageBean mImageBean = new ImageBean();  

            String key = entry.getKey();  

            List<String> value = entry.getValue();  

            mImageBean.setFolderName(key);  

            mImageBean.setImageCounts(value.size());  

            mImageBean.setTopImagePath(value.get(0));//擷取該組的第一張圖檔  

            list.add(mImageBean);  

        return 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

import java.util.concurrent.ExecutorService;  

import java.util.concurrent.Executors;  

import android.graphics.Bitmap;  

import android.graphics.BitmapFactory;  

import android.graphics.Point;  

import android.support.v4.util.LruCache;  

 * 本地圖檔加載器,采用的是異步解析本地圖檔,單例模式利用getInstance()擷取NativeImageLoader執行個體 

 * 調用loadNativeImage()方法加載本地圖檔,此類可作為一個加載本地圖檔的工具類 

public class NativeImageLoader {  

    private LruCache<String, Bitmap> mMemoryCache;  

    private static NativeImageLoader mInstance = new NativeImageLoader();  

    private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1);  

    private NativeImageLoader(){  

        //擷取應用程式的最大記憶體  

        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  

        //用最大記憶體的1/4來存儲圖檔  

        final int cacheSize = maxMemory / 4;  

        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  

            //擷取每張圖檔的大小  

            protected int sizeOf(String key, Bitmap bitmap) {  

                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;  

        };  

     * 通過此方法來擷取NativeImageLoader的執行個體 

    public static NativeImageLoader getInstance(){  

        return mInstance;  

     * 加載本地圖檔,對圖檔不進行裁剪 

     * @param path 

     * @param mCallBack 

    public Bitmap loadNativeImage(final String path, final NativeImageCallBack mCallBack){  

        return this.loadNativeImage(path, null, mCallBack);  

     * 此方法來加載本地圖檔,這裡的mPoint是用來封裝ImageView的寬和高,我們會根據ImageView控件的大小來裁剪Bitmap 

     * 如果你不想裁剪圖檔,調用loadNativeImage(final String path, final NativeImageCallBack mCallBack)來加載 

     * @param mPoint 

    public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){  

        //先擷取記憶體中的Bitmap  

        Bitmap bitmap = getBitmapFromMemCache(path);  

        final Handler mHander = new Handler(){  

            public void handleMessage(Message msg) {  

                super.handleMessage(msg);  

                mCallBack.onImageLoader((Bitmap)msg.obj, path);  

        //若該Bitmap不在記憶體緩存中,則啟用線程去加載本地的圖檔,并将Bitmap加入到mMemoryCache中  

        if(bitmap == null){  

            mImageThreadPool.execute(new Runnable() {  

                @Override  

                public void run() {  

                    //先擷取圖檔的縮略圖  

                    Bitmap mBitmap = decodeThumbBitmapForFile(path, mPoint == null ? 0: mPoint.x, mPoint == null ? 0: mPoint.y);  

                    Message msg = mHander.obtainMessage();  

                    msg.obj = mBitmap;  

                    mHander.sendMessage(msg);  

                    //将圖檔加入到記憶體緩存  

                    addBitmapToMemoryCache(path, mBitmap);  

            });  

        return bitmap;  

     * 往記憶體緩存中添加Bitmap 

     * @param key 

     * @param bitmap 

    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {  

        if (getBitmapFromMemCache(key) == null && bitmap != null) {  

            mMemoryCache.put(key, bitmap);  

     * 根據key來擷取記憶體中的圖檔 

    private Bitmap getBitmapFromMemCache(String key) {  

        return mMemoryCache.get(key);  

     * 根據View(主要是ImageView)的寬和高來擷取圖檔的縮略圖 

     * @param viewWidth 

     * @param viewHeight 

    private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight){  

        BitmapFactory.Options options = new BitmapFactory.Options();  

        //設定為true,表示解析Bitmap對象,該對象不占記憶體  

        options.inJustDecodeBounds = true;  

        BitmapFactory.decodeFile(path, options);  

        //設定縮放比例  

        options.inSampleSize = computeScale(options, viewWidth, viewHeight);  

        //設定為false,解析Bitmap對象加入到記憶體中  

        options.inJustDecodeBounds = false;  

        return BitmapFactory.decodeFile(path, options);  

     * 根據View(主要是ImageView)的寬和高來計算Bitmap縮放比例。預設不縮放 

     * @param options 

     * @param width 

     * @param height 

    private int computeScale(BitmapFactory.Options options, int viewWidth, int viewHeight){  

        int inSampleSize = 1;  

        if(viewWidth == 0 || viewWidth == 0){  

            return inSampleSize;  

        int bitmapWidth = options.outWidth;  

        int bitmapHeight = options.outHeight;  

        //假如Bitmap的寬度或高度大于我們設定圖檔的View的寬高,則計算縮放比例  

        if(bitmapWidth > viewWidth || bitmapHeight > viewWidth){  

            int widthScale = Math.round((float) bitmapWidth / (float) viewWidth);  

            int heightScale = Math.round((float) bitmapHeight / (float) viewWidth);  

            //為了保證圖檔不縮放變形,我們取寬高比例最小的那個  

            inSampleSize = widthScale < heightScale ? widthScale : heightScale;  

        return inSampleSize;  

     * 加載本地圖檔的回調接口 

     * @author xiaanming 

     * 

    public interface NativeImageCallBack{  

        /** 

         * 當子線程加載完了本地的圖檔,将Bitmap和圖檔路徑回調在此方法中 

         * @param bitmap 

         * @param path 

         */  

        public void 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類的代碼

import android.content.Context;  

import android.view.LayoutInflater;  

import android.view.ViewGroup;  

import android.widget.BaseAdapter;  

import android.widget.ImageView;  

import android.widget.TextView;  

import com.example.imagescan.MyImageView.OnMeasureListener;  

import com.example.imagescan.NativeImageLoader.NativeImageCallBack;  

public class GroupAdapter extends BaseAdapter{  

    private List<ImageBean> list;  

    private Point mPoint = new Point(0, 0);//用來封裝ImageView的寬和高的對象  

    private GridView mGridView;  

    protected LayoutInflater mInflater;  

    public int getCount() {  

        return list.size();  

    public Object getItem(int position) {  

        return list.get(position);  

    public long getItemId(int position) {  

        return position;  

    public GroupAdapter(Context context, List<ImageBean> list, GridView mGridView){  

        this.list = list;  

        this.mGridView = mGridView;  

        mInflater = LayoutInflater.from(context);  

    public View getView(int position, View convertView, ViewGroup parent) {  

        final ViewHolder viewHolder;  

        ImageBean mImageBean = list.get(position);  

        String path = mImageBean.getTopImagePath();  

        if(convertView == null){  

            viewHolder = new ViewHolder();  

            convertView = mInflater.inflate(R.layout.grid_group_item, null);  

            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);  

            //用來監聽ImageView的寬和高  

            viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() {  

                public void onMeasureSize(int width, int height) {  

                    mPoint.set(width, height);  

            convertView.setTag(viewHolder);  

        }else{  

            viewHolder = (ViewHolder) convertView.getTag();  

            viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);  

        viewHolder.mTextViewTitle.setText(mImageBean.getFolderName());  

        viewHolder.mTextViewCounts.setText(Integer.toString(mImageBean.getImageCounts()));  

        //給ImageView設定路徑Tag,這是異步加載圖檔的小技巧  

        viewHolder.mImageView.setTag(path);  

        //利用NativeImageLoader類加載本地圖檔  

        Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() {  

            public void onImageLoader(Bitmap bitmap, String path) {  

                ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);  

                if(bitmap != null && mImageView != null){  

                    mImageView.setImageBitmap(bitmap);  

        if(bitmap != null){  

            viewHolder.mImageView.setImageBitmap(bitmap);  

        return convertView;  

    public static class ViewHolder{  

        public MyImageView mImageView;  

        public TextView mTextViewTitle;  

        public 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的大小來裁剪圖檔

import android.util.AttributeSet;  

public class MyImageView extends ImageView {  

    private OnMeasureListener onMeasureListener;  

    public void setOnMeasureListener(OnMeasureListener onMeasureListener) {  

        this.onMeasureListener = onMeasureListener;  

    public MyImageView(Context context, AttributeSet attrs) {  

        super(context, attrs);  

    public MyImageView(Context context, AttributeSet attrs, int defStyle) {  

        super(context, attrs, defStyle);  

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  

        //将圖檔測量的大小回調到onMeasureSize()方法中  

        if(onMeasureListener != null){  

            onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight());  

    public interface OnMeasureListener{  

        public void onMeasureSize(int width, int height);  

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

import android.widget.Toast;  

public class ShowImageActivity extends Activity {  

    private List<String> list;  

    private ChildAdapter adapter;  

        setContentView(R.layout.show_image_activity);  

        mGridView = (GridView) findViewById(R.id.child_grid);  

        list = getIntent().getStringArrayListExtra("data");  

        adapter = new ChildAdapter(this, list, mGridView);  

        mGridView.setAdapter(adapter);  

    public void onBackPressed() {  

        Toast.makeText(this, "選中 " + adapter.getSelectItems().size() + " item", Toast.LENGTH_LONG).show();  

        super.onBackPressed();  

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

import android.widget.CheckBox;  

import android.widget.CompoundButton;  

import android.widget.CompoundButton.OnCheckedChangeListener;  

import com.nineoldandroids.animation.AnimatorSet;  

import com.nineoldandroids.animation.ObjectAnimator;  

public class ChildAdapter extends BaseAdapter {  

     * 用來存儲圖檔的選中情況 

    private HashMap<Integer, Boolean> mSelectMap = new HashMap<Integer, Boolean>();  

    public ChildAdapter(Context context, List<String> list, GridView mGridView) {  

    public View getView(final int position, View convertView, ViewGroup parent) {  

        String path = list.get(position);  

            convertView = mInflater.inflate(R.layout.grid_child_item, null);  

            viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.child_image);  

            viewHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.child_checkbox);  

        viewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {  

            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  

                //如果是未選中的CheckBox,則添加動畫  

                if(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){  

                    addAnimation(viewHolder.mCheckBox);  

                mSelectMap.put(position, isChecked);  

        viewHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : false);  

     * 給CheckBox加點選動畫,利用開源庫nineoldandroids設定動畫  

     * @param view 

    private void addAnimation(View view){  

        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};  

        AnimatorSet set = new AnimatorSet();  

        set.playTogether(ObjectAnimator.ofFloat(view, "scaleX", vaules),   

                ObjectAnimator.ofFloat(view, "scaleY", vaules));  

                set.setDuration(150);  

        set.start();  

     * 擷取選中的Item的position 

    public List<Integer> getSelectItems(){  

        List<Integer> list = new ArrayList<Integer>();  

        for(Iterator<Map.Entry<Integer, Boolean>> it = mSelectMap.entrySet().iterator(); it.hasNext();){  

            Map.Entry<Integer, Boolean> entry = it.next();  

            if(entry.getValue()){  

                list.add(entry.getKey());  

        public CheckBox mCheckBox;  

運作項目,效果如下

Android 使用ContentProvider掃描手機中的圖檔,仿微信顯示本地圖檔效果

看起來還不錯吧,采用的是異步讀取圖檔,對圖檔進行了緩存和裁剪,使得在顯示本地圖檔方面比較流暢,GridView滑動也挺流暢的,也有效的避免OOM的産生,工程中有些東西還沒有貼完全,有興趣的朋友可以下載下傳Demo來運作一下,好了,今天的講解到這裡結束了,感謝大家觀看,有疑問的朋友可以在下面留言,我會為大家解答的!

<a target="_blank" href="http://download.csdn.net/detail/xiaanming/6881013">項目源碼,點選下載下傳</a>

繼續閱讀