最近在开发单页面应用,多个fragment嵌套在一个activity里面,和之前单个Fragment依赖在activivty中管理还是有点不同,我这Fragment嵌套了3层,最终还是出问题了。
一.Fragment生命周期的坑
1.问题描述
我用FragmentManager去管理和切换Fragment,在这过程中用到了回退栈,先声明对于回退栈的出栈操作
fragmentmanager.popBackStack();
在返回时是会调用Fragment的生命周期onCreateView -> onResume的。所以在这个过程中就出问题了,我在onCreateView中设置
editText.setText("balabalabalala");
然后当页面展示之后,我手动去改成"bilibili",然后跳转到另一个Fragment,返回时,原本的Fragment会调用onCreateView方法,这个方法里有执行了editText.setText("balabalabalala"),所以按理来说最后EditText的输出应该是balabalabalala,结果却是bilibili。
2.重现这个问题
因为我用Fragment嵌套加上使用回退栈才出现这个问题,我打算用另一种简单的形式重现这个问题。假如我有一个ActivityA,它里面有一个FragmentA,我从ActivityA跳转到ActivityB再返回的话是不会重新调用FragmentA的生命周期的,所以自然无法再次重现这个问题。但是我可以使用转屏来重现这个问题。没错,在普通的情况下转屏也会出现这个问题。因为转屏会使Activity重新创建,内部的Fragment也会重新创建。
假如你在ActivityA中有个FragmentA,FragmentA中有个EditText,你默认在它的onCreateView方法中去输入一个字符串,再运行的时候手动去改变这个Edittext的内容,然后转屏。你会发现虽然调用了onCreateView,但是Edittext的内容还是你之前手动去输入的内容。
3.解决方法
其实这个就是Fragment生命周期的关系,我们都知道如果Activity被销毁重建的话,可以在onCreate中拿到savedInstanceState再重新赋值,而fragment是在onActivityCreate这个生命周期方法中设置之前的状态的。还有一点因素是设置了id属性的控件会自动去加载它之前的数据(TextView除外,如果你用TextView你就会发现它没法获取销毁前的数据,而是重新设值,因为TextView要另外设置一个属性才能保存销毁前的值,这个属性我忘了是啥了)。
还有一个比较重要的地方,虽然是在onActivityCreate这个地方获取之前的值,但是如果你在onActivityCreate设置数据的话,还是会被原来的值覆盖。所以至少要在onStart方法中去设置,你可以用上面的案例,把在onCreateView中设值改成在onStart中设值,你会发现Fragment重新创建时会被重新成功设值。
这个问题虽然不是一个很严重的问题,但是会很容易被忽略,所以涉及到Fragment被销毁和重建的地方还是要注意一下。
二.DialogFragment宽高的坑
(1)DialogFragment中的Fragment是可以再show一个DialogFragment的。
(2)如果DialogFragment的Fragment中再嵌套Fragment,父布局可以设置wrap_content随子布局的宽高,这是没问题。
(3)我的是7.0的系统,DialogFragment自定义布局时设置父布局的宽高会无效,不管怎么设都是全屏。而父布局设置wrap_content,在父布局中嵌套一层子布局再设具体的宽高就可以正常展示我们想要的结果。
(4)我不知道是不是我自定义DialogFragment的原因,设宽高的话要对嵌套的子布局设才有用,对window设也没用。所以动态设置布局宽高我是对子布局去设置的,对window设置没效果。
(5)默认情况下布局会有间距,我照方法对window去消除边距是没效果的。按照上面说的对布局才管用,我就尝试对布局消除边距,结果还真有效果。
在父布局中加入background,自定义DialogFragment的xml最顶层的布局
android:background="@drawable/ibn_activity_container"
这样就能消除边距了。
至于为什么要在内部布局设才有用,给window设没用,这个我就不知道了。但是如果你在尝试给window设置这些没效果时,你可以对布局去设置。