Android开发必看:一文教你完全理解DataBinding框架(下

tv_msg 会自动生成 tvMsg 字段,因此可以直接调用相关方法。

11. 在 Preview 窗口显示视图的默认值

注意,这里说的预览窗口的视图,而不是真实运行时的视图,我们经常需要在预览窗口预览视图,比如 TextView,有两种方法可以设置。如下:

其中 tools 的优先级较高,同时设置则显示 tools 设置的内容。

12. 在非 UI 线程更改数据

如果绑定的是 LiveData,那么在子线程更新数据,可以使用 postValue。

如果绑定的是 ObservableXxxx 类型的,其 set 方法也可以直接在子线程中调用,DataBinding 支持在子线程中改变数据,也能响应到 UI 上,而且是线程安全的,不过不可以是集合类型的,比如 ObservableArrayMap,是线程不安全的。

因此如果是集合类型的数据,则应该统一在主线程更改数据。

13. 列表绑定

建议采用第三方绑定库:binding-collection-adapter,可以很方便的实现列表绑定。

如果是自己写 Adapter,最关键的地方就在于使用 setVariable 和 executePendingBindings 方法刷新数据,这里就不展开叙述了,需要自行搜索吧。

14. 转换器

举个例子,比如你有个 float 值,需要设置给 TextView 的 text 属性,而 text 要求是字符串,很明显 float 不符合要求,那么就可以定义一个转换器,将 float 转成 String,如下:

package com.imyyq.sample

import androidx.databinding.BindingConversion

@BindingConversion
fun convert(value: Float?) = value?.toString() + “我列个去”

入参是待转换的类型,出参是转换后的类型,即 Float 和 String。

那么就可以在 xml 中使用了。

val mmMutableLiveData2 = MutableLiveData(100.222f)

可能 xml 会报错,这就是 DataBinding 还不是很智能的一个体现,但是已经是可以运行了。以上只是举个例子,总之这个功能是用来转换类型的,会自动匹配入参和出参。

如果你设置如下属性,可能会感到疑惑:

android:background=“@{@android:color/black}”

View 的 setBackground(Drawable background) 方法入参明明是个 Drawable 啊,而 @android:color/black 明显是个 int,int 怎么能赋值给 Drawable 呢?那是因为内置的 androidx.databinding.adapters.Converters 类已经定义了如下转换器:

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}

所以不需要你再定义转换器了。

15. 绑定 View 的方法

DataBinding 库给很多常用属性都添加了支持,比如上面我们用到的 android:text,android:onClick 等,遇到不支持的属性呢?有两种解决方法,可以自定义绑定,也可以根据这个 View 的公共方法名作为属性,自定义绑定我们下面再说,这里先说下绑定 View 的公共方法。

举个例子,TextView 在 xml 中是没有 android:selected 属性的,如果你直接设置,会提示你未知属性:

如果你不使用 DataBinding,即没有 @{} 包裹该值:

android:selected=“true”

那么运行都没法运行,因为没有找到这个属性。那么为什么用 DataBinding 就可以呢? 因为在 TextView 中,有 setSelected 方法,因此,只要你的 View 中,不管是自定义的 View,还是官方的 View,只要提供 setXxxx 方法,就可以有 xxxx 属性,即去掉 set 后把首字母小写,而且支持继承的方法。

比如 View 中 setAlpha 方法,TextView 继承自 View,那么就可以有 alpha 属性。不过对未定义的属性使用 android: 开头的,即使能用,也会报黄色,如上图,所以我们最好还是用 app: 作为开头,如下:

如果不是 set 开头的方法,普通的公共方法也可以的,比如 EditText 的 extendSelection(int index) 方法就不是 set 开头的,那么直接:app:extendSelection=“@{2}” 即可绑定这个方法。

如果你不喜欢默认的方法名称呢?比如你有个自定义 View,有个方法是 test,如下:

@BindingMethods(value = [
BindingMethod(
type = CustomEditText::class,
attribute = “myTest”,
method = “test”)])
class CustomEditText(context: Context?, attrs: AttributeSet?) : AppCompatEditText(context, attrs) {
fun test(index: Int = 0) {
Log.i(“CustomEditText”, “commonLog - test: $index”)
}
}

那么即可在使用 app:test 属性,还可以直接用 myTest 属性

<com.imyyq.sample.CustomEditText
android:id=“@+id/et”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:text=“我是一个小蜜蜂”
myTest=“@{10086}”
app:test=“@{10086}”
/>

通过 BindingMethod 注解,可以自定义属性名,并绑定指定方法。当绑定的数据变化时,会自动调用该方法。

16. 自定义绑定

上面我们讲解了如何绑定已有的方法,那么如果要绑定的方法是无参数的呢?比如 EditText 的 requestFocus() 方法,显然不能通过属性绑定,因为 xml 属性都是有值的。

假设你想对属性自定义自己的逻辑呢?比如 TextView 的 setText 方法,我想要在设置内容的时候做一个判断,把内容中所有的空格换成 = 号,当然,实现这个需求的方式有很多种,这里我们只是拿它来举例,我们该怎么做呢?

答案是通过 BindingAdapter 注解。如下:

@BindingAdapter(value = [“changeText”])
fun changeText(view: TextView, text: String) {
view.text = text.replace(" ", “=”)
}

那么 TextView 就自动拥有了 changeText 这个属性了,如下:

文字间的空格就都会被替换成 = 号。

注意:这里没有指定前缀,所以不能是 app:changeText,一定要和定义的一致

还可以有多个属性:

@BindingAdapter(value = [“imageUrl”, “placeholder”], requireAll = false)
fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) {
}

其中 requireAll 如果为 true,那么必须同时设定以上两个属性,才能绑定成功。为 false 的话,只要有设置 value 定义的其中一个属性,都可以绑定该方法,其他未设置的 value 都是 null。所以如果是 Kotlin 的话,参数得是可空的。

还可以接收旧值,比如:

@BindingAdapter(“android:paddingLeft”)
fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
if (oldPadding != newPadding) {
view.setPadding(newPadding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom())
}
}

这样当你使用 android:paddingLeft 属性时,每当值改变,都会调用 BindingAdapter 注解的 setPaddingLeft 方法,并且将旧值和新值都传过去。

建议自定义的属性不要使用 android: 开头,也不要定义前缀,直接是属性名即可,这样的话, android: 是原生定义的,app: 是 View 的方法,而无前缀的就是自定义的,三类分开,更好读。

自定义绑定参数要注意的点

如果你是 Kt 开发的,那么要注意 BindingAdapter 注解的方法参数要可空,而且不要使用默认参数,如下:

@BindingAdapter(
value = [“onClickCommand”, “isInterval”, “intervalMilliseconds”],
requireAll = false
)
fun onClickCommand(
view: View,
clickCommand: View.OnClickListener,
isInterval: Boolean = GlobalConfig.Click.gIsClickInterval,
intervalMilliseconds: Int = GlobalConfig.Click.gClickIntervalMilliseconds
) {
if (isInterval) {
view.clickWithTrigger(intervalMilliseconds.toLong(), clickCommand)
} else {
view.setOnClickListener(clickCommand)
}
}

以上 isInterval 和 intervalMilliseconds 不仅不可空,还设置了默认参数。但是 Java 是没有默认参数的,所以使用时如果不设置这两个参数,依然会生成默认值,如下:

这样 Kt 的默认参数就失效了,所以应该要可空,而且不要默认参数。如下:

@BindingAdapter(
value = [“onClickCommand”, “isInterval”, “intervalMilliseconds”],
requireAll = false
)
fun onClickCommand(
view: View,
clickCommand: View.OnClickListener?,
isInterval: Boolean?,
intervalMilliseconds: Int?
) {
var interval = isInterval
// xml中没有配置,那么使用全局的配置
if (interval == null) {
interval = GlobalConfig.Click.gIsClickInterval
}
// 没有配置时间,使用全局配置
var milliseconds = intervalMilliseconds
if (milliseconds == null) {
milliseconds = GlobalConfig.Click.gClickIntervalMilliseconds
}
if (interval) {
clickCommand?.let { view.clickWithTrigger(milliseconds.toLong(), it) }
} else {
view.setOnClickListener(clickCommand)
}
}

这样就会生成正确的代码:

17. 自定义双向绑定

上面我们已经说了 EditText 的双向绑定,对于常用的内置属性,使用 @={} 即可,当你是自定义 View 时,需要双向绑定,就得自定义了。我们来个简单的例子,你就明白怎么自定义了。

先来个自定义 View:

class CustomView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
var data: String = “”
}

再自定义绑定的属性:

@BindingAdapter(“time”)
fun setTime(view: CustomView, newValue: String) {
Log.i(“MyBindingConversion”, “commonLog - setTime: $newValue”)
if (view.data != newValue) {
view.data = newValue
}
}

@InverseBindingAdapter(attribute = “time”, event = “timeAttrChanged”)
fun getTime(view: CustomView): String {
Log.i(“MyBindingConversion”, "commonLog - getTime: ")
return view.data
}

@BindingAdapter(“timeAttrChanged”)
fun setListeners(
view: CustomView,
attrChange: InverseBindingListener
) {
Log.i(“MyBindingConversion”, "commonLog - setListeners: ")
// 设置 view 改变的监听,看需求可以是点击,滑动,双击,长按什么的。
view.setOnClickListener {
view.data = “click”
attrChange.onChange()
}
}

以上定义了一个 CustomView,通过 @BindingAdapter 给它定义了 time 属性,也就是属性变化就会调用 setTime,即正向绑定,这个好理解

然后通过 @InverseBindingAdapter 注解,给 time 属性设置了反向绑定,触发反向绑定的方法可通过 event 属性指定,如上指定了 event = timeAttrChanged,对应了第三个方法 setListeners 方法。其实也可以不用显式声明 event,只要在定义的属性 time 后面加上 AttrChanged,即可自动绑定,如上面的 @BindingAdapter(“timeAttrChanged”)。

使用如下:

val mmMutableLiveData3 = MutableLiveData(“start”)

mmMutableLiveData3.value = “end”

<com.imyyq.sample.CustomView
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
time=“@={viewModel.mmMutableLiveData3}”
/>

运行 log 如下:

I: commonLog - setTime: start
I: commonLog - setListeners:
I: commonLog - setTime: end

I: commonLog - getTime:
I: commonLog - setTime: click

首先 mmMutableLiveData3 的默认值是 start,所以调用 setTime,传入 start。然后因为是双向绑定,所以会调用事件监听方法 setListeners,然后 mmMutableLiveData3 的值改变为 end,又再次调用了 setTime。这个过程好理解。

然后 setListeners 定义了 view 的监听事件,当我点击了 view 后,会把 data 属性改成 “click”,然后调用 attrChange.onChange(),则又会触发 setTime 方法,并把新的值传过去。同时 mmMutableLiveData3 的值也会跟着被改成了 click。

以上就完成了自定义的数据双向绑定。要注意的是不要造成死循环了,比如选择地址时,常用滚轮组件,就要避免造成双向绑定死循环。

5、结语

以上就是 DataBinding 的全部功能了,有遗漏吗?如果发现遗漏,欢迎补充哈!我尽可能的都列出来了,主要是要知道有相关的功能,如果觉得某个功能本文说的不详尽,完全可以去搜索与之相关的其他内容。

学完以上内容,你确定不来玩玩 DataBinding 和 MVVMArchitecture 框架吗?

个人觉得 DataBinding 虽好,但是也不要被它禁锢了,刚开始不熟悉的时候,可以慢慢来,遇到实在比较复杂的业务,可以使用 observe LiveData 的方式先完成了,再考虑用 DataBinding 去优化,等你熟悉了,就可以一步到位了。

好了,希望本文可以帮你熟悉使用 DataBinding,祝你好运。

6、最后

Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。

我把自己这段时间整理的Android最重要最热门的学习方向资料放在了**我的GitHub**,里面还有不同方向的自学编程路线、面试题集合/面经、及系列技术文章等。

资源持续更新中,欢迎大家一起学习和探讨。

原文:https://juejin.im/post/6857815150565687303/#heading-4
来源:简书 imyyq_star

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后看一下《Android框架体系架构(高级UI+FrameWork源码)》学习需要的所有知识点的思维导图。在刚刚那份学习笔记里包含了下面知识点所有内容!文章里已经展示了部分!如果你正愁这块不知道如何学习或者想提升学习这块知识的学习效率,那么这份学习笔记绝对是你的秘密武器!

1666)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-Gza9bJVm-1710566851666)]

最后看一下《Android框架体系架构(高级UI+FrameWork源码)》学习需要的所有知识点的思维导图。在刚刚那份学习笔记里包含了下面知识点所有内容!文章里已经展示了部分!如果你正愁这块不知道如何学习或者想提升学习这块知识的学习效率,那么这份学习笔记绝对是你的秘密武器!

[外链图片转存中…(img-R53LGFt0-1710566851666)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>