Android中Fragment传参最佳实践 - 解决切换后参数值丢失问题
1、前言
最近博主在使用TabPageIndicator+FragmentStatePagerAdapter来管理Fragment(Fragment嵌套n个Fragment,外层又以Fragment为元素)的时候,遇到一个奇怪的问题:当第一次切换的时候没有问题,但是当跳到别的tab页面后再跳转回来,原来Fragment中的标记值(比如position)值都会置为初始值,继而引发一个数据混乱的问题。
最开始的传值方式为:
class MyFragment extends Fragment{
int flag;
public void setFlag(int flag){
this.flag = flag;
}
}
//创建与传参
MyFragment my=new MyFragment();
my.setFlag(position);
return my;
起初以为是TabPageIndicator的bug,但是心想那么多人用了都没反馈肯定不是,换了star比较高的MagicIndicator问题依然得不到解决,那么解决的办法呢?
<br/>
2、万能的google
在google敲入Fragment miss value
发现了一篇帖子《Best practice for instantiating a new Android Fragment》,中文翻译为:初始化Fragment的最佳实践。
点进去后发现博主显然也遇到了和我差不多的问题但是并不是,所幸在最佳回答中有一句话提到了解决问题的要点:This Bundle will be available even if the Fragment is somehow recreated by Android
,中文翻译为:当android重新创建一个fragment的时候,依然可以使用这个bundle。
这里提到的bundle就是外部给Fragment传参的bundle,具体的方式是:
public static MyFragment newInstance(int someInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
myFragment.setArguments(args);
return myFragment;
}
在Fragment的OnCreate周期中取出参数:
getArguments().getInt("someInt", 0);
<br/>
3、解决问题
以上传参方式替换为使用官方推荐的Bundle方式即可,终于知道官方不推荐使用setter方式来传参的原因:因为系统在需要的时候帮程序重新创建(又不经过new方法)Fragment的时候,只会维护存于Bundle中的值,别的成员变量都会被重置然后重新初始化
。
<br/>
4、翻译
Stackoverflow这段文字博主觉得有必要阅读以下,以便了解Fragment特性。
先贴出博主蹩脚的翻译(建议阅读原文):
如果android在之后重新创建你的Fragment,它(android)将会调用一个没有参数的构造函数(即默认的构造函数),所以覆盖构造函数并不不是一个解决办法。
如你所言,如果你的Fragment在new出来以后再被Android recreate,那么Fragment获取参数的方法只有一个,也就是利用setArguments函数在传入值。
现在举个例子,如果我们想传一个int类型的参数到fragment里面,可以这么做:
public static MyFragment newInstance(int someInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
myFragment.setArguments(args);
return myFragment;
}
当Fragment被创建的时候,可以在Oncreate函数中获取外部传入的值,获取办法:
getArguments().getInt("someInt", 0);
这样做的好处在于,当Framgnet被ReCreate的时候,这个bundle中的参数依然有效,(不会被覆盖或者重置)。
值得注意的是:setArguments方法应该在Activity的attached周期之前调用。
参考文章:[https://developer.android.com/reference/android/app/Fragment.html][4]
<br/>
###英文原文
If Android decides to recreate your Fragment later, it's going to call the no-argument constructor of your fragment. So overloading the constructor is not a solution.
With that being said, the way to pass stuff to your Fragment so that they are available after a Fragment is recreated by Android is to pass a bundle to the setArguments method.
So, for example, if we wanted to pass an integer to the fragment we would use something like:
public static MyFragment newInstance(int someInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
myFragment.setArguments(args);
return myFragment;
}
And later in the Fragment onCreate() you can access that integer by using:
getArguments().getInt("someInt", 0);
This Bundle will be available even if the Fragment is somehow recreated by Android.
Also note: setArguments can only be called before the Fragment is attached to the Activity.
This approach is also documented in the android developer reference: [https://developer.android.com/reference/android/app/Fragment.html][4]
[1]: https://github.com/hackware1993/MagicIndicator
[2]: https://stackoverflow.com/questions/9245408/best-practice-for-instantiating-a-new-android-fragment
[3]: https://stackoverflow.com/questions/9245408/best-practice-for-instantiating-a-new-android-fragment