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

我用一个碎片代替了一个活动。该活动接受一个意图,其中包含关于该活动应该显示的数据的一些额外信息。

既然我的 Activity 只是一个包装片段的包装器,它执行相同的工作,那么如果我用带标记的 XML 声明片段,我如何将这个包装到片段中呢?

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

28477 次浏览

既然我的 Activity 只是一个包装片段的包装器,它执行相同的工作,那么如果我用带标记的 XML 声明片段,我如何将这个包装到片段中呢?

你不能。

但是,欢迎您在 FragmentManager上调用 findFragmentById()以在片段膨胀后检索片段,然后调用片段上的某个方法以将数据与其关联。虽然显然不能是 setArguments(),但是片段可以通过其他一些方法(onSaveInstanceState()setRetainInstance(true)等等)来保留数据本身。

我看到的唯一解决方案是不使用参数作为数据交换通道。相反,使用您的片段从其他地方获得必要的信息。调用回来获取正确的活动、查询临时存储内存、 Singleton 对象等。.

另一个有用的解决方案是使用允许不相关对象通过 Mediator 设计模式交换消息的框架,如 奥托

这不是一种封装的方式,但是我最终从父活动中“提取”了这个包:

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

另一种选择是不在 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();

在碎片里,

if (getArguments() != null) {
int role = getArguments().getInt(Global.INTENT_INT_ROLE); }

这种方法不像在 xml 中声明它那样简单干净,但是我已经转移到它,因为它使您能够更好地控制片段。

您不能传递一个 Bundle (除非您以编程方式而不是通过 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 属性的内容

我知道这个答案已经太迟了,但是我认为有人需要这个:)

正在控制 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();
}

这个方法对我很有效。

您不会从 Anywhere 传递 Bundle,但是您可以在片段本身的 onAttach 方法中设置参数。

稍后在片段的生命周期方法中,您可以使用这些 bundle 参数。

override fun onAttach(context: Context) {
super.onAttach(context)
if(arguments == null){
val bundle = Bundle()
bundle.putString("mykey", "myvalue")
arguments = bundle
}
}

任何人都可能会问,为什么要在片段中设置参数,而我们可以直接在可用的地方使用这些值。是对的,但是 当您将这些参数传递给其他类(比如说任何视图模型)时,这种方法也可以工作。

比如说

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)


/*
Here I am passing these arguments to a viewmodel
*/
viewModel.prepareData(arguments)
        

--------
--------
--------


}

谢谢。

由于 Android 碎片的1.3.0版本模块(2021年2月) ,他们不推荐使用 onAttachFragment()回调,而是使用 片段附件监听器,它附加到 片段管理器。所以在你的 FragmentActivityonCreate中,你可以这样做:

getSupportFragmentManager().addFragmentOnAttachListener((fragmentManager, fragment) -> {
if (fragment instanceof MyFragment) {
Bundle args = getIntent().getExtras();
fragment.setArguments(args);
}
}

如果在 XML 中使用 <fragment>,这将在 setContentView()之后触发,但如果使用 <FragmentContainerView>,这将在以后触发。

请注意,我注意到了的配置更改可能会在 super.onCreate中重新附加现有的片段,因此此时需要添加侦听器 之前。(也就是说,这对于传递 Bundle参数是不必要的,否则应该首先尝试使用 OnSaveInstanceState ()。)


顺便说一句,如果需要的话,监听“ onAttach”也是一个方便的地方,可以添加片段视图 生命周期的观察者,当片段视图被实例化后,如果需要在 Activity 中执行任何操作的话。例如:

        LiveData<LifecycleOwner> liveData = fragment.getViewLifecycleOwnerLiveData();
// Let this observer be constrained to the fragment's lifecycle.
liveData.observe(fragment, new Observer<LifecycleOwner>() {
@Override
public void onChanged(LifecycleOwner lifecycleOwner) {
// ...do work...
// If you don't need to listen anymore, can clean ourselves up early.
liveData.removeObserver(this);
}
});

或者作为从活动中观察片段的视图生命周期的一种替代方法(并且作为已废弃的片段的 OnActivityCreated ()的替代方法) ,您可以用另一种方法来监听活动的生命周期!从片段的 OnAttach (上下文)回调中,添加自己的实现 OnCreate (@NonNull LificycleOwner 所有者)观察者。回复: OnActivityCreated 已被弃用,如何正确使用 LificycleObserver?