簡體   English   中英

如果使用 setVariable() 方法設置 ViewModel,則 DataBinding 不起作用

[英]DataBinding is not working if setVariable() method is used to set ViewModel

我有ParentFragmentChildFragment 我正在使用 Koin 進行 DI。

在一種情況下,數據綁定不起作用,而在另一種情況下它正在起作用。

非工作案例: ParentFragment

abstract class ParentFragment<T: ViewDataBinding, V: ParentViewModel>: Fragment() {

    @LayoutRes
    abstract fun getLayoutResId(): Int

    abstract fun init()

    protected lateinit var binding: T

    protected abstract val mViewModel: V

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)

        return DataBindingUtil.inflate<T>(inflater, getLayoutResId(), container, false).apply { binding = this }.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        doDataBinding()
    }

    private fun doDataBinding() {
        binding.lifecycleOwner = viewLifecycleOwner 
        binding.setVariable(BR.viewModel, mViewModel)
        binding.executePendingBindings()
        init()
    }

ChidlFragment


class ChildFragment: ParentFragment<FragmentChildBinding, ChildViewModel>() {

    @LayoutRes
    override fun getLayoutResId() = R.layout.fragment_child

    override val mViewModel: ChildViewModel by viewModel()

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

    }

    override fun init() {
         mViewModel.a()
    }

a()方法除了將變量值更改為一些隨機文本之外什么都不做。 此變量綁定到ChildFragment EditText 這些是基本的數據綁定內容。 在問題的末尾提供了此方法的實現。

上面的代碼工作和a()方法被正確調用,我 ChildFragment 中的EditText值沒有改變

工作案例:如果我將代碼更改為此,一切正常。

ParentFragment

abstract class ParentFragment<T: ViewDataBinding>: Fragment() {

    @LayoutRes
    abstract fun getLayoutResId(): Int

    protected lateinit var binding: T


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)

        return DataBindingUtil.inflate<T>(inflater, getLayoutResId(), container, false).apply { binding = this }.root
    }

ChildFragment

class ChildFragment: ParentFragment<FragmentChildBinding>() {

    @LayoutRes
    override fun getLayoutResId() = R.layout.fragment_child

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

        val viewModel: ChildViewModel = getViewModel()
        binding.viewModel = viewModel
        binding.lifecycleOwner = this

        binding.viewModel.a()

    }

我的ChildViewModel類。 請注意,此類在兩種情況下都是相同的:

class ChildViewModel(): ParentViewModel() {
    var password: String = ""

    //This function is being called in both cases. BUT ONLY IN THE SECOND CASE, setting value
    //to "password" is being shown in the "EditText". 
    fun a () {
        Log.d("-------", "ViewModel method called")
        password = "asdasijdj1n2"
    }
}

這里可能有什么問題?

我這樣做的原因是我想盡可能地優化我的ParentFragment以避免子片段中的樣板代碼。

這里有兩個問題,但只有一個是根本原因。

工作案例之所以有效,是因為視圖模型中password屬性的值是在數據綁定實際將其綁定到視圖之前設置的。 在不工作的情況下不會發生這種情況的原因與片段的結構無關 - 只是在視圖模型中設置password值之前調用了binding.executePendingBindings() 這會強制數據綁定綁定要查看的password值,但由於當時它為null ,因此您什么也看不到。

這將我們帶到了問題的根本原因,即您的視圖模型中有一個屬性正在被數據綁定使用並且其值發生了變化,但該屬性是不可觀察的。 數據綁定需要知道它使用的屬性值何時發生變化,以便它可以更新使用這些屬性的視圖。 不工作的情況不起作用的原因是當調用binding.executePendingBindings()時,數據綁定被迫將passwordnull值綁定到視圖,並且無法知道后來更改了password ,因此無法' t 更新視圖。

通過數據綁定使屬性可觀察的兩種方法是將它們聲明為LiveData<T>ObservableField<T>而不是T (其中T是數據的類型)。 如果password已聲明為MutableLiveData<String>ObservableField<String> ,您會在問題的兩種情況下都看到該值出現在視圖中。 這是因為數據綁定會知道值何時發生變化。

因此,總而言之,對於在視圖模型中聲明的屬性使用LiveDataObservableField是一種很好的做法, LiveData屬性用於數據綁定並且其值可以更改。 這樣,就不會出現諸如數據綁定將值綁定到視圖之類的時間問題的可能性。

您可以通過為每個子片段提供視圖模型來解決您的問題。 你可以改變

abstract class ParentFragment<T: ViewDataBinding, V: ParentViewModel>:Fragment() {

    abstract fun provideViewModel(): V

    protected lateinit var binding: T

    protected abstract val mViewModel: V

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)

        return DataBindingUtil.inflate<T>(inflater, getLayoutResId(), container, false).apply { binding = this }.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        doDataBinding()
    }

    private fun doDataBinding() {
        binding.lifecycleOwner = viewLifecycleOwner 
        binding.setVariable(BR.viewModel, provideViewModel().apply { mViewModel = this})
        binding.executePendingBindings()
        init()
    }

在子片段中;

class ChildFragment: ParentFragment<FragmentChildBinding, ChildViewModel>() {

...

override val mViewModel: ChildViewModel by viewModel()

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

}

override fun provideViewModel() = mViewModel

...

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM