目录
Android Jetpack应用指南学习笔记12--DataBinding的简单使用
4.MainActivity代码:主要是绑定view和设置数据
8.1 添加一个事件工具类:EventHandlerListener
17.1 TwoWayBindingViewModelField类:
19.2 RecyclerViewBindingAdapter代码:
19.6 RecyclerViewActivity布局代码:
Android Jetpack应用指南学习笔记12--DataBinding的简单使用
1.DataBinding的简介
DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。使其维护起来更加方便,架构更明确简介。所谓的绑定,是绑定什么呢?
数据直接绑定到UI上,数据改变时UI自动更新 UI上的数据绑定到变量中,当数据(如EditText中的数据)改变时自动更新 DataBinding非常适合用于MVVM模式中充当View和ViewModel的双向通讯的工具,引入DataBinding之后,我们可以少写很多的例如findViewById()之类的代码。
2.引入依赖:
buildFeatures {
viewBinding true
dataBinding true
}3.实体类代码:
/**
* @author: njb
* @date: 2022/9/17 21:35
* @desc:
*/
public class Book {
public String title;
public String author;
public int rating;
public Book(String title,String author){
this.title = title;
this.author= author;
}
}4.MainActivity代码:主要是绑定view和设置数据
public class MainActivity extends AppCompatActivity {
ActivityMainBinding activityMainBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
}
private void initView() {
activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
Book book = new Book("AndroidJetPack应用指南学习之DataBinding简单使用","淡淡的香烟");
book.rating = 5;
activityMainBinding.setBook(book);
}
}5.主界面activity_main代码:
5.1首先是设置数据:
和之前的普通方式不一样,这里布局是以layout开始的,熟悉的小伙伴应该都知道.
<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" tools:context=".MainActivity"> <data> <import type="com.example.databindingdemo.utils.BookRatingUtils" /> <variable name="book" type="com.example.databindingdemo.bean.Book" /> </data>
5.2 在布局中设置数据:

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{book.title}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_content"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{book.author}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<TextView
android:id="@+id/tv_rating"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{BookRatingUtils.getRatingString(book.rating)}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>5.3 布局完整代码如下:
<?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"
tools:context=".MainActivity">
<data>
<import type="com.example.databindingdemo.utils.BookRatingUtils" />
<variable
name="book"
type="com.example.databindingdemo.bean.Book" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{book.title}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_content"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{book.author}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<TextView
android:id="@+id/tv_rating"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{BookRatingUtils.getRatingString(book.rating)}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>6.评分工具类代码:
package com.example.databindingdemo.utils;
/**
* @author: njb
* @date: 2022/9/17 21:52
* @desc: 评分工具类
*/
public class BookRatingUtils {
public static String getRatingString(int rat){
switch (rat){
case 0:
return "非常不好看";
case 1:
return "不好看";
case 2:
return "有一点不好看";
case 3:
return "有一点好看";
case 4:
return "好看";
case 5:
return "非常好看";
}
return "";
}
}
7.实现的效果如下:

8.DataBinding响应事件
8.1 添加一个事件工具类:EventHandlerListener
package com.example.databindingdemo.listener;
import android.content.Context;
import android.view.View;
import android.widget.Toast;
/**
* @author: njb
* @date: 2022/9/18 0:02
* @desc:
*/
public class EventHandlerListener {
private Context context;
public EventHandlerListener(Context context){
this.context = context;
}
public void onButtonClicked(View view){
Toast.makeText(context,"I am clicked!",Toast.LENGTH_SHORT).show();
}
}8.2 在布局中添加事件和监听
<data>
<import type="com.example.databindingdemo.utils.BookRatingUtils" />
<variable
name="book"
type="com.example.databindingdemo.bean.Book" />
<variable
name="EventHandler"
type="com.example.databindingdemo.listener.EventHandlerListener" />
</data>
<Button
android:id="@+id/btn_test"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="Click me"
android:textColor="@color/white"
android:textSize="20sp"
android:onClick="@{EventHandler.onButtonClicked}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_rating" />9.添加事件的监听和使用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
initListener();
}
private void initListener() {
activityMainBinding.setEventHandler(new EventHandlerListener(this));
}10.事件响应的效果如下:
11.添加自定义的BindingAdapter
/**
* @author: njb
* @date: 2022/9/18 0:36
* @desc: 自定义BindingAdapter
*/
public class BindingAdapterImage {
@BindingAdapter("image")
public static void setImage(ImageView image,int imgResource){
image.setImageResource(imgResource);
}
@BindingAdapter("netWorkImage")
public static void setNetWorkImage(ImageView imageView,String imgUrl){
if(!TextUtils.isEmpty(imgUrl)){
Picasso.get().load(imgUrl).placeholder(R.drawable.ic_launcher_background)
.error(R.mipmap.ic_launcher).centerCrop().resize(300,300)
.into(imageView);
}else {
imageView.setBackgroundColor(Color.RED);
}
}
@BindingAdapter("padding")
public static void setPadding(View view,int oldPadding, int newPadding){
if(oldPadding != newPadding){
view.setPadding(newPadding,newPadding,newPadding,newPadding);
}
}
}12.自定义BindAdapter布局如下:
<?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
name="netWorkImage"
type="String" />
<variable
name="localImage"
type="int" />
<variable
name="imagePadding"
type="int" />
<variable
name="ClickHandler"
type="com.example.databindingdemo.BindAdapterActivity.ClickHandler" />
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_local"
android:layout_width="0dp"
android:layout_height="50dp"
app:image="@{localImage}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints" />
<ImageView
android:id="@+id/iv_netWork"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_local"
app:netWorkImage="@{netWorkImage}"
app:padding="@{imagePadding}"
tools:ignore="MissingConstraints" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:onClick="@{ClickHandler.onClick}"
android:text="change padding"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_netWork"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>13.BindAdapterActivity代码:
package com.example.databindingdemo;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.example.databindingdemo.databinding.ActivityBindingAdapterBinding;
/**
* @author: njb
* @date: 2022/9/18 0:41
* @desc:
*/
public class BindAdapterActivity extends AppCompatActivity {
private ActivityBindingAdapterBinding bindingAdapterBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
}
private void initView() {
bindingAdapterBinding = DataBindingUtil.setContentView(this,R.layout.activity_binding_adapter);
bindingAdapterBinding.setLocalImage(R.mipmap.ic_launcher);
bindingAdapterBinding.setNetWorkImage("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F013b3d5c04dc59a80121ab5d0776b2.jpg%402o.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1666026424&t=89bb1c43518c696a2000ba442b9be1d6");
bindingAdapterBinding.setImagePadding(20);
bindingAdapterBinding.setClickHandler(new ClickHandler());
}
public class ClickHandler {
public void onClick(View view){
bindingAdapterBinding.setImagePadding(120);
}
}
}14.自定义BindingAdapter效果如下:

改变Padding后的效果如下:
15.使用ViewModel实现双向绑定:
package com.example.databindingdemo.bean;
import android.util.Log;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import com.example.databindingdemo.BR;
/**
* @author: njb
* @date: 2022/9/18 16:27
* @desc: 使用ViewModel实现双向绑定
*/
public class TwoWayBindingViewModel extends BaseObservable {
private LoginModel loginModel;
public TwoWayBindingViewModel(){
loginModel = new LoginModel();
loginModel.userName = "JetPack";
}
@Bindable
public String getUserName(){
return loginModel.userName;
}
public void setUserName(String userName){
if(userName != null && !userName.equals(loginModel.userName)){
loginModel.userName = userName;
notifyPropertyChanged(BR.userName);
Log.d("--viewModel--",userName);
}
}
}
16.ViewModel布局和Activity如下:
16.1 ViewModel布局:
<?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">
<data>
<variable
name="viewModel"
type="com.example.databindingdemo.bean.TwoWayBindingViewModel" />
<!-- <variable
name="viewModel"
type="com.example.databindingdemo.bean.TwoWayBindingViewModelField" />-->
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="0dp"
android:layout_height="60dp"
android:background="@color/colorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_margin="20dp"
android:text="@={viewModel.userName}"
android:textColor="@color/white"
android:textSize="20sp"
android:gravity="center"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
16.2 ViewModelActivity代码如下:
package com.example.databindingdemo;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.example.databindingdemo.bean.TwoWayBindingViewModel;
import com.example.databindingdemo.bean.TwoWayBindingViewModelField;
import com.example.databindingdemo.databinding.ActivityTwoWayBindingBinding;
/**
* @author: njb
* @date: 2022/9/18 16:30
* @desc:
*/
public class TwoWayBindingActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
}
private void initView() {
ActivityTwoWayBindingBinding twoWayBindingBinding = DataBindingUtil.setContentView(this,R.layout.activity_two_way_binding);
twoWayBindingBinding.setViewModel(new TwoWayBindingViewModel());
//twoWayBindingBinding.setViewModel(new TwoWayBindingViewModelField());
}
}
17.使用ObservableField优化双向绑定:
17.1 TwoWayBindingViewModelField类:
package com.example.databindingdemo.bean;
import android.util.Log;
import androidx.databinding.BaseObservable;
import androidx.databinding.ObservableField;
/**
* @author: njb
* @date: 2022/9/18 16:42
* @desc: 使用ObservableField优化双向绑定
*/
public class TwoWayBindingViewModelField extends BaseObservable {
private ObservableField<LoginModel> loginModelObservableField;
public TwoWayBindingViewModelField() {
LoginModel loginModel = new LoginModel();
loginModel.userName = "Jack";
loginModelObservableField = new ObservableField<>();
loginModelObservableField.set(loginModel);
}
public void setUserName(String userName) {
loginModelObservableField.get().userName = userName;
Log.d("--fieldViewModel--", userName);
}
public String getUserName() {
return loginModelObservableField.get().userName;
}
}
17.2 布局中使用方式如下:
17.3 Activity中使用代码:

17.4 日志打印:
可以看到两种方式基本上差不多,使用ObservableField方式时通过ObservableField将LoginModel对象包装起来,并为对象写好Getter和Setter方法,在布局中依然通过@={}的方式完成双向绑定,运行程序发现getUserName方法在程序启动时被自动调用,当用户修改EditText中的内容时setUserName被自动调用,对于Getter方法也无需添加@Bindable,使用方便了许多.

18.双向绑定的运行效果图如下:
19.RecyclerView的双向绑定机制:
19.1 RecyclerViewAdapter:
package com.example.databindingdemo.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.example.databindingdemo.R;
import com.example.databindingdemo.bean.Book;
import com.example.databindingdemo.databinding.LayoutItemBinding;
import java.util.List;
/**
* @author: njb
* @date: 2022/9/18 17:16
* @desc:
*/
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
private List<Book> bookList;
public RecyclerViewAdapter(List<Book> books){
this.bookList = books;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutItemBinding layoutItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.layout_item,parent,false);
return new MyViewHolder(layoutItemBinding);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Book book = bookList.get(position);
holder.layoutItemBinding.setBook(book);
}
@Override
public int getItemCount() {
return bookList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
LayoutItemBinding layoutItemBinding;
public MyViewHolder(LayoutItemBinding itemView) {
super(itemView.getRoot());
layoutItemBinding = itemView;
}
}
}19.2 RecyclerViewBindingAdapter代码:
package com.example.databindingdemo.adapter;
import android.graphics.Color;
import android.text.TextUtils;
import android.widget.ImageView;
import androidx.databinding.BindingAdapter;
import com.example.databindingdemo.R;
import com.squareup.picasso.Picasso;
/**
* @author: njb
* @date: 2022/9/18 17:07
* @desc:
*/
public class RecyclerViewBindingAdapter {
@BindingAdapter("itemImage")
public static void setNetWorkImage(ImageView imageView, String imgUrl){
if(!TextUtils.isEmpty(imgUrl)){
Picasso.get().load(imgUrl).placeholder(R.drawable.ic_launcher_background)
.error(R.mipmap.ic_launcher).centerCrop().resize(300,300)
.into(imageView);
}else {
imageView.setBackgroundColor(Color.RED);
}
}
}19.3 RecyclerViewViewModel代码:
/**
* @author: njb
* @date: 2022/9/18 17:12
* @desc:
*/
public class RecyclerViewViewModel {
public List<Book> getBooks(){
List<Book> books = new ArrayList<>();
for(int i=0;i<100;i++){
Book book = new Book(Constants.title,Constants.content);
book.img = Constants.imgUrl;
books.add(book);
}
return books;
}
}19.4 layout_item布局代码:
<?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">
<data>
<variable
name="book"
type="com.example.databindingdemo.bean.Book" />
<variable
name="EventHandler"
type="com.example.databindingdemo.listener.EventHandlerListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_local"
android:layout_width="140dp"
android:layout_height="140dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:gravity="center"
app:itemImage="@{book.img}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{book.title}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toRightOf="@+id/iv_local"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_content"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@{book.author}"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintLeft_toRightOf="@+id/iv_local"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>19.5 RecyclerViewActivity代码:
package com.example.databindingdemo.activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.example.databindingdemo.R;
import com.example.databindingdemo.adapter.RecyclerViewAdapter;
import com.example.databindingdemo.databinding.ActivityRecyclerBindingAdapterBinding;
import com.example.databindingdemo.viewmodel.RecyclerViewViewModel;
/**
* @author: njb
* @date: 2022/9/18 17:26
* @desc:
*/
public class RecyclerViewActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
}
private void initView() {
ActivityRecyclerBindingAdapterBinding recyclerBindingAdapterBinding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_binding_adapter);
recyclerBindingAdapterBinding.recyclerView.setHasFixedSize(true);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(new RecyclerViewViewModel().getBooks());
recyclerBindingAdapterBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerBindingAdapterBinding.recyclerView.setAdapter(adapter);
}
}19.6 RecyclerViewActivity布局代码:
<?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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
20.RecyclerView实现双向绑定的效果如下:



