Android Data Binding 初探

本文介绍了Android DataBinding的基本使用方法,包括如何设置启用DataBinding、定义数据类、在XML布局文件中绑定数据以及设置视图监听等。此外,还探讨了如何利用数据绑定更新UI,减少代码量并提升开发效率。

Android Data Binding 初探
Android Data Binding 进阶
Android Data Binding 配合BaseAdapter

关于 Data Binding

Data Binding 是实现 MVVM 开发模式的一种框架。

自从遇到了 Data Binding,老实说,我都不太想用 MVP 开发模式了。

在 Android 中,要实现 MVP 开发模式,得定义很多 View Interface,还要定义很多 Presenter 类,职责分离了,程序的耦合性也优于 MVC 开发模式,但是开发起来还是略显繁琐。

不过哦,MVP 开发模式在 Android 中还是最常见,最好用的一种开发模式,毕竟 MVVM 开发模式在 Android 中推出来没那么久 (2015 年 Google IO大会提出),还没那么普遍,估计还有不少地方有待提高。在Android Studio 中提示也不是很完善,例如,在 xm 布局文件中,定义 View 监听方法不是很智能,没有提示。

虽说在2015年推出来的,但我现在才知道它的好,真是“相识恨晚”。这么好的一个“偷懒”神器,竟然被我给无视了。再说一下我使用 DataBinding 的心得,它不仅是“偷懒”神器,而且性能也没有丝毫减少(运行期间几乎0反射),而且还可以减少很多设置或更新UI的代码(通过数据绑定View)。当然还有很多很多其他的优点,自己慢慢探索。

下面,我给大家演示一下怎么使用。

基本使用

  • 使用工具:Android Studio 2.3.1 (好像2.0.0以上才支持 Data Binding)
设置可以使用 Data Binding

在 App module 的 build.gradle 文件添加:

android {
    ...
    dataBinding {
        enabled = true
    }
}
定义数据类

普通的数据类,如Person

public class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}
在布局文件绑定数据

xml 布局文件解释

<?xml version="1.0" encoding="utf-8"?>
<!-- 使用layout标签作为根布局 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 使用data标签绑定数据 -->
    <data>
        <!-- 使用variable标签定义数据名和数据类型 -->
        <variable
            name="person"
            type="com.johan.study.Person" />
    </data>

    <!-- 我们平常使用的根布局放到这里 -->
    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        tools:context="com.johan.study.MainActivity">

        <!-- 通过@{}方式访问绑定的数据 -->
        <TextView
            android:id="@+id/name_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="16dp"
            android:textColor="@android:color/holo_blue_light"
            android:text="@{person.name}"
            />

    </LinearLayout>

</layout>
在 Activity 设置 xml 布局文件的数据

我们在 xml 布局文件中绑定了一个 Person 类型数据,但是还没有值的,我们需要在 Activity 中赋值

public class MainActivity extends AppCompatActivity {

    private Person person = new Person("Johan", 25);
    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 不再使用setContentView直接设置布局文件
        // 使用DataBindingUtil的setContentView方法设置布局文件,同时返回一个ViewDataBinding对象
        // ViewDataBinding对象是编译器自动帮我们生成的,类名是根据我们的布局文件名字命名的
        // 如 R.layout.activity_main,类名就是 ActivityMainBinding,应该很容易看懂
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        // 赋值给 xml 的 person,只有赋值了,xml 才能得到值,这一步很容易忘记
        binding.setPerson(person);

        // 我们不再使用 findViewById 来说去View了,编译器会自动帮我们生成 xml 布局中有id属性的View
        // View 的名字也是根据定义 id 属性的值来决定的
        // id="@+id/name_view",View的名字叫做 nameView,很容易理解吧
        TextView nameView = binding.nameView;

    }
}

在这里我们可以看到 Data Binding 的一个优点,即使我们不使用 Butterknife 等 View 注解框架,也不再使用 findViewById 方法来获取View了。

设置 View 的监听

当然,还可以在 xml 设置各种监听,在传统的 xml 布局文件中,View 好像只能定义 onClick 事件。Data Binding 支持定义各种事件,如 onLongClick,onFocusChange,onTextChanged 等事件。

我们演示一下怎么在 xml 布局文件中,添加 EditText 的 onTextChanged 事件。

(1)首先,我们定义处理事件的方法:

public class NameHandler {
    // 处理事件的方法,名字和参数一定是一样的,这里是TextWatcher接口的onTextChanged方法
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // 更新数据
        person.setName(s.toString());
        // 还得加上这个,才能更新UI
        binding.setPerson(person);
    }
}

可能你会说,还得加上 binding 的 setPerson 方法才能更新 View 此不是很麻烦吗?我待会再说这个,不用急哈。

(2) xml 布局文件定义 EditText,定义绑定 onTextChanged 事件,并设置事件的处理方法。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 数据绑定 -->
    <data>
        <variable
            name="person"
            type="com.johan.study.Person" />
        <!-- 定义处理事件的类 -->
        <variable
            name="handler"
            type="com.johan.study.MainActivity.NameHandler" />
    </data>

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        tools:context="com.johan.study.MainActivity">

        <!-- 定义onTextChanged事件,添加事件的处理方法 handler.onTextChanged -->
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:hint="输入姓名"
            android:inputType="text"
            android:textSize="16dp"
            android:onTextChanged="@{handler.onTextChanged}"
            />

        <TextView
            android:id="@+id/name_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="16dp"
            android:textColor="@android:color/holo_blue_light"
            android:text="@{person.name}"
            />

    </LinearLayout>

</layout>

(3)别忘了,由于我们在 xml 布局文件中,定义了事件的处理类,但是我们还没有给赋值给handler,我们需要在 Activity 中赋值。

public class MainActivity extends AppCompatActivity {

    private Person person = new Person("Johan", 25);
    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setPerson(person);
        TextView nameView = binding.nameView;
        // 赋值给 xml 布局文件的 handler
        binding.setHandler(new NameHandler());
        // 注意哦,由于我们的 EditText 并没有设置 id 属性,是取不出来的
    }

    public class NameHandler {
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            person.setName(s.toString());
            binding.setPerson(person);
        }
    }

}
数据绑定

我们上面的例子中,虽然更新了 person 的 name 属性值,但是不会立刻更新 View,要 binding 重新赋值才能更新 View。这是由于我们的数据还没有完全绑定。

我们要在数据类 Person 做点手脚:

public class Person extends BaseObservable {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }

}

Person 数据类继承了 BaseObservable 数据观察类,在各个属性的 get 方法添加 Bindable 注解,在各个set 方法中,设置完值后,通知更新 UI 。

这样,我们就不用,每次更新数据类,还得更新 UI 了。

这适用于数据类,粒度相对有点大,而且所有数据类要得继承 BaseObservable,get和set方法还是略显繁琐,所以 Android 的 Data Binding 还提供了另一种方式来显示数据绑定View,那就是ObservableField 作为字段类型。

我们定义一个 Person 的额外信息类 PersonExtra:

// 不再继承 BaseObservable
public class PersonExtra {
    // 使用 ObservableField 作为字段类型
    public ObservableField<String> address = new ObservableField<>();
}

我们在 xml 布局文件再添加多一个View,定义 PersonExtra,并绑定数据:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="person"
            type="com.johan.study.Person" />
        <!-- 定义 PersonExtra -->
        <variable
            name="personExtra"
            type="com.johan.study.PersonExtra" />
        <variable
            name="handler"
            type="com.johan.study.MainActivity.NameHandler" />
    </data>

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        tools:context="com.johan.study.MainActivity">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:hint="输入姓名"
            android:inputType="text"
            android:textSize="16dp"
            android:onTextChanged="@{handler.onTextChanged}"
            />

        <TextView
            android:id="@+id/name_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="@{@dimen/space_1 + @dimen/space_2}"
            android:textSize="16dp"
            android:textColor="@android:color/holo_blue_light"
            android:text="@{person.name}"
            />

        <!-- 使用personExtra.address -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="16dp"
            android:textColor="@android:color/holo_orange_light"
            android:text="@{personExtra.address}"
            />

    </LinearLayout>

</layout>

布局文件和之前的一样,我们来看看Activity:

public class MainActivity extends AppCompatActivity {

    private Person person = new Person("Johan", 25);
    private PersonExtra personExtra = new PersonExtra();
    private ActivityMainBinding binding;
    private List<Person> personList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setPerson(person);
        // 赋值给 xml 定义的 personExtra
        binding.setPersonExtra(personExtra);
        TextView nameView = binding.nameView;
        binding.setHandler(new NameHandler());
    }

    public class NameHandler {
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            person.setName(s.toString());
            personExtra.address.set("广东省深圳市");
        }
    }

}

我们看看触发 onTextChanged 事件的方法,更新 address 属性的方式使用了 set 方法,我们 address 属性赋值之后,一样可以自动更新 UI,同样的效果。

基本的使用方法到这里,由于篇幅问题,下一篇我们来看看 Data Binding 的进阶。

这是本人初次使用 Data Binding 的一些总结, 如有错误,请不吝赐教,谢谢!!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值