如何在不绑定到 ViewModel (MVVM) 中的 UI 的情况下使用 android 导航?
How to use android navigation without binding to UI in ViewModel (MVVM)?
我正在使用在 Google I/O 2018 年发布的 android 导航,似乎我可以通过绑定到某个视图或使用 NavHost
来使用它从片段中获取它。但我需要的是根据多种条件从我的第一个片段导航到 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 中保留导航逻辑
How can I navigate from ViewModel?
答案是请不要。 ViewModel 旨在存储和管理 UI 相关数据。
新答案
在我之前的回答中,我说过我们不应该从 ViewModel 导航,原因是因为要导航,ViewModel 必须引用 Activities/Fragments,我相信(可能不是最好的,但是我仍然相信它)从来都不是一个好主意。
但是,在 Google 推荐的应用程序架构中,它提到我们 应该从模型 驱动 UI。后来我想,他们是什么意思?
所以我检查了 "android-architecture" 中的样本,我发现了一些有趣的方法 Google 是如何做到的。
请在此处查看:todo-mvvm-databinding
事实证明,他们确实 从模型 驱动 UI。但是怎么办?
- 他们创建了一个界面 TasksNavigator,基本上只是一个导航界面。
- 然后在 TasksViewModel 中,他们拥有对 TaskNavigator 的引用,因此他们可以驱动 UI 而无需直接引用 Activities / Fragments。
- 最后,TasksActivity 实现了 TasksNavigator 以提供每个导航操作的详细信息,然后将 navigator 设置为 TasksViewModel。
我可以推荐两种方法。
- 使用 LiveData 进行通信并告诉片段进行导航。
- 创建一个名为 Router 的 class,它可以包含您的导航逻辑和对片段或导航组件的引用。 ViewModel 可以与路由器通信 class 进行导航。
您可以使用可选的自定义枚举类型并观察视图中的变化:
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
}
}
})
}
如果您只有一个目的地,您可以只使用布尔值而不是枚举。
我正在使用在 Google I/O 2018 年发布的 android 导航,似乎我可以通过绑定到某个视图或使用 NavHost
来使用它从片段中获取它。但我需要的是根据多种条件从我的第一个片段导航到 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 中保留导航逻辑
How can I navigate from ViewModel?
答案是请不要。 ViewModel 旨在存储和管理 UI 相关数据。
新答案
在我之前的回答中,我说过我们不应该从 ViewModel 导航,原因是因为要导航,ViewModel 必须引用 Activities/Fragments,我相信(可能不是最好的,但是我仍然相信它)从来都不是一个好主意。
但是,在 Google 推荐的应用程序架构中,它提到我们 应该从模型 驱动 UI。后来我想,他们是什么意思?
所以我检查了 "android-architecture" 中的样本,我发现了一些有趣的方法 Google 是如何做到的。
请在此处查看:todo-mvvm-databinding
事实证明,他们确实 从模型 驱动 UI。但是怎么办?
- 他们创建了一个界面 TasksNavigator,基本上只是一个导航界面。
- 然后在 TasksViewModel 中,他们拥有对 TaskNavigator 的引用,因此他们可以驱动 UI 而无需直接引用 Activities / Fragments。
- 最后,TasksActivity 实现了 TasksNavigator 以提供每个导航操作的详细信息,然后将 navigator 设置为 TasksViewModel。
我可以推荐两种方法。
- 使用 LiveData 进行通信并告诉片段进行导航。
- 创建一个名为 Router 的 class,它可以包含您的导航逻辑和对片段或导航组件的引用。 ViewModel 可以与路由器通信 class 进行导航。
您可以使用可选的自定义枚举类型并观察视图中的变化:
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
}
}
})
}
如果您只有一个目的地,您可以只使用布尔值而不是枚举。