Android: Databinding笔记
关于databinding
databinding,即数据绑定。google帮助我们在android上实现mvvm的一个框架。具体的MVVM我理解不深,可以看下这篇文章:Android DataBinding:再见Presenter,你好ViewModel!。
不过从个人感觉上,databinding确实让代码简洁了很多,更新数据的工作也没有这么繁琐了。
下面是我的学习笔记。边学边记。遇到的错误也会记录下来。我没说到的知识点,可以带上梯子到Data Binding Library看。
配置
1,首先,Gradle版本需要1.5.0-alpha1以上。
2,如果Android Studio版本大于1.5,那只需要在对应的module的build.gradle中添加
android{
    ...
    dataBinding{
        enabled = true;
    }
}
3,如果Android Studio的版本小于1.5,大于1.3,在project中加入依赖:
classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.android.databinding:dataBinder:1.0-rc0'
修改对应module的build.gradle
apply plugin: 'com.android.databinding'
建立数据源
都说是数据绑定,我们首先需要一个java bean类作为数据源。
我们可以简单地建一个user类
public class User {
    private String userName;
    private int age;
    public User(String userName, int age){
        setUserName(userName);
        setAge(age);
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
实际上,DataBinding读写数据的时候,是调用get/set方法的。如果不定义变量,只写get/set方法也是可行的。
public class User {
    public String getUserName() {
        return "xiao xin";
    }
    public void setUserName(String userName) {
    }
    public int getAge() {
        return 33;
    }
    public void setAge(int age) {
    }
}
数据更新的时候,view同步更新。
1.让数据源的类继承BaseObservable类,并且在需要更新的时候调用notifyPropertyChanged()方法。
代码中的BR是databinding生成的对应数据源的静态变量。和R.id同理。
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);
   }
}
2.可以直接把变量定义为ObservableFields类型的变量,那样每次变量的值发生变化,view也会同步刷新。
private static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}
一个,特别的,Observable类型的集合 --> ObservableArrayMap
定义和普通的map差不多
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
在layout中使用:
<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap<String, Object>"/>
</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"/>
布局
用了Data Binding的布局文件布局就要变一变了。根元素是一个layout节点。layout节点下面是data节点和原来布局文件的根节点。
格式如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
    </data>
    
    <!--用DataBinding之前的根节点-->
    <LinearLayout>
    ...
    </LinearLayout>
</layout>
如果要使用user类中的数据,可以这样写
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
 
        <!--定义了一个User类的变量-->
        <variable
            name="user"
            type="com.test.db.User" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!--通过@{user.userName},直接把user里的userName变量的数据写入textview-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.userName}" />
    </LinearLayout>
</layout>
DataBinding还是挺智能的,如果某些变量没有赋值,会自动赋一个空值。
。
相同名字的类
如果有两个相同名字的类,我们可以这样
<data>
    <import
        alias="TrueUser"
        type="com.ehang.databindingtest.bean.User" />
    <import
        alias="FakeUser"
        type="com.ehang.databindingtest.User" />
        
    <variable
        name="user"
        type="TrueUser" />
    <variable
        name="user2"
        type="FakeUser" />
</data>
然后分别调用user和user2两个变量就好了。
其他数据类型的变量
我们还可以定义String,int,map等变量
<data>
    <import type="java.util.ArrayList" />
    <import type="java.util.HashMap" />
    
    <variable
        name="list"
        type="ArrayList" />
    <variable
        name="map"
        type="HashMap" />
        
    <variable
        name="str"
        type="String" />
    <variable
        name="boo"
        type="boolean" />
    <variable
        name="num"
        type="int" />
</data>
定义好的变量可以在实际的布局中使用了。
运算符
Data Binding还支持大多数的运算符。加减乘除,字符合并,逻辑云算法,二元三元等等。
下面是一个三元元算符的例子。
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text='@{boo?"true": "false"}'/>
数据绑定
需要在activity中对数据进行绑定。即把数据源和布局关联起来。
public class MainActivity extends AppCompatActivity {
    private User mUser;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注意,这里已经没有setContentView()方法了。
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mUser = new User("xiao xin", 33);
        binding.setUser(mUser);
    }
}
至此,就完成了一个简单的DataBinding了。
动态添加view时,使用databinding
每一个使用了databinding的布局,都会自动生成一个ViewDatabinding的子类。在例子中,布局R.layout.layout3使用了databinding,从而生成了Layout3Binding类。我们可以通过这个类的 bind(布局实例) 方法来进行数据绑定。
 RelativeLayout.LayoutParams relativeLayoutParams = new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
    ViewGroup layout1 = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.layout3, null);
    
    rootLayout.addView(layout1, relativeLayoutParams);
    
    Layout3Binding binding1 = Layout3Binding.bind(layout1);
    binding1.setData(data);
替换字符
在string.xml中
    <string name="util">%1$s千克 %2$s米</string>
其中有两个替换字符。
布局文件中
android:text="@{String.format(@string/util,model.weight,`123`)}"
util中的字符串中的替换字符分别替换成model.height和123。显示的结果会是“weight的值 千克 123米”
如果只有一个替换字符时,用
    <string name="util">%s米</string>
include
给include进来的布局绑定数据,可以这样:
<include layout="@layout/name"
    bind:user = "@{user}"/>
某个布局用了databinding,这个布局include的所有布局都要用databinding
include进来的是一个databindiing布局,并不能拿来做动画操作。比如布局a中include了布局b,对b做动画,不能直接使用databinding.b,要用databinding.b.b布局的父布局。
import
有些时候我们需要用到其他工具类的静态方法,或者直接拿一个bean的单例来做数据绑定,我们需要先import一下。
同样是在data标签里面:
 <import type="com.ehang.communication.ViewModel.HudInfoViewModel"/>
然后就可以使用了
 android:text="@{HudInfoViewModel.getInstance().altitude}"
databinding类不存在的错误
databinding绑定的数据源,如果返回的是一个数字,那就会报这个错误