天天看点

android中data binding的使用

Android Data Binding介绍

Data Binding是一个MVVM的架构框架,使用Data Binding对于我们开发应用有下面好处:

  • 可以直接在layout布局中的xml中绑定数据
  • 分离视图与业务逻辑
  • 适用于android 2.1以上的版本

搭建Data Binding环境

使用Data Binding需要下面条件:

  • gradle1.3 以上
  • android studio1.3以上(当然现在都到了2.2了^_^)

在项目的根目录下build.gradle添加dependenncy

android中data binding的使用

为用到 Data Binding 的模块添加插件,修改对应的 build.gradle

android中data binding的使用

需要注意的是,如果studio在2.0版本之后,可以直接在android中添加

dataBinding{
        enabled = true
    }
           

Data Binding入门

下面我通过一个简单的栗子,来实现业务和逻辑分离,并在不居中直接绑定数据。

新建一个java bean类

public class UserInfo {

    public String name;
    public String phone;
}
           

添加layout布局

在layout中绑定数据,我们需要在原本的layout的跟布局中使用layout,并制定其和那个类相关。

android中data binding的使用

另外我们也可以通过import来引入我们的user类对象

android中data binding的使用

注意:java.lang.* 包中的类会被自动导入,可以直接使用

使用DataBindingUtil绑定布局

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
        // 通过DataBindingUtil重新设置布局
        ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
        // 创建一个UserInfo,并绑定数据
        UserInfo userInfo = new UserInfo();
        userInfo.name = "haha";
        userInfo.phone = "1234567890";
        // setUser方法是根据在xml中通过variable配置的user自动生成的
        binding.setUser(userInfo);
    }
           

可以看到,系统自动为我们的activity_main布局生成了对应的ActivityMainBinding类,我们也可以自己定义该Binding类的类名

<data class="TestLayoutMain">
        <import type="com.example.myapplication.UserInfo" alias="AliasUserInfo"/>
        <variable
            name="user"
            type="AliasUserInfo"/>
</data>
           

此时生成的Binding类名获取方式如下:

布局中引用user类

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{user.name}"
         />
           

另外,我们也可以使用jdk中提供的数据类型来进行设置,比如:

<variable
    name="sex"
    type="String">
</variable>
           

为UserInfo对象添加sex属性

public String sex;
           

在布局中使用该属性

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{sex}"
        />
           

通过引用布局绑定数据

另外我们可以通过include标签来引用我们的指定的布局,并且传入user对象。

创建布局

user_layout.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.example.myapplication.UserInfo"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
            <TextView
                android:layout_width="match_parent" 
                android:layout_height="match_parent"
                android:text="@{user.phone}"
                />
    </LinearLayout>
</layout>
           

在主布局中引用该布局

接下来,我们需要在主布局中通过include标签,引用到该布局,并且传递我们的user对象。

<include layout="@layout/user_layout"
        bind:user="@{user}"
        />
           

使用类方法

我们也可以在布局中直接使用定义好的类里面的方法

public class DataUtils {

    public static String changeCharacter(String originalStr) {
        if (null != originalStr) {
            return originalStr.toUpperCase();
        }
        return null;
    }
}
           

在布局中引入当前类

<data>
        <import type="com.example.myapplication.UserInfo"/>
        <import type="com.example.myapplication.DataUtils"/>
        <variable
            name="user"
            type="UserInfo"/>
    </data>
           

直接调用该类的静态方法:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{DataUtils.changeCharacter(user.name)}"
         />
           

类型别名

如果我们在布局中导入了两个不同包下的UserInfo对象,此时我们到底该使用哪一个呢。其实我们可以通过alias别名来进行设置。

android中data binding的使用

三元运算符

<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#ff0000"
        android:text="@{user.name ?? user.phone}"
        />
           

上面@{user.name ?? user.phone}等价于:

user.name != null ? user.name : user.phone

使用资源数据

这里,我们新创建两个颜色资源:

<color name="red">#ff0000</color>
<color name="blue">#0000ff</color>
           

在布局中使用:

android中data binding的使用
<plurals name="numberOfSongsAvailable">
        <item quantity="one">One song found.</item>
        <item quantity="other">%d songs found.</item>
 </plurals>
           

在布局中使用:

android中data binding的使用

类型转换器

DataBinding也为我们提供了一个类型转换器,当xml中的类型,和需要显示的类型不同的时候,我们只需要在对应的实体类中添加一个@BindingConversion注解即可。

public class UserInfo {

    public String name;
    public String phone;
    public boolean mIshow;
    public String sex;

    @BindingConversion
    public static int convertToString(int color) {
        switch (color) {
            case Color.RED:
                return R.string.red;
            case Color.BLUE:
                return R.string.blue;
        }
        return R.string.app_name;
    }
}
           
android中data binding的使用

可以看到,上面我们在text中返回的是int类型,所以这里通过BindingConversion注解进行类型转换。

<variable
            name="time"
            type="java.util.Date"/>
<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{time}"
        />

@BindingConversion
public static String convertDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(date);
}
           

NullPointException的检测

在DataBinding中,会自动为我们避免出现空指针,比如:user.name和user.age,如果user是null,那么user.name会返回null,user.age是int类型,所以默认返回0

集合类型数据的引用

常用的集合:arrays、lists、sparse lists以及maps,为了简便都可以使用[]来访问

  • 在UserInfo中增加一个Map集合
public Map<String,Object> map = new ArrayMap<>();
//添加设置数据
userInfo.map.put("hello",);
userInfo.map.put("world","世界");
           
  • 在布局中使用
<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text='@{String.valueOf((Integer)user.map["hello"])}'
        />
<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text='@{user.map["world"]}'
 />
           

带Id的View

当我们在布局中为某一个控件增加id时候,DataBinding会自动生成该根据该id自动生成对应的实例,看下面栗子:

<TextView
        android:id="@+id/dataBindId"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
TestLayoutMain binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);
        binding.dataBindId.setText("hello world");
           

ViewStub支持

<ViewStub
        android:id="@+id/my_viewstub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/user_layout"
        />
TestLayoutMain binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);
binding.myViewstub.inflate();
           

ViewStub的使用

<ViewStub
        android:id="@+id/my_viewstub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/stub_layout"
        />

// 注意这里AS可能会标红,可能是支持不好,但是不影响编译和运行
binding.myViewstub.getViewStub().inflate();
           

事件绑定

同时我们可以通过DataBinding来绑定事件到指定的View,看下面的栗子:

<variable
            name="clickListener"
            type="android.view.View.OnClickListener" />
 <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{user.name}"
        android:onClick="@{clickListener}"
        />
           

我们知道,当在xml布局文件中定义的每一个variable对象,系统都会生成其对应的setXXX方法。

TestLayoutMain binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);

binding.setClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(), "setClickListener bind click ok", Toast.LENGTH_LONG).show();
            }
        });
           

监听数据变化

我们知道,很多时候我们的界面是需要实时改变数据的,当然DataBinding也为我们封装好了,看下面的栗子。

  • 创建POJO实例
public class UserInfo extends BaseObservable{

    public String name;

    public UserInfo(String name) {
        this.name = name;
    }

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

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

}
           

这里我们需要动态改变name属性

1. 实体类继承自BaseObservable类

2. 为需要动态改变的属性增加get和set方法

3. 在get方法添加@Bindable注解

4. 在set方法中调用notifyPropertyChanged(BR.xx);来通知数据变化,其中xx为需要改变的属性

  • 在布局中引用该实体类
<import type="com.detail.pojo.UserInfo" alias="ObserUser"/>
<variable
     name="obserUser"
     type="ObserUser"/>

<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{obserUser.name}"
        />
           
  • 在代码中动态改变属性内容
final com.detail.pojo.UserInfo user = new com.detail.pojo.UserInfo("hello");
        binding.setClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                user.setName("world");
            }
        });
        binding.setObserUser(user);
           

使用ObservableField监听数据变化

前面我们创建的实体类,需要为属性设置get和set方法并且需要在set方法中通过notifyPropertyChanged来通知数据变化。这样多少有些麻烦,好在系统为我们提供了一系列的ObservableField,如下:

ObservableField,ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,

ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableParcelable

通过这些封装ObservableField就无需提供get和set方法了。看下面栗子:

  • 创建POJO实体类
public class UserInfo extends BaseObservable{

    /**
     * ObservableField,ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,
     * ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableParcelable
     */

    public ObservableField<String> name = new ObservableField<>();
    public ObservableInt age = new ObservableInt();
    public ObservableBoolean isShow = new ObservableBoolean();

}
           
  • 创建布局,通过variable标签引用该POJO
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data class="CustomeLayout">
        <variable
            name="user"
            type="com.detail.pojo.UserInfo"/>
        <variable
            name="onClickListener"
            type="android.view.View.OnClickListener"/>
    </data>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{user.name}"
         />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{user.age}"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{user.isShow ? "show" : "donotshow"}'
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{user.name}"
        android:onClick="@{onClickListener}"
        />
</LinearLayout>
</layout>
           
  • 在代码中动态改变属性
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CustomeLayout binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.custom_main);

        final com.detail.pojo.UserInfo user = new UserInfo();
        user.age.set();
        user.name.set("hello world");
        user.isShow.set(true);

        binding.setUser(user);

        binding.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG,"age is :"+user.age.get()+"  name is :"+user.name.get()+"   isShow is :"+user.isShow.get());
                user.age.set();
                user.name.set("yes it changed");
                user.isShow.set(false);
            }
        });
    }
           

看到了吧,其实同样很简单,google工程师已经为我们封装的很完美了。

参考:

Android官方数据绑定框架DataBinding(一)

Android官方数据绑定框架DataBinding(二)

Android官方数据绑定框架DataBinding(三)