片段中的成员变量与setArguments


67

我注意到,在Fragments的Android参考(尤其是DialogFragment)中,它们所做的一些事情与我期望的有所不同:

1)。使用public static foo newInstance()方法而不是构造函数。
2)。使用setArguments而不是成员变量将值传递给onCreateDialog。

我已经阅读到使用反射时newInstance似乎是更可取的。但是我真的不明白为什么他们要通过捆绑包传递参数。尽管使用成员变量会更安全(不使用字符串从映射中获取),并且开销会更少。

有什么想法吗?

Answers:


50

我也偶然发现了这一点,发现Bundle在实例字段上使用参数有一些好处:

  • 如果它在BundleAndroid系统中,它就会知道并可以创建和销毁您的代码Fragment(使用强制性的无参数/默认构造函数和常用的生命周期方法),然后再次传入参数包。这样一来,在低内存消耗的狂潮或最终的方向改变上,所有的论据都不会丢失(这在旋转较少的模拟器中进行开发后,第一次部署到实际设备时常常使我感到震惊)。

  • 您可以BundleActivity原样将多余的内容传递给Fragment布局中的嵌入内容;例如,当我有一个Activity显示Fragment“全屏”并且需要一些ID(或ContentProviderURI)才能知道要显示/执行的操作时,我经常使用它。有时,我甚至会Bundle在传递(或复制)之前添加更多内容,例如

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    
      if (savedInstanceState == null) { // not a re-creation
        final Bundle args = new Bundle(getIntent().getExtras());
        args.putInt(CoverImageFragment.BACKGROUND_RESOURCE, android.R.color.black);
        final Fragment fragment = CoverImageFragment.newInstance(args);
        getSupportFragmentManager()
          .beginTransaction()
          .add(android.R.id.content, fragment)
          .commit();
      }
    }
    
  • 它使开发方式Fragment接近于Activity,即Bundle作为“输入参数,无异常”。

至于您提到的缺点:

  • 我认为开销很小,因为您很可能不会Bundle在紧密的循环中查询,因此请一次获取参数数据onCreate()onViewCreate()等是没有那么糟糕。

  • 对于类型安全,Bundle具有所有不同getXXXX()方法,甚至在缺少某些内容/可选时也可以通过重载来提供默认值:)

至于newInstance()方法,我认为它们是封装newsetArguments()调用我的Fragment;的简单方法。有时MyFragment newInstance(String singleIdOfWhatToDisplay),我会提供一个附加功能,它可以一次性创建BundleFragment,并返回一个随时可用的Fragment实例。


newInstance方法可以实现构造函数无法实现的功能?我可以setArguments从构造函数调用。
马提亚斯(Matthias)2012年

@Matthias我认为在这种情况下创建工厂方法是更好的样式……
Alex Lockwood

2
newInstance不是工厂方法(如您所要的那样,请使用工厂方法模式-参见en.wikipedia.org/wiki/Factory_method_pattern);它是静态的,因此不能被覆盖,它对于返回的类型也不通用。据我所知,除了个人喜好之外,直接使用构造函数没有任何优势。但是,我隐约记得片段需要默认的构造函数吗?也许这里的想法是不必定义2个ctor,一个定义为bundle参数,另一个定义为空。最佳的揣测。
Matthias

1
我不明白。如果我们使用setArguments,则以某种方式保留了我们传递的Bundle?怎么样?旋转后重新创建Activity和Fragment时,为什么我们在宿主Activity中创建的Bundle可以再次神奇地用于实例化Fragment?除非用于实例化Fragment的Bundle本身是手动保存在Activity中的,否则在重新创建Activity时自然不会丢失它吗?我仍然看不到调用setArguments和在Fragment中编写setter之间的区别。
rmir​​abelle

1
@Levit 1)我很确定参数束会保留。从源:提供此片段的构造参数。这只能在片段附加到其活动之前被调用;也就是说,您应该在构建片段后立即调用它。此处提供的参数将在片段销毁和创建过程中保留。
tir38

21

我发现这是一个非常令人困惑的问题(许多乱七八糟的问题之一)。

setArguments() 是一种针对Android的非常无济于事的解决方法,该需求对于Fragments提供了无参数构造函数。

我的困惑波涛汹涌。首先,你在你的自然覆盖的方法Fragment(例如onCreateonCreateView)收到一个Bundle表示该参数savedInstanceState你的Fragment。该实例的状态显然有NOTHING任何与你通过存储的值setArguments(),并通过检索getArguments()。两者都使用a Bundle,都Bundles可能在相同的重写方法中访问它们,两者之间没有任何关系。

其次,目前尚不清楚Android如何使用setArguments()。Android会调用您的无参数构造函数来重新构建您Fragment的旋转对象,但显然ALSO会调用构造setArguments()时最后一次调用的方法Fragment

嗯???

太神奇了,但事实如此。所有这些疯狂创建Bundles都是setArguments()为了弥补对无参数Fragment构造函数的需求。

简而言之,我正在使用staticnewInstance方法创建我的Fragment

public MyFragment() {
    //satisfy Android
}

public static MyFragment newInstance(long record_id) {
    Log.d("MyFragment", "Putting " + record_id + " into newInstance");
    MyFragment f = new MyFragment();
    Bundle args = new Bundle();
    args.putLong("record_id", record_id);
    f.setArguments(args);
    return f;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    /**
     * Perform an immediate check of arguments,
     * which ARE NOT the same as the bundle used
     * for saved instance state.
     */
    Bundle args = getArguments();
    if(args != null) {
        record_id = args.getLong("record_id");
        Log.d("MyFragment", "found record_id of " + String.valueOf(record_id));
    }
    if(savedInstanceState != null) {
        //now do something with savedInstanceState
    }
}

10

我对Android编程非常陌生,但这是我目前对该问题的理解:

片段的构造函数不能有任何参数。当您的活动暂停时,可以释放您的片段。在恢复活动之前,系统会创建Fragment的新版本并调用构造函数。如果使用非默认构造函数,Android应该如何知道Fragments构造函数的参数的类型和值?

我不认为该捆绑包已经发布。该包将精确保留,以便在使用默认构造函数重新创建后,可以将其传递回Fragment。

菲利普·里查特(Philipp Reichart)在他的文章中对此进行了掩盖(实际上比起掩盖了更多)。


2
您确定您是Android新手吗?这是一个简洁,精心设计的答案。
tir38

2

只是想给参数增加一个缺点,就是必须动态创建片段。如果您是从xml创建的,则自变量不能很好地工作。我真的很讨厌。


1
您可以使用来访问来自片段的活动args,getActivity().getIntent().getExtras()并且效果很好。
laalto 2012年

1
我认为,为了传递参数,您需要在代码中创建片段。如果您在xml中定义片段,我不记得有没有一种方法可以传递参数。在那儿?
havexz 2012年

我正在尝试在我的Fragment的零参数构造函数中执行setArguments(new Bundle()),以解决无法在xml实例化的片段中使用get / setArguments的问题。
JohnnyLambada 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.