如果使用 setVariable() 方法设置 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()方法除了将变量值更改为一些随机文本外,什么也不做。这种变种注定要在ChildFragmentEditText.这些是基本的数据绑定内容。此方法的实现在问题末尾提供。

上面的代码正在工作a()方法被正确调用EditText但我的 ChildFragment 中的值没有改变

工作案例:如果我将我的代码更改为此代码,则一切正常。

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后来被更改,因此它无法更新视图。

使属性可通过数据绑定观察的两种方法是将它们声明为LiveData<T>ObservableField<T>,而不是T(其中T是数据类型(。如果password被声明为MutableLiveData<String>ObservableField<String>,您将在问题的两种情况下都看到该值出现在视图中。这是因为数据绑定知道值何时更改。

因此,总之,最好对在数据绑定中使用的视图模型中声明的属性使用LiveDataObservableField,并且其值可以更改。这样,数据绑定将值绑定到视图时就不会出现计时问题。

您可以通过为每个子片段提供视图模型来解决问题。您可以更改

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
...

}

相关内容

最新更新