Android 使用导航组件时如何设置对话框的目标片段
我正在使用Android 使用导航组件时如何设置对话框的目标片段,android,kotlin,android-architecture-components,android-navigation,android-architecture-navigation,Android,Kotlin,Android Architecture Components,Android Navigation,Android Architecture Navigation,我正在使用childFragmentManager在片段内或使用supportFragmentManager在活动内显示一个对话框,在此过程中,我希望设置目标片段,如下所示: val textSearchDialog = TextSearchDialogFragment.newInstance() textSearchDialog.setTargetFragment(PlaceSearchFragment@this, 0) 但是当运行该代码时,我得到一个错误: java.lang.Illega
childFragmentManager
在片段内或使用supportFragmentManager
在活动内显示一个对话框,在此过程中,我希望设置目标片段,如下所示:
val textSearchDialog = TextSearchDialogFragment.newInstance()
textSearchDialog.setTargetFragment(PlaceSearchFragment@this, 0)
但是当运行该代码时,我得到一个错误:
java.lang.IllegalStateException:片段
TextSearchDialogFragment{b7fce67#0}声明的目标片段
不属于的PlaceSearchFragment{f87414#0 id=0x7f080078}
这个碎片管理员
我不知道如何访问导航组件用来管理片段显示的
FragmentManager
,是否有解决方案?更新:作为导航2.3.0的一部分,导航添加了对的明确支持,其中有一个关于的特定部分,作为使用共享视图模型的替代方案
先前的答案:
建议使用-aViewModel
在片段与导航体系结构组件之间进行通信,该模式位于通过使用ViewModelProvider(getActivity())检索ViewModel
而实现的活动级别
根据,这提供了许多好处:
- 该活动不需要做任何事情,也不需要了解有关此通信的任何信息
- 除了
SharedViewModel
契约之外,片段不需要相互了解。如果其中一个碎片消失,另一个碎片将继续正常工作
- 每个片段都有自己的生命周期,不受另一个片段生命周期的影响。如果一个片段替换了另一个片段,UI将继续工作,不会出现任何问题
您还可以使用。详细说明接受的答案,在比整个活动更小的范围内共享ViewModels:
(1) 创建一个共享视图模型,用于在该活动中的片段之间共享数据
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;
}
}
(3) 让片段实现对话框的回调接口,并在不设置目标片段的情况下加载对话框
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
}
});
SharedViewModelSVM=ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
getDouble().observe(这个,新的观察者(){
@凌驾
更改后的公共空间(双倍){
//用双倍做什么
}
});
使用viewmodel和fragment ktx,您可以在父片段和子片段之间托管一个共享的viewmodel,因此您可以将viewmodel存储在父片段中,而不是让您的活动包含viewmodel的实例并存储数据,直到该活动完成,当弹出实例化viewmodel的片段时,viewmodel将被清除
进口
ParentFragment(SharedViewModel主机)
儿童碎片
因此,这样做将在父片段中承载sharedviewmodel,并且依赖于该父片段的子片段将有权访问该sharedviewmodel的相同实例,并且当您弹出(即销毁片段)时,您的onCleared()
方法将向您的viewmodel激发,该shareviewmodel及其所有数据都将被清除
这样,您的MainActivity就不必包含片段共享的所有数据,也不必在每次留下使用SharedViewModel的片段时清除这些数据
现在在alpha中,您可以使用viewmodel在导航之间传递数据,该viewmodel将在导航之间保存数据,假设您希望在片段B和片段a之间共享数据,现在您只需两行即可完成
现有答案中没有一个能真正回答您的问题-在使用导航组件时,如何设置对话框的目标片段?
事实证明,我们不需要使用共享视图模型的(坦率地说是可怕的)模式。一旦您知道如何使用导航组件,就可以很容易地设置目标片段
我写了一整篇文章,你可以在这里读到:
您也可以在此处查看要点:
它们的键是一个自定义的片段工厂
:
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()
}
}
问题似乎是,您必须使用相同的片段管理器,而不是子对象,所以使用getFragmentManager()很好,稍后会考虑一下。谢谢你指出这一点。我认为这是一个错误的答案。您不可能只为了与对话框通信而创建一个在父活动的整个生命周期中都存在的共享ViewModel。事实上,导航框架并没有添加目标片段。若谷歌推荐,共享视图模型听起来不太合适。它看起来类似于通过全局变量从函数返回值。在导航到详细信息/编辑片段时,我使用newFragment.setTargetFragment(this)
实现预期的流,然后在返回之前调用getTargetFragment().onActivityResult(result,getTargetRequestCode())
(例如fragmentManager.popBackstack())。其中一个好处是Android可以自动处理其中一个片段不存在的情况。不过,我还没有找到使用导航组件实现这种流的方法。@jskierbi-当然,这个问题有相当大的问题-您的目标片段没有启动,因此在那里执行任何操作都是不安全的,您将两个片段紧密地耦合在一起,并且您被迫使用一个意图(和可打包的对象)实际上任何物体都可以。当然,它并不完美,因此我建议为本机navigateForResult()
类型的API加上星号。@ianhanniballake当两个共享片段都被销毁时,这个sharedViewModel会发生什么?它会是普通教育还是仅仅停留在那里直到活动进入记忆?比公认的答案要好得多。作用域为t的SharedViewModel
class ParentFragment:Fragment() {
private val model: SharedViewModel by viewModels()
}
class ChildFragment:Fragment(){
private val model: SharedViewModel by viewModels ({requireParentFragment()})
}
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()
}
}