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

多個Fragment界面重疊和拍照閃退問題

map() 拉取 圖片 getheight eat details directory 實例化 ict

記錄最近遇到的多個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相機、相冊獲取圖片顯示並保存到SD卡

多個Fragment界面重疊和拍照閃退問題