Android DataBinding 实战全解【转载】

2024-08-23 18:32

本文主要是介绍Android DataBinding 实战全解【转载】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2015年的Google IO大会上,Android 团队发布了一个数据绑定框架(Data Binding Library),官方原生支持 MVVM 模型。数据绑定的概念并不陌生,Web开发中已经很是普遍,因此DataBinding或多或少地都借鉴了Web端开成熟的经验,其语法与使用方式都和JSP中的EL表达式非常类似。经过不断地强化,到了2016年,DataBinding已经可以支持数据双向绑定。

1. DataBinding简介

DataBinding解决了Android UI 编程的一个痛点,其主要优势在于:

  1. 对于MVVM的支持:官方原生支持 MVVM 模型可以让我们在不改变既有代码框架的前提下,非常容易地使用这些新特性。
  2. 提高开发效率:
    • 去掉Acitivity和Fragments中更新UI数据的代码,让业务逻辑和UI代码分离;
    • XML成为UI数据的唯一真实来源;
    • 减少定义view id 和使用findViewById();
  3. 性能高、功能强:
    • 充分考虑了性能因素,高效的绑定和更新数据;
    • 更安全,在编译时会发现由于错误的ID而引起的Errors;
    • 保证代码在主线程经常;

在DataBinding推出之前,市面上已经有类似的第三方库在被使用,比如:

  • ButterKnife;
  • Android Annotations;
  • RoboBinding;

DataBinding让这些依赖注入框架会慢慢地失去市场,因为在 Java代码中直接使用View变量的情况会越来越少。

但是DataBinding毕竟还在发展阶段,有些地方还值得完善:

  • IDE的支持不足善:虽然DataBinding是Google亲生的,但是Android Studio中的支持还不是不尽如人意,尤其是在XML文件中代码提示有待加强;
  • 报错信息不直接:这个是老生常谈的问题,虽然DataBinding在编译时能发现数据绑定的错误,但是报错信息往往不直观,需要用户在很多错误信息中自己去找错误点。
  • 没有重构的支持:这可以说是所有数据绑定框架都会面临的问题,因为数据的绑定都是在XML中,一旦用户需要继承原来的代码,就压面临这将所有数据绑定代码重新编写。

2. DataBinding的基本使用

准备工作

  1. 确保Android stuido的版本一定要大于1.3。
  2. Android的Gradle插件版本不低于 1.5.0-alpha1:
classpath 'com.android.tools.build:gradle:1.5.0'
  1. 然后修改对应模块(Module)的 build.grade:
android {....dataBinding {enabled = true}
}

工程创建完成后,我们通过一个最简单的例子来说明 Data Binding 的基本用法。

布局文件

使用DataBinding之后,xml的布局文件就不再用于单纯地展示UI元素,还需要定义UI元素用到的变量。所以,它的根节点不再是一个 ViewGroup,而是变成了layout,并且新增了一个节点data,用来定义绑定的数据。

<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><!--添加要绑定的数据--></data><!--下面的布局文件就是原先的根节点(Root Element)--><LinearLayout>....</LinearLayout>
</layout>

data节点的作用就像一个桥梁,Activity中将data需要数据传入,XML定义data数据绑定的位置,搭建了 View 和 Model 之间的通路,也就是实现MVVM的ViewModel

下面说明下如何在XML定义data节点:我们先在 xml 布局文件的data节点中声明variable节点,这个节点将会定义变量的名称、类型,为UI元素提供数据(例如将String绑定到TextView的android:text属性上)。

 <data><!--java.lang.*包是自动导入的不需要你再次的导入--><!--当出现了import相同的类名的时候需要为其指定外号alias加以区分--><import type="com.example.administrator.databindingdemo.Student"/><import type="com.example.administrator.databindingdemo.MainActivity.Presenter"/><variablename="student"type="Student"/><variablename="presenter"type="Presenter"/><variablename="visibility"type="Boolean"/>
</data>

其中Presenter类时用来绑定方法的类,我们稍后再说;Student类是我们定义的一个类,用来呈现数据,其定义如下:

public class Student extends BaseObservable{private String firstName;private String lastName;public void setFirstName(String firstName) {this.firstName = firstName;//提示该属性刷新了notifyPropertyChanged(BR.firstName);//提示所有的属性都刷新//notifyAll();}//注解来提示Binding生成这个字段@Bindablepublic String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;notifyPropertyChanged(BR.lastName);}@Bindablepublic String getFirstName() {return firstName;}public Student(String firstName, String lastName) {this.firstName = firstName;this.lastName = lastName;}@Overridepublic String toString() {return "Student{" +"firstName='" + firstName + '\'' +", lastName='" + lastName + '}';}
}

Studnet类继承了BaseObservable,这个是数据更新时所需要的,我们稍后在再来讨论。

当我们设置DataBinding生效后,如果一个xml文件的被如上这般设置了Data节点,编译器就会自动生成一个继承自 ViewDataBinding 的类(build 目录下)。其命名规则也是固定的:如果 xml 的文件名叫 activity_main.xml,那么生成的类就是 ActivityMainBinding。

绑定数据变量

下面需要修改MainActivity的onCreate方法,用 DatabindingUtil.setContentView()来替换掉setContentView(),然后创建一个Student对象,通过mBinding.setStudent(mStudent)与 variable 进行绑定。

Student mStudent = new Student("guo","chengqian");
private ActivityMainBinding mBinding;
Boolean visiblity = true;@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);//绑定变量mBinding.setStudent(mStudent);mBinding.setPresenter(new Presenter());mBinding.setVisibility(visiblity);
}

ActivityMainBinding类中所有的set方法是根据variable名称生成的。我们在XML文件中定义了三个变量,所以会生成三个set方法。

在XML中使用变量

当数据传入ViewDataBinding与Variable绑定之后,xml的UI元素就可以直接使用了。

<TextViewandroid:id="@+id/textView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@{student.firstName}"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:visibility="@{visibility?View.VISIBLE:View.GONE}"android:text="@{student.lastName}"
/>

直接在对应属性上@{variable}就可以使用该变量的值以及对应的属性。

现在运行程序,就会看到我们传入Student的姓名已经显示出来了

1500001896236.png

3. 事件方法绑定

仅仅是在UI上绑定数据还远远不够,UI上的响应事件也需要被处理,因此需要要把响应事件的方法绑定到View上。

事件方法的绑定有两种方法:第一种是方法引用,这种方法要求函数的参数个数,类型以及顺序都会原生的事件方法相同;第二种方法是使用lambda表达式,这种方法更为灵活,可以自定义参数。

我们来看下例子:
这是一个方法类的实现,之前在xml的data节点中已经声明了变量,在Activity中也进行了绑定。

//方法类
public class Presenter{//方法引用 参数要求符合接口定义public void onTextChanged(CharSequence s, int start, int before, int count){mStudent.setFirstName(s.toString());//如果已经为该类继承了BaseObservable 就不需要再赋值了;//mBinding.setStudent(mStudent);}//方法引用,参数要求符合接口定义;public void onClick(View view){Toast.makeText(MainActivity.this,"onClick!",Toast.LENGTH_SHORT).show();}//自定义方法public void onClickListenerBinding(Student student){Toast.makeText(MainActivity.this, student.getLastName(),Toast.LENGTH_SHORT).show();}
}

首先在EidtText控件中使用,将onTextChanged方法绑定。

<EditTextandroid:id="@+id/first_name_ed_txt"android:onTextChanged="@{presenter.onTextChanged}"android:layout_width="wrap_content"android:layout_height="wrap_content"android:hint="First Name"/>

这样一旦输入的内容改变,变量student中对应的firstName属性也会跟着改变。

同样,我们将onClick方法绑定到一个TextView上

<TextViewandroid:id="@+id/textView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@{student.firstName}"android:onClick="@{presenter.onClick}"/>

但是这种方法不够灵活,绑定的方法必须和View所要求的接口参数一致。为了更为灵活的绑定方法,还可以使用Lambda表达式。

   <TextViewandroid:id="@+id/textView2"android:layout_width="match_parent"android:layout_height="wrap_content"android:visibility="@{visibility?View.VISIBLE:View.GONE}"android:text="@{student.lastName}"android:onClick="@{()-> presenter.onClickListenerBinding(student)}"/>

lambda表达式实际上也是创建了匿名函数,然后再这个匿名函数中调用了Presenter中的方法,只不过省去了匿名函数的申明,只保留最为核心的逻辑部分。

此外,lambd表达式支持传入Context参数,比如Presenter中定义这样的函数

public void onStudentLongClick(Student student, Context context){Toast.makeText(LambdaActivity.this, "LongClick: "+student.toString(), Toast.LENGTH_SHORT).show();
}

Button中可以这样调用,Context参数会被自动生成:

<Button......android:longClickable="true"android:onClick="@{() -> presenter.onStudentLongClick(student,context)}"....../>

4. DataBinding所支持的表达式

为了扩展更为强大的功能,DataBinding在使用变量时支持一下表达式:

  • 运算符:Mathematical + - / * %
  • 字符串连接:String concatenation +
  • 逻辑运算:Logical && ||
  • 位操作: Binary & | ^
  • Unary + - ! ~
  • 移位操作:Shift >> >>> <<
  • 比较操作:Comparison == > < >= <=
  • 类型判断:instanceof
  • Grouping ()
  • Literals - character, String, numeric, null
  • 类型转化:Cast
  • Method calls
  • Field access
  • Array access []
  • Ternary operator ?:

但不支持的表达式:

  • this
  • super
  • new
  • Explicit generic invocation

Null Coalescing 运算符

DataBinding对于引用变量是否为空,也有特殊的支持,使用符号??

android:text="@{student.firstName ??student.lastName}"

就等价于

android:text="@{student.firstName != null ? student.firstName : student.lastName}"

5. 在include文件里使用BataBinding

为了让Xml布局文件更为简洁易懂,对于重复使用的布局可以单独建立布局文件,然后使用include导入。DataBinding也考虑到了这种使用情况。可以使用bind:variable_name导入数据变量。

<!--当你使用include的时候,你可以使用命名空间和属性中的变量名将数据传送到另一个布局中去-->  
<include  layout="@layout/include_show_name"  bind:presenter="@{presenter}"bind:student="@{student}"bind:visibility="@{visibility}" /> 

需要注意的是:

  1. 一定要使用app命名空间xmlns:app="http://schemas.android.com/apk/res-auto"
  2. 必须在ViewGroup的根布局中导入布局文件,如果在非根节点的ViewGroup中使用include会导致错误;

6. 在SubView中的使用

在SubView的使用方式和在Include中使用是类似的。

<ViewStubbind:visibility="@{visibility}"android:id="@+id/view_stub"android:layout_width="368dp"android:layout_height="wrap_content"android:layout="@layout/viewstub"
/>

然后在Activity中通过DataBindingUtil获得SubView对象,然后填充View;

//viewstub inflate;
View inflate = mBinding.viewStub.getViewStub().inflate();

7. 双向绑定

上面提的内容都是单向绑定,也就是把后台的数据绑定到前台显示出来。DataBinding如果只有如此功能,也不足为奇,最重要的功能点还是还在于双向绑定,前台显示的数据改变也会自动影响后台的数据。

首先我们新建一个数据对象类

public class FormModel extends BaseObservable {private String name;private String password;@Bindable //该注解表示数据绑定public String getName() {return name;}public void setName(String name) {this.name = name;//通知name属性发生改变notifyPropertyChanged(BR.name);}//注解标志该属性是绑定的@Bindablepublic String getPassword() {return password;}public void setPassword(String password) {this.password = password;//通知有属性发生改变notifyPropertyChanged(BR.password);}public FormModel(String name, String password) {this.name = name;this.password = password;}
}

该类继承了BaseObservable,在getter方法之前使用@Bindable,标记该属性就是需要绑定的,会在BR类中生成对应的ID;在setter方法中notifyPropertyChanged(BR.ID),通知对应ID的属性发生了改变。

然后在XML中使用“@={variable}”表示数据绑定,如下。

<layoutxmlns:android="http://schemas.android.com/apk/res/android"        xmlns:tools="http://schemas.android.com/tools"><data><variablename="model"type="com.example.administrator.databindingdemo.FormModel"/></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.example.administrator.databindingdemo.TowWayBindingActivity"><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Name"android:text="@={model.name}"/><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:hint="pwd"android:text="@={model.password}"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="@{model.name}"/></LinearLayout>
</layout>

最后在Activity中传入变量。

public class TowWayBindingActivity extends AppCompatActivity {ActivityTowWayBindingBinding mBinding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding = DataBindingUtil.setContentView(this,R.layout.activity_tow_way_binding);FormModel model = new FormModel("user", "12345");mBinding.setModel(model);//属性改变监听器model.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {@Overridepublic void onPropertyChanged(Observable sender, int propertyId) {if(BR.name == propertyId)Toast.makeText(TowWayBindingActivity.this, "姓名改变",Toast.LENGTH_SHORT).show();}});}
}

这是运行程序就会发现,在前端输入姓名时,其他绑定name属性的地方也会改变,说明后台数据也响应改变了。

8. 表达式链与隐式更新

当多个View的属性相互关联式时,可以通过表达式链实现隐式更新,比如:

<CheckBox android:id="@+id/check_box"/>
<ImageView android:visibility="check_box.checked?View.VISIBILE:View.GONE"/>

9. 添加动画

数据绑定必然涉及到UI的变化,默认情况下往往都是很生硬的UI变化,没有任何过度。DataBinding也提供办法来插入动画。

实现方法也很简单,就是设置一个重新绑定时的回调函数addOnRebindCallback,在函数中设置当前View的UI变化时开启一定延时。

public class AnimationActivity extends AppCompatActivity {ActivityAnimationBinding mBinding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding = DataBindingUtil.setContentView(this,R.layout.activity_animation);mBinding.setPresenter(new AnimatorPresenter());//设置重绑定回调函数mBinding.addOnRebindCallback(new OnRebindCallback() {@Overridepublic boolean onPreBind(ViewDataBinding binding) {ViewGroup viewGroup = (ViewGroup) binding.getRoot();//开启延时位移动画          TransitionManager.beginDelayedTransition(viewGroup);return true;}});}public class AnimatorPresenter{public void onCheckedChanged(boolean isChecked){mBinding.setShowImage(isChecked);}}
}

在XML中正常使用DataBinding

<layoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data><import type="android.view.View"/><variablename="presenter"type="com.example.administrator.databindingdemo.AnimationActivity.AnimatorPresenter"/><variablename="showImage"type="Boolean"/></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.example.administrator.databindingdemo.AnimationActivity"><ImageViewandroid:contentDescription="Animation Show"android:layout_width="100dp"android:layout_height="100dp"android:src="@drawable/default_avatar"android:visibility="@{showImage?View.VISIBLE:View.GONE}"/><CheckBoxandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="showImage"android:onCheckedChanged = "@{(v,isChecked)->presenter.onCheckedChanged(isChecked)}"/></LinearLayout>
</layout>

这样图片在显示或者出现的过程中都会添加一个CheckBox位移匀速渐变的动画效果。

需要说明的是,虽然布局文件中CheckBox原本并没有onCheckedChanged属性,但是CheckBox类有对应的属性,并且设有setOnCheckedChanged方法来设置该属性,所以也是可以进行绑定的。

按照惯例,给出Demo源码:
https://github.com/sunrongxin7666
欢迎大家指正批评

需要说明的是,由于篇幅的限制,还有动态绑定和在RecyclerView中使用没有介绍,如果感兴趣请读者参考一下两篇文章,基于以上的内容,相信读者不会很难理解。
Android DataBinding完全解析
精通 Android Data Binding



作者:登高且赋
链接:https://www.jianshu.com/p/0fe0b6b7dae1
來源:简书

这篇关于Android DataBinding 实战全解【转载】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100171

相关文章

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount