关于DataBinding的内容比较多,所以我准备分上下两篇.
整体的逻辑内容包含:
- 什么是视图绑定
- 视图绑定的使用
- 表达式的使用
- 常见的事件绑定
- Adapter适配器的处理
- 自定义绑定适配器
上篇主要讲怎么使用,如果已经对DataBinding有点了解的话,可以直接从这篇开始看.如果对基础知识还没有了解的话,可以点击上篇进行学习.
Android 中 JetPack(二)DataBinding(视图绑定)(上)
本文知识点
- 表达式的使用
- 常见的事件绑定
- Adapter适配器的处理
- 自定义绑定适配器
1. 表达式的使用
关于表达式我还是多啰嗦几句,虽然支持表达式,但是尽力别用复杂的表达式.要不然错了你真的不太好排查...切记!!!
官网上说支持的表达式:
- 算术运算符 + - / * %
- 字符串连接运算符 +
- 逻辑运算符 && ||
- 二元运算符 & | ^
- 一元运算符 + - ! ~
- 移位运算符 >> >>> <<
- 比较运算符 == > < >= <=(请注意,< 需要转义为 <)
- instanceof
- 分组运算符 ()
- 字面量运算符 - 字符、字符串、数字、null
- 类型转换
- 方法调用
- 字段访问
- 数组访问 []
- 三元运算符 ?:
先来一段代码开开胃...
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
还是很好理解的,相信只要写过java的应该都能看懂.
还有一点需要注意的,当你使用的时候出现null或者0的时候莫方
例如,在表达式 @{user.name}中,如果user为Null,则为user.name分配默认值null.如果您引用 user.age,其中age的类型为int,则数据绑定使用默认值0.
以下是一些特殊的用法:
-
- NUll的合并
android:text="@{user.displayName ?? user.lastName}"
<!--等价于-->
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
-
- 集合
<variable
name="testList"
type="java.util.ArrayList<String>" />
<!--以下是使用-->
<TextView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="@{testList[0]}"
app:layout_constraintTop_toBottomOf="@id/name" />
划重点 千万不要写成List 而是要写成List<String> 千万注意!!!
Map的话和这个类似,但是map的key要用单引号例如android:text="@{map[`firstName`]}" 剩下的自己尝试一下就好了.
-
- 资源
关于资源可以结合字符串资源一起看,这样可以更好的理解.
android:layout_marginLeft="@{name!=null ? @dimen/largePadding : @dimen/smallPadding}"
例如上面这个样子,其实就是字符串加上一些相应的组合而已...没有什么好说的.一般也都用不到
-
- 默认参数 因为xml在赋值的时候有相应的null和0等默认的参数,但是有不想看到页面上显示null,这个时候就有用了一个默认参数的概念了
android:text='@{userData.name, default="Placeholder text"}'
android:text="@{userData.name, default=`Placeholder text`}"
android:text="@{userData.name, default="Placeholder text"}"
android:text="@{userData.name, default=@string/app_name}"
但是我看还是很多人喜欢这种写法
android:text="@{userData.name ?? @string/app_name}"
看个人爱好吧... 尝试一下就好了.如果还是不明白看看StackOverflow的文章
-
- 包含
在开发中难免用到include标签进行布局,这个时候问题就产生了,我的一个参数怎么从主xml传递到include的xml中去?
开始的时候我看官方文档,里面说直接说是按照下面的方式进行处理:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/name"
bind:binduser="@{user}"/>
</LinearLayout>
</layout>
但是我想了好久都没想明白这个user是从哪里蹦出来的.于是乎就...
其实这个是在相应的include的布局中添加了一个
<data>
<variable
name="binduser"
type="com.example.User" />
</data>
这样是不是就一下子明白了,其实就是在指定的布局中设置一个相应的参数,然后在include的时候导入就好了.
2. 常见的事件绑定
关于事件绑定这块官方的例子里面 都是单独创建一个类进行事件处理.我个人觉得这样有个弊端,因为这个点击事件一定是和Activity之间交换的,如果里面存在的参数过多的话,那么就很难传值了,所以我个人觉得这几最好创建一个匿名内部类去处理逻辑,这样就可以拿到Activity中的数据.
好了,说了上面这些,就可以撸码了.
kotlin代码示例:
inner class TaskKotlinOpt {
private val TAG = TaskKotlinOpt::class.simpleName
fun click() {
Log.e(TAG, "点击事件触发了")
}
}
java代码示例:
public class TaskJavaOpt {
public void click() {
Log.e("TAG", "点击事件触发了");
}
}
xml中的代码,注意这里的onClick
<variable
name="kotlinOpt"
type="com.angle.android.databinding.TestKotlinActivity.TaskKotlinOpt" />
<variable
name="javaOpt"
type="com.angle.android.databinding.TestJavaActivity.TaskJavaOpt" />
<!--下面是代码引用-->
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()->kotlinOpt/javaOpt.click()}"
android:text="点击事件"
app:layout_constraintTop_toBottomOf="@id/source" />
这里说明一下,我还是把主要的内容都写在一块了,记得区分,到时候别说我代码有问题哈!!!因为没有参数,所以前面就是一个()紧接着跟了一个->kotlinOpt是一个参数名称,后面跟的方法.如果有参数的话,可以写成这个样子android:onClick="@{()->kotlinOpt.click(userData)}" 然后修改下面的代码:
kotlin代码示例:
fun click(userKotlin: UserKotlin) {
Log.e(TAG, "点击事件触发了${userKotlin.name}")
}
java代码示例:
public void click(UserJava userJava) {
Log.e("TAG", "点击事件触发了" + userJava.getName());
}
关于事件绑定这块还有一个问题需要说明一下,尽量不要处理Textwatch这种复杂的监听.千万不要...
3. Adapter适配器的处理
关于适配器的处理,其实和Activity差不多.只是生成Binding对象的方式不一样.
我这里把所有的代码都贴到里面
kotlin代码示例:
class TestKotlinAdapter(private val context: Context) : RecyclerView.Adapter<TestKotlinAdapter.TestHolder>() {
private var list: List<String>? = null
fun setData(list: List<String>) {
this.list = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestHolder {
val itemViewBinding = DataBindingUtil.inflate<ItemViewBinding>(
LayoutInflater.from(context),
R.layout.item_view,
parent,
false
)
return TestHolder(itemViewBinding.root)
}
override fun onBindViewHolder(holder: TestHolder, position: Int) {
val itemViewBinding = DataBindingUtil.getBinding<ItemViewBinding>(holder.itemView)
itemViewBinding?.showTv?.text = list?.get(position)
itemViewBinding?.executePendingBindings()
}
override fun getItemCount(): Int {
return list?.size ?: 0
}
class TestHolder(view: View) : RecyclerView.ViewHolder(view)
}
java代码示例:
class TestJavaAdapter extends RecyclerView.Adapter<TestJavaAdapter.TestHolder> {
private Context mContext;
private List<String> mList;
public TestJavaAdapter(Context mContext) {
this.mContext = mContext;
}
public void setData(List<String> list) {
mList = list;
notifyDataSetChanged();
}
@NonNull
@Override
public TestHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemViewBinding itemViewBinding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.item_view, parent, false);
return new TestHolder(itemViewBinding.getRoot());
}
@Override
public void onBindViewHolder(@NonNull TestHolder holder, int position) {
ItemViewBinding itemViewBinding = DataBindingUtil.getBinding(holder.itemView);
if (itemViewBinding != null) {
String showStr = mList.get(position);
itemViewBinding.showTv.setText(showStr);
itemViewBinding.executePendingBindings();
}
}
@Override
public int getItemCount() {
return mList != null ? mList.size() : 0;
}
public class TestHolder extends RecyclerView.ViewHolder {
public TestHolder(@NonNull View itemView) {
super(itemView);
}
}
}
这里只要看onBindViewHolder方法中的绑定就可以.其他的可以不用关注.但是后面的一句executePendingBindings()一定要加,这句是更新列表的语句.剩下的就没有什么好说的了.其实这里面也从侧面讲述了kotlin的代码比java的代码简洁.
4. 自定义绑定适配器
这个绑定适配器的方式其实还挺好用的,最典型的例子就是加载图片.
下面看看代码的示例: kotlin代码示例:
class AdapterKotlinUtils {
@BindingAdapter("kotlinImageUrl", "kotlinLoadUrl")
fun loadImage(view: ImageView, url: String) {
//todo 加载图片的操作
}
}
java代码示例:
public class AdapterJavaUtils {
@BindingAdapter({"javaImageUrl", "javaLoadUrl"})
public void loadImage(View view, String url) {
//todo 加载图片的操作
}
}
在使用的时候需要在xml中以auto设置属性就可以了.
<xmlns:app="http://schemas.android.com/apk/res-auto">
<!--上面的是使用-->
<ImageView app:kotlinLoadUrl="@{xxx.imageUrl}" />
这里说明一下哈,view这个属性是当前的View,是必须的.后面的参数直接赋值就好了.其实这里还可以传相应的实体类,例如对象,回调方法等... 这个写法和之间介绍的表达式一样.感兴趣的可以尝试一下.