Reputation: 22038
With Android databinding, I can do the following:
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onClick()}" />
My ViewModel
does not have to implement OnClickListener
, but just have a method:
public void onClick() {
}
What it passes to the onClick
attribute in the Xml looks like a lambda to me.
How can I do this with my own BindingAdapters
?
What I want:
Let's assume I want to bind touch events and I want to pass the MotionEvent
, I would imagine this to look in the Xml like this:
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:onTouch="(view, event) -> viewModel.onTouch(event)" />
and the BindingAdapter
something like:
@BindingAdapter("onTouch")
public static void onTouch(View view, ??? lambda) {
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(final View v, final MotionEvent event) {
return lambda(event);
}
});
}
I do not want my ViewModel to implement OnTouchListener
and bind it like:
@BindingAdapter()
public static void onTouch(View view, View.OnTouchListener onTouchListener) {
view.setOnTouchListener(onTouchListener);
}
and I do not want bind the touch event directly to my ViewModel
like:
@BindingAdapter()
public static void onTouch(final View view, final MyViewModel myViewModel) {
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(final View v, final MotionEvent event) {
return myViewModel.onTouch(view, myViewModel);
}
});
}
Is this possible with Databinding?
Upvotes: 6
Views: 2891
Reputation: 2516
As you want it to work like the built-in onClick, let's take a look at the official binding adapter for onClick attribute:
@BindingAdapter({"android:onClick", "android:clickable"})
public static void setOnClick(View view, View.OnClickListener clickListener, boolean clickable) {
view.setOnClickListener(clickListener);
view.setClickable(clickable);
}
As you can see, the binding adapter doesn't take a lambda as argument, it takes a listener. But you can pass a lambda to it in xml (since it is a single method interface)
So here is the binding adapter you need: (I chose another attribute name, since onTouch is already used by framework)
@BindingAdapter("onImageTouch")
public static void onImageTouch(View view, View.OnTouchListener onTouchListener) {
view.setOnTouchListener(onTouchListener);
}
This is how you use it in xml:
app:onImageTouch="@{(view, event) -> viewModel.onImageTouch(event)}"
And here is the corresponding method in viewmodel:
public boolean onImageTouch(MotionEvent event){
Timber.e("OnImageTouch is called");
return true;
}
(It does nothing, but notice that it should return a boolean because onTouch callback returns a boolean.)
You don't need to implement OnTouchListener in your viewmodel or activity. This works very similar to the example with onClick that you gave in the beginning.
Let me note that there is already a built-in binding adapter for onTouch attribute, but I guess it was just an example and you in fact want to learn how to make custom ones. But be careful not to choose attribute names that already exist in the framework, to avoid any clashes.
Upvotes: 10
Reputation: 559
You can use a lambda with your own method if it returns a boolean
, and use the attribute app:onTouchListener
. No custom BindingAdapter
needed.
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:onTouchListener="(view, event) -> viewModel.onTouch(event)" />
android:onTouch
also works but you get an Unknown attribute
lint warning
Upvotes: 3
Reputation: 3818
It is possible to pass data to your view model however you also have to pass view along with data. so in your case. what you need to do is as follow.
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onClick(view.someData)}" />
And in your View Model.
public void onClick(View v,String someData) {
}
Upvotes: -1