如何使用MVVM从另一个片段中打开一个片段

时间:2020-07-07 01:26:59

标签: android kotlin android-fragments android-mvvm

我有一个片段ProductsFragment,单击该按钮时我有一个按钮AddProduct,我想打开另一个片段AddProductFragment

我正在使用MVVM体系结构

我通过此链接完成了以下提到的实现,但是我不太了解或没有提及我想导航到的片段

错误消息

enter image description here

ProductsFragment- 此处采用可查看的方法创建问题 *

class ProductsFragment: Fragment() {
    private lateinit var binding: ProductsBinding
    private lateinit var navController: NavController
    private lateinit var productsViewModel: ProductsViewModel


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding =  DataBindingUtil.inflate(inflater, R.layout.products, container, false)
        val dao = SubscriberDatabase.getInstance(activity!!.applicationContext).productDAO
        val repository = ProductRepository(dao)
        val factory = ProductsViewModelFactory(repository, activity!!.applicationContext)
        productsViewModel = ViewModelProvider(this, factory).get(ProductsViewModel::class.java)
        binding.productsViewModel = productsViewModel
        binding.lifecycleOwner = this
        val view = binding.root

        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        navController = Navigation.findNavController(view)
        productsViewModel.navigateScreen.observe(activity!!, EventObserver {
            navController.navigate(it)  //issues is here
        })
    }
}

产品

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data class=".ProductsBinding">
        <variable
            name="productsViewModel"
            type="com.rao.iremind.ProductsViewModel" />
    </data>
<LinearLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Testing text"/>

    <Button
        android:id="@+id/btn_add_product"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add product"
        android:onClick="@{() -> productsViewModel.addProduct()}"/>
    <View
        android:id="@+id/frgSpace"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>
</layout>

ProductViewModel

class ProductsViewModel (
    private val repository: ProductRepository,
    private val context: Context
): ViewModel() {

    private val _navigateScreen = MutableLiveData<Event<Any>>()
    val navigateScreen: LiveData<Event<Any>> = _navigateScreen

    fun addProduct() {
        Toast.makeText(context, "Products view model", Toast.LENGTH_LONG).show()
        _navigateScreen.value = Event(R.id.frgSpace)
    }
}

事件

open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

class EventObserver<Int>(private val onEventUnhandledContent: (Int) -> Unit) : Observer<Event<Int>> {
    override fun onChanged(event: Event<Int>?) {
        event?.getContentIfNotHandled()?.let {
            onEventUnhandledContent(it)
        }
    }
}

ProductsViewModelFactory

class ProductsViewModelFactory (
    private val repository: ProductRepository,
    private val context: Context
) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ProductsViewModel::class.java)) {
            return ProductsViewModel(repository, context) as T
        }
        throw IllegalArgumentException("Unknown View Model class")
    }
}

我想导航到该片段

class AddProductFragment: Fragment() {
    private lateinit var binding: AddProductBinding
    private lateinit var addProductViewModel: AddProductViewModel
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding =  DataBindingUtil.inflate(inflater, R.layout.add_product, container, false)
        val dao = SubscriberDatabase.getInstance(activity!!.applicationContext).productDAO
        val repository = ProductRepository(dao)
        val factory = ProductsViewModelFactory(repository, activity!!.applicationContext)
        addProductViewModel = ViewModelProvider(this, factory).get(AddProductViewModel::class.java)
        binding.addProductViewModel = addProductViewModel
        binding.lifecycleOwner = this
        val view = binding.root

        return view
    }
}

谢谢 R

1 个答案:

答案 0 :(得分:2)

您的EventObserver类似乎正在期待一个Int,但是您正在Any中发送LiveData<Event<Any>>

尝试更改

private val _navigateScreen = MutableLiveData<Event<Any>>()
val navigateScreen: LiveData<Event<Any>> = _navigateScreen

private val _navigateScreen = MutableLiveData<Event<Int>>()
val navigateScreen: LiveData<Event<Int>> = _navigateScreen

我还建议您在此行中将activity!!替换为viewLifecycleOwner

productsViewModel.navigateScreen.observe(viewLifecycleOwner, EventObserver {...})

,以便您的片段在其视图被破坏时不会收到任何LiveData更新。