天使漫步IT工作室

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

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »