[英]DataBinding is not working if setVariable() method is used to set ViewModel
我有ParentFragment
和ChildFragment
。 我正在使用 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()
時,數據綁定被迫將password
的null
值綁定到視圖,並且無法知道后來更改了password
,因此無法' t 更新視圖。
通過數據綁定使屬性可觀察的兩種方法是將它們聲明為LiveData<T>
或ObservableField<T>
而不是T
(其中T
是數據的類型)。 如果password
已聲明為MutableLiveData<String>
或ObservableField<String>
,您會在問題的兩種情況下都看到該值出現在視圖中。 這是因為數據綁定會知道值何時發生變化。
因此,總而言之,對於在視圖模型中聲明的屬性使用LiveData
或ObservableField
是一種很好的做法, 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.