多個Fragment介面重疊和拍照閃退問題
----------------------------------------------------------------------------------------------------------------------------------------------------------------
環境:Activity(一個)+Fragment(多個)
問題一:多個fragment出現重疊現象
首先,Android管理Fragment有兩種方式,使用add、hide、show的方式和replace方式,兩種方式各有優缺點。
--> replace方式
如果使用這種方式,是可以避免重疊的問題,但是每次replace會把生命週期全部執行一遍,如果在這些生命週期函式里拉取資料的話,就會不斷重複的載入重新整理資料,所以我們並不推薦使用這種方式。
--> add、hide、show的方式
雖然這種方式避免了可能重複載入重新整理資料的問題,但是會出現重疊的問題。
原因:
當系統記憶體不足,Fragment 的宿主 Activity 回收的時候,Fragment 的例項並沒有隨之被回收。Activity 被系統回收時,會主動呼叫 onSaveInstance() 方法來儲存檢視層(View Hierarchy),所以當 Activity 通過導航再次被重建時,之前被例項化過的 Fragment 依然會出現在 Activity 中,此時的 FragmentTransaction 中的相當於又再次 add 了 fragment 進去的,hide()和show()方法對之前儲存的fragment已經失效了,所以就出現了重疊。
然而我們還是推薦使用這個,我們可以解決。
解決方法:MainActivity.Class中重寫onAttachFragment,重新讓新的Fragment指向了原本未被銷燬的fragment,它就是onAttach方法對應的Fragment物件
@Override public void onAttachFragment(Fragment fragment) { if (tab1 == null && fragment instanceof Tab1Fragment) tab1 = fragment; if (tab2 == null && fragment instanceof Tab2Fragment) tab2 = fragment; if (tab3 == null && fragment instanceof Tab3Fragment) tab3 = fragment; if (tab4 == null && fragment instanceof Tab4Fragment) tab4 = fragment; }
問題二:呼叫系統相機拍照閃退
在onActivityResult方法裡通過Intent的getData方法獲取的資料轉換成bitmap並顯示在介面上,有時候會有取不到資料,或者顯示的bitmap會非常小,如果將bitmap儲存到sd卡後會發現,圖片的解析度很低,並且圖片大小也是經過壓縮的,不管將相機的畫素設定多高,最後通過這種方式返回的bitmap總是經過壓縮了的。如果想獲得理想的照片大小和解析度改如何處理呢?以下是我的處理方法,雖然不是最好,但是幫我解決了這個需求。我先來簡述一下為什麼返回的圖片是經過了壓縮的。
大家都知道,現在手機畫素少則500W或800W,多則4KW(某亞),就拿常見的800W畫素的相機拍出來的照片來說,解析度大概在3200*2400左右,我的測試機型是LG optimus 2x,2.3.7的系統,用800W畫素拍出來大概就是這個解析度,照片大小在2M左右。試想一下,在Android系統中最常用的Bitmap格式(ARGB_8888)一個畫素佔用4byte(位元組),例如一張解析度為3200*2400px的照片,其耗用記憶體情況是:3200*2400*4/1024/1024=29.296875(MB)。如果為了一張圖片,耗用這麼大的記憶體,肯定是不合理的,並且,官方文件中有說明,Android系統分配給每個應用的最大記憶體是16M,所以,系統為了防止應用記憶體佔用過大,對於在應用內通過相機拍攝的圖片最終返回來的結果進行了壓縮,壓縮後的圖片變得很小,通過之前說的getData的方式只能滿足比如顯示個頭像這樣的需求,如果要顯示大圖,就會出現模糊的情況。如何獲得清晰的大圖,思路如下:
1.拍照時,將拍得的照片先儲存在本地,其中啟動相機程式如下;
Intent getImageByCameraIntent = new Intent("android.media.action.IMAGE_CAPTURE"); Uri imageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "image.jpg")); getImageByCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(getImageByCameraIntent, TAKE_PHOTO);
2.在onActivityResult方法中再將圖片取出,並經過縮小處理再顯示在介面上或儲存在自定義資料夾下,其中壓縮比例自定義;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case TAKE_PICTURE: //將儲存在本地的圖片取出並縮小後顯示在介面上 Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/image.jpg"); Bitmap newBitmap = ImageTools.zoomBitmap(bitmap, bitmap.getWidth() / SCALE, bitmap.getHeight() / SCALE); //由於Bitmap記憶體佔用較大,這裡需要回收記憶體,否則會報out of memory異常 bitmap.recycle(); //將處理過的圖片顯示在介面上,並儲存到本地 //取當前時間為照片名 String imageName= DateFormat.format("yyyyMMdd_hhmmss",Calendar.getInstance(Locale.CHINA))+".png"; ImageTools.savePhotoToSDCard(newBitmap, getPhotoPath(), imageName); break; default: break; } } }
其中ImageTools是自定義的圖片工具類,zoomBitmap()和savePhotoToSDCard()方法具體如下:
/** * Resize the bitmap * * @param bitmap * @param width * @param height * @return */ public static Bitmap zoomBitmap(Bitmap bitmap, int width, int height) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); Matrix matrix = new Matrix(); float scaleWidth = ((float) width / w); float scaleHeight = ((float) height / h); matrix.postScale(scaleWidth, scaleHeight); Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true); return newbmp; } /** * Save image to the SD card * @param photoBitmap * @param photoName * @param path */ public static void savePhotoToSDCard(Bitmap photoBitmap,String path,String photoName){ if (checkSDCardAvailable()) { File dir = new File(path); if (!dir.exists()){ dir.mkdirs(); } File photoFile = new File(path , photoName ); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(photoFile); if (photoBitmap != null) { if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) { fileOutputStream.flush(); } } } catch (FileNotFoundException e) { photoFile.delete(); e.printStackTrace(); } catch (IOException e) { photoFile.delete(); e.printStackTrace(); } finally{ try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
說明:由於Android給bitmap分配的記憶體最大不超過8M,所以對使用完的較大的Bitmap要釋放記憶體,呼叫其recycle()方法即可。然後將縮小(縮小方法在Demo原始碼中有)後的bitmap顯示在介面上或儲存到SD卡,至於之前儲存的原圖,可以刪掉,也可以放在那,下次拍照時,這張原圖就會被下一張照片覆蓋,所以SD卡上使用只有一張臨時圖片,佔用也不是很大。
---------------------------------------------------------------------------------------------------------------------------------
參考資料:
http://blog.csdn.net/whitley_gong/article/details/51987911 //關於Fragment重疊問題分析和解決
https://m.th7.cn/show/14/201612/1045726.html //android系統相機的使用、及解決拍照閃退的問題
http://m.blog.csdn.net/article/details?id=8654137 //Android相機、相簿獲取圖片