19

我正在使用 的片段childFragmentManager内或在 Activity内显示一个对话框supportFragmentManager,在此过程中我想设置目标片段,如下所示:

val textSearchDialog = TextSearchDialogFragment.newInstance()
textSearchDialog.setTargetFragment(PlaceSearchFragment@this, 0)

但是在运行该代码时,我得到了错误:

java.lang.IllegalStateException: Fragment TextSearchDialogFragment{b7fce67 #0 0} 声明的目标片段 PlaceSearchFragment{f87414 #0 id=0x7f080078} 不属于这个 FragmentManager!

我不知道如何访问FragmentManager用于管理片段显示的导航组件,是否有解决方案?

4

4 回答 4

17

更新:作为 Navigation 2.3.0 的一部分,Navigation 添加了对返回结果的显式支持,其中包含关于从 Dialog 目标返回结果的特定部分,作为使用共享 ViewModel 的替代方法。

上一个答案:

在 Fragment 与导航架构组件之间进行通信的推荐模式是通过sharedViewModel - aViewModel存在于通过检索ViewModelusing实现的 Activity 级别ViewModelProvider(getActivity())

根据文档,这提供了许多好处:

  • 该活动不需要做任何事情,也不需要知道任何有关此通信的信息。
  • SharedViewModel除了契约之外,片段不需要相互了解。如果其中一个片段消失,另一个片段将继续照常工作。
  • 每个片段都有自己的生命周期,不受另一个片段生命周期的影响。如果一个片段替换了另一个片段,则 UI 将继续工作而不会出现任何问题。

您还可以使用导航图范围的 ViewModel在比整个活动更小的范围内共享ViewModel 。

于 2018-06-08T02:35:59.950 回答
6

详细说明接受的答案:

(1) 创建一个共享视图模型,用于在该 Activity 内的片段之间共享数据。

public class SharedViewModel extends ViewModel {

    private final MutableLiveData<Double> aDouble = new MutableLiveData<>();

    public void setDouble(Double aDouble) {
        this.aDouble.setValue(aDouble);
    }

    public LiveData<Double> getDouble() {
        return aDouble;
    }
}

(2) 将您想要访问的数据存储在视图模型中。请注意视图模型 (getActivity) 的范围。

SharedViewModel svm =ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
svm.setDouble(someDouble);

(3)让fragment实现dialog的回调接口,加载dialog而不设置目标fragment。

fragment.setOnDialogSubmitListener(this);
fragment.show(getActivity().getSupportFragmentManager(), TAG);

(4) 在对话框内检索数据。

SharedViewModel svm =ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
svm.getDouble().observe(this, new Observer<Double>() {
    @Override
    public void onChanged(Double aDouble) {
        // do what ever with aDouble
    }
}); 
于 2019-03-18T15:14:20.707 回答
4

使用视图模型和片段 ktx,您可以在父片段和子片段之间托管共享视图模型,因此您可以将视图模型存储在父片段中,而不是让您的活动包含视图模型的实例并存储数据直到该活动完成片段,这样做,当您弹出实例化视图模型的片段时,视图模型将被清除

进口

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
 implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'

ParentFragment(SharedViewModel 主机)

class ParentFragment:Fragment() {
 private val model: SharedViewModel by viewModels()
}

子片段

class ChildFragment:Fragment(){
private val model: SharedViewModel by viewModels ({requireParentFragment()})
}

因此,这样做会将 sharedviewmodel 托管在父片段中,并且取决于该父片段的子片段将可以访问相同的实例,SharedViewModel并且当您弹出(也称为销毁片段)时,您的onCleared()方法将在您的视图模型中触发并且该 shareviewmodel 将被清除,它的所有数据也将被清除。

这样,您的 MainActivity 就不会包含 Fragment 共享的所有数据,并且您无需在每次离开使用SharedViewModel

现在在 alpha 中,您还可以使用视图模型在导航之间传递数据,该视图模型将保存导航之间的数据,假设您想在 Fragment B 和 Fragment A 之间共享数据,现在您只需两行即可完成

https://developer.android.com/guide/navigation/navigation-programmatic#returning_a_result

于 2020-03-21T18:17:48.907 回答
0

现有答案都没有真正回答您的问题 - 使用导航组件时我们如何设置对话框的目标片段?

事实证明,我们不需要使用共享 ViewModel 的(坦率地说是可怕的)模式。一旦您知道如何使用导航组件设置目标片段实际上非常容易。

我已经写了整篇文章,你可以在这里阅读:

https://lukeneedham.medium.com/using-targetfragment-with-jetpack-navigation-component-9c4302e8c062

您还可以在此处查看要点:

https://gist.github.com/LukeNeedham/83f0bdaa8d56d03d11f727967eb327f2

他们的关键是一个习惯FragmentFactory

fun FragmentManager.autoTarget() {
    fragmentFactory = ChildManagerFragmentFactory(this)
}

class ChildManagerFragmentFactory(
    private val fragmentManager: FragmentManager
) : AutoTargetFragmentFactory() {
    override fun getCurrentFragment() =
        fragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.firstOrNull()
}

abstract class AutoTargetFragmentFactory : FragmentFactory() {
    abstract fun getCurrentFragment(): Fragment?

    override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
        val fragment = super.instantiate(classLoader, className)
        val currentFragment = getCurrentFragment()
        fragment.setTargetFragment(currentFragment, REQUEST_CODE)
        return fragment
    }

    companion object {
        const val REQUEST_CODE = 0
    }
}

然后像这样简单地使用:

class MainActivity : AppCompatActivity(R.layout.activity_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportFragmentManager.autoTarget()
    }
}
于 2020-11-24T22:23:19.857 回答