I have a form in my Android app that is spread across multiple pages with a ViewPager.
To keep it simple, it just returns View-objects instead of instantiating Fragments.
Each view is made of a layout and a viewmodel (The same instance I use for all the views) and data binding to set the values automatically.
The problem is that the checkboxes is not filled and have a white checkmark if they are in a view that is not in the front when loading the activity.
If I change the order, so my view with the checkboxes are the first one loaded by the ViewPager, I can see the checkboxes being checked (with an animation) and they are marked correctly.
See the attached screenshots:
View not being the first loaded
View being the first loaded
I have the same problem with radio buttons.
An example of a Checkbox's databinding:
<CheckBox
android:id="@+id/checkbox_calling"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_weight="1"
android:checked="@={viewModel.observation.calling}"
android:text="@string/label_calling" />
UPDATE:
I have currently solved the problem by setting a OnPageChangeListener
on the ViewPager
and when the ViewPager
scrolls to the page with the checkboxes, I uncheck the checkboxes and then set them to the original value.
The animation is then shown correctly and the checkboxes ends up with the correct checkmarks.
I would however still appreciate finding out what I've done wrong or if there is a problem with Android databinding itself.
I think that it has to do with the way checkbox are animated. So I looked up how to skip the animation, and calling jumpDrawablesToCurrentState
on the checkbox after checking it does the trick for me.
I created this Kotlin extension property to deal with the problem (obviously only useful if you are writing Kotlin code, not Java)
/**
* Set the state of a checkbox skipping animations.
*/
var CheckBox.checked: Boolean
get() = isChecked
set(value) {
if(isChecked != value) {
isChecked = value
jumpDrawablesToCurrentState()
}
}
Now in my code I write checkbox_view.checked = true
instead of checkbox_view.isChecked = true
The accepted answer from Vivere explains the issue and solution perfectly.
The extended solution from Clyde is a great idea, but it caused issues for me. With some little tweaks I fixed it.
My final piece of code:
/**
* Do not animate the check, but check it directly, due to issue in combination with ViewPager:
* https://stackoverflow.com/questions/42997873/android-checkboxes-not-checked-correctly-in-viewpager-with-data-binding
*/
var CompoundButton.isCheckedInViewPager: Boolean
get() = isChecked
set(value) {
if (isChecked != value) {
isChecked = value
}
jumpDrawablesToCurrentState()
}
What was my issue with Clyde's code?
My Checkbox is located on the 3rd page of the ViewPager. To visualise what happened I added some logs:
var CompoundButton.isCheckedInViewPager: Boolean
get() = isChecked
set(value) {
Log.d("TEST", "set")
if (isChecked != value) {
Log.d("TEST", "value: $value")
Log.d("TEST", "isChecked: $isChecked")
isChecked = value
Log.d("TEST", "isChecked: $isChecked")
}
jumpDrawablesToCurrentState()
}
Logs:
// Navigate from page 1 to page 2 (page 3 with the checkbox is loaded due to the ViewPager):
...: set
...: value: true
...: isChecked: false
...: isChecked: true
// Navigate to page 3 to verify the checkbox's visual state
// Navigate back to page 1 and forward to page 2 again
...: set
// Navigate to page 3 to verify the checkbox's visual state
So when I navigate to the checkbox the first time it'll all be allright. If I navigate back to the 1st and to the 3rd again, the checkbox is unchecked again in Clyde's solution, because if (isChecked != value)
is false: jumpDrawablesToCurrentState
will not be called and the issue arises nonetheless.
ps. for clarity's sake: the checkbox will be checked onViewCreated
by observing a LiveData<Boolean>
in my ViewModel
. This value is true by default, so the checkbox should be checked.
Note:
Next to solving the issue with this code, I extended CompoundButton to support RadioButtons as well.
Finally, the name isCheckedInViewPager describes better when to use it, and won't be forgotten so easily if another developer starts calling isChecked (thanks to autocomplete).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With