Android MVVM架构模式 详解和综合运用(二)

绑定事件处理程序

DataBinding的一个关键就是View和ViewModel可以关联在一起,实现双向交互。例如在View层触发了一个事件,例如点击onClick,DataBinding可以在layout布局文件中的属性@{ }表达式中来处理这个事件,而不用自己去设置监听器来处理事件。例如,View.onClickListener中有一个方法是onClick,所以在view元素节点上有对应的属性android:onClick。

在DataBinding支持库中,有两种方式去处理事件。
1.方法引用:在DataBinding表达式中,你可以直接调用声明的对象的方法,但是这个方法的方法签名也就是参数列表必须和对应的监听器内包含的方法的方法签名一样。当表达式判断出属性值是对象的方法引用时,DataBinding包装这个对象和方法到一个新的监听器listener对象中,并在当前view中设置这个listener。如果表达式中判断是方法引用是null,那么DataBinding并不会创建一个监听器,而是设置一个null监听器。
2.监听器绑定:这个表达式使用的是lambda表达式,当事件发生时,这个表达式就会被DataBinding计算。DataBinding总会创建一个新的监听器,并在当前view设置这个监听器。当View的事件分发的时候,这个监听器就会计算lambda表达式,也就是调用lambda表达式。

这两种方式的区别就是方法引用是在数据绑定的时候就创建实际的监听器并绑定到View,而监听器绑定是在事件被触发的时候,实时计算表达式然后去处理事件。监听器绑定方法在事件发生时才运算Binding表达式,所以可以运行任意数据绑定表达式。在方法引用中,方法参数列表必须和监听器中方法的参数列表一致。而在监听器绑定方法中并不要求,而要求方法的返回值必须和监听器中方法的返回值类型相同。这两种方法的效果是一样的。下面举例说明:

首先在MainActivity中声明一个方法onClick,必须带有View参数:

    @Override
    protected void initView() {
        binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
        binding.setMainActivity(this);
        binding.setMainHandler(new MainHandler());
    }

    public void onClick(View view){
        Toast.makeText(this, "Click Me!!!", Toast.LENGTH_SHORT).show();
        people.setFirstName("222222");
    }

然后在layout文件中声明mainActivity变量,在上面的setMainActivity中初始化:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="mainActivity"
            type="view.MainActivity"/>

        <variable
            name="mainHandler"
            type="module.MainHandler"/>
    </data>

   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

     <Button
        android:layout_width="100dp"
        android:layout_height="60dp"
        android:background="@drawable/bg_main_btn"
        android:text="Click me"
        android:onClick="@{mainActivity::onClick}"/>

   </LinearLayout>  
</layout>

这样就点击button按钮就可以直接调用MainActivity中的onClick方法了。

下面举例监听器绑定的方法。我们可以再分离activity中的代码,我们可以创建一个model事件处理模块MainHandler,例如:

public class MainHandler {

    public void showToast(Context context, String str){
        Toast.makeText(context,str,Toast.LENGTH_SHORT).show();
    }
}

layout中button修改为如下:

        <Button
            android:layout_marginTop="10dp"
            android:layout_width="100dp"
            android:layout_height="60dp"
            android:background="@drawable/bg_main_btn"
            android:text="Click me"
            android:onClick='@{()->mainHandler.showToast(context,"click two")}'/>
            //或者android:onClick='@{(view)->mainHandler.showToast(context,"click two")}'

这里使用了 lambda表达式( )->,其中( )可以传递view参数,也可以不传递。监听器绑定方法提供了两种选择,一种是忽略所有参数也就是不传递参数,另一种是显式传递所有参数。这个表达式等价于下面这个例子:

new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //其中这个参数v就是传到lambda表达式中的圆括号中( )
            }
        }

Android Studio会自动帮我们生成lambda表达式:
这里写图片描述

lambda表达式支持多 个参数传递 ,例如:

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}

<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

当监听器中的方法返回值不是void类型时,lambda表达式中的调用的方法也必须返回相同类型的值。例如,当监听Long Click事件时,你的表达式必须返回boolean类型,:

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}

 android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果表达式中需要使用谓词运算符,可以使用void作为一个符号:

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

Data对象以及新增数据类型

当使用传统的数据类型来创建JavaBean对象时,修改JavaBean对象并不会自动刷新UI。但是DataBinding库为我们提供了三种不同的数据更改通知机制来使用不同的数据类型创建JavaBean,在数据改变的时候会自动刷新UI,分别是Observable objects, observable fields, and observable collections。说到底就是观察者模式的实现了,当被观察者也就是数据改变的时候,就通知观察者即UI进行刷新。

  • Observable Objects
    DataBinding库提供了一个BaseObservable基类,这个基类实现了observable接口,然后会在对象的所有属性上绑定一个监听器去监听属性的变化,然后发出通知。
    observable接口里面包含了添加和移除监听器的方法,但是发出通知的行为则由开发者来实现。在BaseObservable基类中已经帮我们实现了注册监听器和移除监听器的方法,但是继承这个基类的子类则需要在属性改变的时候负责通知,可以在getter方法中使用@Bindable注解 和 在setter方法中使用notifyPropertyChanged方法发出通知。
  • private static class User extends BaseObservable {
       private String firstName;
       private String lastName;
       @Bindable
       public String getFirstName() {
           return this.firstName;
       }
       @Bindable
       public String getLastName() {
           return this.lastName;
       }
       public void setFirstName(String firstName) {
           this.firstName = firstName;
           notifyPropertyChanged(BR.firstName);
       }
       public void setLastName(String lastName) {
           this.lastName = lastName;
           notifyPropertyChanged(BR.lastName);
       }
    }

    被@Bindable注解的属性会在编译的时候在BR class中生成一个变量条目。这个BR class自动生成在dataBinding的父目录的同级目录中,里面主要存储了我们在layout布局<r;data>中声明的变量名和被@Bindable注解 的属性名。
    这里写图片描述

  • ObservableFields
    上面在BaseObservable子类中使用java引用类型或基本数据类型时都需要加@Bindable注解和notifyPropertyChanged方法来达到数据关联UI的效果,但是DataBinding提供了一些封装类来帮忙简化这些操作,例如ObservableField, ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable。从名字上可以看出,对应java中的数据类型,如boolean , byte等待。其中ObservableFields是只有一个字段的独立对象,这个字段类型时使用了T泛型,所以可以在初始化ObservableFields对象时指定数据类型。
  • private static class User {
       public final ObservableField<String> firstName =
           new ObservableField<>();
       public final ObservableField<String> lastName =
           new ObservableField<>();
       public final ObservableInt age = new ObservableInt();
    }

    创建了新对象后,就可以使用set和get方法获取属性值了:

    user.firstName.set("Google");
    int age = user.age.get();

  • Observable Collections集合
    DataBinding库为我们提供了几个集合包装类来帮助我们保存数据,例如:ObservableArrayMap,ObservableArrayList等。
  • ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
    user.put("firstName", "Google");
    user.put("lastName", "Inc.");
    user.put("age", 17);

    在layout布局文件中,可以通过String key来访问map中的value:

    <data>
        <import type="android.databinding.ObservableMap"/>
        <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
    </data><TextView
       android:text='@{user["lastName"]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    <TextView
       android:text='@{String.valueOf(1 + (Integer)user["age"])}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

    ObservableArrayList适合key是int的list,也就是可以通过索引来访问:

    ObservableArrayList<Object> user = new ObservableArrayList<>();
    user.add("Google");
    user.add("Inc.");
    user.add(17);

    在layout布局文件中,可以通过索引来访问list中的值:

    <data>
        <import type="android.databinding.ObservableList"/>
        <import type="com.example.my.app.Fields"/>
        <variable name="user" type="ObservableList&lt;Object&gt;"/>
    </data><TextView
       android:text='@{user[Fields.LAST_NAME]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    <TextView
       android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

    View元素ID

    当在Layout布局文件中为view元素的android:id声明一个值时,DataBinding库会自动为这个view元素生成一个public final的变量。DataBinding通过遍历View视图树,然后提取每一个带有id值的view元素。这个机制比使用findViewById方法获取view快很多。例如:

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"
       android:id="@+id/firstName"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"
      android:id="@+id/lastName"/>
       </LinearLayout>
    </layout>

    在binding类中会生成变量:

    public final TextView firstName;
    public final TextView lastName;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值