在上一篇文章《Android从零开始搭建MVVM架构(1)——Databinding入门》中,我们已经学习了Databinding的基础使用,本篇我们来学习BindingAdapter的用法,我们经常会使用自定义控件还有Android的一些控件,如RecyclerView等,当我们在这些控件的属性,就需要用到BindingAdapter,例如如下的情况:
代码语言:javascript复制 <com.gcssloop.widget.ArcSeekBar
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="10dp"
app:arc_colors="@array/arc_colors_custom"
app:arc_max="100"
app:arc_min="0"
app:arc_open_angle="0"
binding:arc_progress="@{viewModel.currentProgress}"
app:arc_rotate_angle="270"
app:arc_thumb_color="#fff"
app:arc_thumb_mode="FILL"
app:arc_thumb_radius="5.5dp"
app:arc_width="4dp"
/>
这个控件中有一个自定义属性arc_progress,针对于这个属性就需要使用BindingAdapter进行绑定
为什么会有BindingAdapter
我们先来看一个布局:
代码语言:javascript复制 <TextView
android:id="@ id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
当我们给name设值为“Jack”的时候,DataBinding要做的事情就是:要把值为的“Jack”的字符串赋值给TextView的命名空间为android的text属性。
如果是你写DataBinding,面对上面的情况,也许会这样做:
1.忽略命名空间android
2.根据属性值text和binding表达式的值CharSequence “Jack”在TextView中寻找有如下签名的方法:
setText(CharSequence text)
3.在id为user_name的TextView上调用:setText(“Jack”)
但这样会带来什么问题?我个人思考如下:
1.不够灵活,比如text的值从未发生过改变呢,重复设置不是浪费资源?造成性能的消耗
2.如果我有特定的需求,在某些情况下才值绑定进去,这样就灵活处理
3.如果一些第三方控件,它里面设置text的方法不叫setText(CharSequence text),而是setChar(CharSequence text),那岂不是找不到方法了?
为了让我们能更加灵活的进行数据绑定,就引出了BindingAdapter
使用BindingAdapter
Android Databinding框架中已经为我们准备了大部分控件的一些属性的BindingAdapter,就比如上面的TextView:
代码语言:javascript复制@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);
}
将上面这个BindingAdapter注解的方法总结就是:
当在TextView上设置text属性,且设置的值的类型是CharSequence时,就不要直接调用TextView相应的setText方法,而是调用用户定义的这个BindingAdapter方法。
声明:BindingAdapter注解的方法取什么名字都行,因为压根不关心这个方法的名字,BindingAdapter只需要通过"android:text",TextView,CharSequence 这3个就可以确定出唯一的方法
从上图可以看出Databinding框架中已经写好了很多Android自身控件的BindingAdapter
自定义BindingAdapter
回到文章一开始提到的,如果是第三方控件,或者我们自己的自定义控件,那我们就需要自己定义BindingAdapter了,如何使用?还是拿文章一开始的自定义控件为例子
代码语言:javascript复制 <com.gcssloop.widget.ArcSeekBar
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="10dp"
app:arc_colors="@array/arc_colors_custom"
app:arc_max="100"
app:arc_min="0"
app:arc_open_angle="0"
binding:arc_progress="@{viewModel.currentProgress}"
app:arc_rotate_angle="270"
app:arc_thumb_color="#fff"
app:arc_thumb_mode="FILL"
app:arc_thumb_radius="5.5dp"
app:arc_width="4dp"
/>
代码语言:javascript复制 <data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="com.demo.viewmodel.TestViewModel" />
</data>
我的自定义BindingAdapter写法如下
代码语言:javascript复制public final class ArcSeekBarAdapter {
@SuppressWarnings("unchecked")
@BindingAdapter(value = {"app:arc_progress"})
public static void setProgress(ArcSeekBar arcSeekBar, int progress) {
arcSeekBar.setProgress(progress);
}
}
通过上面这些代码就可以实现自定义控件的数据绑定