DataBinding
- 一、添加配置
- 二、使用
- 修改布局文件
- 具体使用
- 单向绑定、方法绑定
- 双向绑定、加载网络图片例子
- Adapter中使用
- 使用 ObservableField
一、添加配置
如果需要使用databinding 需要在gradle中添加如下配置
defaultConfig {......//开启dataBindingdataBinding {enabled = true}.......}
二、使用
修改布局文件
选中布局文件根节点,代码提示(使用黄色提示,或者快捷键) Convert to data binding layout

修改后布局文件如下
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"><data></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"tools:context=".study.DataBindingActivity">......</LinearLayout>
</layout>
具体使用
单向绑定、方法绑定
直接上demo代码,具体说明会在代码注释中给出,后面给出总结和注意事项
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"><data><!-- 使用 variable 声明变量 ,此后我们便可以使用 @{} 使用java一样的代码--><variablename="user"type="com.twomonth.twapp.bean.User" /><!-- 点击事件 --><variablename="click"type="com.twomonth.twapp.study.DemoActivity.Listener" /></data><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:padding="40dp"tools:context=".study.DemoActivity"><!-- ✅使用 @{} 的形式,就可以使用引用对象中的数据了 --><EditTextandroid:id="@+id/edit_userName"android:layout_width="match_parent"android:layout_height="wrap_content"tools:layout_editor_absoluteX="0dp"tools:layout_editor_absoluteY="145dp"android:text="@{user.userName}"android:hint="请输入用户名"/><EditTextandroid:id="@+id/edit_userPassword"android:layout_width="match_parent"android:layout_height="wrap_content"tools:layout_editor_absoluteX="0dp"tools:layout_editor_absoluteY="210dp"android:text="@{user.passWord}"android:hint="请输入密码"/><!-- ✅使用方法的时候,像下面这样类似于箭头函数 --><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="login"android:onClick="@{()->click.changeUserName()}"android:id="@+id/button"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.userName}"android:id="@+id/tv_userName"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.passWord}"android:id="@+id/tv_password"/><ImageViewandroid:id="@+id/iv_head"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"/></LinearLayout>
</layout>
activity 和 实体类
public class DemoActivity extends AppCompatActivity {ActivityDemoBinding binding;User user;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);/*** 将布局文件与activity关联,替代了原来的 setContentView()方法。这样一来省去了手写 findViewById() ,可以通过binding获取控件引用,例如:* binding.button;* binding.editUserName;* binding.getRoot(); 获取顶层布局,在fragment中需要使用这个方法,返回view* @Override* public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {* // 在fragment中使用的时候如下* mFDBinding = DataBindingUtil.inflate(inflater,getLayoutRes(),container,false);* mFDBinding.getRoot().setBackground(new WaterMarkBgView(getContext(), labels, -30, 15));* return mFDBinding.getRoot();* }*/binding = DataBindingUtil.setContentView(this,R.layout.activity_demo);user = new User();user.setUserName("userName");user.setPassWord("pass");user.setHeadImage("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2496571732,442429806&fm=26&gp=0.jpg");/*** 设置user对象之前,需要先在布局文件中使用 variable 标签声明。*/binding.setUser(user);binding.setClick(new Listener());}public class Listener{public void changeUserName(){user.setUserName("改变了用户名");Log.e("twomonth",user.toString());}}
}
public class User {public String userName;private String passWord;public String getHeadImage() {return headImage;}public void setHeadImage(String headImage) {this.headImage = headImage;}private String headImage;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassWord() {return passWord;}public void setPassWord(String passWord) {this.passWord = passWord;}@Overridepublic String toString() {return "User{" +"userName='" + userName + '\'' +", passWord='" + passWord + '\'' +", headImage='" + headImage + '\'' +'}';}
}
做到这里我们运行demo 变回看到下面这个界面

我们会发现,几个好处:
- 不用再写findViewById()这样的样板式代码
- 创建的user对象中的数据自动填充到了对应的位置,不用再setText()
但是操作一下会发现,LOGIN按钮点击事件是生效了,从日志输出中可以看出user对象已经发生了改变,但是EditText 中的数据却没有发生变化。这是因为我们缺了一个很重要的东西,LiveData,我们应该怎么做,才能让user对象数据发生变化的时候,Editext中的数据会自动更新?
//1.继承 BaseObservable
public class User extends BaseObservable{// 2.使用 @Bindable 注解属性,这里要注意,如果属性是public 的,需要在声明的时候注解// 如果属性是 private 的,需要在 get 方法上注解@Bindablepublic String userName;private String passWord;public String getHeadImage() {return headImage;}public void setHeadImage(String headImage) {this.headImage = headImage;}private String headImage;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;// 3. 在public 属性上注解就可以了,但是在private 属性上仅仅注解是不管用的,还需要在属性值发生改变之后// 通知出去,这里的 BR 是系统生成的notifyPropertyChanged(BR.userName);// 与上面的 notifyPropertyChanged 用法相似,区别是 上面的方法只通知相关的控件刷新内容,// 下面这个方法则是通知所有控件刷新内容// notifyChange();}@Bindablepublic String getPassWord() {return passWord;}public void setPassWord(String passWord) {this.passWord = passWord;}@Overridepublic String toString() {return "User{" +"userName='" + userName + '\'' +", passWord='" + passWord + '\'' +", headImage='" + headImage + '\'' +'}';}
}
对user对象进行了改造,改造完成后在运行demo 点击LOGIN 按钮,会发现,不仅日志中的输出改变了,对应的 EditextView 和 TextView中的数据也发生了变化,这说明我们的单向绑定已经成功了,这里的单相绑定是指数据实体发生改变的时候,会及时的通知到布局文件,改变显示的内容。
单向绑定的基本用法就是如此了,我们再来看一下双向绑定
双向绑定、加载网络图片例子
上面的demo中我们操作LOGIN 按钮可以改变显示内容,但是反过来,我们改变EditText 中的内容时,却发现,对应的user对象中的userName并不会发生对应的变化。
我们可以修改changeUserName()方法尝试一下:
public class Listener{public void changeUserName(){Log.e("twomonth",user.toString());}}
修改后,我们启动demo 手动改变EditText中的内容,然后点击LOGIN按钮,结果果然,显示在EditText中的内容虽然被我们改变了,但是日志输出中我们会发现,user对象并没有发生变化。
如果要做到Editext中的改变也能及时通知到user对象,就需要双向绑定 将@{} 改成@={},
修改后,我们的布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"><data><!-- 使用 variable 声明变量 ,此后我们便可以使用 @{} 使用java一样的代码--><variablename="user"type="com.twomonth.twapp.bean.User" /><!-- 点击事件,方法使用 --><variablename="click"type="com.twomonth.twapp.study.DemoActivity.Listener" /></data><LinearLayout...tools:context=".study.DemoActivity"><EditText...android:text="@={user.userName}"android:hint="请输入用户名"/><EditText...android:text="@={user.passWord}"android:hint="请输入密码"/><Button...android:onClick="@{()->click.changeUserName()}"android:id="@+id/button"/><TextView...android:text="@={user.userName}"android:id="@+id/tv_userName"/><TextView...android:text="@={user.passWord}"android:id="@+id/tv_password"/>...</LinearLayout>
</layout>
这个时候我们再去运行demo,向 EditText中输入内容的时候会发现,下面的TextView中的内容同时也在发生变化,点击LOGIN按钮,user从日志中也可以看出,user对象也确确实实发生了改变。至此,双向绑定也就基本实现了。
还有一个imageView 没有处理,怎样能够方便快捷的使用网络图片加载框架,将网络图片也绑定到布局文件中呢:
public class ImageViewDataBinding {// 1写个类,2使用注解声明一个静态方法,3布局文件中使用@BindingAdapter("imageUrl")public static void setImageUrl(ImageView imageView,String imageUrl){Picasso.with(imageView.getContext()).load(imageUrl).placeholder(R.drawable.ic_launcher_background).into(imageView);}
}
<ImageViewandroid:id="@+id/iv_head"android:layout_width="wrap_content"android:layout_height="wrap_content"app:imageUrl="@{user.headImage}"android:layout_gravity="center_horizontal"/>
补充
我们很多时候会碰到
<import type="com.xxx.xxx.User1" /><importalias="seconedUser"type="com.xxx.xxx.User2" /><variablename="user1"type="User1" /><!--这里引用的是第一个import--><variablename="user2"type="com.xxx.xxx.User2" /><!--这里引用的是第二个import--><variablename="user3"type="seconedUser" /><!--这里引用的是第二个import-->
Adapter中使用
直接上代码,解释和步骤都在里面了
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.DemoViewHolder> {@NonNull@Overridepublic DemoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {// 1. 获取databindingxxxBinding demoBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.xxx,parent,false);return new DemoViewHolder(xxxBinding);}@Overridepublic void onBindViewHolder(@NonNull DemoViewHolder holder, int position) {// 3.在这里就可以使用 demoBinding 了}@Overridepublic int getItemCount() {return 0;}static class DemoViewHolder extends RecyclerView.ViewHolder{public DemoViewHolder(@NonNull View itemView) {super(itemView);}// 2.重载构造方法,使用 getRoot 获取根Viewpublic DemoViewHolder(xxxBinding dataBinding){super(dataBinding.getRoot());}}
}
使用 ObservableField
使用 ObservableField 类型的变量,不再需要继承 BaseObservable ,也就不需要在数据刷新的时候进行通知了,相比较而言更加灵活。
官方封装好的还有 ObservableShort、ObservableBoolean、ObservableByte …等一系列!这里就不再赘述了,感兴趣的可以尝试一下。
public class User2{public ObservableField<String> userName;private ObservableField<String> passWord;public ObservableField<String> getUserName() {return userName;}public void setUserName(ObservableField<String> userName) {this.userName = userName;}public ObservableField<String> getPassWord() {return passWord;}public void setPassWord(ObservableField<String> passWord) {this.passWord = passWord;}
}
User2 对象的使用
user2 = new User2();user2.setUserName(new ObservableField<>("userName"));user2.setPassWord(new ObservableField<>("123456"));// 改变参数的时候 使用ObservableField 的 set 和 get 方法user2.getUserName().set("");user2.getUserName().get();
DataBinding 还提供了 ObservableMap 和 ObservableList
使用描述
<import type="com.twomonth.twapp.bean.User"/><import type="androidx.databinding.ObservableMap" /><!-- 要用 "<"和 ">",而不是尖括号--><variablename="teacher"type="ObservableMap<String,User>" /><import type="androidx.databinding.ObservableList" /><variablename="student"type="ObservableList<String>" /><EditTextandroid:id="@+id/edit_userName"android:layout_width="match_parent"android:layout_height="wrap_content"tools:layout_editor_absoluteX="0dp"tools:layout_editor_absoluteY="145dp"android:text='@{teacher["wang"]}'android:hint="请输入用户名"/><EditTextandroid:id="@+id/edit_userPassword"android:layout_width="match_parent"android:layout_height="wrap_content"tools:layout_editor_absoluteX="0dp"tools:layout_editor_absoluteY="210dp"android:text="@={student[0]}"android:hint="请输入密码"/>
ObservableMap<String,User> observableMap = new ObservableArrayMap<>();observableMap.put("wang",new User());binding.setTeacher(observableMap);


















