使用DataBinding库绑定事件

55

我正在使用随Android M一起发布的DataBinding Library将XML中的事件与视图绑定。 我正在遵循Android Developers中的示例,逐步实现。 对于像可见性、文本这样的视图属性,它可以正常工作,但如果我尝试绑定onclick,则不会按预期工作。 这是我到目前为止尝试过的示例代码:

 <data>
    <import type="android.view.View"/>
    <variable name="user" type="com.example.databinding.User"/>
    <variable name="handlers" type="com.example.databinding.MyHandlers"/>
</data>

 <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user.firstName}"
    android:visibility="@{user.isFriend ? View.VISIBLE : View.GONE}" />
 <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Click Me"
    android:id="@+id/button"
    android:layout_gravity="left"
    android:onClick="@{handlers.onClickFriend}"/>

主活动:

  public class MainActivity extends AppCompatActivity {

  User user;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding binding = 
    DataBindingUtil.setContentView(this,R.layout.activity_main);
    user = new User("Pankaj","Kumar",true,true);
    binding.setUser(user);
   }
 }

MyHandlers:

public class MyHandlers {
public void onClickFriend(View view){
    Log.i(MyHandlers.class.getSimpleName(),"Now Friend");
}

public void onClickEnemy(View view){
    Log.i(MyHandlers.class.getSimpleName(),"Now Enemy");
  }
}

我只写了必须的代码来提高可读性。有人能帮我吗。

9个回答

107

我认为你还需要绑定 handlers,也许可以在 onCreate 中进行类似这样的操作:

MyHandlers handlers = new MyHandlers();
binding.setHandlers(handlers);

13
没错,如果您将自己的Activity类用作处理程序,那么您只需要执行以下操作:binding.setHandlers(this); - dorsz
如果您有许多相同的操作,可以使用此库来简化操作 - https://github.com/drstranges/ActionHandler - Roman_D
2
@dorsz +100 给你!我找了好几个小时才找到这个! - kirtan403
2
太好了!我希望文档能更好地概述这个问题!感谢您提供如此好的答案! - BlackHatSamurai
好的,非常感谢。 - Pankaj Kant Patel
显示剩余3条评论

63

多种设置点击事件的方式

  1. 将处理程序传递给绑定。

    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); Handler handler = new Handler(); binding.setHandler(handler);

  2. 设置点击事件(使用以下任何一种)

    android:onClick="@{handler::onClickMethodReference}"

或者

android:onClick="@{handler.onClickMethodReference}"

android:onClick="@{() -> handler.onClickLamda()}"

或者

android:onClick="@{(v) -> handler.onClickLamdaWithView(v)}"
android:onClick="@{() -> handler.onClickLamdaWithView(model)}"

请查看Handler类以了解相关信息。

public class Handler {
    public void onClickMethodReference(View view) {
        //
    }
    public void onClickLamda() {
        //
    }
    public void onClickLamdaWithView(View view) {
        //
    }
    public void onClickLamdaWithObject(Model model) {
        //
    }
}

请注意:

  • 当您的参数与属性onClick相同时,可以使用方法引用(::)
  • 您可以像onClickLamdaWithObject示例那样传递任何对象。
  • 如果您需要传递View对象,则只需使用(v)->表达式。

进一步阅读

https://developer.android.com/topic/libraries/data-binding/expressions


这是一个很好的答案。请注意,仅带有箭头符号的底部三个是截至2019年3月让您能够从xml中ctrl-jump到处理程序代码的唯一选项。 - Gober

16

在你的 XML 中使用这种格式:

android:onClick="@{handlers::onClickFriend}"

注意::的使用,在xml编辑器中不用担心红线,因为当前这是Android Studio xml编辑器的一个bug

这里的handlers是来自数据标签的变量:

<data>
    <variable name="handlers" type="com.example.databinding.MyHandlers"/>
</data>

并且onClickFriend是您的方法:

public class MyHandlers {
    public void onClickFriend(View view) {
        Log.i(MyHandlers.class.getSimpleName(),"Now Friend");
    }
}

新增

要在xml中处理onLongClick,请添加以下内容:

android:onLongClick="@{handlers::onLongClickFriend}"
请在您的ViewModel类中添加onLongClickFriend方法:
public class MyHandlers {
    public boolean onLongClickFriend(View view) {
        Log.i(MyHandlers.class.getSimpleName(),"Long clicked Friend");
        return true;
    }
}

已添加

如果您需要显示toast消息,可以使用接口(更好的变体),或在构造函数中传递contextMyHandlers类中:

public class MyHandlers {
    public boolean onLongClickFriend(View view) {
        Toast.makeText(view.getContext(), "On Long Click Listener", Toast.LENGTH_SHORT).show();
        return true;
    }
}

1
你好,你知道如何处理 onlongclick 事件吗? - Mussa
无法运行。无法编译该项目。Unknown attribute android:onLongClick. - Mussa
1
很抱歉,请尝试在方法中将 void 更改为 boolean,像这样:public boolean onLongClickFriend(View view),并在结尾添加 return true; - walkmn
@walkmn 如何在 onClickFriend 方法中显示 Toast 或传递上下文? - YLS
1
我们可以使用 view.getContext() 方法在每个方法中获取 context,所以不需要通过构造函数传递它。 - cgb_pandey

4

您应该这样做

android:onClick="@{() -> handlers.onClickFriend()}"

1
你的意思是 android:onClick="@{(view) -> handlers.onClickFriend}",否则这个 lambda 表达式将无法匹配 onClick 事件的签名。 - AymenDaoudi
2
@AymenDaoudi 更正一下,应该是 android:onClick="@{(view) -> handlers.onClickFriend()}" - Oleksii Masnyi
唯一对我有效的方法来自@AlekseyMasny。谢谢! - Alexander Haroldo da Rocha
2
() -> function() 语法是完全有效的。请查看文档:https://developer.android.com/topic/libraries/data-binding/expressions#listener_bindings - dominicoder

4

如果你要使用你的活动, 最好替换掉自动绑定的context对象,否则你会浪费空间。

为了在需要时在绑定表达式中使用, 生成了一个名为context的特殊变量。 context的值是根View的getContext()中的Context。 如果有该名称的显式变量声明, 将覆盖context变量。

binding.setContext(this);

并且

<variable name="context" type="com.example.MyActivity"/>

请注意,如果您只使用纯字符串 onClick="someFunc" ,那么这根本不是数据绑定功能。这是一种较旧的功能,它使用一些反射来查找上下文中的方法。


3
我是一名有用的助手,可以为您翻译文本。
我发布这篇文章只是为了介绍两种实现方式。 1.通过监听器绑定 2.通过方法引用
布局:
<layout...>
<data>

        <variable
            name="handlers"
            type="com.example.databinding.MyPresenter" />
        <variable name="user" type="com.example.databinding.User"/>
</data>

<LinearLayout
  android:layout_width="wrap_content"
  android:layout_height="wrap_content">

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            android:text="Using Listener Binding"
            android:onClick="@{() -> handlers.onLisClick(user)}"/>

     <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            android:text="Using Method Ref"
            android:onClick="@{handlers::onButtonClicked}"/>            

</LinearLayout>
</layout>

活动:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding =
                DataBindingUtil.setContentView(this, R.layout.activity_main);
        MyPresenter presenter = new MyPresenter();
        User user = new User("Alex","RJ")
        binding.setUser(user);
        binding.setHandlers(presenter);
    }

我的演示者:

public class MyPresenter{

//using listener binding
public void onLisClick(User user){
//do something..
}


//using method reference
public void onButtonClicked(View view){

// do something
}

}

注意:
1.使用方法引用时,方法签名应与您为任何其他onClick方法编写的方法相同,即公共和View作为参数。
2.使用侦听器绑定时,您有利于直接传递对象(如果需要)并执行任何操作。

2

不必创建独立的MyHandlers类并调用setHandlers来处理android:onClick。您可以直接在MainActivity中使用方法:public void onClickFriend(View view)public void onClickEnemy(View view)

活动视图:

public class MainActivity extends AppCompatActivity {
    User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding =
                DataBindingUtil.setContentView(this, R.layout.activity_main);
        user = new User("Pankaj", "Kumar", true, true);
        binding.setUser(user);
    }

    public void onClickFriend(View view) {
        Log.i(MyHandlers.class.getSimpleName(), "Now Friend");
    }

    public void onClickEnemy(View view) {
        Log.i(MyHandlers.class.getSimpleName(), "Now Enemy");
    }
}

一个布局:
<data>
    <import type="android.view.View"/>
    <variable name="user" type="com.example.databinding.User"/>
</data>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user.firstName}"
    android:visibility="@{user.isFriend ? View.VISIBLE : View.GONE}" />

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Click Me"
    android:id="@+id/button"
    android:layout_gravity="left"
    android:onClick="@{onClickFriend}"/>

请看使用数据绑定库实现MVVM模式的示例:http://cases.azoft.com/mvvm-android-data-binding


4
android:onClick="@{onClickFriend}" 没有效果,但是 android:onClick="onClickFriend" 像往常一样正常工作。 - Bharatesh
3
几乎正确。您可以将自己的活动作为处理程序使用,并且布局中的onClick类似于android:onClick =“@{handlers :: onClickFriend}”,但是您需要在活动中绑定处理程序,就像这样:binding.setHandlers(this); - dorsz
1
你好,你知道如何处理 onlongclick 事件吗? - Mussa

1
我发布这篇文章是因为我遇到了另一种情况。如果你有两个活动引用布局文件,其中一个定义了onclick事件而另一个没有,你会收到相同的警告,并且在定义事件的活动中会出现奇怪的情况。
为了检查这个问题,我建议右键单击布局名称并按下“查找引用”来查找布局文件的用途。别忘了在此之后重新构建应用程序。

0
对于那些在处理长按事件方面有困难的人: 首先,在布局中创建一个带有ID的视图。
<data>
        <variable
            name="tempDesc"
            type="String" />
        <variable
            name="activity"
            type="com.naruto.trangoapp.MainActivity" />
</data>

<TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{(view) -> activity.changeDescText(view)}"
            android:text="@{tempDesc}" />

在你的onCreate方法中,使用视图的ID名称来设置任何侦听器:
binding.textView.setOnLongClickListener(this::onLongClick);

然后只需创建一个同名的布尔方法,例如onLongClick,如下所示:

private boolean onLongClick(View l) {
        Toast.makeText(this, "Description", Toast.LENGTH_SHORT).show();
        return true;
    }

就是这样了!

注意:您还可以通过在onCreate方法中将上下文设置为活动变量,将任何方法设置为布局中的任何视图:

binding.setActivity(this);

然后,在您的布局中定义和传递方法名称与视图,以便在Activity文件中使用它。就像我使用了一个名为“activity”的变量名来定义一个changeDescText(v)方法,用于我的TextView。 以下是我在Activity文件中的方法:

public void changeDescText(View view) {
        binding.setTempDesc("Description Changed");
    }

不要在数据绑定中持有活动引用,否则会导致内存泄漏。 - TheLibrarian
@TheLibrarian 你有相关的来源吗? - Florian Walther

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接