以 lambda 作为参数的 BindingAdapter

asc*_*sco 6 java data-binding android android-databinding

通过 Android 数据绑定,我可以执行以下操作:

<View
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:onClick="@{() -> viewModel.onClick()}" />
Run Code Online (Sandbox Code Playgroud)

我的ViewModel不必实现OnClickListener,但只有一个方法:

public void onClick() {

}
Run Code Online (Sandbox Code Playgroud)

它传递给onClickXml 中的属性的内容在我看来就像是 lambda。

我怎样才能用自己的来做到这一点BindingAdapters

我想要什么: 假设我想绑定触摸事件并且我想传递MotionEvent,我想在 Xml 中看起来像这样:

<View
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:onTouch="(view, event) -> viewModel.onTouch(event)" />
Run Code Online (Sandbox Code Playgroud)

以及BindingAdapter类似的东西:

@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);
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

我不希望我的 ViewModel 实现OnTouchListener并绑定它,如下所示:

@BindingAdapter()
public  static  void onTouch(View view, View.OnTouchListener onTouchListener) {
    view.setOnTouchListener(onTouchListener);
}
Run Code Online (Sandbox Code Playgroud)

我不想触摸事件直接绑定到我的ViewModel喜欢:

@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);
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

这可以通过数据绑定实现吗?

Oya*_*nlı 9

由于您希望它像内置的 onClick 一样工作,让我们看一下onClick 属性的官方绑定适配器:

@BindingAdapter({"android:onClick", "android:clickable"})
public static void setOnClick(View view, View.OnClickListener clickListener, boolean clickable) {
    view.setOnClickListener(clickListener);
    view.setClickable(clickable);
}
Run Code Online (Sandbox Code Playgroud)

如您所见,绑定适配器不采用 lambda 作为参数,而是采用侦听器。但是您可以在 xml 中将 lambda 传递给它(因为它是单个方法接口)

所以这是您需要的绑定适配器:(我选择了另一个属性名称,因为 onTouch 已被框架使用)

@BindingAdapter("onImageTouch")
public static void onImageTouch(View view, View.OnTouchListener onTouchListener) {
    view.setOnTouchListener(onTouchListener);
}
Run Code Online (Sandbox Code Playgroud)

这是在 xml 中使用它的方式:

app:onImageTouch="@{(view, event) -> viewModel.onImageTouch(event)}"
Run Code Online (Sandbox Code Playgroud)

这是 viewmodel 中相应的方法:

public boolean onImageTouch(MotionEvent event){
    Timber.e("OnImageTouch is called");
    return true;
}
Run Code Online (Sandbox Code Playgroud)

(它什么也不做,但请注意它应该返回一个布尔值,因为 onTouch 回调返回一个布尔值。)

您不需要在视图模型或活动中实现 OnTouchListener。这与您在开始时给出的 onClick 示例非常相似。

让我注意一下,已经有一个针对 onTouch 属性的内置绑定适配器,但我想这只是一个示例,您实际上想学习如何制作自定义适配器。但请注意不要选择框架中已存在的属性名称,以避免任何冲突。