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

时间:2018-06-08 06:22:53

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

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

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

NavHostFragment.findNavController(this).navigate(R.id.action_selection, bundle)

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

8 个答案:

答案 0 :(得分:46)

他们在2.3.0-alpha02版本中添加了fix for this

如果从片段A 导航到片段B A ,则需要 B 的结果:

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

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

findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)

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

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

答案 1 :(得分:25)

自片段KTX 1.3.0-alpha04起,Android支持在片段之间或片段与活动之间传递数据。它类似于startActivityForResult逻辑。

这是导航组件的示例。您可以详细了解here

build.gradle

implementation "androidx.fragment:fragment-ktx:1.3.0-alpha04"

FragmentA.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)
    }
}

FragmentB.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()
        }
    }
}    

答案 2 :(得分:9)

谷歌:you should try to use shared 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;
    }
}

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

使用共享ViewModel的DetailsFragment:

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

答案 3 :(得分:5)

使用这些扩展功能

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

因此,如果您要将结果从片段 B 发送到片段 A

内部片段 B

setNavigationResult(false, "someKey")

内部片段 A

val result = fragment.getNavigationResultLiveData<Boolean>("someKey")
result.observe(viewLifecycleOwner){ booleanValue-> doSomething(booleanValue)

答案 4 :(得分:1)

对@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)
}

答案 5 :(得分:0)

我建议您使用NavigationResult库,它是JetPack导航组件的附加组件,可让您navigateUp与捆绑包一起使用。 我还在媒体上写了blog post

答案 6 :(得分:0)

我创建了一个类似于 LeHaine 的答案的包装函数,但它可以处理更多情况。

要将结果从子级传递给父级:

findNavController().finishWithResult(PickIntervalResult.WEEKLY)

从父级中的子级获取结果:

findNavController().handleResult<PickIntervalResult>(
    viewLifecycleOwner,
    R.id.navigation_notifications, // current destination
    R.id.pickNotificationIntervalFragment // child destination
    ) { result ->
        binding.textNotifications.text = result.toString()
}

我的包装器不像 LeHaine 的包装器那么简单,但它是通用的,可以处理以下情况:

  1. 一位家长有几个孩子
  2. Result 是任何实现 Parcelable 的类
  3. 对话目的地

查看 github 上的实现或查看 an article that explains how it works

答案 7 :(得分:-1)

只是其他答案的替代...

以 MutableShareFlow 为核心的 EventBus 以共享对象(例如:repo)为核心,并在 here 中描述了一个观察者

看起来事情正在从 LiveData 转向 Flow 方向。

值得一看。