如何在使用导航组件时设置对话框的目标片段

时间:2018-06-08 01:06:36

标签: android kotlin android-architecture-components android-navigation android-architecture-navigation

我在使用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,是否有解决方案?

4 个答案:

答案 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)

ParentFragment(SharedViewModel主机)

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

ChildFragment

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
    }
});