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

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

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

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

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

我们看看控制台的打印

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

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

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

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

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

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

首先将当前的bundle对象赋值给一个全局的mArguments对象,mArguments时FragmentState对象的一个属性,FragmentState时Fragment的一个内部类,代表着Fragment的状态。

  1. final class FragmentState implements Parcelable {
  2. final String mClassName;
  3. final int mIndex;
  4. final boolean mFromLayout;
  5. final int mFragmentId;
  6. final int mContainerId;
  7. final String mTag;
  8. final boolean mRetainInstance;
  9. final boolean mDetached;
  10. final Bundle mArguments;
  11. final boolean mHidden;
  12. Bundle mSavedFragmentState;
  13. Fragment mInstance;
  14. public FragmentState(Fragment frag) {
  15. mClassName = frag.getClass().getName();
  16. mIndex = frag.mIndex;
  17. mFromLayout = frag.mFromLayout;
  18. mFragmentId = frag.mFragmentId;
  19. mContainerId = frag.mContainerId;
  20. mTag = frag.mTag;
  21. mRetainInstance = frag.mRetainInstance;
  22. mDetached = frag.mDetached;
  23. mArguments = frag.mArguments;
  24. mHidden = frag.mHidden;
  25. }

然后Activity保存状态的时候会调用onSaveInstanceState方法

  1. protected void onSaveInstanceState(Bundle outState) {
  2. outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
  3. outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
  4. // 调用saveAllState方法保存Fragment状态
  5. Parcelable p = mFragments.saveAllState();
  6. if (p != null) {
  7. // 将结果保存到Bundle中
  8. outState.putParcelable(FRAGMENTS_TAG, p);
  9. }
  10. if (mAutoFillResetNeeded) {
  11. outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
  12. getAutofillManager().onSaveInstanceState(outState);
  13. }
  14. getApplication().dispatchActivitySaveInstanceState(this, outState);
  15. }

saveAllState方法最终调用到FragmentManager的saveAllState方法中

  1. Parcelable saveAllState() {
  2. // 找到所有的存活的Fragment
  3. int N = mActive.size();
  4. // 代表Fragment状态的数组
  5. FragmentState[] active = new FragmentState[N];
  6. boolean haveFragments = false;
  7. for (int i=0; i<N; i++) {
  8. Fragment f = mActive.valueAt(i);
  9. if (f != null) {
  10. if (f.mIndex < 0) {
  11. throwException(new IllegalStateException(
  12. "Failure saving state: active " + f
  13. + " has cleared index: " + f.mIndex));
  14. }
  15. haveFragments = true;
  16. // 找到所有的Fragment,为FragmentState数组初始化
  17. FragmentState fs = new FragmentState(f);
  18. active[i] = fs;
  19. // 保证Fragment已经创建了并且没有参数保存过
  20. if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
  21. // 保存Fragment的状态
  22. fs.mSavedFragmentState = saveFragmentBasicState(f);
  23. FragmentManagerState fms = new FragmentManagerState();
  24. // active是上面代表Fragment状态的数组,至此,fragment的状态就被保存到
  25. FragmentManagerState
  26. fms.mActive = active;
  27. fms.mAdded = added;
  28. fms.mBackStack = backStack;
  29. fms.mNextFragmentIndex = mNextFragmentIndex;
  30. if (mPrimaryNav != null) {
  31. fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
  32. }
  33. saveNonConfig();
  34. return fms;
  35. }

接下来我们看看恢复数据的流程

在Activity的onCreate中有下面的代码

  1. // 取出之前保存的数据
  2. Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
  3. mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
  4. ? mLastNonConfigurationInstances.fragments : null);

最终调用到了FragmentManager的restoreAllState方法

  1. void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
  2. if (state == null) return;
  3. // 取出保存的数据
  4. FragmentManagerState fms = (FragmentManagerState)state;
  5. if (fms.mActive == null) return;
  6. // ... 省去部分代码
  7. // Build the full list of active fragments, instantiating them from
  8. // their saved state.
  9. // 根据之前保存的状态初始化新的Fragment
  10. mActive = new SparseArray<>(fms.mActive.length);
  11. for (int i=0; i<fms.mActive.length; i++) {
  12. FragmentState fs = fms.mActive[i];
  13. if (fs != null) {
  14. FragmentManagerNonConfig childNonConfig = null;
  15. if (childNonConfigs != null && i < childNonConfigs.size()) {
  16. childNonConfig = childNonConfigs.get(i);
  17. }
  18. // 调用instantiate方法创建Fragment
  19. Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
  20. if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
  21. mActive.put(f.mIndex, f);
  22. // Now that the fragment is instantiated (or came from being
  23. // retained above), clear mInstance in case we end up re-restoring
  24. // from this FragmentState again.
  25. fs.mInstance = null;
  26. }
  27. }
  1. public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
  2. Fragment parent, FragmentManagerNonConfig childNonConfig) {
  3. if (mInstance == null) {
  4. final Context context = host.getContext();
  5. if (mArguments != null) {
  6. mArguments.setClassLoader(context.getClassLoader());
  7. }
  8. // 初始化Fragment,把之前保存的参数传过去
  9. if (container != null) {
  10. mInstance = container.instantiate(context, mClassName, mArguments);
  11. } else {
  12. mInstance = Fragment.instantiate(context, mClassName, mArguments);
  13. }
  14. if (mSavedFragmentState != null) {
  15. mSavedFragmentState.setClassLoader(context.getClassLoader());
  16. mInstance.mSavedFragmentState = mSavedFragmentState;
  17. }
  18. mInstance.setIndex(mIndex, parent);
  19. mInstance.mFromLayout = mFromLayout;
  20. mInstance.mRestored = true;
  21. mInstance.mFragmentId = mFragmentId;
  22. mInstance.mContainerId = mContainerId;
  23. mInstance.mTag = mTag;
  24. mInstance.mRetainInstance = mRetainInstance;
  25. mInstance.mDetached = mDetached;
  26. mInstance.mHidden = mHidden;
  27. mInstance.mFragmentManager = host.mFragmentManager;
  28. if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
  29. "Instantiated fragment " + mInstance);
  30. }
  31. mInstance.mChildNonConfig = childNonConfig;
  32. return mInstance;
  33. }

// 初始化的方法

  1. public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
  2. try {
  3. Class<?> clazz = sClassMap.get(fname);
  4. if (clazz == null) {
  5. // Class not found in the cache, see if it's real, and try to add it
  6. clazz = context.getClassLoader().loadClass(fname);
  7. if (!Fragment.class.isAssignableFrom(clazz)) {
  8. throw new InstantiationException("Trying to instantiate a class " + fname
  9. + " that is not a Fragment", new ClassCastException());
  10. }
  11. sClassMap.put(fname, clazz);
  12. }
  13. // 调用Fragment无参数的构造函数
  14. Fragment f = (Fragment) clazz.getConstructor().newInstance();
  15. if (args != null) {
  16. args.setClassLoader(f.getClass().getClassLoader());
  17. // 设置参数,然后我们就可以用getArgument方法获取了
  18. f.setArguments(args);
  19. }
  20. return f;

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

如何向一个Fragment传递参数---setArguments方法的介绍的更多相关文章

  1. spring boot:thymeleaf给fragment传递参数的方法(spring boot 2.3.3)

    一,thymeleaf如何给fragment传递参数? 1,如果是全局的参数,可以用interceptor中传递 非全局参数,可以从controller中传递 2,引用片断时也可以传递参数 说明:刘宏 ...

  2. android fragment传递参数_fragment之间传值的两种方法

    在Activity中加载Fragment的时候.有时候要使用多个Fragment切换.并传值到另外一个Fragment.也就是说两个Fragment之间进行参数的传递.查了很多资料.找到两种方法.一种 ...

  3. C++向main函数传递参数的方法(实例已上传至github)

    通常情况下,我们定义的main函数都只有空形参列表: int main(){...} 然而,有时我们确实需要给mian传递实参,一种常见的情况是用户设置一组选项来确定函数所要执行的操作.例如,假定ma ...

  4. jsp中四种传递参数的方法

    jsp中四种传递参数的方法如下: 1.form表单 2.request.setAttribute();和request.getAttribute(); 3.超链接:<a herf="i ...

  5. Jsp传递参数的方法

    今天老师讲了jsp中四种传递参数的方法,我觉得总结一下,挺好的,以备后用! 1.form表单 2.request.setAttribute();和request.getAttribute(); 3.超 ...

  6. Javascript 定时器调用传递参数的方法

    文章来源:  https://m.jb51.net/article/20880.htm 备注:先记下,以后整理: Javascript 定时器调用传递参数的方法,需要的朋友可以参考下. 无论是wind ...

  7. springMVC controller间跳转 重定向 传递参数的方法

    springMVC controller间跳转 重定向 传递参数的方法 spring MVC框架controller间跳转,需重定向.有几种情况:不带参数跳转,带参数拼接url形式跳转,带参数不拼接参 ...

  8. JSP页面之间传递参数的方法有哪些?

    JSP页面之间传递参数的方法有哪些? 解答: 1)request 2)session 3)application 4)提交表单 5)超链接

  9. php cli传递参数的方法

    php cli传递参数的方法 <pre>$options = "f:g:"; $opts = getopt( $options ); print_r($opts); & ...

随机推荐

  1. STM32启动BOOT0 BOOT1设置方法

    原理图 启动方式 第一种启动方式是最常用的用户FLASH启动.默认启动方式 第二种启动方式是STM32内嵌的SRAM启动.该模式用于调试 第三种启动方式是系统存储器启动方式,不建议使用这种,速度比较慢 ...

  2. CentOS自动备份MySql

    1.确认Crontab是否安装 service crond startcrontab -l 2.编写备份脚本 cd mkdir backup cd backup vim auto.sh /usr/bi ...

  3. 获取impala下所有的数据库建表语句

    方法一: 现在的导出还是有缺陷的,导出的文件中还是存在其他不必要的信息 #!/bin/bash ##获取数据库 databases=$(hive -e "show databases; ex ...

  4. Navicat连接腾讯云实例MySQL

    Navicat连接腾讯云实例MySQL 授权所有的用户通过root账户 root密码登陆远程数据库 连接腾讯云实例上的MySQL数据库 这里的密码填入数据库的密码 这里的密码填入登陆云实例的密码也就是 ...

  5. pxc5.7 报错:WSREP_SST: [ERROR] xtrabackup_checkpoints missing

    PXC 5.7 WSREP_SST: [ERROR] xtrabackup_checkpoints missing PXC5.7,在启动其中的一个节点,碰到了 [ERROR] xtrabackup_c ...

  6. 菜单项(Menu)的初步认识 以及 多级菜单(SubMenu)的初步认识

    MainActivity.class public class MainActivity extends AppCompatActivity { private TextView textView; ...

  7. 团队第二次作业:需求分析&系统设计

    所属课程 https://edu.cnblogs.com/campus/xnsy/Autumn2019SoftwareEngineeringFoundation/ 作业要求 https://edu.c ...

  8. CSS 中用户自定义字体 @font-face

    @font-face 允许网页中使用自定义的字体,这些自定义的字体被放置在服务器上,从而让网页摆脱对访问者计算机上字体环境的依赖. 简单的说,有了@font-face,只需将字体上传到服务器端,无论访 ...

  9. 美团面经-java开发

     美团(1)1 1 2 3 5 8...,求第n项写了个递归,面试官问了两个,n=-1,和极限最大值情况下怎么办.我回答,会导致栈的内存空间溢出.又问了,在栈里会是个怎样的过程.(2)打开摩拜单车页面 ...

  10. Android利用json进行网络解析

    必须单开一个线程,android界面的主线程不会负责通信模块