如何向一个Fragment传递参数---setArguments方法的介绍

来源:互联网 发布:数据研究中心美国大学 编辑:程序博客网 时间:2024/05/17 22:23

在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,如果我问你怎么向一个Fragment传递参数,你是不是会首先想到通过构造方法,当面试被问到这个问题的时候我也是这么想的,后来发现自己错了,现在给大家讲一下究竟该怎么做。

首先我们看构造方法这种方式为什么不行,根据Android文档说明,当一个fragment重新创建的时候,系统会再次调用 Fragment中的默认构造函数。 注意这里:是 默认构造函数。
这句话更直白的意思就是:当你小心翼翼的创建了一个带有重要参数的Fragment的之后,一旦由于什么原因(横竖屏切换)导致你的Fragment重新创建。——-很遗憾的告诉你,你之前传递的参数都不见了,因为recreate你的Fragment的时候,调用的是默认构造函数。

首先我们通过构造函数来传递参数,代码如下

public class MainActivity extends FragmentActivity {    private FragmentManager manager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        manager = getSupportFragmentManager();        /*         * 这里为什么要进行空判断,因为在屏幕旋转的时候,系统会执行onSaveInstanceState         * 方法去保存当前activity的状态,然后activity会重建,执行onCreate方法,如果我们不判断         * savedInstanceState是否为空,那么每次就会执行下面的commit操作,向Fragmnet传递参数,         * 这样参数的却会保留下来,但是我们不应该每次都去传递参数。当进行了空判断时,当Activity重建         * 的时候,会调用Fragment的默认构造函数,所以我们传递过去的参数不能保留了。         */        if(savedInstanceState == null){            manager.beginTransaction().replace(R.id.fl_main, new FragmentOne("params")).commit();        }    }    @Override    protected void onSaveInstanceState(Bundle outState) {        System.out.println("=========savedInstanceState ");        super.onSaveInstanceState(outState);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
public class FragmentOne extends Fragment {    private TextView textView;    private String params = "default";    public FragmentOne(){        System.out.println("===========default constructor");    }    /**     * 通过构造方法接收传递过来的参数     * @param content     */    public FragmentOne(String content){        params = content;    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.fragment1, container,false);        textView = (TextView) view.findViewById(R.id.textview);        textView.setText(params);        return view;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这里写图片描述

可以看到此时传递过来的参数已经不见了,说明通过构造方法传递参数是不行的。

我们看看控制台的打印

这里写图片描述

注意:
这里我们必须写出默认的构造函数,因为Fragment重建的时候,会调用默认的构造函数,也就是空参数的构造函数,如果我们只是给出了带参数的构造函数,系统是不会为我们创建空参数的构造函数的,如果你不写,在Fragment重建的时候就会发生下面的错误。

这里写图片描述

接下来看看官方推荐的setArguments方法:

public class MainActivity extends FragmentActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        if (savedInstanceState == null) {            getSupportFragmentManager().beginTransaction()                    .replace(R.id.fl_main, FragmentOne.newInstance("params"))                    .commit();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
public class FragmentOne extends Fragment{    private TextView textView;    public View onCreateView(LayoutInflater inflater,            @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.fragment_one, null);        textView = (TextView) view.findViewById(R.id.textview);        if(getArguments()!=null){            //取出保存的值            textView.setText(getArguments().getString("name"));        }        return view;    }    public static  FragmentOne newInstance(String text){        FragmentOne fragmentOne = new FragmentOne();        Bundle bundle = new Bundle();        bundle.putString("name", text);        //fragment保存参数,传入一个Bundle对象        fragmentOne.setArguments(bundle);        return fragmentOne;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这里写图片描述

可以看到,屏幕旋转以后参数也保留下来了。

接下来我们通过源码看看Bundle这个参数到底如何保留下来的,
点进去Fragment的setArguments方法:

  public void setArguments(Bundle args) {        if (mIndex >= 0) {            throw new IllegalStateException("Fragment already active");        }        mArguments = args;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

首先将当前的bundle对象赋值给一个全局的mArguments对象,然后我们看这个mAruments对象在哪里用到了

public Fragment instantiate(Activity activity, Fragment parent) {        if (mInstance != null) {            return mInstance;        }        if (mArguments != null) {            mArguments.setClassLoader(activity.getClassLoader());        }        //重点在这里        mInstance = Fragment.instantiate(activity, mClassName, mArguments);        if (mSavedFragmentState != null) {            mSavedFragmentState.setClassLoader(activity.getClassLoader());            mInstance.mSavedFragmentState = mSavedFragmentState;        }        mInstance.setIndex(mIndex, parent);        mInstance.mFromLayout = mFromLayout;        mInstance.mRestored = true;        mInstance.mFragmentId = mFragmentId;        mInstance.mContainerId = mContainerId;        mInstance.mTag = mTag;        mInstance.mRetainInstance = mRetainInstance;        mInstance.mDetached = mDetached;        mInstance.mFragmentManager = activity.mFragments;        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,                "Instantiated fragment " + mInstance);        return mInstance;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

然后我们进入Fragment的instantiate方法

public static Fragment instantiate(Context context, String fname, Bundle args) {        try {            Class<?> clazz = sClassMap.get(fname);            if (clazz == null) {                // Class not found in the cache, see if it's real, and try to add it                clazz = context.getClassLoader().loadClass(fname);                sClassMap.put(fname, clazz);            }            Fragment f = (Fragment)clazz.newInstance();            if (args != null) {                args.setClassLoader(f.getClass().getClassLoader());                f.mArguments = args;            }            return f;        } catch (ClassNotFoundException e) {            throw new InstantiationException("Unable to instantiate fragment " + fname                    + ": make sure class name exists, is public, and has an"                    + " empty constructor that is public", e);        } catch (java.lang.InstantiationException e) {            throw new InstantiationException("Unable to instantiate fragment " + fname                    + ": make sure class name exists, is public, and has an"                    + " empty constructor that is public", e);        } catch (IllegalAccessException e) {            throw new InstantiationException("Unable to instantiate fragment " + fname                    + ": make sure class name exists, is public, and has an"                    + " empty constructor that is public", e);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这里通过无參反射构造了一个新的Fragment对象,然后把我们保存的Bundle对象传递给了新的Fragment对象的mAruments成员变量。通过上面的分析,我们就知道了为什么setArguments方法能够保留参数了。

注意:
setArguments方法的调用必须要在Fragment与Activity关联之前。
这句话可以这样理解,setArgument方法的使用必须要在FragmentTransaction 的commit之前使用。


原文链接:http://blog.csdn.net/small_lee/article/details/50553881

阅读全文
0 0