记录最近遇到的多个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卡