1. 程式人生 > >多個Fragment介面重疊和拍照閃退問題

多個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相機、相簿獲取圖片