I want to use a ViewModel
with a Databinding to disable a Checkbox when a Button is clicked, but the UI won't update unless the fragment is destroyed and recreated.
There seem to be many similar questions, but the ones that are most similar seem to all be solved by setting binding.lifecycleOwner
, which I've already done.
fragment_checkbox_databinding.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".CheckboxDatabindingFragment">
<data>
<variable
name="viewModel"
type=".CheckboxDatabindingFragmentViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@ id/my_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="@{viewModel.checkboxEnabled}" />
<Button
android:id="@ id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.startClicked()}" />
</LinearLayout>
</layout>
CheckboxDatabindingFragment.kt:
class CheckboxDatabindingFragment() : Fragment() {
private lateinit var viewModel: CheckboxDatabindingFragmentViewModel
private var _binding: FragmentCheckboxDatabindingBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(this).get(CheckboxDatabindingFragmentViewModel::class.java )
_binding = FragmentCheckboxDatabindingBinding.inflate(inflater, container, false)
binding.lifecycleOwner = viewLifecycleOwner // androidx.fragment.app.FragmentViewLifecycleOwner
binding.viewModel = viewModel
return binding.root
}
...
}
CheckboxDatabindingFragmentViewModel.kt:
class CheckboxDatabindingFragmentViewModel(application: Application) : AndroidViewModel(application) {
var checkboxEnabled = true
fun startClicked() {
checkboxEnabled = false
}
}
I know I could easily carry out this functionality by manually getting the checkbox view in CheckboxDatabindingFragment.kt
and setting the isEnabled
property, but that defeats the purpose of the DataBinding.
I need the fragment to be the owner, not the activity, so how can I get the fragment UI to update?
CodePudding user response:
Problem:
var checkboxEnabled = true
The top statement creates a non-observable boolean, so no changes on that variable can be observed and submitted to the layout through the life cycle owner (which in your case is the fragment).
Sets the LifecycleOwner that should be used for observing changes of LiveData in this binding. If a LiveData is in one of the binding expressions and no LifecycleOwner is set, the LiveData will not be observed and updates to it will not be propagated to the UI.
So, using binding.lifecycleOwner = viewLifecycleOwner
alone is not enough to publish the changes to the layout through databinding, but also you've to use observable objects that can be observed by the lifeCycleOwner and eventually publish any changes on them to the layout via the DataBinding.
Solution:
To solve this you need to change the checkboxEnabled
to an observable:
Option 1: use ObservableBoolean:
class CheckboxDatabindingFragmentViewModel(application: Application) : AndroidViewModel(application) {
val checkboxEnabled = ObservableBoolean(true)
fun startClicked() {
checkboxEnabled.set(false)
}
}
Option 2: use MutableLiveData:
class CheckboxDatabindingFragmentViewModel(application: Application) : AndroidViewModel(application) {
val checkboxEnabled = MutableLiveData(true)
fun startClicked() {
checkboxEnabled.value = false
}
}