android仿最新6.2版本相簿,android仿6.2相簿
仿相簿選擇圖片,查看大圖,寫的不太好,希望評論指出不足,諒解,先介紹一下我的基本思路
第一步擷取手機上的所有圖片路徑:
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; ContentResolver contentResolver = getContentResolver(); //擷取jpeg和png格式的檔案,並且按照時間進行倒序 Cursor cursor = contentResolver.query(uri, null, MediaStore.Images.Media.MIME_TYPE + "=\"image/jpeg\" or " + MediaStore.Images.Media.MIME_TYPE + "=\"image/png\"", null, MediaStore.Images.Media.DATE_MODIFIED+" desc"); if (cursor != null){ while (cursor.moveToNext()){ //do something } handler.sendEmptyMessage(0); }
我的儲存格式
/** 按時間排序的所有圖片list */ private ArrayList<SingleImageModel> allImages; /** 按目錄排序的所有圖片list */ private ArrayList<SingleImageDirectories> imageDirectories; /** * 一個檔案夾中的圖片資料實體 */ private class SingleImageDirectories{ /** 父目錄的路徑 */ public String directoryPath; /** 目錄下的所有圖片實體 */ public ImageDirectoryModel images; }
一個是全部圖片的儲存順序,第二個是按照目錄的圖片儲存順序
第二步,擷取到圖片之後,放入到gridview中進行顯示,但是BitmapFactory.decodeFile()函數會非常耗時,所以為了使得非常流暢的顯示圖片,建立一個類AlbumBitmapCacheHelper.class,用來非同步載入圖片,
該類使用LruCache<String,Bitmap> cache來緩衝Bitmap,使得儲存圖片不會造成oom,我這裡設定cache的初始大小為1/4的運行時記憶體
然後使用ThreadPoolExecutor線程池來處理圖片的顯示,線程池大小應該設定適中
做完這兩件事情之後就可以用來載入圖片了,方法getBitmap用來返回圖片
Bitmap bitmap = getBitmapFromCache(path, width, height); //如果能夠從緩衝中擷取符合要求的圖片,則直接回調 if (bitmap != null) { } else { //建立線程放入線程池去處理該圖片的顯示 } return bitmap;
如果cache中找不到該圖片,則調用BitmapFactory.decodeFile()去載入圖片,載入圖片不能夠直接載入原圖,會造成OOM,所以要去計算壓縮比
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); options.inSampleSize = computeScale(options, width, height); options.inJustDecodeBounds = false; bitmap = BitmapFactory.decodeFile(path, options); //擷取之後,放入緩衝,以便下次繼續使用 if (bitmap != null && cache!=null) { cache.put(path, bitmap); }
方法computeScale()主要是計算圖片最小的壓縮比,
這樣在gridview中的getview方法中去調用AlbumBitmapCacheHelper.class的getBitmap()方法即可,但是這樣會有很多的問題:
一個問題就是圖片顯示會閃,這主要是由於getview的view的複用導致一個imageview被設定多次background,解決方案就是使用settag方法holder.iv_content.setTag(path);將要顯示的imageview的tag設定為需要顯示的圖片路徑,這樣在回調的時候使用方法gridView.findViewWithTag(path),找到這個imageview進行顯示,閃的問題就解決了
第二個問題就是載入速度很慢,拉的速度很快的情況下,圖片要很久才會載入出來,特別是很大的圖片,比如拍照和的照片,
解決方案:第一方案就是在AlbumBitmapCacheHelper類中維護一個ArrayList<String> currentShowString,在getview方法中,如果該圖片要顯示,則直接將path加入到該list中,同時如果這個view的tag不為空白,說明該view的原來的path是不需要顯示的,所以需要將這個path從list中刪除:
//最佳化顯示效果 if(holder.iv_content.getTag() != null) { String remove = (String) holder.iv_content.getTag(); AlbumBitmapCacheHelper.getInstance().removePathFromShowlist(remove); } AlbumBitmapCacheHelper.getInstance().addPathToShowlist(path);這樣線上程池中的處理方式就是先查看需要顯示的path是否在list中,如果沒有在list中,則該線程直接關閉,如果在list中,則顯示該圖片
if (!currentShowString.contains(path)||cache==null) { return;}
第二個方案就是如果顯示的圖片很大,特別是拍照,的圖片,decode有時會耗時幾秒中,顯示效果非常好,我自己想出來的處理的方式就是
***第一步,從應用的緩衝temp目錄下取,如果取不到,***第二步,計算圖片的壓縮比例samplesize,如果samplesize < 4,圖片的BitmapFactory.decodeFile()時間短,直接返回圖片,但是如果 samplesize > 4,執行第三步***第三步則將壓縮後的圖片存入temp目錄下,以便下次快速取出這樣顯示圖片的效果就出來了,顯示的速度除了和一樣第一次大圖載入慢之外,之後的顯示就能很快了,
if (!new File(CommonUtil.getDataPath()).exists()) new File(CommonUtil.getDataPath()).mkdirs(); //臨時檔案的檔案名稱 String tempPath = CommonUtil.getDataPath() + hash + ".temp"; //如果該檔案存在 if (new File(tempPath).exists()) bitmap = BitmapFactory.decodeFile(tempPath); ...... //第三步,如果縮放比例大於4,該圖的載入會非常慢,所以將該圖儲存到臨時目錄下以便下次的快速載入 if (options.inSampleSize >= 4) { try { File file = new File(tempPath); if (!file.exists()) file.createNewFile(); FileOutputStream fos = new FileOutputStream(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); fos.write(baos.toByteArray()); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
問題就差不多解決了
第三步大圖的查看,大圖只要是使用的網上找的ZoomImageView+viewpagger的組合,但是使用這個出現的問題就是很容易OOM,沒辦法,我的處理方式就是在點進去大圖的時候
public void releaseHalfSizeCache() { cache.resize((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));}
直接將cache的大小變成原來的一半,因為查看大圖頁,載入一張大圖佔用的記憶體就很大,這樣顯示效果頁還湊合,有別的方法,一定要留言告訴我
注意:大圖的查看由於需要通過intent傳遞資料,但是intent傳遞的資料大小不能太大,如果手機上有幾千張圖片,則資料量大小可能會超過intent所能傳遞的最大量,所以可以寫入一個公用的地方,記憶體,資料庫,檔案都可以,
//TODO 這裡由於涉及到intent傳遞的資料不能太大的問題,所以如果需要,這裡要進行另外的處理,寫入到記憶體或者寫入到檔案中 intent.putExtra(PickBigImagesActivity.EXTRA_DATA, getAllImagesFromCurrentDirectory());
我暫時沒有處理~~
第四步就是圖片選擇完成之後,完成善後工作,將AlbumBitmapCacheHelper類中cache清空,差不多就這樣了,還有很多的小問題,比片時間的顯示,具體大家看源碼
最新版源碼下載
github地址,有bug的話,我會隨時在github上更新
由於源碼是android studio的工程,所以不能直接匯入eclipse,必須要手動拷貝檔案,在這裡放出所有的檔案,方便eclipse