前言
这一章是我们DataBinding详解的最后一章,我们要在这章里面熟悉这个组件的其它细节,如自定义属性、Lambda表达式等,闲话少叙,我们开始吧。
参考代码地址:GitHub - guoergongzi/GMVVMDemo
参考代码Module:gdatabindingdemo4
1、自定义属性
DataBinding提供了一个注解BindingAdapter来支持自定义属性,用这个注解修饰的方法可以获取并操作属性所在的控件以及属性的内容,我们来直接用加载网络图片来测试一下这个功能,首先我们依赖一下Glide来进行最后的加载动作:
implementation 'com.github.bumptech.glide:glide:4.15.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.0'
然后我们新建一个类BindingAdapterImpl
,在这个类中用BindingAdapter注解修饰一个加载图片方法:
@BindingAdapter("bind_imageUrl")
public static void loadNetImage(ImageView imageView, String imageUrl) {
Glide.with(imageView.getContext()).load(imageUrl).into(imageView);
}
然后我们用这个方法在xml中给一个ImageView加载一个网络图片的url:
<variable
name="imageUrl"
type="String" />
。。。
<ImageView
bind_imageUrl="@{imageUrl}"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/string_empty"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
这里一定要记得在AndroidManifest.xml文件中申请网络权限:
<uses-permission android:name="android.permission.INTERNET" />
运行代码,发现图片显示成功。这个特性在很多需求的开发中都很实用,除了加载网络图片以外,调整布局大小、绑定适配器等都可以通过这个特性实现。
2、多自定义属性结合
分析上面的代码,我们发现@BindingAdapter标记的方法中第一个参数是对应的布局对象,第二个参数就是我们在model中提供的数据,但是当我们需要多个数据时又该怎么处理呢?下面我们就试一试给一个控件设置宽高。
@BindingAdapter({"bind_width", "bind_height"})
public static void updateViewSize(View view, int width, int height) {
view.postDelayed(() -> {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = width;
layoutParams.height = height;
view.setLayoutParams(layoutParams);
}, 2000);
}
我们先定义了一个方法,这个方法的注解中用花括号定义了两个自定义属性,并且被注解的方法有了三个属性,分别是对应的布局对象,以及两个自定义属性。
<variable
name="viewHeight"
type="int" />
<variable
name="viewWidth"
type="int" />
。。。
<View
bind_height="@{viewHeight}"
bind_width="@{viewWidth}"
android:layout_width="30dp"
android:layout_height="20dp"
android:layout_marginTop="16dp"
android:background="@color/black" />
在xml文件中,我们定义viewHeight、viewWidth两个属性,并将它们设置给一个View控件。
mainBinding.setViewHeight(200);
mainBinding.setViewWidth(300);
在MainActivity中给viewHeight和viewWidth赋值。运行起来后发现2000毫秒后控件的宽高发生了变化,说明这种自定义属性组合的用法DataBinding是支持的。
3、覆盖系统属性
有时候我们需要对某些系统属性做一些处理,这个需求也可以通过BindingAdapter实现。
@BindingAdapter("android:text")
public static void updateText(TextView textView, String content) {
textView.setText(content);
textView.postDelayed(() -> textView.setText(content + "-ABC"), 2000);
}
这里我们还是先用BindingAdapter注解了一个方法,但是对应的属性不再是我们自定义的属性,而是系统属性android:text。
<variable
name="textContent"
type="String" />
。。。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@{textContent}"
android:textColor="@color/black"
android:textSize="33sp" />
随后在xml文件中给TextView设置text属性为textContent。
mainBinding.setTextContent("测试");
最后在MainActivity中给属性textContent赋值。运行以后发现文本中的内容在2000毫秒后发生了改变,说明系统属性已经被我们成功覆盖了。这个特性使用时一定要万分小心,因为这个特性会影响整个项目中该属性的使用,思考和测试不够全面的话极易导致异常发生。
4、Lambda表达式
我们在编写xml文件时经常会使用到一些回调,典型的如我们的点击事件,这个时候如果能把这个监听实现之间写在xml中会让我们的代码简洁很多,那么有什么办法可以直接在xml中写接口实现呢?使用lambda表达式就可以了。
public class ClickProcessor {
public void showToast(Context context) {
Toast.makeText(context, "点击了测试按钮", Toast.LENGTH_SHORT).show();
}
}
我们首先准备一个方法来测试我们的点击事件是否被触发。
<variable
name="clickProcessor"
type="com.gegz.gdatabindingdemo4.ClickProcessor" />
。。。
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="@{() -> clickProcessor.showToast(context)}"
android:text="点击测试" />
然后在xml中用lambda表达式来实现android:onClick属性,并在回调中用showToast方法测试点击事件。运行起来后点击按钮,发现Toast正常弹出,说明我们可以在xml中用lambda表达式来实现事件监听。
5、数据转换
除了BindingAdapter
以外,DataBinding还有一个BindingConversion
注解,这个注解可以帮助我们实现数据内容的调整和数据类型转换,下面我们用一个例子来了解一下。
@BindingConversion
public static Drawable conversionStringToColor(String str) {
if ("蓝色".equals(str)) {
return new ColorDrawable(ContextCompat.getColor(MyApplication.getInstance(), R.color.purple_700));
}
return new ColorDrawable(ContextCompat.getColor(MyApplication.getInstance(), R.color.white));
}
首先实现一个BindingConversion
注解的方法,内容是把String类型的参数转换成Drawable类型。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background='@{"蓝色"}'
android:text="测试数据类型转换"
android:textColor="@color/white" />
然后在xml中使用“蓝色”作为background属性的值,运行发现文本框的背景颜色是蓝色的。
6、include布局
我们在实际开发中常常用到include标签来复用一些布局,这时候需要把属性传递给被导入的布局中。首先我们先准备一个被导入的布局的布局文件:
<layout xmlns:android="<http://schemas.android.com/apk/res/android>">
<data>
<variable
name="includeString"
type="String" />
</data>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{includeString,default = 测试include布局}" />
</layout>
可以看到,被导入的布局也是需要使用layout标签来标识该布局需要被DataBinding框架处理。之后我们在主界面的xml文件中把这个文件导入进来:
<variable
name="includeString"
type="String" />
。。。
<include
layout="@layout/layout_include_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
bind:includeString="@{includeString}" />
然后给对应的属性赋值:
mainBinding.setIncludeString("测试include布局");
这就是MVVM中使用include标签导入布局的方法。
7、viewStub布局
除了include之外,viewStub也是我们常用的标签,我们这里方便起见直接使用include布局部分的布局来进行学习,在主界面的xml文件中用viewStub的方式导入布局:
<variable
name="viewStubString"
type="String" />
。。。
<ViewStub
android:id="@+id/layout_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/layout_include_test"
bind:includeString="@{viewStubString}" />
然后给属性赋值:
if (mainBinding.layoutStub.getViewStub() != null) {
mainBinding.layoutStub.getViewStub().inflate();
mainBinding.setViewStubString("测试viewStub布局");
}
这样我们运行起来就能看到viewStub布局显示了我们赋值的内容了。
8、资源引用
在实际开发中,我们常常使用resources文件来管理,比如strings、colors等,我们这里用以下例子来看看怎么在DataBinding中使用资源,首先在strings文件中声明一个字符串:
<string name="string_resources_test">资源文件:%s</string>
然后在主界面的xml文件中声明一个TextView来显示这段字符:
<TextView
android:id="@+id/layout_resources"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text='@{@string/string_resources_test("测试"),default = "测试资源文件"}' />
运行代码,界面上显示“资源文件:测试-ABC”。(这里的-ABC是我们覆盖系统text属性产生的)
下章预告
我们本节把之前遗留下来的DataBinding的用法和细节全部都收尾了。之前四节我们深入的了解了一下DataBinding这个框架,在这个学习和分享的过程中,我自己也收获了不少成长,比如之前我使用DataBinding写点击事件时总写在java代码里,而知道DataBinding支持lambda表达式以后,我发现把点击事件直接写在xml文件中更加优雅简单。这说明我们对常用框架的深入学习完全是值得的,虽然很多特性和细节我们用不上,但只要其中有一个点给我们带来启发,让我们学会更高效的使用这个框架,对我们未来的开发效率都是有帮助的。带着这个认知,我们接下来继续深入了解一下剩下三个框架——LiveData、ViewModel、LifeCycle。
参考文档:
CSDN:Android DataBinding 运算符、BindingAdapter、 BindingConversion --xiaow
CSDN:Android 安卓DataBinding(九)·运算符 --第三女神程忆难