Android 응용 프로그램 에서 ContentProvider 를 사용 하여 로 컬 그림 을 스 캔 하고 표시 합 니 다.

35026 단어 Android그림 표시
예전 에 친구 들 이 저 에 게 로 컬 이미지 선택 에 관 한 데모 가 있 냐 고 물 었 습 니 다.위 챗 과 비슷 한 효과 가 있 냐 고 물 었 습 니 다.그 는 인터넷 에 이런 데모 가 없다 고 했 습 니 다.저 에 게 이 효과 에 관 한 데 모 를 쓸 수 있 냐 고 물 었 습 니 다.그래서 저 는 위 챗 의 로 컬 이미지 선택 에 관 한 데 모 를 연 구 했 습 니 다.제 가 본 떠 서 여러분 께 공유 하 겠 습 니 다.앞으로 이런 수요 가 있 는 친구 에 게 도움 이 되 었 으 면 좋 겠 습 니 다.주로 ContentProvider 를 사용 하여 핸드폰 에 있 는 그림 을 스 캔 하고 GridView 로 그림 을 표시 합 니 다.GridView 와 ListView 로 그림 을 표시 하 는 문 제 는 골 치 아 픈 문제 입 니 다.저희 핸드폰 의 메모리 가 제한 되 어 있 고 핸드폰 이 모든 프로그램 에 분배 하 는 메모리 도 제한 되 어 있 기 때문에 사진 이 많은 상황 에서 OOM 의 발생 과 수반 되 기 쉽 습 니 다.하지만 현재 도 많은 오픈 소스 이미지 디 스 플레이 프레임 워 크 가 있 습 니 다.많은 이미지 디 스 플레이 를 최적화 시 켰 습 니 다.여러분 들 이 관심 이 있 으 시 면 알 아 보 실 수 있 습 니 다.오늘 제 글 은 LruCache 라 는 유형 과 이미지 에 대응 하 는 재단 을 사 용 했 습 니 다.그러면 OOM 의 발생 을 최대한 피 할 수 있 습 니 다.우 리 는 먼저 위 챗 의 효 과 를 살 펴 보 겠 습 니 다.
201645151626071.png (480×854)
201645151655089.png (480×854)
다음은 이 효 과 를 실현 합 시다.우선 이미지 스 캔 이라는 항목 을 새로 만 듭 니 다.
먼저 첫 번 째 화면 을 봅 시다.핸드폰 에 있 는 그림 을 스 캔 한 다음 에 그림 이 있 는 폴 더 에 따라 분류 하고 있 는 폴 더 에 있 는 그림 과 폴 더 에 있 는 그림 개 수 를 표시 합 니 다.우 리 는 인터페이스 요소(폴 더 이름,폴 더 그림 개수,폴 더 의 그림 한 장)이 세 가지 속성 을 실체 대상 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 레이아웃 입 니 다.위의 그림 을 보 셔 도 됩 니 다.그의 효 과 는 두 장의 그림 이 추 가 된 효과 라 고 생각 하 실 것 입 니 다.사실은 아 닙 니 다.뒤의 중첩 효 과 는 배경 그림 일 뿐 입 니 다.코드 를 먼저 붙 입 니 다.

<?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> 
위의 레이아웃 코드 를 보 셔 도 됩 니 다.위 에 사용자 정의 MyImageView 를 사용 하고 있 습 니 다.저 는 이 사용자 정의 MyImageView 의 역할 을 말 하지 않 고 잠시 후에 말씀 드 리 겠 습 니 다.코드 를 계속 보 겠 습 니 다.
첫 번 째 인터페이스의 주요 코드

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: 
        //      
        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() { 
       
      @Override 
      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 의 코드 를 보기 전에 중요 한 종 류 를 보 겠 습 니 다.로 컬 이미지 로 더 Native ImageLoader

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   
 *   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) { 
       
      //          
      @Override 
      protected int sizeOf(String key, Bitmap bitmap) { 
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024; 
      } 
    }; 
  } 
   
  /** 
   *         NativeImageLoader    
   * @return 
   */ 
  public static NativeImageLoader getInstance(){ 
    return mInstance; 
  } 
      
      

  /** 
   *       ,         
   * @param path 
   * @param mCallBack 
   * @return 
   */ 
  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 path 
   * @param mPoint 
   * @param mCallBack 
   * @return 
   */ 
  public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){ 
    //       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       ,             ,  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          
   * @param key 
   * @return 
   */ 
  private Bitmap getBitmapFromMemCache(String key) { 
    return mMemoryCache.get(key); 
  } 
   
   
  /** 
   *   View(   ImageView)              
   * @param path 
   * @param viewWidth 
   * @param viewHeight 
   * @return 
   */ 
  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 가 미 끄 러 지 는 유창 도 를 높이 며 그 중에서 몇 가지 중요 한 방법 을 소개 합 니 다.
coptute Scale()은 그림 을 재단 해 야 하 는 비율 을 계산 합 니 다.컨트롤 의 크기 와 그림 의 크기 에 따라 비례 를 정 합 니 다.그림 이 컨트롤 보다 크 면 재단 합 니 다.그렇지 않 으 면 필요 하지 않 습 니 다.
decodeThumbBitmapForFile()방법 은 그림 을 재단 한 비율 을 계산 한 후에 파일 에서 그림 을 불 러 오 는 것 입 니 다.options.inJustDecodeBounds=true 를 설정 하여 메모리 가 차지 하지 않 는 다 는 것 을 표시 합 니 다.그러나 그림 의 구체 적 인 크기 를 가 져 올 수 있 습 니 다.coptute Scale()을 이용 하여 비율 을 계산 하여 options.inJustDecodeBounds=false 를 다시 비트 맵 을 해석 합 니 다.이렇게 해서 그림 을 재단 했다.
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 류 코드 입 니 다.

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);//    ImageView        
  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); 
       
      //    ImageView     
      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    Tag,             
    viewHolder.mImageView.setTag(path); 
     
     
    //  NativeImageLoader        
    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; 
  } 
 
   
} 
우선,우 리 는 모든 아 이 템 의 그림 경 로 를 이 ImageView 에 태그 한 다음 에 NativeImageLoader 를 이용 하여 로 컬 그림 을 불 러 옵 니 다.그러나 우리 가 표시 하 는 그림 의 너비 와 높이 는 GirdView item 의 ImageView 크기 보다 훨씬 클 수 있 습 니 다.따라서 메모 리 를 절약 하기 위해 서 는 그림 을 재단 해 야 합 니 다.loadNativeImage(final String path)를 이용 하여 그림 을 재단 해 야 합 니 다.final Point mPoint,final Native ImageCallBack mCallBack)방법 은 ImageView 의 너비 와 높이 를 가 져 와 야 합 니 다.
하지만 getView()에서 ImageView 의 너비 와 높이 를 가 져 오 려 면 문제 가 있 습 니 다.getView()에서 아 이 템 을 처음 표시 할 때 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 의 코드 를 사용자 정의 합 니 다.OnMeasure Listener 감청 만 설정 하면 MyImageView 측정 이 끝 난 후에 측정 한 너비 와 높이 를 onMeasure Size()로 조정 한 다음 에 MyImageView 의 크기 에 따라 그림 을 재단 할 수 있 습 니 다.

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); 
     
    //           onMeasureSize()    
    if(onMeasureListener != null){ 
      onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight()); 
    } 
  } 
 
  public interface OnMeasureListener{ 
    public void onMeasureSize(int width, int height); 
  } 
   
} 
위 에 있 는 이 코드 들 은 첫 번 째 인터페이스의 기능 을 완 성 했 습 니 다.그 다음 에 GridView 의 item 을 누 르 면 다른 화면 으로 이동 하여 이 폴 더 아래 의 모든 그림 을 표시 합 니 다.기능 은 첫 번 째 화면 과 차이 가 많 지 않 고 GridView 를 사용 하여 그림 을 표시 합 니 다.두 번 째 인터페이스의 레이아웃 코드 는 붙 이지 않 고 인터페이스의 코드 를 직접 붙 입 니 다.

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 위 에 우리 가 정의 한 MyImageView 는 그림 을 표시 하 는 데 사용 되 고,또 하나의 CheckBox 는 우리 가 선택 한 상황 을 기록 합 니 다.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);//    ImageView        
  /** 
   *             
   */ 
  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); 
       
      //    ImageView     
      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,      
        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        
    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     ,     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 
   * @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; 
  } 
 
 
} 
두 번 째 인터페이스의 Adapter 는 첫 번 째 화면 과 차이 가 많 지 않 습 니 다.CheckBox 가 하나 더 있어 서 그림 선택 상황 을 기록 할 수 있 습 니 다.저 희 는 CheckBox 에 setOn Checked Change Listener 감청 을 설정 해 야 합 니 다.위 챗 이 선택 한 후에 CheckBox 는 애니메이션 효과 가 있 습 니 다.그래서 저 는 Ninold androids 애니메이션 라 이브 러 리 를 이용 하여 CheckBox 에 애니메이션 효 과 를 추 가 했 습 니 다.addAnimation()방법 을 직접 호출 하면 추가 할 수 있 습 니 다.getSelectItems()방법 은 우리 가 선택 한 item 의 position 를 얻 을 수 있 습 니 다.선택 한 position 를 알 게 되면 다른 정 보 는 모두 알 수 있 습 니 다.위 챗 은 그림 을 미리 보 는 기능 이 있 습 니 다.저 는 추가 하지 않 습 니 다.이 수요 가 있 으 면 스스로 추가 할 수 있 습 니 다.추천 해 드릴 게 요.https://github.com/chrisbanes/PhotoView
실행 항목,효 과 는 다음 과 같 습 니 다.

보기 에는 괜 찮 은 것 같 습 니 다.비동기 로 그림 을 읽 고 그림 을 캐 시 하고 재단 하여 로 컬 그림 을 표시 하 는 데 유창 합 니 다.GridView 의 미끄럼 도 원활 하고 OOM 의 발생 을 효과적으로 피 할 수 있 습 니 다.

좋은 웹페이지 즐겨찾기