Reputation: 17654
When using Android two-way databinding, do I have to use static BindingAdapter
s on the View
or is it somehow possible to simply use observable instance fields? In the documentation I always see observable fields only on the ViewModel
s, not on the View
. I tried implementing observable fields on my View
with
var myValue: String = ""
@Bindable get(): String {
return field
}
set(value: String) {
field=value
setText(value)
notifyPropertyChanged(BR.myValue) // my View implements the Observable interface
}
but when I compile this (with ./gradlew assembleDebug --stacktrace
to get the details), it fails with
ERROR: Cannot find a getter for <com.example.test.MyAutoCompleteTextView app:myValue>
that accepts parameter type 'java.lang.String'
If a binding adapter provides the getter, check that the adapter is annotated correctly
and that the parameter type matches.
So, isn't it possible to use observable fields on the View
side of two-way databinding like it's possible on the ViewModel
? The reason I want to use observable fields instead of static BindingAdapter
s is that my View
has some more complex logic/state than I can handle in the BindingAdapter
(well, from the static BindingAdapter
I could just call through to myViewInstance.myValue
, but somehow that feels wrong to me)
Update
I built a minimum (not) working example, available on Github By default it uses one-way binding, which works fine. Changing
app:myValue="@{viewModel.realValue}"
to
app:myValue="@={viewModel.realValue}"
in activity_main.xml
will lead to not very informative compilation errors. Use ./gradlew assembleDebug --stacktrace
to get a long output which includes
ERROR: Cannot find a getter for
<com.example.test.MyAutoCompleteTextView app:myValue>
that accepts parameter type 'java.lang.String'
Can anyone have a look at this and let me know what I am doing wrong?
Upvotes: 2
Views: 1397
Reputation: 21
When using Android two-way databinding, do I have to use static BindingAdapters on the View or is it somehow possible to simply use observable instance fields?
I think it's pretty much possible for you to do that with the Observable instance fields. However if this logic is being reused somewhere else as well, I would personally prefer to use BindingAdapters
.
In your Observable class, modify your code to this:
@get:Bindable
var myValue: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.myValue) // my View implements the Observable interface
}
I checked out your repo and realized that you are using MutableLiveData now, so you don't need the above code in that case. I also couldn't find any BindingAdapter there. Make sure your code BindingAdapter looks somewhat like this:
@BindingAdapter("myValue")
@JvmStatic
fun TextView.setMyValue(realValue: String) {
val finalValue : String = realValue.dataManipulation() // Do your data manipulation here
setText(finalValue) // Make sure it's a string
}
And then finally your TextView in XML should look like this:
<com.example.test.MyAutoCompleteTextView
...
app:myValue="@{viewModel.realValue}"
... />
Let me know if this worked.
EDIT
So what we are looking for is basically Bindable variables in BaseObservable class. Let's focus on three things: your XML, your activity/fragment and your BindingModel class.
Your BindingModel class basically extends your BaseObservable class.
class BindingModel: BaseObservable(){
@get:Bindable
var myValue: String = ""
set(value) {
var finalValue = manipulateData(value) //Write your logic here
field = finalValue
notifyPropertyChanged(BR.myValue) // my View implements the Observable interface
}
...
}
In your Activity/Fragment, create a variable of type BindingModel
. Observe your LiveData and update the variable.
class MainActivity : AppCompatActivity() {
...
val model = BindingModel()
...
override fun onCreate(savedInstanceState: Bundle?) {
...
binding.lifecycleOwner = this // Specify the current activity as the lifecycle owner.
binding.model = model
viewModel.realValue.observe(this, Observer {
model.myValue = it
})
} // End of onCreate()
...
} // End of Activity code
Moving on to your XML, you need to declare the model
data binding variable there.
...
<data>
...
<variable
name="model" //Make sure the name is same
type="com.example.test.<path-to-your-file>.BindingModel" />
</data>
...
<com.example.test.MyAutoCompleteTextView
...
android:text="@{model.realValue}" //Don't forget this
... />
And voila! It should work. Let me know if it doesn't.
Upvotes: 1