欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android應(yīng)用中使用ContentProvider掃描本地圖片并顯示

 更新時(shí)間:2016年04月05日 15:18:57   作者:xiaanming  
這篇文章主要介紹了Android應(yīng)用中使用ContentProvider掃描本地圖片并顯示的方法,比調(diào)用本地圖庫(kù)的方法更加靈活和可定制,需要的朋友可以參考下

之前群里面有朋友問(wèn)我,有沒(méi)有關(guān)于本地圖片選擇的Demo,類(lèi)似微信的效果,他說(shuō)網(wǎng)上沒(méi)有這方面的Demo,問(wèn)我能不能寫(xiě)一篇關(guān)于這個(gè)效果的Demo,于是我研究了下微信的本地圖片選擇的Demo,自己仿照的寫(xiě)了下分享給大家,希望對(duì)以后有這樣子需求的朋友有一點(diǎn)幫助吧,主要使用的是ContentProvider掃描手機(jī)中的圖片,并用GridView將圖片顯示出來(lái),關(guān)于GridView和ListView顯示圖片的問(wèn)題,一直是一個(gè)很頭疼的問(wèn)題,因?yàn)槲覀兪謾C(jī)的內(nèi)存有限,手機(jī)給每個(gè)應(yīng)用程序分配的內(nèi)存也有限,所以圖片多的情況下很容易伴隨著OOM的發(fā)生,不過(guò)現(xiàn)在也有很多的開(kāi)源的圖片顯示框架,對(duì)顯示很多圖片進(jìn)行了優(yōu)化,大家有興趣的可以去了解了解,今天我的這篇文章使用的是LruCache這個(gè)類(lèi)以及對(duì)圖片進(jìn)行相對(duì)應(yīng)的裁剪,這樣也可以盡量的避免OOM的發(fā)生,我們先看下微信的效果吧

201645151626071.png (480×854)

201645151655089.png (480×854)

接下來(lái)我們就來(lái)實(shí)現(xiàn)這些效果吧,首先我們新建一個(gè)項(xiàng)目,取名ImageScan
首先我們先看第一個(gè)界面吧,使用將手機(jī)中的圖片掃描出來(lái),然后根據(jù)圖片的所在的文件夾將其分類(lèi)出來(lái),并顯示所在文件夾里面的一張圖片和文件夾中圖片個(gè)數(shù),我們根據(jù)界面元素(文件夾名, 文件夾圖片個(gè)數(shù),文件夾中的一張圖片)使用一個(gè)實(shí)體對(duì)象ImageBean來(lái)封裝這三個(gè)屬性

package com.example.imagescan; 
 
/** 
 * GridView的每個(gè)item的數(shù)據(jù)對(duì)象 
 * 
 * @author len 
 * 
 */ 
public class ImageBean{ 
  /** 
   * 文件夾的第一張圖片路徑 
   */ 
  private String topImagePath; 
  /** 
   * 文件夾名 
   */ 
  private String folderName;  
  /** 
   * 文件夾中的圖片數(shù) 
   */ 
  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; 
  } 
   
} 

接下來(lái)就是主界面的布局啦,上面的導(dǎo)航欄我沒(méi)有加進(jìn)去,只有下面的GridView,所以說(shuō)主界面布局中只有一個(gè)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> 

接下來(lái)就是GridView的Item的布局,看上面的圖也行你會(huì)認(rèn)為他的效果是2張圖片添加的效果,其實(shí)不是,后面的疊加效果只是一張背景圖片而已,代碼先貼上來(lái)

<?xml version="1.0" encoding="UTF-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  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_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:gravity="center" 
    android:layout_below="@id/framelayout" 
    android:layout_centerHorizontal="true" 
    android:ellipsize="end" 
    android:singleLine="true" 
    android:text="Camera" 
    android:textSize="16sp" /> 
 
</RelativeLayout> 

看到上面的布局代碼,也行你已經(jīng)發(fā)現(xiàn)了,上面使用的是自定義的MyImageView,我先不說(shuō)這個(gè)自定義MyImageView的作用,待會(huì)再給大家說(shuō),我們繼續(xù)看代碼
第一個(gè)界面的主要代碼

package com.example.imagescan; 
 
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; 


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: 
        //關(guān)閉進(jìn)度條 
        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掃描手機(jī)中的圖片,此方法在運(yùn)行在子線(xiàn)程中 
   */ 
  private void getImages() { 
    //顯示進(jìn)度條 
    mProgressDialog = ProgressDialog.show(this, null, "正在加載..."); 
     
    new Thread(new Runnable() { 
       
      @Override 
      public void run() { 
        Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 
        ContentResolver mContentResolver = MainActivity.this.getContentResolver(); 
 
        //只查詢(xún)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(); 
 
           
          //根據(jù)父路徑名將圖片放入到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的數(shù)據(jù)源,因?yàn)槲覀儝呙枋謾C(jī)的時(shí)候?qū)D片信息放在HashMap中 
   * 所以需要遍歷HashMap將數(shù)據(jù)組裝成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()這個(gè)方法,該方法是使用ContentProvider將手機(jī)中的圖片掃描出來(lái),我這里只掃描了手機(jī)的外部存儲(chǔ)中的圖片,由于手機(jī)中可能存在很多的圖片,掃描圖片又比較耗時(shí),所以我們?cè)谶@里開(kāi)啟了子線(xiàn)程去獲取圖片,掃描的圖片都存放在Cursor中,我們先要將圖片按照文件夾進(jìn)行分類(lèi),我們使用了HashMap來(lái)進(jìn)行分類(lèi)并將結(jié)果存儲(chǔ)到mGruopMap(Key是文件夾名,Value是文件夾中的圖片路徑的List)中,分類(lèi)完了關(guān)閉Cursor并利用Handler來(lái)通知主線(xiàn)程
然后是subGroupOfImage()方法,改方法是將mGruopMap的數(shù)據(jù)組裝到List中,在List中存放GridView中的每個(gè)item的數(shù)據(jù)對(duì)象ImageBean, 遍歷HashMap對(duì)象,具體的邏輯看代碼,之后就是給GridView設(shè)置Adapter。
設(shè)置item點(diǎn)擊事件,點(diǎn)擊文件夾跳轉(zhuǎn)到展示文件夾圖片的Activity, 我們需要傳遞每個(gè)文件夾中的圖片的路徑的集合
看GroupAdapter的代碼之前,我們先看一個(gè)比較重要的類(lèi),本地圖片加載器NativeImageLoader
package com.example.imagescan; 
 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Point; 
import android.os.Handler; 
import android.os.Message; 
import android.support.v4.util.LruCache; 
 
/** 
 * 本地圖片加載器,采用的是異步解析本地圖片,單例模式利用getInstance()獲取NativeImageLoader實(shí)例 
 * 調(diào)用loadNativeImage()方法加載本地圖片,此類(lèi)可作為一個(gè)加載本地圖片的工具類(lèi) 
 */ 
public class NativeImageLoader { 
  private LruCache<String, Bitmap> mMemoryCache; 
  private static NativeImageLoader mInstance = new NativeImageLoader(); 
  private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1); 
   
   
  private NativeImageLoader(){ 
    //獲取應(yīng)用程序的最大內(nèi)存 
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 
 
    //用最大內(nèi)存的1/4來(lái)存儲(chǔ)圖片 
    final int cacheSize = maxMemory / 4; 
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { 
       
      //獲取每張圖片的大小 
      @Override 
      protected int sizeOf(String key, Bitmap bitmap) { 
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024; 
      } 
    }; 
  } 
   
  /** 
   * 通過(guò)此方法來(lái)獲取NativeImageLoader的實(shí)例 
   * @return 
   */ 
  public static NativeImageLoader getInstance(){ 
    return mInstance; 
  } 
      
      
  /** 
   * 加載本地圖片,對(duì)圖片不進(jìn)行裁剪 
   * @param path 
   * @param mCallBack 
   * @return 
   */ 
  public Bitmap loadNativeImage(final String path, final NativeImageCallBack mCallBack){ 
    return this.loadNativeImage(path, null, mCallBack); 
  } 
   
  /** 
   * 此方法來(lái)加載本地圖片,這里的mPoint是用來(lái)封裝ImageView的寬和高,我們會(huì)根據(jù)ImageView控件的大小來(lái)裁剪Bitmap 
   * 如果你不想裁剪圖片,調(diào)用loadNativeImage(final String path, final NativeImageCallBack mCallBack)來(lái)加載 
   * @param path 
   * @param mPoint 
   * @param mCallBack 
   * @return 
   */ 
  public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){ 
    //先獲取內(nèi)存中的Bitmap 
    Bitmap bitmap = getBitmapFromMemCache(path); 
     
    final Handler mHander = new Handler(){ 
 
      @Override 
      public void handleMessage(Message msg) { 
        super.handleMessage(msg); 
        mCallBack.onImageLoader((Bitmap)msg.obj, path); 
      } 
       
    }; 
     
    //若該Bitmap不在內(nèi)存緩存中,則啟用線(xiàn)程去加載本地的圖片,并將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); 
           
          //將圖片加入到內(nèi)存緩存 
          addBitmapToMemoryCache(path, mBitmap); 
        } 
      }); 
    } 
    return bitmap; 
     
  } 
 
   
   
  /** 
   * 往內(nèi)存緩存中添加Bitmap 
   * 
   * @param key 
   * @param bitmap 
   */ 
  private void addBitmapToMemoryCache(String key, Bitmap bitmap) { 
    if (getBitmapFromMemCache(key) == null && bitmap != null) { 
      mMemoryCache.put(key, bitmap); 
    } 
  } 
 
  /** 
   * 根據(jù)key來(lái)獲取內(nèi)存中的圖片 
   * @param key 
   * @return 
   */ 
  private Bitmap getBitmapFromMemCache(String key) { 
    return mMemoryCache.get(key); 
  } 
   
   
  /** 
   * 根據(jù)View(主要是ImageView)的寬和高來(lái)獲取圖片的縮略圖 
   * @param path 
   * @param viewWidth 
   * @param viewHeight 
   * @return 
   */ 
  private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight){ 
    BitmapFactory.Options options = new BitmapFactory.Options(); 
    //設(shè)置為true,表示解析Bitmap對(duì)象,該對(duì)象不占內(nèi)存 
    options.inJustDecodeBounds = true; 
    BitmapFactory.decodeFile(path, options); 
    //設(shè)置縮放比例 
    options.inSampleSize = computeScale(options, viewWidth, viewHeight); 
     
    //設(shè)置為false,解析Bitmap對(duì)象加入到內(nèi)存中 
    options.inJustDecodeBounds = false; 
     
    return BitmapFactory.decodeFile(path, options); 
  } 
   
   
  /** 
   * 根據(jù)View(主要是ImageView)的寬和高來(lái)計(jì)算Bitmap縮放比例。默認(rèn)不縮放 
   * @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的寬度或高度大于我們?cè)O(shè)定圖片的View的寬高,則計(jì)算縮放比例 
    if(bitmapWidth > viewWidth || bitmapHeight > viewWidth){ 
      int widthScale = Math.round((float) bitmapWidth / (float) viewWidth); 
      int heightScale = Math.round((float) bitmapHeight / (float) viewWidth); 
       
      //為了保證圖片不縮放變形,我們?nèi)捀弑壤钚〉哪莻€(gè) 
      inSampleSize = widthScale < heightScale ? widthScale : heightScale; 
    } 
    return inSampleSize; 
  } 
   
   
  /** 
   * 加載本地圖片的回調(diào)接口 
   * 
   * @author xiaanming 
   * 
   */ 
  public interface NativeImageCallBack{ 
    /** 
     * 當(dāng)子線(xiàn)程加載完了本地的圖片,將Bitmap和圖片路徑回調(diào)在此方法中 
     * @param bitmap 
     * @param path 
     */ 
    public void onImageLoader(Bitmap bitmap, String path); 
  } 
} 
該類(lèi)是一個(gè)單例類(lèi),提供了本地圖片加載,內(nèi)存緩存,裁剪等邏輯,該類(lèi)在加載本地圖片的時(shí)候采用的是異步加載的方式,對(duì)于大圖片的加載也是比較耗時(shí)的,所以采用子線(xiàn)程的方式去加載,對(duì)于圖片的緩存機(jī)制使用的是LruCache,使用手機(jī)分配給應(yīng)用程序內(nèi)存的1/4用來(lái)緩存圖片,除了使用LruCache緩存圖片之外,還對(duì)圖片進(jìn)行了裁剪,舉個(gè)很簡(jiǎn)單的例子,假如我們的控件大小是100 * 100, 而我們的圖片是400*400,我們加載這么大的圖片需要很多的內(nèi)存,所以我們采用了圖片裁剪,根據(jù)控件的大小來(lái)確定圖片的裁剪比例,從而減小內(nèi)存的消耗,提高GridView滑動(dòng)的流暢度,介紹里面幾個(gè)比較重要的方法
computeScale()計(jì)算圖片需要裁剪的比例,根據(jù)控件的大小和圖片的大小確定比例,如果圖片比控件大,我們就進(jìn)行裁剪,否則不需要。
decodeThumbBitmapForFile()方法是根據(jù)計(jì)算好了圖片裁剪的比例之后從文件中加載圖片,我們先設(shè)置options.inJustDecodeBounds = true表示解析不占用內(nèi)存,但是我們能獲取圖片的具體大小,利用computeScale()計(jì)算好比例,在將options.inJustDecodeBounds=false,再次解析Bitmap,這樣子就對(duì)圖片進(jìn)行了裁剪。
loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack)我們?cè)诳蛻?hù)端只需要調(diào)用該方法就能獲取到Bitmap對(duì)象,里面的具體邏輯是先判斷內(nèi)存緩存LruCache中是否存在該Bitmap,不存在就開(kāi)啟子線(xiàn)程去讀取,為了方便管理加載本地圖片線(xiàn)程,這里使用了線(xiàn)程池,池中只能容納一個(gè)線(xiàn)程,讀取完了本地圖片先將Bitmap加入到LruCache中,保存的Key為圖片路徑,然后再使用Handler通知主線(xiàn)程圖片加載好了,之后將Bitmap和路徑回調(diào)到方法onImageLoader(Bitmap bitmap, String path)中,該方法的mPoint是用來(lái)封裝控件的寬和高的對(duì)象
如果不對(duì)圖片進(jìn)行裁剪直接這個(gè)方法的重載方法loadNativeImage(final String path, final NativeImageCallBack mCallBack) 就行了,邏輯是一樣的,只是這個(gè)方法不對(duì)圖片進(jìn)行裁剪
接下來(lái)就是GridView的Adapter類(lèi)的代碼
package com.example.imagescan; 
 
import java.util.List; 
 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Point; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.GridView; 
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);//用來(lái)封裝ImageView的寬和高的對(duì)象 
  private GridView mGridView; 
  protected LayoutInflater mInflater; 
   
  @Override 
  public int getCount() { 
    return list.size(); 
  } 
 
  @Override 
  public Object getItem(int position) { 
    return list.get(position); 
  } 
 
 
  @Override 
  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); 
  } 
   
 
  @Override 
  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); 
       
      //用來(lái)監(jiān)聽(tīng)I(yíng)mageView的寬和高 
      viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() { 
         
        @Override 
        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設(shè)置路徑Tag,這是異步加載圖片的小技巧 
    viewHolder.mImageView.setTag(path); 
     
     
    //利用NativeImageLoader類(lèi)加載本地圖片 
    Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() { 
       
      @Override 
      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); 
    }else{ 
      viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no); 
    } 
     
     
    return convertView; 
  } 
   
   
   
  public static class ViewHolder{ 
    public MyImageView mImageView; 
    public TextView mTextViewTitle; 
    public TextView mTextViewCounts; 
  } 
 
   
} 
首先我們將每個(gè)item的圖片路徑設(shè)置Tag到該ImageView上面,然后利用NativeImageLoader來(lái)加載本地圖片,但是我們顯示的圖片的寬和高可能遠(yuǎn)大于GirdView item中ImageView的大小,于是為了節(jié)省內(nèi)存,我們需要對(duì)圖片進(jìn)行裁剪,需要對(duì)圖片裁剪我們利用loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack)方法,我們就必須要獲取ImageView的寬和高了
但是我們想在getView()中獲取ImageView的寬和高存在問(wèn)題,在getView()里面剛開(kāi)始顯示item的時(shí)候利用ImageView.getWidth() 獲取的都是0,為什么剛開(kāi)始獲取不到寬和高呢,因?yàn)槲覀兪褂肔ayoutInflater來(lái)將XML布局文件Inflater()成View的時(shí)候,View并沒(méi)有顯示在界面上面,表明并沒(méi)有對(duì)View進(jìn)行onMeasure(), onLayout(), onDraw()等操作,必須等到retrue convertView的時(shí)候,表示該item對(duì)應(yīng)的View已經(jīng)繪制在ListView的位置上了, 此時(shí)才對(duì)item對(duì)應(yīng)的View進(jìn)行onMeasure(), onLayout(), onDraw()等操作,這時(shí)候才能獲取到Item的寬和高,于是我想到了自定義ImageView,在onMeasure()中利用回調(diào)的模式主動(dòng)通知我ImageView測(cè)量的寬和高,但是這有一個(gè)小小的問(wèn)題,就是顯示GridView的第一個(gè)item的時(shí)候,獲取的寬和高還是0,第二個(gè)就能正常獲取了,第一個(gè)寬和高為0,表示我們不對(duì)第一張圖片進(jìn)行裁剪而已,在效率上也沒(méi)啥問(wèn)題,不知道大家有沒(méi)有好的方法,可以在getView()中獲取Item中某個(gè)控件的寬和高。

自定義MyImageView的代碼,我們只需要設(shè)置OnMeasureListener監(jiān)聽(tīng),當(dāng)MyImageView測(cè)量完畢之后,就會(huì)將測(cè)量的寬和高回調(diào)到onMeasureSize()中,然后我們可以根據(jù)MyImageView的大小來(lái)裁剪圖片

package com.example.imagescan; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.widget.ImageView; 
 
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); 
  } 
 
  @Override 
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     
    //將圖片測(cè)量的大小回調(diào)到onMeasureSize()方法中 
    if(onMeasureListener != null){ 
      onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight()); 
    } 
  } 
 
  public interface OnMeasureListener{ 
    public void onMeasureSize(int width, int height); 
  } 
   
} 

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

package com.example.imagescan; 
 
import java.util.List; 
 
import android.app.Activity; 
import android.os.Bundle; 
import android.widget.GridView; 
import android.widget.Toast; 
 
public class ShowImageActivity extends Activity { 
  private GridView mGridView; 
  private List<String> list; 
  private ChildAdapter adapter; 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    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); 
     
  } 
 
  @Override 
  public void onBackPressed() { 
    Toast.makeText(this, "選中 " + adapter.getSelectItems().size() + " item", Toast.LENGTH_LONG).show(); 
    super.onBackPressed(); 
  } 
   
   
} 

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

package com.example.imagescan; 
 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.List; 
import java.util.Map; 
 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Point; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.CheckBox; 
import android.widget.CompoundButton; 
import android.widget.ImageView; 
import android.widget.CompoundButton.OnCheckedChangeListener; 
import android.widget.GridView; 
 
import com.example.imagescan.MyImageView.OnMeasureListener; 
import com.example.imagescan.NativeImageLoader.NativeImageCallBack; 
import com.nineoldandroids.animation.AnimatorSet; 
import com.nineoldandroids.animation.ObjectAnimator; 
 
public class ChildAdapter extends BaseAdapter { 
  private Point mPoint = new Point(0, 0);//用來(lái)封裝ImageView的寬和高的對(duì)象 
  /** 
   * 用來(lái)存儲(chǔ)圖片的選中情況 
   */ 
  private HashMap<Integer, Boolean> mSelectMap = new HashMap<Integer, Boolean>(); 
  private GridView mGridView; 
  private List<String> list; 
  protected LayoutInflater mInflater; 
 
  public ChildAdapter(Context context, List<String> list, GridView mGridView) { 
    this.list = list; 
    this.mGridView = mGridView; 
    mInflater = LayoutInflater.from(context); 
  } 
   
  @Override 
  public int getCount() { 
    return list.size(); 
  } 
 
  @Override 
  public Object getItem(int position) { 
    return list.get(position); 
  } 
 
 
  @Override 
  public long getItemId(int position) { 
    return position; 
  } 
   
  @Override 
  public View getView(final int position, View convertView, ViewGroup parent) { 
    final ViewHolder viewHolder; 
    String path = list.get(position); 
     
    if(convertView == null){ 
      convertView = mInflater.inflate(R.layout.grid_child_item, null); 
      viewHolder = new ViewHolder(); 
      viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.child_image); 
      viewHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.child_checkbox); 
       
      //用來(lái)監(jiān)聽(tīng)I(yíng)mageView的寬和高 
      viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() { 
         
        @Override 
        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.mImageView.setTag(path); 
    viewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 
       
      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
        //如果是未選中的CheckBox,則添加動(dòng)畫(huà) 
        if(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){ 
          addAnimation(viewHolder.mCheckBox); 
        } 
        mSelectMap.put(position, isChecked); 
      } 
    }); 
     
    viewHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : false); 
     
    //利用NativeImageLoader類(lèi)加載本地圖片 
    Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() { 
       
      @Override 
      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); 
    }else{ 
      viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no); 
    } 
     
    return convertView; 
  } 
   
  /** 
   * 給CheckBox加點(diǎn)擊動(dòng)畫(huà),利用開(kāi)源庫(kù)nineoldandroids設(shè)置動(dòng)畫(huà) 
   * @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 
   * @return 
   */ 
  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()); 
      } 
    } 
     
    return list; 
  } 
   
   
  public static class ViewHolder{ 
    public MyImageView mImageView; 
    public CheckBox mCheckBox; 
  } 
 
 
} 

第二個(gè)界面的Adapter跟第一個(gè)界面差不多,無(wú)非多了一個(gè)CheckBox用來(lái)記錄圖片選擇情況,我們只需要對(duì)CheckBox設(shè)置setOnCheckedChangeListener監(jiān)聽(tīng),微信的選中之后CheckBox有一個(gè)動(dòng)畫(huà)效果,所以我利用nineoldandroids動(dòng)畫(huà)庫(kù)也給CheckBox加了一個(gè)動(dòng)畫(huà)效果,直接調(diào)用addAnimation()方法就能添加了,getSelectItems()方法就能獲取我們選中的item的position了,知道了選中的position,其他的信息就都知道了,微信有對(duì)圖片進(jìn)行預(yù)覽的功能,我這里就不添加了,如果有這個(gè)需求可以自行添加,給大家推薦一個(gè)https://github.com/chrisbanes/PhotoView

運(yùn)行項(xiàng)目,效果如下

看起來(lái)還不錯(cuò)吧,采用的是異步讀取圖片,對(duì)圖片進(jìn)行了緩存和裁剪,使得在顯示本地圖片方面比較流暢,GridView滑動(dòng)也挺流暢的,也有效的避免OOM的產(chǎn)生。

相關(guān)文章

最新評(píng)論