14

我正在使用在 Google I/O 2018 上展示的 android 导航,似乎我可以通过绑定到某个视图或使用NavHost从 Fragment 获取它来使用它。但我需要的是根据几个条件从我的第一个片段从 ViewModel 导航到另一个特定视图。对于ViewModel,我扩展AndroidViewModel,但我不明白下一步该怎么做。我无法getApplication转换为 Fragment/Activity,也无法使用NavHostFragment. 此外,我不能只将导航绑定到,onClickListener因为startFragment它只包含一个ImageView. 我该如何导航ViewModel

class CaptionViewModel(app: Application) : AndroidViewModel(app) {
private val dealerProfile = DealerProfile(getApplication())
val TAG = "REGDEB"


 fun start(){
    if(dealerProfile.getOperatorId().isEmpty()){
        if(dealerProfile.isFirstTimeLaunch()){
            Log.d(TAG, "First Time Launch")
            showTour()
        }else{
            showCodeFragment()
            Log.d(TAG, "Show Code Fragment")

        }
    }
}

private fun showCodeFragment(){
    //??
}

private fun showTour(){
    //??
}

}

我的片段

class CaptionFragment : Fragment() {
private lateinit var viewModel: CaptionViewModel
private val navController by lazy { NavHostFragment.findNavController(this) }

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    viewModel = ViewModelProviders.of(this).get(CaptionViewModel::class.java)
    return inflater.inflate(R.layout.fragment_caption, container, false)
}


override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    viewModel.start()

}

}

我想在 ViewModel 中保留导航逻辑

4

3 回答 3

16

如何从 ViewModel 导航?

答案是请不要。ViewModel 旨在存储和管理与 UI 相关的数据。

新答案

在我之前的回答中,我说过我们不应该从 ViewModel 导航,原因是因为要导航,ViewModel 必须引用活动/片段,我相信(可能不是最好的,但我仍然相信)永远不会一个好主意。

但是,在 Google 推荐的应用架构中,它提到我们应该从 model 驱动 UI。在我思考之后,它们是什么意思?

所以我检查了一个来自“android-architecture”的样本,我发现了谷歌是如何做到这一点的一些有趣的方式。

请在此处查看:todo-mvvm-databinding

事实证明,它们确实从 model 驱动 UI。但是怎么做?

  1. 他们创建了一个界面TasksNavigator,基本上只是一个导航界面。
  2. 然后在TasksViewModel中,他们有对 TaskNavigator 的引用,因此他们可以在不直接引用活动/片段的情况下驱动 UI。
  3. 最后,TasksActivity 实现了 TasksNavigator 以提供每个导航操作的详细信息,然后将 navigator 设置为 TasksViewModel。
于 2018-06-07T16:43:48.333 回答
0

有两种方法我可以推荐这样做。

  1. 使用 LiveData 进行通信并告诉片段导航。
  2. 创建一个名为 Router 的类,它可以包含您的导航逻辑和对片段或导航组件的引用。ViewModel 可以与路由器类进行通信以进行导航。
于 2022-01-19T18:10:34.477 回答
0

您可以使用可选的自定义枚举类型并观察视图中的变化:

enum class NavigationDestination {
    SHOW_TOUR, SHOW_CODE_FRAGMENT
}

class CaptionViewModel(app: Application) : AndroidViewModel(app) {
    private val dealerProfile = DealerProfile(getApplication())
    val TAG = "REGDEB"

    private val _destination = MutableLiveData<NavigationDestination?>(null)
    val destination: LiveData<NavigationDestination?> get() = _destination
    
    fun setDestinationToNull() {
        _destination.value = null
    }
    

    fun start(){
        if(dealerProfile.getOperatorId().isEmpty()){
            if(dealerProfile.isFirstTimeLaunch()){
                Log.d(TAG, "First Time Launch")
                _destination.value = NavigationDestination.SHOW_TOUR
            }else{
                _destination.value = NavigationDestination.SHOW_CODE_FRAGMENT
                Log.d(TAG, "Show Code Fragment")

            }
        }
    }
}

然后在您的视图中观察 viewModel 目标变量:

viewModel.destination.observe(this, Observer { status ->
            if (status != null) {
                viewModel.setDestinationToNull()
                status?.let {
                    when (status) {
                        NavigationDestination.SHOW_TOUR -> {
                            // Navigate to your fragment
                        }
                        NavigationDestination.SHOW_CODE_FRAGMENT -> {
                            // Navigate to your fragment
                        }
                    }
                })
            }

如果您只有一个目的地,您可以只使用布尔值而不是枚举。

于 2022-02-03T06:04:49.370 回答