【Jetpack】学穿:DataBinding → 数据绑定 (使用篇)(上)

简介: 前面的章节 《【Jetpack】学穿:ViewBinding → 视图绑定》 剥源码的时候就有看到 DataBinding 相关的代码。ViewBinding(视图绑定) 的作用和原理一言以蔽之:作用 → 代替findViewById 的同时,还能保证 空安全 和 类型安全,且 支持Java;原理 → AGP为模块中的每个XML生成绑定类,本质上还是findViewByid,只是自动生成控件实例,并一一对应;

0x1、引言


继续回来学穿Jetpack,带来第三个组件 DataBinding (数据绑定)。在前面的章节 《【Jetpack】学穿:ViewBinding → 视图绑定》 剥源码的时候就有看到 DataBinding 相关的代码。


ViewBinding(视图绑定) 的作用和原理一言以蔽之:


  • 作用代替findViewById 的同时,还能保证 空安全类型安全,且 支持Java
  • 原理 → AGP为模块中的每个XML生成绑定类,本质上还是findViewByid,只是自动生成控件实例,并一一对应;


可以把 ViewBinding 看做 DataBinding 功能的 子集,它有的DataBinding都有,而且还多了 数据绑定


何为数据绑定? 在维基百科中的定义如下:


是将 "提供器" 的数据源与 "消费者" 绑定并使其同步的一种通用技术。通常用两种不同语言的数据/信息源完成,如XML数据绑定。在UI数据绑定中,相同语言但不同逻辑功能的数据与信息对象被绑定在一起(例如Java UI元素到Java对象)。在数据绑定过程中,每个数据更改会由绑定到数据的元素自动反射。术语"数据绑定"也指一个外部数据表示随元素更改产生变化,并且底层数据自动更新以反映此更改。


又长又臭,举个简单例子就秒懂了:


一个存储数量的变量count,一个显示数量的TextView,两者绑定,当修改count的值时,TextView自动刷新。


数据源(Model)更新,绑定视图(View) 自动更新,不用开发仔再去手动setXxx(),道理就这么简单。


这种玩法又叫 单向绑定,还有一种 双向绑定,绑定视图发生改变时,数据源也跟着改变,比如:


点击显示数量的TextView,显示的数量自增1,存储数量的变量也自增1。


互相影响,这就是双向绑定。咳...都是些浅显的概念,具体怎么做?


观察者模式 实现,数据变量与View实例关联,数据变量有更新时,遍历回调关联View实例对应设置值的方法。


自己造轮子,可以,但Duck不必~


Jetpack库中的 DataBinding组件 已经封装好一套了,要做的就是熟读文档,然后大胆使用~


API变化日新月异,建议以官方文档为准《数据绑定库》,本文也是基于此文档展开的学习。


0x2、写个最简单的例子


通过一个超简单的例子来帮助大家了解DataBinding,先有基础认知,再往下学就容易多了。


① 启用DataBinding


DataBinding与AGP捆绑,无需声明这个库的依赖,在模块级别的 build.gradle 添加下述配置启用即可 ( 区分AS版本 )。


apply plugin: 'kotlin-kapt'
android {
    ...
    // AS 4.0以下
    dataBinding{
        enabled true
    }
    // AS 4.0及以上
    buildFeatures {
        dataBinding true
    }
    // 还可以这样写
    buildFeatures.dataBinding = true
}


② 用DataBinding之前


未使用DataBinding之前,先写布局:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tv_test"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:gravity="center"
        android:text="计数器:0" />
    <Button
        android:id="@+id/bt_test"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="加1" />
</LinearLayout>


再写Activity:


class TestActivity : AppCompatActivity() {
    private var mCount: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)
        findViewById<Button>(R.id.bt_test).setOnClickListener {
            findViewById<TextView>(R.id.tv_test).text = "计数器:${++mCount}"
        }
    }
}


运行效果如下(点击按钮,计数器自增1):


网络异常,图片无法展示
|


司空见惯的常规操作,代码中主动setText()去更新TextView的文本,接着换成DataBinding试试看。


③ 用DataBinding之后


来到布局xml文件,鼠标点到 根布局 LinearLayout,按 Alt + Enter,点击 Convert to data binding layout,自动生成一波DataBinding所需的布局。


网络异常,图片无法展示
|


生成后的文件内容:


网络异常,图片无法展示
|


多了两个标签,接着开始改造,data标签中添加属性,修改TextView的android:text指向属性:


网络异常,图片无法展示
|


接着到Activity:


网络异常,图片无法展示
|


运行后,点击加1按钮,计数+1,效果与setText()一致,修改属性值,绑定的TextView文本跟着自动刷新。


看着 灰常简单!接着系统过一波详细用法,读者按需查阅即可~


0x3、详细用法


① xml布局文件


先是必须遵守的铁律:


根结点必须为<layout>,只能存在一个<data>和一个直接子View结点。


1) variable (变量标签)


变量的 属性名name不能包含_下划线,否则再kt文件里会找不到变量,有时可能需要 指定自定义类型


<variable name="user" type="cn.coderpig.awayfornoise.entity.User"/>
<!-- 也可以先import,然后直接用简写类名 -->
<import type="cn.coderpig.awayfornoise.entity.User"/>
<variable name="user" type="User" />
<!-- 当需要使用两个同名但不同包名的类,可以使用alias别名属性 -->
<import type="com.example.User" />
<import type="cn.coderpig.awayfornoise.entity.User" alias="CpUser" />
<variable name="user" type="CpUser" />


2) data (数据标签)


它有个属性class,可以自定义DataBinding生成的类名及路径 (一般不需要):


<!--自定义类名-->
<data class="CustomDataBinding"></data>
<!--自定义生成路径以及类型,自动在包名下生成包以及类-->
<data class=".CustomDataBinding"></data>
<!-- 一般没必要自定义路径,生成位置直接全局搜 CustomDataBindingImpl -->


3) @{}表达式


支持下述运算符和关键字:


  • 算术运算符 + - / * %
  • 字符串连接运算符 +
  • 逻辑运算符 && ||
  • 二元运算符 & | ^
  • 一元运算符 + - ! ~
  • 移位运算符 >> >>> <<
  • 比较运算符 == > < >= <=(请注意,< 需要转义为 &lt;)
  • instanceof
  • 分组运算符 ()
  • 文字 - 字符、字符串、数字、null
  • 类型转换
  • 方法调用
  • 字段访问
  • 数组访问 []
  • 三元运算符 ?:


使用示例如下:


android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'


不支持关键字及操作:this、super、new、显式泛型调用。


null合并运算符(??):如果左边不为Null,取左边,否则取右边,示例如下:


android:text="@{user.displayName ?? user.lastName}"
<!-- 等价于 -->
android:text="@{user.displayName != null ? user.displayName : user.lastName}"


属性引用:表达式中可以引用类的属性,如:


android:text="@{user.lastName}"


空安全:DataBinding生成的代码会 自动检查null值并避免出现空指针异常


如user为null,会为user.lastName分配默认null值,如果引用user.age,age为int,分配默认值0。


View引用:可以通过ID引用布局中其他的View,会将ID转换为 驼峰式大小写,示例如下:


<EditText
    android:id="@+id/example_text"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"/>
<TextView
    android:id="@+id/example_output"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{exampleText.text}"/>


集合:可以使用[]运算符访问集合,如Array、List、Map等,示例如下:


<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String>"/>
    <variable name="sparse" type="SparseArray&lt;String>"/>
    <variable name="map" type="Map&lt;String, String>"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
<!-- 访问集合 -->
android:text="@{list[index]}"
android:text="@{sparse[index]}"
android:text="@{map[key]}"
<!-- 也可以用object.key表示法在map中引用值 -->
android:text="@{map.key}"


:变量的元素类型type的值不能包含 '<' 字符,直接 List<String> 这样写会引起XML语法错误。需要对 '<' 做下 转义,即 &lt;


字符串


可以用 单引号('') 包裹特征值,这样就可以在表达式中使用双引号了,示例如下:


android:text='@{map["firstName"]}'


可以用双引号扩住特征值,然后用 反单引号(``) 将字符串括起来,示例如下:


android:text="@{map[`firstName`]}"


还支持用 + 号拼接字符串哦~


相关文章
|
6月前
|
前端开发 Android开发
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
|
Android开发
Android JetPack组件之DataBinding的使用详解
Android JetPack组件之DataBinding的使用详解
236 0
|
API Android开发 Kotlin
【Jetpack】学穿:Activity Results API(下)
【Jetpack】学穿:Activity Results API
364 0
|
API 开发者
【Jetpack】学穿:Activity Results API(中)
【Jetpack】学穿:Activity Results API
239 0
|
API 开发者
【Jetpack】学穿:Activity Results API(上)
【Jetpack】学穿:Activity Results API
213 0
|
存储 缓存 数据管理
【Jetpack】学穿:ViewModel → 视图模型(下)
本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态
269 0
|
XML 数据格式 Python
【Jetpack】学穿:ViewModel → 视图模型(中)
本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态
160 0
|
存储 编解码 数据管理
【Jetpack】学穿:ViewModel → 视图模型(上)
本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态
199 0
|
缓存 Java 编译器
【Jetpack】学穿:LiveData → ???(下)
在开始这篇文章前,我就遇到了第一个关于LiveData的问题:该怎么翻译这个词呢?
309 0
【Jetpack】学穿:LiveData → ???(中)
在开始这篇文章前,我就遇到了第一个关于LiveData的问题:该怎么翻译这个词呢?
166 0