首页 > 代码库 > 多个Fragment界面重叠和拍照闪退问题

多个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相机、相册获取图片显示并保存到SD卡

 

多个Fragment界面重叠和拍照闪退问题