Android 中为什么要用Fragment.setArguments(Bundle bundle)来传递参数

Fragment在Android3.0开始提供,并且在兼容包中也提供了Fragment特性的支持。Fragment的推出让我们编写和管理用户界面更快捷更方便了。

但当我们实例化自定义Fragment时,为什么官方推荐Fragment.setArguments(Bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数呢?为了弄清这个问题,我们可以做一个测试,分别测试下这两种方式的不同

首先,我们来测试下通过构造方法传递参数的情况

public class FramentTestActivity extends ActionBarActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState == null) {
      getSupportFragmentManager().beginTransaction()
          .add(R.id.container, new TestFragment("param")).commit();
    }
  }
  public static class TestFragment extends Fragment {
    private String mArg = "non-param";
    public TestFragment() {
      Log.i("INFO", "TestFragment non-parameter constructor");
    }
    public TestFragment(String arg){
      mArg = arg;
      Log.i("INFO", "TestFragment construct with parameter");
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      View rootView = inflater.inflate(R.layout.fragment_main, container,
          false);
      TextView tv = (TextView) rootView.findViewById(R.id.tv);
      tv.setText(mArg);
      return rootView;
    }
  }
} 

可以看到我们传递过来的数据正确的显示了,现在来考虑一个问题,如果设备配置参数发生变化,这里以横竖屏切换来说明问题,显示如下

发生了什么问题呢?我们传递的参数哪去了?为什么会显示默认值?不急着讨论这个问题,接下来我们来看看Fragment.setArguments(Bundle bundle)这种方式的运行情况

public class FramentTest2Activity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout. activity_main);
       if (savedInstanceState == null) {
          getSupportFragmentManager().beginTransaction()
                 .add(R.id. container, TestFragment.newInstance("param")).commit();
       }
    }
    public static class TestFragment extends Fragment {
       private static final String ARG = "arg";
       public TestFragment() {
          Log. i("INFO", "TestFragment non-parameter constructor" );
       }
       public static Fragment newInstance(String arg){
          TestFragment fragment = new TestFragment();
          Bundle bundle = new Bundle();
          bundle.putString( ARG, arg);
          fragment.setArguments(bundle);
           return fragment;
       }
       @Override
       public View onCreateView(LayoutInflater inflater, ViewGroup container,
              Bundle savedInstanceState) {
          View rootView = inflater.inflate(R.layout. fragment_main, container,
                 false);
          TextView tv = (TextView) rootView.findViewById(R.id. tv);
          tv.setText(getArguments().getString( ARG));
           return rootView;
       }
    }
} 

我们再来看看横竖屏切换后的运行情况

看到了吧,我们传递的参数在横竖屏切换的情况下完好保存了下来,正确的显示给用户
那么这到底是怎么回事呢,我们知道设备横竖屏切换的话,当前展示给用户的Activity默认情况下会重新创建并展现给用户,那依附于Activity的Fragment会进行如何处理呢,我们可以通过源码来查看

先来看看Activity的onCreate(Bundle saveInstance)方法

 protected void onCreate(Bundle savedInstanceState) {
  if (DEBUG_LIFECYCLE ) Slog.v( TAG, "onCreate " + this + ": " + savedInstanceState);
  if (mLastNonConfigurationInstances != null) {
    mAllLoaderManagers = mLastNonConfigurationInstances .loaders ;
  }
  if (mActivityInfo .parentActivityName != null) {
    if (mActionBar == null) {
      mEnableDefaultActionBarUp = true ;
    } else {
      mActionBar .setDefaultDisplayHomeAsUpEnabled( true);
    }
  }
  if (savedInstanceState != null) {
    Parcelable p = savedInstanceState.getParcelable( FRAGMENTS_TAG );
    mFragments .restoreAllState(p, mLastNonConfigurationInstances != null
        ? mLastNonConfigurationInstances .fragments : null);
  }
  mFragments .dispatchCreate();
  getApplication().dispatchActivityCreated( this , savedInstanceState);
  mCalled = true ;
} 

由于我们的Fragment是由FragmentManager来管理,所以可以跟进FragmentManager.restoreAllState()方法,通过对当前活动的Fragmnet找到下面的代码块

 for (int i=0; i<fms.mActive.length; i++) {
      FragmentState fs = fms.mActive[i];
      if (fs != null) {
       Fragment f = fs.instantiate(mActivity, mParent);
        if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
        mActive.add(f);
        // Now that the fragment is instantiated (or came from being
        // retained above), clear mInstance in case we end up re-restoring
        // from this FragmentState again.
        fs.mInstance = null;
      } else {
        mActive.add(null);
        if (mAvailIndices == null) {
          mAvailIndices = new ArrayList<Integer>();
        }
        if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
        mAvailIndices.add(i);
      }
} 

接下来我们可以看看FragmentState.instantitate()方法的实现

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 ;
  } 

可以看到最终转入到Fragment.instantitate()方法

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);
  } 

通过此方法可以看到,最终会通过反射无参构造实例化一个新的Fragment,并且给mArgments初始化为原先的值,而原来的Fragment实例的数据都丢失了,并重新进行了初始化

通过上面的分析,我们可以知道Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递参数

以上所述是小编给大家介绍的Android 中为什么要用Fragment.setArguments(Bundle bundle)来传递参数,希望对大家有所帮助,如果大家有任何疑问欢迎给我给我留言,小编会及时回复大家的!

(0)

相关推荐

  • Android intent之间复杂参数传递方法详解

    本文详细讲述了Android intent之间复杂参数传递方法.分享给大家供大家参考,具体如下: Intent是Activity与Activity之间,Activity与Service之间传递参数的介质,而这两种通常实现的是Java基本对象类型和String的传递. 在实际项目中,页面之间传值,除了以上几种,经常还有传递Object对象.List类型.List<Object>类型和全局变量等等的需求.本文就是介绍怎么传递这几种类型的参数. 一.传递List<String>和List

  • Android Activity之间相互调用与传递参数的原理与用法分析

    本文实例讲述了Android Activity之间的相互调用与传递参数.分享给大家供大家参考,具体如下: Activity之间是如何调用的 在javaWeb程序中,jsp与jsp之间的调用是通过重定向完成的,而在Android中,Activity与Activity之间的切换是通过Intent来完成的. 所谓Intent,它是Android中非常重要的内置组件,他可以理解为"我要干一件什么事情".在Android中有3大组件:Activity,Service.Broadcast,他们之间

  • Android Activity中使用Intent实现页面跳转与参数传递的方法

    本文实例讲述了Android Activity中使用Intent实现页面跳转与参数传递的方法.分享给大家供大家参考,具体如下: 新建一个FirstAvtivity.java package com.zhuguangwei; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.O

  • Android 中为什么要用Fragment.setArguments(Bundle bundle)来传递参数

    Fragment在Android3.0开始提供,并且在兼容包中也提供了Fragment特性的支持.Fragment的推出让我们编写和管理用户界面更快捷更方便了. 但当我们实例化自定义Fragment时,为什么官方推荐Fragment.setArguments(Bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数呢?为了弄清这个问题,我们可以做一个测试,分别测试下这两种方式的不同 首先,我们来测试下通过构造方法传递参数的情况 public class FramentT

  • Android中使用TabHost 与 Fragment 制作页面切换效果

    三个标签页置于顶端 效果图: 在文件BoardTabHost.java中定义页面切换的效果:切换页面时,当前页面滑出,目标页面滑入.这是2个不同的动画设定动画时要区分对待 import android.content.Context; import android.util.AttributeSet; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import

  • element UI 中的 el-tree 实现 checkbox 单选框及 bus 传递参数功能

    el-tree 单选功能 在日常项目开发中,会经常遇到,树形结构的查询方式,为了快速方便开发,常常会使用到快捷的ui组件去快速搭树形结构,这里我用的是 element ui 中的 el-tree .第一次接触这种功能的时候也是各种网站查询,虽然也都能实现功能,但是都会有一些小问题,就很难受,那么我们废话不多说(好像也说了不少呢),直接上效果. el-tree 单选 html 代码 *** 注: load 和 lazy 属性不是需要的粘贴时请删除.(只有需要懒加载的树才需要,关于怎样构建懒加载树以

  • Android 中使用RadioGroup和Fragment实现底部导航栏的功能

    在一些购物商城中经常会遇到这类效果,效果图如下: 先看效果图 步骤一: 完成对主界面main.xml的创建: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/

  • Android中传递对象的三种方法的实现

    Android中,Activity和Fragment之间传递对象,可以通过将对象序列化并存入Bundle或者Intent中进行传递,也可以将对象转化为JSON字符串,进行传递. 序列化对象可以使用Java的Serializable的接口.Parcelable接口.转化成JSON字符串,可以使用Gson等库. 1.Serializable public class Author implements Serializable{ private int id; private String name

  • Fragment跳转时传递参数及结果回传的方法(推荐)

    今天总结一下Fragment间的参数传递及结果返回的方法. 效果图: 1.点击"加载第二个Fragment按钮",加载出第二个Fragment,同时传递过去参数:"从Fragment1传来的参数"这几个String: 2.当用户点击第二个Fragment中的几个图片时,将点中的结果返回给第一个Fragment,将用户的选择在第一个Fragment显示出来 一.基本架构搭建 首先,我们要把整个架构搭起来,然后再进行参数传递和回传 (一).基本XML构建: 根据上面的效

  • Android中Fragment的解析和使用详解

    前言 Android Fragment的生命周期和Activity类似,实际可能会涉及到数据传递,onSaveInstanceState的状态保存,FragmentManager的管理和Transaction,切换的Animation. 我们首先简单的介绍一下Fragment的生命周期. 大致上,从名字就可以判断出每个生命周期是干嘛的. AppCompatActivity就是FragmentActivity的子类,如果想使用Fragment,是要继承FragmentActivity,因为考虑到兼

  • Android中Activity和Fragment传递数据的两种方式

    1.第一种方式,也是最常用的方式,就是使用Bundle来传递参数 MyFragment myFragment = new MyFragment(); Bundle bundle = new Bundle(); bundle.putString("DATA",values);//这里的values就是我们要传的值 myFragment.setArguments(bundle); 然后在Fragment中的onCreatView方法中,通过getArgments()方法,获取到bundle

  • Android中Fragment的加载方式与数据通信详解

    一.加载方式 1. 静态加载 1.1 加载步骤 (1) 创建fragment:创建自定义Fragment类继承自Fragment类,同时将自定义Fragment类与Fragment视图绑定(将layout转换成View) View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) inflater用于绑定Fragment的布局文件,同时将该布局转换成View对象并返回:con

  • Android中的Fragment类使用进阶

    0.回顾 Fragment 代表 Activity 当中的一项操作或一部分用户界面. 一个 Activity 中的多个 Fragment 可以组合在一起,形成一个多部分拼接而成的用户界面组件,并可在多个 Activity 中复用.一个 Fragment 可被视为 Activity 中一个模块化的部分, 它拥有自己的生命周期,并接收自己的输入事件,在 Activity 运行过程中可以随时添加或移除它 (有点类似"子 Activity",可在不同的 Activity 中重用). Fragm

随机推荐