2014年4月18日

android 解決使用gridview讀取相簿中大量圖片產生縮圖太久及滑動掛掉的問題

記得之前寫了一篇用gridview來讀取相簿照片的縮圖,忘記的可以看這篇回想一下。 ("Android 使用gridview 顯示相簿中的縮圖")

最近在做測試時,沒想到有個客戶很認真的測試了這個功能,他將手機裡的相簿塞了兩千多張照片,然後測 run了幾次,結果嘛在讀取縮圖時花了一些時間,畫面才開啟。

而用測試程式跑的結果,雖然畫面一下子就打開了,但是 .... 不幸福的事發生了,沒想到滑到一半程式就掛了。

看似在讀大量圖片時,記憶體緩存不足造成。

想辦法1:
先載入一部份照片,如果滑動到相簿底部時,再下載更多照片,好處是可以不需要載入所有的照片,節省更多的記憶。這個方法絕對是可行的,但每次要下載照片時,滑動就會卡卡的。

gridView.setOnScrollListener( new OnScrollListener() {

     @Override
     public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {

          if (firstVisibleItem + visibleItemCount == totalItemCount) {
                isScrollFoot = true;
         } else {
                isScrollFoot = false;
         }
    }

     @Override
     public void onScrollStateChanged(AbsListView view, int scrollState) {

          if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                    && isScrollFoot) { // 滾動靜止且滾動到最底部
               //讀取下20張照片的code

         }
    }
});

想辦法2:
讓畫面開啟後,照片每載入一部份(比如20張),再載入下20張,直到全部照片下載完畢。雖然這個方法並沒有節省到記憶體,但是我們在滑動時會比較順,但似乎拼命滑動時還是有可能掛掉。

 // 非同步執行讀取相簿
 private static final class AsyncReadPictures extends
            AsyncTask<String[], Void, Boolean> {

        private MainActivity context = null;

        public AsyncReadPictures(MainActivity context) {
             this. context = context;
       }

        @Override
        protected Boolean doInBackground(String[]... params) {
             // TODO Auto-generated method stub
             int readedindex = context. index;
             for ( int i = context. index; i < context. count; i++) {

                  if ( context. cursor.isClosed()) {
                        return null;
                 }

                  context. cursor.moveToPosition(i);
                  // context.logger.info("imagecursor moveToPosition:" + i);

                  // 取得照片的ID
                  int id = context. cursor.getInt( context. image_column_index); // id在table中第一行

                  int dataColumnIndex = context. cursor
                            .getColumnIndex(MediaStore.Images.Media. DATA);

                  // arrPath[i] = imagecursor.getString(dataColumnIndex); //
                  // 照片路徑(完整路徑),範例:/ mnt/sdcard /external_sd/DCIM/Camera/2013-03-25
                 String filepath = context. cursor.getString(dataColumnIndex); // 照片路徑(完整路徑),範例:/ mnt/sdcard /external_sd/DCIM/Camera/2013-03-25

                  // logger.info(i + " " + arrPath[i]); // 19.19.02.jpg

                 File file = new File(filepath);

                  if (file.exists() == false) {

                        // logger.info("file not exists");
                        continue;
                 } else {

                        // logger.info("file length space:" + file.length());
                        if (file.length() == 0) {
                             // logger.info("file length is 0:" + file.length());
                             continue;
                       }
                 }

                  context. thumbs.add( context. index, id + "");
                  context. imagePaths.add( context. index, filepath);
                  context. index++;

                  if ((readedindex + 20) == context. index) {
                        break;
                 }
            }

             return true;
       }

        @Override
        protected void onPostExecute(Boolean result) {
             super.onPostExecute(result);
             // context.dialog.dismiss();
             if (result != null) {

                  context. imageAdapter.notifyDataSetChanged();

                  if ( context. index < context. count) {
                        context.readNextPictures();
                 }

            }
       }

        @Override
        protected void onPreExecute() {
             super.onPreExecute();

       }

 }

 @Override
 public void onDestroy() {
        super.onDestroy();
        if ( cursor != null) {
             cursor.close();
       }
 }

 public void readNextPictures() {
       AsyncReadPictures async = new AsyncReadPictures(MainActivity.this );
       async.execute();
 }

以上兩種方法,看似有那麼些幫助,但是仍存記憶體緩存不足的問題。

在ImageAdapter裡填添一個help物件來存放view等相關物件,包含icon的bitmap,這樣一來滑動時可減少記憶體的浪費。

在ImageAdapter.java的getView()中,以下段code取代:

LayoutInflater inflater = (LayoutInflater) context
                     .getSystemService(Context. LAYOUT_INFLATER_SERVICE );
View rowview = null;
ImageAdapterHolder holder;
ImageView imgview_photoItem_photo;

if (convertView == null) {

    // 連結xml 中實體物件
    LayoutInflater inflater = (LayoutInflater) context
              .getSystemService(Context. LAYOUT_INFLATER_SERVICE );
    rowview = inflater.inflate(R.layout.listitem_photo, parent, false); // 設定list中Item的 xml
    layout = (ViewGroup) rowview.findViewById(R.id.rl_photoItem);
    imgview_photoItem_photo = (ImageView) rowview
              .findViewById(R.id. img_photoItem_photo);
    cb_photoItem_checkBox = (CheckBox) rowview
              .findViewById(R.id. cb_photoItem_checkBox);

    holder = new ImageAdapterHolder();
    holder.setImgv_photoItem_photo(imgview_photoItem_photo);
    holder.setLayout( layout);
    layout.setTag(holder);

} else {
    holder = ( ImageAdapterHolder) convertView.getTag();
    layout = holder.getLayout();
    imgview_photoItem_photo = holder.getImgv_photoItem_photo();
}

imgview_photoItem_photo.setId(position);

如此一來,滑動照片的流暢度更好了。