如何使用Navigation Architecture Component从片段中获取结果?

Nom*_*sta 24 android android-architecture-components android-jetpack android-architecture-navigation

假设我们有两个片段:MainFragmentSelectionFragment.第二个是用于选择某个对象的构建,例如整数.从第二个片段接收结果有不同的方法,如回调,总线等.

现在,如果我们决定使用导航架构组件导航到第二个片段,我们可以使用以下代码:

NavHostFragment.findNavController(this).navigate(R.id.action_selection, bundle)
Run Code Online (Sandbox Code Playgroud)

其中bundleBundle(当然)的实例.正如您所看到的那样,无法访问SelectionFragment我们可以进行回调的位置.问题是,如何使用Navigation Architecture Component接收结果?

小智 74

他们在 2.3.0-alpha02 版本中为此添加了一个修复程序

如果从片段 A导航到片段 B并且A需要来自B的结果:

findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Type>("key")?.observe(viewLifecycleOwner) {result ->
    // Do something with the result.
}
Run Code Online (Sandbox Code Playgroud)

如果在Fragment B 上并且需要设置结果:

findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)
Run Code Online (Sandbox Code Playgroud)

我最终为此创建了两个扩展:

fun Fragment.getNavigationResult(key: String = "result") =
    findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>(key)

fun Fragment.setNavigationResult(result: String, key: String = "result") {
    findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该被接受为正确的答案。如此简单并且完美地完成工作 (4认同)
  • 请注意 `currentBackStackEntry` 将无法与 `&lt;dialog&gt;` 目的地一起正常工作(请参阅[文档](https://developer.android.com/guide/navigation/navigation-programmatic#additional_considerations)),您需要使用 `getBackStackEntry( )` 加上您目的地的 ID (3认同)

ars*_*ent 53

由于 Fragment KTX 1.3.0-alpha04 Android 支持在片段之间或片段和活动之间传递数据。这类似于startActivityForResult逻辑。

这是导航组件的示例。你可以在这里阅读更多关于它的信息

构建.gradle

implementation "androidx.fragment:fragment-ktx:1.3.0-alpha04"
Run Code Online (Sandbox Code Playgroud)

片段A.kt

class FragmentA : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        // Step 1. Listen for fragment results
        setFragmentResultListener(FragmentB.REQUEST_KEY) { key, bundle -> 
            // read from the bundle
        }

        // Step 2. Navigate to Fragment B
        findNavController().navigate(R.id.fragmentB)
    }
}
Run Code Online (Sandbox Code Playgroud)

片段B.kt

class FragmentB : Fragment() {

    companion object {
        val REQUEST_KEY = "FragmentB_REQUEST_KEY"
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        buttonA.setOnClickListener { view -> 
            // Step 3. Set a result
            setFragmentResult(REQUEST_KEY, bundleOf("data" to "button clicked"))

            // Step 4. Go back to Fragment A
            findNavController().navigateUp()
        }
    }
}    
Run Code Online (Sandbox Code Playgroud)

  • 不适合我,片段结果侦听器从未调用过,也不知道为什么 (3认同)

小智 22

使用这些扩展功能

fun <T> Fragment.getNavigationResult(key: String = "result") =
    findNavController().currentBackStackEntry?.savedStateHandle?.get<T>(key)

fun <T> Fragment.getNavigationResultLiveData(key: String = "result") =
        findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)

fun <T> Fragment.setNavigationResult(result: T, key: String = "result") {
    findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
}
Run Code Online (Sandbox Code Playgroud)

所以如果你想将结果从片段B发送到片段A

内部片段B

setNavigationResult(false, "someKey")
Run Code Online (Sandbox Code Playgroud)

内部片段A

val result = fragment.getNavigationResultLiveData<Boolean>("someKey")
result.observe(viewLifecycleOwner){ booleanValue-> doSomething(booleanValue)
Run Code Online (Sandbox Code Playgroud)

重要的提示

在片段 B 中,您需要将结果 (setNavigationResult()) 设置为启动或恢复状态(在 onStop() 或 onDestroy() 之前),否则 previousBackStackEntry 将已经为空。

重要说明#2

如果您只想处理一次结果,则必须在 SavedStateHandle 上调用 remove() 以清除结果。如果您不删除结果,LiveData 将继续将最后一个结果返回给任何新的 Observer 实例。

官方指南中的更多信息。

  • 我尝试使用 saveStateHandle 并能够使用结果设置密钥,但我从未在观察者中收到更新。我正在使用版本 2.3.4... (2认同)

Ami*_*ifi 16

根据Google的说法:您应该尝试使用共享的ViewModel.请查看以下Google的示例:

共享ViewModel,它将包含共享数据,可以从不同的片段访问.

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新ViewModel的MasterFragment:

public class MasterFragment extends Fragment {

    private SharedViewModel model;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

DetailsFragment使用共享的ViewModel:

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, item -> {
           // Update the UI.
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 问题是您正在使用getActivity()将视图模型限定在活动范围内。这意味着它将永远不会被清除(使用额外的内存,当您稍后导航回这些片段并显示陈旧数据时,可能会导致意外结果)。您应该改用`ViewModelProviders.of(parent)...`。 (9认同)
  • 如果我们没有视图模型怎么办? (3认同)
  • @CarsonH​​olzheimer 这里的父级是什么? (2认同)
  • @CarsonH​​olzheimer 你说的问题在这里解决了 /sf/ask/3859613691/ (2认同)

Mus*_*han 6

通过对 @LeHaine 的答案进行一点改进,您可以使用这些方法进行导航2.3.0-alpha02及以上操作

fun <T> Fragment.getNavigationResult(key: String) =
    findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)

fun <T> Fragment.setNavigationResult(result: T, key: String) {
    findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

6230 次

最近记录:

6 年,1 月 前