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

时间 2014-04-19 23:05:55 CSDN博客
原文  http://blog.csdn.net/tu_bingbing/article/details/24143249
主题 安卓开发

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 {@Overrideprotected 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)来传递参数相关推荐

  1. Android Fragment传递参数Fragment.setArguments(Bundle bundle)

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

  2. Fragment.setArguments()

    在我们平常开发的时候Fragment是经常使用的,而我们创建一个Fragment对象的时候一般都会 通过 new Fragment()构造方法来实现,如果要传递参数我们一般会重载构造方法,例如 new ...

  3. Android常用控件之Fragment仿Android4.0设置界面

    Fragment是Android3.0新增的概念,是碎片的意思,它和Activity很相像,用来在一个Activity中描述一些行为或部分用户界面:使用多个Fragment可以在一个单独的Activi ...

  4. android viewpager fragment传值,Android开发中如何解决Fragment +Viewpager滑动页面重复加载的问题...

    前言 之前在做一个Viewpager上面加载多个Fragment时总会实例化已经创建好的Fragmnet对象类似 viewPager.setAdapter(new FragmentPagerAdapt ...

  5. android fragment界面滑动切换效果,Android App中使用ViewPager+Fragment实现滑动切换效果...

    在android应用中,多屏滑动是一种很常见的风格,没有采用viewpager的代码实现会很长,如果采用ViewPager,代码就会短很多,但是使用ViewPager也有弊端:需要导入android- ...

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

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

  7. Android Studio初学者实例:Fragment学习--仿美团外卖界面

    本次课程为Fragment为主题,课程的示例仿美团外卖界面,不同于底部导航栏的Fragment案例,此界面分为左侧切换与顶部切换.本文先是发布代码与效果,后续讲解将会在后续补充.先看看效果: 首先是布 ...

  8. Android studio实现底部导航,Android 开发之BottomBar+ViewPager+Fragment实现炫酷的底部导航效果...

    BottomBar BottomBar是Github上的一个开源框架,因为从1.3.3开始不支持fragments了,要自己配置,弄了很久,不管是app的fragment还是V4 的程序总是总是闪退. ...

  9. Android开发之fragment传递参数的两种方法

    非常简单一种是通过bundle,另外一种是通过fragment提供的instantiate(过时方法),都是kotlin版本,直接上代码吧: 方法一: //fragment传递参数方法一val fra ...

最新文章

  1. android+邮箱删除邮件,在Android上删除烦人的语音邮件通知 | MOS86
  2. JavaScript[对象.属性]集锦
  3. 控制语句 for while if switch
  4. 人才迁徙潮,2019年互联网各梯队排名重组,最适合程序员去的互联网公司有哪些?...
  5. dst matlab,DSTcode DST跟踪算法MATLAB代码,复杂环境中仿多目标 实现的单 Other systems 其他 272万源代码下载- www.pudn.com...
  6. 解决com.alibaba.fastjson.JSONException: autoType is not support
  7. Java支持的编码格式
  8. [USACO5.1] Musical Themes
  9. 浅谈Kotlin(四):控制流
  10. 行测测评——矩阵、圆形、环形三角图形数列推理解题技巧
  11. 电视hdr测试软件,HDR+4K一个都不能少 本地资源播放测试
  12. 浅析Minecraft直播弹幕模组BakaDanmaku源码
  13. c++创建一个linux deamon进程
  14. 2019.05 随笔
  15. cpu之RegDst_Ins
  16. 为什么Google是人工智能发展的主要动力?
  17. 苏宁低调内测“宁互宝”,网络互助成巨头必争之地...
  18. 梯度下降,随机梯度下降,代码实现
  19. Git 修改已提交的 commit 信息
  20. 贝壳后台开发面经(22 届春招)

热门文章

  1. 未识别的网络无法连接Internet解决方法
  2. 部署Exchange Server 2007 SCC
  3. 市场与需求带动 向智能安放转型成大势所趋
  4. Hadoop2.7.3完全分布式搭建
  5. Android上关于view的事件问题
  6. 安卓入门笔记之Activity
  7. 生产交接班管理系统的安装设置并下载
  8. BGP no-advertise
  9. shell学习笔记 (2)
  10. 给.net程序打内存补丁-转