从当前片段将数据传递回上一个片段

26

我正在我的应用程序中使用导航抽屉。我有一个MainActivity和其他是Fragments。问题是,假设我有三个片段,如A,B,C。

现在,在A中我有一个按钮并从A发送数据到B。
例如putString(“datafrom A”,“datafrom A”);
现在在B中,我从A接收数据。
我在B中有一个按钮,我正在从B发送数据到C。
例如putString(“datafrom B”,“datafrom B”);
现在在C中,我从B接收数据。
然后,我在C中有一个按钮,并从C发送数据到B。
例如putString(“datafrom C”,“datafrom C”);

因此,似乎在B中我正在从两个不同的片段中获取数据。我尝试了所有使用Activity并使用startActivityforresult时都能很好地工作。但如何管理所有片段?


当您从A发送数据到B时,您必须维护一个布尔值,指明我来自A。同样的情况也适用于C->A,您必须使用相同的布尔值,指明您来自C并且正在向B发送数据。 - Ravindra Kushwaha
但是我该如何检查?它是从哪个片段传递的? - chris
当您从A发送数据到B时,请使用BOOLEAN值TRUE.....而当您从C发送数据到A时,请使用BOOLEAN值FALSE。 - Ravindra Kushwaha
但是,当您从A>B传递数据时,应用程序将崩溃并显示NPE。原因是它无法获取C>B的false值...明白吗? - chris
如果有人在2021年寻找导航组件的答案:请查看https://dev59.com/m1MI5IYBdhLWcg3wcK7K#61239011 - HBB20
显示剩余2条评论
3个回答

73

更新

Androidx Activity 1.2.0-alpha02Androidx Fragment 1.3.0-alpha4开始,官方 Android 开发者指南建议使用活动/片段结果 API 替代已弃用的Activity.onActivityResult(int, int, Intent)Fragment.setTargetFragment(Fragment, int)方法:

强烈建议使用在 AndroidX Activity 1.2.0-alpha02 和 Fragment 1.3.0-alpha02 中引入的 Activity Result APIs。

因此,要将数据从 C 传递回 B 片段,请在片段 B 的 FragmentManager 上调用 setFragmentResultListener(),如以下示例所示:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setFragmentResultListener("requestKey") { requestKey, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result
     }
}

在片段 C 中,使用相同的 requestKey ,通过使用 setFragmentResult() API,在同一 FragmentManager 上设置结果。示例:
setFragmentResult("requestKey", bundleOf("bundleKey" to "result"))

更多详细信息,请查看 此指南


以下答案已过时

当您从 B 启动 Fragment C 时,您可以调用 setTargetFragment()。例如:

FragmentC fragmentC = FragmentC.newInstance();
fragmentC.setTargetFragment(FragmentB.this, REQUEST_CODE);
getFragmentManager().beginTransaction().replace(R.id.container, fragmentC).commit();

然后当您想从C向片段B传递数据时,您可以调用以下代码:

getTargetFragment().onActivityResult(
                getTargetRequestCode(),
                Activity.RESULT_OK,
                new Intent().putExtra("datafrom C", "datafrom C")
);

然后在您的 Fragment B 中的 onActivityResult() 方法中获取它:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode==REQUEST_CODE && resultCode==Activity.RESULT_OK) {
        String datafromC = data.getStringExtra("datafrom C");   
    }
}

无法解决newInstance()和无法解决REQUEST_CODE。 - chris
newInstance() 是我经常用来初始化 Fragment 的方法,你也可以使用 FragmentC fragmentC = new FragmentC();。 - CodePlay
REQUEST_CODE是一个常量整数,用于标识触发onActivityResult()方法的片段。如果片段C是唯一将数据传回片段B的片段,则可以将整数分配给它。例如:fragmentC.setTargetFragment(FragmentB.this, 1); 然后在片段B的onActivityResult()方法中检查requestCode==1。 - CodePlay
6
如此整洁、简单和本土的方式。然而似乎有很多人不知道它。 - Kirill Starostin
2
@KirillStarostin,手动调用ActivityResult有什么好处? - Tim

5

当您从Fragment A向Fragment B发送数据时,请使用以下相同的布尔值:

FragmentA -> FragmentB

FragmentB ldf = new FragmentB ();
Bundle args = new Bundle();
args.putBoolean("BOOLEAN_VALUE",true);
ldf.setArguments(args);

getFragmentManager().beginTransaction().add(R.id.container, ldf).commit();

当你从Fragment C向Fragment B发送数据时,使用与在Fragment A到B中使用的相同的BOOLEAN,如下所示-

FragmentC -> FragmentB

FragmentB ldf = new FragmentB ();
    Bundle args = new Bundle();
    args.putBoolean("BOOLEAN_VALUE",false);
    ldf.setArguments(args);

    getFragmentManager().beginTransaction().add(R.id.container, ldf).commit();

在最后,我们需要检查在FragmentB中接收到的值来自哪里,例如Fragment A或Fragment C。
FragmentB
   Boolean getValue= getArguments().getBoolean("BOOLEAN_VALUE");  
   if(getValue)
   {
    //VALUE RECEIVED FROM FRAGMENT A
   }
   else
   {
   //VALUE RECEIVED FROM FRAGMENT C
   }

不错..我在尝试,但需要使用putBoolean函数。 - chris
@chris,抱歉我已经更新了解决方案,请检查一下。让我知道它是否对你有效。 - Ravindra Kushwaha
@chris..这对你可行吗?如果可以,请将答案标记为正确,因为这将有助于未来也遇到类似问题的其他人。 - Ravindra Kushwaha
但是当我从C>B时,从A获取的值将变为null。 - chris
当您从C->B时,A的值有什么用处?您还可以做一件事情,即从B->C发送A的值,然后再从C->B接收它。 - Ravindra Kushwaha

4
自2017年以来,事情发生了很大的变化。我发布的答案基本上是一个示例,来自 https://developer.android.com ,并且提供了一个好的解决方案,在这个解决方案中,无论有多少个片段,它们都不知道彼此的存在,但仍然能够创建一个简单而优雅的机制,可以在不费太多力气的情况下使用。
这个答案基于 ViewModelsLiveData
注:如果您对 Architecture Components 不熟悉,我强烈建议您随时学习它,因为它将提高您的生产速度,并减少项目中的错误数量。
以下所有内容都是从以下链接中引用的: 源代码(Kotlin/Java) 在片段之间共享数据
“两个或更多片段需要相互通信”是非常常见的情况。想象一个常见的情况:主细节片段,在其中,用户从列表中选择一个项目,另一个片段显示所选项目的内容。这种情况从未是简单的,因为两个片段都需要定义一些接口描述,并且所有者活动必须将它们绑定在一起。此外,两个片段都必须处理另一个片段尚未被创建或不可见的情况。
通过使用ViewModel对象,可以解决这个常见痛点。这些片段可以使用它们的活动范围共享一个ViewModel来处理此通信,如下面的示例代码所示:
class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
        })
    }
}

注意,这两个片段都检索包含它们的活动。这样,当这些片段各自获取ViewModelProvider时,它们将接收到相同的SharedViewModel实例,该实例作用域限定为此活动。
这种方法提供以下好处: - 活动无需执行任何操作或了解此通信。 - 除了SharedViewModel契约之外,片段不需要互相了解。如果其中一个片段消失了,另一个片段仍然像平常一样工作。 - 每个片段都有自己的生命周期,并且不受其他片段生命周期的影响。如果一个片段替换了另一个片段,则UI继续正常工作,没有任何问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接