Android——JetPack{DataBinding}

DataBingding概述

DataBinding

我们知道,布局文件通常只负责UI控件的布局工作。页面通过setContentView()方法关联布局文件,再通过UI控件的id找到控件。接着在页面中通过代码对控件进行操作。在这种方式下,页面承担了绝大部分的工作量。Google提出了DataBinding。DataBinding的出现让布局文件承担了部分原本属于页面的工作。
DataBinding具有如下优势

  • 项目更简洁,可读性更高。部分与UI控件相关的代码可以在布局文件中完成
  • 不再需要findViewById()方法。
  • 布局文件可以包含简单的业务逻辑。UI控件能够直接与数据模型中的字段绑定,甚至能够响应用户的交互

DataBinding的简单绑定

我们先看一个未采用DataBinding的编码方式
我们在Activity中通过3个TextView控件,来分别展示Book对象中的三个字段。在没有DataBinding之前

  1. 布局文件


    

    

    

  1. Book对象
public class Book {
    public String title;
    public String author;
    public String rating;

    public Book(String title, String author, String rating) {
        this.title = title;
        this.author = author;
        this.rating = rating;
    }
}
  1. Activity
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_data_bing2);
        tvTitle = ((TextView) findViewById(R.id.tvTitle));
        tvAuthor = ((TextView) findViewById(R.id.tvAuthor));
        tvRating = ((TextView) findViewById(R.id.tvRating));
        Book book = new Book("bookTitle", "bookAuthor", "bookRating");
        tvTitle.setText(book.title);
        tvAuthor.setText(book.author);
        tvRating.setText(book.rating);
    }

采用DataBinding完成简单的绑定

  1. 在app的build.gradle中启用数据绑定
    dataBinding {
        enabled = true
    }
  1. 修改布局文件
    在布局文件外层加入标签。我们可以使用AndroidStudio的 “Convert to data binding layout”选项,Android Studio会自动生成相关代码



    

    

    

        

        

        
    

这样做的目的是告诉DataBinding库,我们希望对该布局文件实行绑定。此时,rebuild该项目,DataBinding库会自动生成绑定该布局文件所需要的类。

  1. 实例化布局文件
    在没有DataBinding组件时,我们通常通过Activity.setContentView()方法实例化布局文件,然后通过findViewById()方法找到布局文件中对应的UI控件。有了DataBinding组件后,就可以告别findViewById()方法了。我们可以通过DataBindingUtil.setContentView()方法实例化布局文件该方法返回实例化后的布局文件对象,名字与布局文件的名字保持一致,并在后面加上Binding
    假如activity_main,则实例化后的布局文件类名为ActivityMainBinding
        ActivityDataBing2Binding viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_bing2);
  1. 将数据传递到布局文件
    在绑定布局文件后,我们需要将数据“传递”给布局文件。
    在布局文件中定义布局变量,布局变量指定对象的类型和名字,名字可以自定义
    
        
    

将数据传递给布局文件后,我们就可以在Activity中,通过setVariable()方法,将Book对象传递给布局文件的布局变量了

    Book book = new Book("book", "authorbook", "10");
        viewDataBinding.setVariable(BR.book, book);

这里的BR类类似于Android项目中常见的R类,是由DataBinding库自动生成的,用于统一存放所有布局变量的id

DataBinding为了方便使用,为布局变量提供了Setter类,因此,我们也可以直接使用setBook()方法,将Book对象传递给布局文件中对应的布局变量。

        Book book = new Book("book", "authorbook", "10");
        viewDataBinding.setBook(book);
  1. 绑定布局变量与成员变量
    Book对象已经传递到布局文件中了,此时,我们便可以将UI控件与Book对象的属性进行绑定。为此DataBinding库提供了布局表达式,布局表达式以@{}的格式作为属性值存在
  

        

        

这样我们就完成了布局文件和数据的绑定

  1. 在布局文件中引入静态类
    有时候我们需要在布局文件中引用一些Java工具类,帮助我们处理一些简单的逻辑
    我们现在有一个打分工具类,来帮助我们将阿拉伯数字的分数对应为中文
public class BookRatingUtil {
    public static String getRatingString(int rating) {
        switch (rating) {
            case 0:
                return "零";
            case 1:
                return "一";
            case 2:
                return "二";
            case 3:
                return "三";
            case 4:
                return "四";
            case 5:
                return "五";
        }
        return "";
    }
}

我们可以在布局文件中通过标签导入静态工具类

 

接着在UI控件中调用静态方法

        

DataBinding响应事件

我们可以通过Button控件,来让DataBinding响应onClick事件

  1. 创建文件,并将其转为DataBinding布局
    
  1. 编写一个叫做EventHandleListener,用于接收和响应Button的onClick事件
public class EventHandleListener {
    private Context context;

    public EventHandleListener(Context context) {
        this.context = context;
    }

    public void onButtonClicked(View view) {
        Toast.makeText(context, "clicked", Toast.LENGTH_SHORT).show();
    }
}
  1. 在布局文件的标签中定义布局变量



  1. 在Activity中通过DataBinding将Activity与布局文件绑定,然后将响应事件实例化,传入布局文件
        viewDataBinding.setEventHandler(new EventHandleListener(this));

  1. 通过布局表达式,调用EventHandleListener中的方法
        

然后运行程序,点击按钮可以看到点击事件能够正常响应

BindingAdapter的原理

在gradle中启用DataBinding库,它会为我们生成绑定所需要的各种类。这其中包括大量针对UI控件的、名为XXXBindingAdapter的类。这些类中包含各种静态方法,并且在这些静态方法前面都有@BindingAdapter标签。标签中的别名对应于UI控件在布局文件中的属性

自定义BindingAdapter的基本方法

我们来使用ImageView来演示如何使用自定义BindingAdapter

  1. 我们使用Picasso库来加载图片,在app的build.gradle文件中添加与Picasso库相关的依赖
   implementation 'com.squareup.picasso:picasso:2.71828'
  1. 在Manifest文件中加入访问网络权限


  1. 编写处理图片的BindingAdapter类
    需要注意的是在BindingAdapter中的方法均为静态方法。第1个参数为调用者本身,也就是当前的ImageView;第2个参数是布局文件在调用该方法时传递的参数。在静态方法前需要加入@BindingAdapter()标签,并为该方法起一个别名,此处为image,布局文件正是通过别名来调用该方法的
public class ImageViewBindingAdapter {

    @BindingAdapter("image")
    public static void setImage(ImageView image, String imageUrl) {
        if (!TextUtils.isEmpty(imageUrl)) {
            Picasso.get()
                    .load(imageUrl)
                    .placeholder(R.drawable.ic_launcher_background)
                    .error(R.mipmap.ic_launcher)
                    .into(image);
        }else{
            image.setBackgroundColor(Color.DKGRAY);
        }
    }
}
  1. 在布局文件中调用BindingAdapter
    需要在布局变量中定义一个String,用于存放网络地址
        

然后在ImageView中通过别名,也就是我们在ImageViewBindingAdapter文件中定义好的别名image来调用静态方法

        
  1. 在Activity中为布局变量赋值
        viewDataBinding.setNetworkImage("https://xxxxx.jpg");

双向绑定

单向绑定与双向绑定

在前面的介绍中,我们所有使用的方式都属于单向绑定,单向绑定就是数据model中的数据会同步显示到view中。

实现双向绑定

我们现在要实现一个登录页面,需要一个用于输入用户名的EditText,一个用于保存用户登录信息的Model类LoginModel。我们希望将EditText和LoginModel中的userName字段进行双向绑定。

  1. 编写LoginModel类
public class LoginModel {
    public String userName;
}

  1. 编写一个用于存放于实现双向绑定相关的业务逻辑的类。这个类继承自BaseObservable,因为无论是单向绑定还是双向绑定,本质都是观察者模式
public class TwoWayBindViewModel extends BaseObservable {
    private LoginModel loginModel;

    public TwoWayBindViewModel() {
        loginModel = new LoginModel();
        loginModel.userName = "wx";
    }

    @Bindable
    public String getUserName() {
        return loginModel.userName;
    }


    public void setLoginModel(LoginModel loginModel) {
        this.loginModel = loginModel;
        notifyPropertyChanged(BR.userName);
    }
}
  • @Bindable标签:告诉编译器 被注解标注的字段为我们希望进行绑定的字段
  1. 设置布局变量
viewDataBinding.setViewModel(new TwoWayBindViewModel());
  1. 布局文件
    使用@={} 替换@{}

使用ObservableField优化双向绑定

实际上上面的做法有一些问题,我们需要让我们的viewModel必须继承自BaseObservable,在Getter方法前还要添加@Bindable标签,以告诉编译器我们要绑定该字段,最后在Setter方法中还需要手动调用notifyPropertyChanged()方法来通知观察者
我们可以使用ObservableField,它能将普通对象包装成一个可观察对象。ObservableField可用于包装各种基本类型、集合数组类型和自定义类型的数据

DataBinding在Recycler中的使用

你可能感兴趣的:(Android——JetPack{DataBinding})