I'm currently using bindings to dynamically set the texts of various text views using the android view models. At the moment the view models look something like this:
class MyViewModel(
resources: Resources,
remoteClientModel: Model = Model()
) : ObservableViewModel() {
init {
observe(remoteClientModel.liveData) {
notifyChange()
}
fun getTextViewTitle(): String = when {
someComplicatedExpression -> resources.getString(R.string.some_string, null)
else -> resources.getString(R.string.some_other_string)
}
}
And the xml layout:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="my.app.signature.MyViewModel"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.textViewTitle}"
android:textAlignment="center"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
However I would like to remove the "resources: Resources" that is injected into the view model, since the resources are coupled with the Activity. The code now simply returns the string resource id instead:
fun getTextViewTitle(): Int = when {
someComplicatedExpression -> R.string.some_string
else -> R.string.some_other_string
}
Hence I've removed the activity dependency. The compiler thinks this is fine but it crashes in runtime with the following exception: android.content.res.Resources$NotFoundException: String resource ID #0x0.
This happens when trying to attach the lifeCycleOwner to the binding using:
override fun onActivityCreated(savedInstanceState: Bundle?) {
// Some more code....
binding.lifecycleOwner = activity
// Some more code....
I'm not sure how to remove the resource dependency from the view model without having it crash in runtime.
EDIT:
For clarification: The ObservableViewModel in my example is the very same one as the one found here:
https://developer.android.com/topic/libraries/data-binding/architecture
Used to perform notifyChange.
The issue here is the code is trying to call textView.setText(0) which results in an error since there is no string resource with id 0x0. This is happening because getTextViewTitle() return an Int and the view binding functionality will make it default as 0 (when initializing).
https://developer.android.com/topic/libraries/data-binding/expressions#property_reference
From the docs
Avoiding null pointer exceptions
Generated data binding code automatically checks for null values and avoid null pointer exceptions. For example, in the expression @{user.name}, if user is null, user.name is assigned its default value of null. If you reference user.age, where age is of type int, then data binding uses the default value of 0.
Maybe something like this could work,
android:text='@{viewModel.textViewTitle == 0 ? "" : @string{viewModel.textViewTitle}}'
or
android:text='@{viewModel.textViewTitle, default=""}'
To solve this simply make a context available in the view, so that you can call context.getString(...) in your view.
<data>
<import type="androidx.core.content.ContextCompat" />
<variable
name="viewModel"
type="my.application.path.SomeViewModel" />
</data>
<....
....
android:text="@{context.getString(viewModel.textResource)}"
...
/>
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