我在使用childFragmentManager
的片段内部或使用supportFragmentManager
在Activity中显示对话框,在此过程中我想设置目标片段,如下所示:
val textSearchDialog = TextSearchDialogFragment.newInstance()
textSearchDialog.setTargetFragment(PlaceSearchFragment@this, 0)
但是当运行该代码时,我收到错误:
java.lang.IllegalStateException:Fragment TextSearchDialogFragment {b7fce67#0 0}声明了目标片段 不属于的PlaceSearchFragment {f87414#0 id = 0x7f080078} 这个FragmentManager!
我不知道如何访问导航组件用于管理片段显示的FragmentManager
,是否有解决方案?
答案 0 :(得分:7)
片段与导航架构组件之间进行通信的推荐模式是通过使用{{1}检索ViewModel
来实现活动级别的shared ViewModel
- ViewModel
。 }
根据the documentation,这提供了许多好处:
- 活动不需要做任何事情,也不需要了解这种沟通。
- 除
ViewModelProviders.of(getActivity())
合约外,碎片不需要彼此了解。如果其中一个碎片消失,另一个碎片会照常工作。- 每个片段都有自己的生命周期,不受另一个片段生命周期的影响。如果一个片段替换另一个片段,则UI将继续正常工作。
答案 1 :(得分:4)
使用viewmodel和fragment ktx,您可以在父片段和子片段之间托管一个共享的viewmodel,因此您不必让活动包含viewmodel的实例并在活动完成之前存储数据,而可以存储viewmodel在父片段中执行此操作,当您弹出实例化viewmodel的片段时,该viewmodel将被清除
from bs4 import BeautifulSoup
import requests
def search(soup):
results = soup.find_all(string="Union",recursive=True)
print(len(results))
web_url = "https://news.google.com/topics/CAAqJggKIiBDQkFTRWdvSUwyMHZNRGx6TVdZU0FtVnVHZ0pKVGlnQVAB?hl=en-IN&gl=IN&ceid=IN%3Aen"
r = requests.get(web_url)
soup = BeautifulSoup(r.text,'html.parser')
search(soup)
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
class ParentFragment:Fragment() {
private val model: SharedViewModel by viewModels()
}
因此,执行此操作会将sharedviewmodel托管在父片段中,并且取决于该父片段的子片段将有权访问class ChildFragment:Fragment(){
private val model: SharedViewModel by viewModels ({requireParentFragment()})
}
的同一实例,并且在您弹出(也就是销毁该片段)时,您的SharedViewModel
方法将在您的viewmodel处触发,并且shareviewmodel将被清除,并且所有数据也将清除。
这样,您就没有MainActivity来包含片段共享的所有数据,并且每次您留下使用onCleared()
现在,在Alpha中,您还可以使用视图模型在导航之间传递数据,该视图模型将在导航之间保存数据,假设您想在Fragment B和Fragment A之间共享数据,现在只需两行即可完成>
https://developer.android.com/guide/navigation/navigation-programmatic#returning_a_result
答案 2 :(得分:1)
现有答案中没有一个实际上回答您的问题-使用导航组件时如何设置对话框的目标片段?
事实证明,我们不需要使用共享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()
}
}
答案 3 :(得分:0)
详细说明接受的答案:
(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;
}
}
(2)将您想要访问的数据存储在视图模型中。注意视图模型的范围(getActivity)。
SharedViewModel svm =ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
svm.setDouble(someDouble);
(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
}
});