如果我在XML布局中声明一个片段,如何将其传递给Bundle?


70

我有一个被片段替换的活动。该活动采用了一个意图,该意图具有有关该活动应该显示哪些数据的一些额外信息。

现在,我的Activity只是一个Fragment的包装器,它可以完成相同的工作,如果我使用标记在XML中声明该片段,如何将其捆绑到Fragment上呢?

如果我要使用FragmentTransaction将Fragment放入ViewGroup中,我将有机会在Fragment构造函数中传递此信息,但是我想知道片段是在XML中定义的情况。


Answers:


51

现在,我的Activity只是一个Fragment的包装器,它可以完成相同的工作,如果我使用标记在XML中声明该片段,如何将其捆绑到Fragment上呢?

你不能

不过,欢迎您拨打findFragmentById()FragmentManager检索片段后的通货膨胀,然后调用该片段的一些方法来与之相关联的数据。虽然显然是不能setArguments(),您的片段可以安排守住数据本身过去通过一些其他方式的构造变化(onSaveInstanceState()setRetainInstance(true),等)。


当我问这个问题时,我决定走另一条路。但是就在今天,我遇到了类似的情况,并回到了这篇文章。我以为可以试试看。setArguments解决方案似乎不起作用:10-24 12:48:33.276:E / AndroidRuntime(21417):由以下原因引起:java.lang.IllegalStateException:片段已处于活动状态尝试仅在Fragment上调用方法。
种植场

前几天,我遇到了相同的IllegalStateException。问题似乎是您需要调用setContentView()才能使Fragments膨胀。但setContentView()也将它们附加到Activity上,这使得调用它为时已晚setArguments()
Michael

3
这不应标记为正确。不正确 根据片段文档(developer.android.com/reference/android/app/…,必须在将片段附加到活动之前调用setArguments()。如果可以通过findFragmentById()找到片段,则该片段具有正确的解决方案,请参见stackoverflow.com/questions/21403040/…–
尼尔·塞恩斯伯里

@Neil:我能理解你困惑的根源。我做了一个小小的修改,以澄清setArguments()通话时间。
CommonsWare 2014年

6
有关如何将数据传递到XML定义的Fragment的选项,请参见stackoverflow.com/questions/18124150/…
TheIT

45

这不是一种封装的方法,但是我最终从父活动中“拉出”了捆绑包:

Bundle bundle = getActivity().getIntent().getExtras();

12

您不能传递捆绑包(除非您以编程方式而不是通过XML来对片段进行膨胀),但是可以通过XML将参数(或属性)传递给片段。

该过程类似于您定义“视图”自定义属性的方式。除了AndroidStudio(当前)之外,该过程无法帮助您。

假设这是使用参数的片段(我将使用kotlin,但它也完全适用于Java):

class MyFragment: Fragment() {

    // your fragment parameter, a string
    private var screenName: String? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        if (screenName == null) {
            screenName = arguments?.getString("screen_name")
        }
    }
}

而您想做这样的事情:

<fragment
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/myFragment"
    android:name="com.example.MyFragment"
    app:screen_name="@string/screen_a"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

注意 app:screen_name="@string/screen_a"

要使其工作,只需将其添加到值文件中(fragment_attrs.xml或选择所需的任何名称):

<!-- define your attribute name and type -->
<attr name="screen_name" format="string|reference"/>

<!-- define a bunch of constants you wanna use -->
<string name="screen_a" translatable="false">ScreenA</string>
<string name="screen_b" translatable="false">ScreeenB</string>

<!-- now define which arguments your fragment is gonna have (can be more then one) -->
<!-- the convention is "FragmentClassName_MembersInjector" -->
<declare-styleable name="MyFragment_MembersInjector">
    <attr name="screen_name"/>
</declare-styleable>

差不多完成了,您只需要在片段中读取它,因此添加方法:

override fun onInflate(context: Context?, attrs: AttributeSet?, savedInstanceState: Bundle?) {
    super.onInflate(context, attrs, savedInstanceState)
    if (context != null && attrs != null && screenName == null) {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.MyFragment_MembersInjector)
        if (ta.hasValue(R.styleable.MyFragment_MembersInjector_screen_name)) {
            screenName = ta.getString(R.styleable.MyFragment_MembersInjector_screen_name)
        }
        ta.recycle()
    }
}

等,片段中的XML属性:)

局限性:

  • Android Studio(到目前为止)不会在布局XML中自动完成此类参数
  • 您无法传递Parcelable,只能传递可以定义为Android属性的内容

这就是我所需要的。十分感谢。
bikram

救星,正是我想要的。
Yuvraj Pandey,

11

另一个选择是不在XML中声明该片段。我知道这不是您想要做的。但是,您可以在视图中声明一个简单的布局,如下所示:

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" />

然后,在您的Activity课堂上,以编程方式用片段填充布局。这样,您可以使用args传递参数。

 FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    MyFragment fragment = MyFragment.newInstance();
    Bundle args = new Bundle();
    args.putInt(Global.INTENT_INT_ROLE, 1);
    fragment.setArguments(args);
    fragmentTransaction.add(R.id.fragment_container, fragment, "MyActivity");
    fragmentTransaction.commit();

这种方法不像在xml中声明那样干净简洁,但是我已经转向它,因为它为您提供了对片段的更多控制。


这有点“不做那个”的方法,但是对我而言,比通过单例对象交换数据的想法更有意义(另一个答案表明)
Konrad Morawski

如果单例对象不受持久数据库的支持,则Android可以在后台破坏您的应用程序进程,并稍后尝试通过片段参数或捆绑包重新创建其前一状态。单例将无法恢复并且应用程序状态将丢失,这会导致不良的UX-尝试通过使用片段参数或通过数据库备份单例来避免这样做。
A. Steenbergen

6

我知道答案为时已晚,但我认为有人需要它:)

只是在活动中覆盖 onAttachFragment()

@Override
public void onAttachFragment(Fragment fragment)
{
    super.onAttachFragment(fragment);

    if (fragment.getId() == R.id.frgBlank)
    {
        Bundle b = new Bundle();
        b.putString("msg", "Message");

        fragment.setArguments(b);
    }
}

并在片段onCreateView方法中

Bundle b = getArguments();
if (b != null)
{
    Toast.makeText(getBaseContext(), b.getString("msg"), Toast.LENGTH_SHORT).show();
}

3

我看到的唯一解决方案是不要将参数用作数据交换通道。相反,您可以制作片段以从其他地方获取必要的信息。进行回叫以获得适当的活动,请查阅临时存储存储器,Singleton对象等。

另一个可能有用的解决方案是采用允许无关对象通过Mediator设计模式(如Otto)交换消息的框架。

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.