关于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绑定的数据源,如果返回的是一个数字,那就会报这个错误