简介
官方解释
Android data binding operates primarily at compile time, processing expressions found in the layout file and generating code in the application. This package contains common code that will be included with the application, just like support library components.
Demo解决了什么问题
-
对象
对视图
的绑定(只修改对象的值,绑定的视图随之改变) -
对象
和视图
的双向
绑定(除了上面一点特性,还能通过修改视图,从而改变对象的值) - 结合
ListView
去更新列表视图的数据(之前项目有一个需求,点进去查看动态详情,评论后返回动态列表,动态列表显示的评论数量对应增加)
环境
开发环境
- APP运行环境: API 7+即可
- 开发IDE: Android Studio
环境配置
确保jcenter在repositories列表
allprojects {
repositories {
jcenter()
}
}
开启DataBinding支持
android {
...
dataBinding{
enabled = true;
}
}
例子
对象绑定到视图
Model定义
public class User {
public ObservableField<String> firstName = new ObservableField<>();
public ObservableField<String> lastName = new ObservableField<>();
public User() {
}
@Override
public String toString() {
return firstName.get() + " " + lastName.get();
}
}
使用
ObservableField
类来使firstName
和lastName
属性变得可观察.当变化后,去更新对用绑定的视图
布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="demo.august1996.top.databingdingusage.bean.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/firstNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:textAlignment="center" />
<EditText
android:id="@+id/lastNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:textAlignment="center" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="test"
android:text="测试(修改数据后显示)" />
</LinearLayout>
</layout>
布局与以前不一样的是,使用
layout
作为root标签.
引入<data>
标签,其中
-
variable
为绑定的对象 -
user
为绑定对象映射过来的名称(类似于形参
) -
type
为绑定对象的类型
接着就是变量解析的语法了
@{变量名.成员属性}
Activity:
package demo.august1996.top.databingdingusage.activity;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.bean.User;
import demo.august1996.top.databingdingusage.databinding.ActivityDataBind2ViewBinding;
public class DataBind2ViewActivity extends AppCompatActivity {
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_bind2_view);
user = new User();
user.firstName.set("Yu");
user.lastName.set("jianbin");
ActivityDataBind2ViewBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_data_bind2_view);
binding.setUser(user);
}
public void test(View v) {
user.firstName.set("August");
user.lastName.set("1996");
Toast.makeText(this, user.toString(), Toast.LENGTH_SHORT).show();
}
}
其中
XXXBinding
这个类是Android Studio帮我们生成的.命名规范是参照布局文件名字:把下划线命名改成驼峰命名
,例如activity_data_bind2_view.xml
变成了ActivityDataBind2ViewBinding
.
通过
DataBindingUtil.setContentView
我们获取了对应的Binding
对象,其中setUser方法
就是我们在布局文件里面写了user变量
,然后系统自动生成的.
现在Activity全局变量user就已经和布局文件里面的user关联上了
所以通过
test
函数修改了user对象
的值.对应的两个EditText
控件也进行了更新
对象与视图的双向绑定
对于上面的例子,也只能作为
单向绑定
.我们还需要做一个改变视图里面的内容.然后去更新对象的值.
思路
其实很简单,我们只需要给EditText添加内容修改监听.然后再把
user
对象更新就可以了。
新建UserWatcher类
package demo.august1996.top.databingdingusage.watcher;
import android.text.Editable;
import android.text.TextWatcher;
import demo.august1996.top.databingdingusage.bean.User;
/**
* Created by August on 16/6/17.
*/
public class UserWatcher {
private User user;
public UserWatcher(User user) {
this.user = user;
}
public TextWatcher firstNameWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
user.firstName.set(editable.toString());
}
};
public TextWatcher lastNameWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
user.lastName.set(editable.toString());
}
};
}
没什么,就只在编辑结束后设置
user
对应属性的值
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="demo.august1996.top.databingdingusage.bean.User" />
<variable
name="watcher"
type="demo.august1996.top.databingdingusage.watcher.UserWatcher" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/firstNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addTextChangedListener="@{watcher.firstNameWatcher}"
android:text="@{user.firstName}"
android:textAlignment="center" />
<EditText
android:id="@+id/lastNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addTextChangedListener="@{watcher.lastNameWatcher}"
android:text="@{user.lastName}"
android:textAlignment="center" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="test"
android:text="测试(尝试修改上面数据,显示数据后修改数据)" />
</LinearLayout>
</layout>
这次我们引入了
watcher
这个变量和它的类型.
然后我们给EditText
都增加了对应的android:addTextChangedListener=
时间监听.通过这样,我们就可以把EditText
的监听和对应的Watcher
进行关联
Activity代码:
package demo.august1996.top.databingdingusage.activity;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.bean.User;
import demo.august1996.top.databingdingusage.databinding.ActivityViewBind2DataBinding;
import demo.august1996.top.databingdingusage.watcher.UserWatcher;
public class ViewBind2DataAndDataBind2ViewActivity extends AppCompatActivity {
private User user;
private UserWatcher watcher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_bind2_data);
user = new User();
user.firstName.set("Yu");
user.lastName.set("jianbin");
watcher = new UserWatcher(user);
ActivityViewBind2DataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_view_bind2_data);
binding.setUser(user);
binding.setWatcher(watcher);
}
public void test(View v) {
Toast.makeText(this, user.toString(), Toast.LENGTH_SHORT).show();
user.firstName.set("August");
user.lastName.set("1996");
}
}
我们通过
test
方法先把原来的user
对象的值显示出来.
如果我们修改EditText
的值,我们发现user
对象的值已经可以同步了
然后再去修改user
对象的值,EditText
的值也同步了.
结合ListView使用
个人觉得最大的用处还是结合ListView去使用这个特性
item_user子布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="stu"
type="demo.august1996.top.databingdingusage.bean.Student" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/firstNameET"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="firstName"
android:text="@{stu.firstName}"
android:textAlignment="center" />
<EditText
android:id="@+id/lastNameET"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="lastName"
android:text="@{stu.lastName}"
android:textAlignment="center" />
</LinearLayout>
<Button
android:id="@+id/testBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="修改对象" />
</LinearLayout>
</layout>
可以看到跟前面的主布局是差不多的
Model定义
package demo.august1996.top.databingdingusage.bean;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import demo.august1996.top.databingdingusage.BR;
/**
* Created by August on 16/6/17.
*/
public class Student extends BaseObservable {
private String firstName;
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Bindable
public String getFirstName() {
return firstName;
}
@Bindable
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
这里的模型定义时需要继承
BaseObservable
,并且所有需要绑定的对象的getter
方法都需要使用@Bindable
注解,来表示该属性是需要绑定的
BR.class
也是Android Studio帮我们生成的,类似R.class
.里面是一些我们需要绑定的属性的信息
当属性更新时,我们必须使用notifyPropertyChanged
方法去提醒属性被更新,请求绑定视图同步.
所以我们必须在setter
方法里面更新完对象值后调用notifyPropertyChanged
方法
Adapter的编写
package demo.august1996.top.databingdingusage.adapter;
import android.databinding.DataBindingUtil;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import demo.august1996.top.databingdingusage.BR;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.bean.Student;
import demo.august1996.top.databingdingusage.databinding.ItemUserBinding;
/**
* Created by August on 16/6/17.
*/
public class UserAdapter extends BaseAdapter {
private ArrayList<Student> mDatas;
public UserAdapter(ArrayList<Student> mDatas) {
this.mDatas = mDatas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int i) {
return mDatas.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(final int i, View convertView, ViewGroup viewGroup) {
ItemUserBinding binding;
if (convertView == null) {
binding = DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item_user, viewGroup, false);
convertView = binding.getRoot();
convertView.setTag(binding);
} else {
binding = (ItemUserBinding) convertView.getTag();
}
binding.setVariable(BR.stu, mDatas.get(i));
binding.executePendingBindings();
convertView.findViewById(R.id.testBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDatas.get(i).setFirstName(mDatas.get(i).getFirstName() + i);
mDatas.get(i).setLastName(mDatas.get(i).getLastName() + i);
}
});
return convertView;
}
}
与一般Adapter不同的地方主要是在于
getView
方法.
通过DataBindingUtil.inflate()
方法我们拿到一个Binding
对象.
然后ListView
显示的子视图view
通过binding.getRoot()
来获得
通过binding.setVariable();
去设置子布局
的变量
最后使用executePendingBindings
方法来通知子视图更新
其中按钮的监听就是修改
对象的值了,然后就有视图
同步对象
的效果了
主布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/mListView"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</RelativeLayout>
超简单...
Activity代码
package demo.august1996.top.databingdingusage.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;
import java.util.ArrayList;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.adapter.UserAdapter;
import demo.august1996.top.databingdingusage.bean.Student;
public class ListViewActivity extends AppCompatActivity {
private ListView mListView;
private ArrayList<Student> mData;
private UserAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
mListView = (ListView) findViewById(R.id.mListView);
mData = new ArrayList<>();
for (int i = 0; i < 10; i++) {
mData.add(new Student("王" + i, "" + i));
}
mAdapter = new UserAdapter(mData);
mListView.setAdapter(mAdapter);
}
}
超简单...
import关键字
这个直接上例子会比较好理解
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="java.util.List" />
<import type="java.util.Map" />
<import type="java.lang.String" />
<variable
name="list"
type="List<String>" />
<variable
name="map"
type="Map<String,String>" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{list[0]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{list[1]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{map[`0`]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text='@{map["1"]}' />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@{@drawable/img}" />
</LinearLayout>
</layout>
Activity代码
package demo.august1996.top.databingdingusage.activity;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.HashMap;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.databinding.ActivityImportViewBinding;
public class ImportViewActivity extends AppCompatActivity {
private ArrayList<String> mList;
private HashMap<String, String> mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_import_view);
mList = new ArrayList<>();
mMap = new HashMap<>();
for (int i = 0; i < 2; i++) {
mList.add("列表" + i);
mMap.put(String.valueOf(i), "集合" + i);
}
ActivityImportViewBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_import_view);
binding.setList(mList);
binding.setMap(mMap);
}
}
其中
<
这个符号不是乱码出现的.它真的是需要这样写.不能写成<