android:layout_width=“match_parent”
android:layout_height=“match_parent”>
</androidx.constraintlayout.widget.ConstraintLayout>
在activity中,DataBinding直接生成了对应的setter方法。所以可以直接使用setVideoEntity将对象传递给布局类。如下所示:
val videoEntity = VideoEntity()
//绑定实体类
binding.videoEntity = videoEntity
- 绑定布局变量
将对象传递进去直接,就可以通过@{}
直接在布局文件中将对应的变量绑定给控件了
<TextView
android:text=“@{videoEntity.videoName}” />
<TextView
android:text=“@{videoEntity.videoIntroduction}” />
<TextView
android:text=“@{videoEntity.videoStarring}” />
在@{}
中也可以直接使用静态方法
<TextView
android:text=“@{String.valueOf(1)}” />
==================================================================================
通过Button控件,演示DataBinding如何响应onClick事件。
- 创建布局文件转为DataBinding布局
<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”>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”>
</androidx.constraintlayout.widget.ConstraintLayout>
- 声明一个EventHandleListener专门处理点击事件
class EventHandleListener(private val videoEntity:VideoEntity) {
fun onButtonClick1(view: View) {
Toast.makeText(context, “click1”, Toast.LENGTH_SHORT).show()
}
fun onButtonClick2(view: View) {
Toast.makeText(context, “click2”, Toast.LENGTH_SHORT).show()
}
}
- 布局中定义EventHandleListener变量,并将点击事件传递进button。通过双冒号语法
::
进行调用
<variable
name=“eventHandlerLayout”
type=“com.zxf.jetpackrelated.databinding.simpleUse.SimpleDataBindingActivity.EventHandleListener”/>
<Button
android:onClick=“@{eventHandlerLayout::onButtonClick1}”
/>
<Button
android:onClick=“@{eventHandlerLayout::onButtonClick2}”
/>
- activity中将EventHandleListener传递给布局文件
binding.eventHandler = EventHandleListener(videoEntity)
=====================================================================================
简单来说就是怎么将,数据传递到通过<include>
标签包裹的二级页面呢?
下面就将上面的按钮响应事件通过<include>
包裹一下,将EventHandleListener进行一个二级传递。
- 在一级页面定义
<include>
,将上面的按钮布局包裹进来
<include
android:id=“@+id/in_bt”
layout=“@layout/layout_databinding_base” />
- 在一级页面同样声明一下EventHandleListener变量,可以通过二级页面声明的EventHandleListener的name,将对象直接传递给二级页面
<variable
name=“eventHandler”
type=“com.zxf.jetpackrelated.databinding.simpleUse.SimpleDataBindingActivity.EventHandleListener” />
<include
android:id=“@+id/in_bt”
layout=“@layout/layout_databinding_base”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
app:eventHandlerLayout=“@{eventHandler}”/>
====================================================================================
当启用databinding
之后,会生成大量的类,其中包括针对UI的各种命名为XXXBindAdapter的类。
截取一下TextViewBindingAdapter类的部分源码。源码展示了DataBinding库针对android:text属性所编写的代码
@BindingAdapter(“android:text”)
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don’t set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don’t set anything.
}
view.setText(text);
}
在布局文件中通过@{}
绑定的text,最终通过生成的BindingImpl类,调用了对应的setText静态方法。
所以总的来说,在布局文件里面绑定的数据,可以调用到BindingAdapter修饰的静态方法。
=================================================================================
仅官方提供的BindAdapter肯定是不够的,所以需要自定义BindAdapter,实现更多的更复杂的业务关系。
下面展示使用ImageView自定义BindAdapter。
- BindAdapter注解介绍
@Target(ElementType.METHOD)
public @interface BindingAdapter {
String[] value();
boolean requireAll() default true;
}
-
value
即分配给布局文件中使用的名字 -
requireAll
如果需要一下子定义多个value
,用于区别是否所有的都需要,默认为true,如果为true,则布局文件中,一旦使用了其中一个其他的也必须要声明,不然编译不通过。 -
使用glide进行图片加载
添加依赖和网络访问权限
implementation ‘com.github.bumptech.glide:glide:4.12.0’
annotationProcessor ‘com.github.bumptech.glide:compiler:4.12.0’
- 定义ImageView的BindAdapter类
需要注意的是,BindingAdapter中的方法均为静态方法。第1个参数为调用者本身,即ImageView;第2个参数是布局文件在调用该方法时传递过来的参数
对于Kotlin
来说,只需要将函数声明为顶级函数,就对应java的静态方法了。
@BindingAdapter(“image”)
fun setImage(imageView: ImageView, imageUrl: String?) {
if (imageUrl.isNotNullOrEmpty()) {
Glide.with(imageView)
.load(imageUrl)
.into(imageView)
} else {
imageView.setBackgroundColor(Color.BLUE)
}
}
//isNotNullOrEmpty 是自定义的扩展函数
fun CharSequence?.isNotNullOrEmpty(): Boolean {
return !isNullOrEmpty()
}
- 布局文件中调用BindingAdapter类
方便期间,直接使用上面的VideoEntity类了,里面有对应的图片地址字段videoImageUrl
然后使用image
绑定布局
<ImageView
app:image=“@{videoEntity.videoImageUrl}” />
- 运行结果(加上之间简单使用的整体结果)
- BindAdapter方法重载
如果想网络图片加载url不存在则加载本地资源呢?多传递一个参数就好了,怎么传呢?举个🌰
/**
-
方法的参数以value={“”,“”}的形式存在
-
变量requireAll用于告诉DataBinding库这些参数是否都要赋值,默认值为true,即全部需要赋值,这里写成false;
-
如果设置为true,则对应的属性必须在xml一起生命,否则编译报错
*/
@BindingAdapter(value = [“image”, “defaultImageRes”], requireAll = false)
fun setImage2(imageView: ImageView, imageUrl: String?, @DrawableRes imageRes: Int) {
if (imageUrl.isNotNullOrEmpty()) {
Glide.with(imageView)
.load(imageUrl)
.into(imageView)
} else {
imageView.setImageResource(imageRes)
}
}
- BindAdapter可选旧值
在某些情况下,可能希望在方法中得到该属性之前的值。假如,在修改控件的padding时,可能希望得到修改前的padding,以防止方法重复调用。
举个🌰
/**、
- 需要注意的是,使用可选旧值时,方法中的参数顺序需要先写旧值,后写新值。即oldPadding在前,newPadding在后。
*/
@BindingAdapter(value = [“padding”])
fun setPadding(view: View, oldValue: Int, newValue: Int) {
LogUtil.d(“------------paddingValueLog: oldValue: o l d V a l u e n e w V a l u e : oldValue newValue: oldValuenewValue:newValue”)
if (oldValue != newValue) {
view.setPadding(newValue, newValue, newValue, newValue)
}
}
需要注意的是,使用可选旧值时,方法中的参数顺序需要先写旧值,后写新值。即oldPadding在前,newPadding在后。
绑定的代码就不写了,做了个例子,默认设置padding为30,点击时变为60,看一下控制台打印的数据
2021-10-12 11:10:06.690 12768-12768/com.zxf.jetpackrelated D/[setPadding(CustomBindingAdapterKt:74)]: ------------paddingValueLog: oldValue:0 newValue:30
2021-10-12 11:10:08.252 12768-12768/com.zxf.jetpackrelated D/[setPadding(CustomBindingAdapterKt:74)]: ------------paddingValueLog: oldValue:30 newValue:60
很明显看到,程序启动时,padding由0变为30。单击button后,程序由30变为60.
=========================================================================
上面所有写的都只是简单的绑定,当数据变化后,目前是没有通知到UI进行刷新,同时UI刷新后,数据也没有变化。
下面通过EditText和TextView控件简单实现双向绑定。
-
使用BaseObservable实现双向绑定
-
编写实体类DisplayEntity
class DisplayEntity(
var displayStr: String
)
- 编写一个用于存放与实现双向绑定相关的业务逻辑的类,继承BaseObservable
class TwoBindingEntityObservable : BaseObservable() {
private val displayEntity: DisplayEntity = DisplayEntity(“双向绑定测试”)
/**
-
在getter 方法上面增加@Bindable 告诉编译器希望对这个字段进行双向绑定
-
在xml中直接使用displayStr 进行绑定
*/
@Bindable
fun getDisplayStr(): String = displayEntity.displayStr
/**
-
setter 方法在用户编译edittext的时候被自动调用,需要在这里面对displayStr字段进行手动更新
-
调用notifyPropertyChanged方法通知观察者数据已变更
-
需要对值进行判断否则会产生循环调用的问题
*/
fun setDisplayStr(displayStr: String) {
//需要对值进行判断否则会产生循环调用的问题
if (displayStr.isNotNullOrEmpty() && displayStr == displayEntity.displayStr) {
return
}
displayEntity.displayStr = displayStr
notifyPropertyChanged(BR.displayStr)
}
}
初始化时为字段displayStr设置了默认值,接着为该字段写了Getter和Setter方法。注意,在Getter方法前加上了@Bindable标签,告诉编译器,希望对这个字段进行双向绑定。而Setter方法会在用户编辑EditText中的内容时,被自动调用,需要在该方法内对userName字段进行手动更新。
notifyPropertyChanged()是BaseObservable类中的一个方法,会通知UI进行刷新。
- 设置布局以及activity(注意::双向绑定使用
@={}
)
<variable
name=“entityObservable”
type=“com.zxf.jetpackrelated.databinding.twoWayBinding.TwoBindingEntityObservable” />
<EditText
android:text=“@={entityObservable.displayStr}”/>
<TextView
android:text=“@={entityObservable.displayStr” />
binding.entityObservable = TwoBindingEntityObservable()
-
这样在编辑EditText的时候,对应的TextView也会展示对应的数据,实现了双向绑定
-
使用ObservabIeFieId更简单的实现双向绑定
-
创建ObservabIeFieId
class TwoBindingEntityObservable3 {
val displayEntityField = ObservableField(“双向绑定测试-use observableField”)
}
- 设置布局和activity
<variable
name=“entityObservable3”
type=“com.zxf.jetpackrelated.databinding.twoWayBinding.TwoBindingEntityObservable3” />
<EditText
android:text=“@={entityObservable3.displayEntityField}”/>
<TextView
android:text=“@={entityObservable3.displayEntityField” />
binding.entityObservable = TwoBindingEntityObservable3()
- 就可以了,是不是很简单!
databinding默认实现了一系列实现Observable接口的字段类型,可以选择使用
BaseObservable,
ObservableBoolean,
ObservableByte,
ObservableChar,
ObservableDouble,
ObservableField,
ObservableFloat,
ObservableInt,
ObservableLong,
ObservableParcelable,
ObservableShort,
ViewDataBinding
===========================================================================================
同理可以自定义BindAdapter实现更加个性化的绑定(通知到UI),那么一样可以自定义当UI变化进行反绑(通知到数据)
这时候就用到了InverseBindingAdapter。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持)
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
- Android BAT大厂面试题(有解析)
中…(img-jaVCk9tf-1711961106945)]
[外链图片转存中…(img-hsJDpxoH-1711961106945)]
[外链图片转存中…(img-9miAICYJ-1711961106945)]
[外链图片转存中…(img-Tn3ETZGZ-1711961106946)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-X8um5aqb-1711961106946)]
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持)
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
[外链图片转存中…(img-HR338Bmw-1711961106946)]
[外链图片转存中…(img-HhbnMWIP-1711961106947)]
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
[外链图片转存中…(img-bRYdzjNN-1711961106947)]
- Android BAT大厂面试题(有解析)
[外链图片转存中…(img-ovHsu6MS-1711961106947)]