一、前言
本篇主要记录下绑定适配器的用法,下面是一个常见的例子,用于将字符串绑定到android:text
属性上面
<?xml version="1.0" encoding="utf-8"?>
<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">
<data>
<variable
name="value"
type="String" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/update_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@{value}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="value" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
假如想要自己对该控件做一些拓展操作的话可以使用绑定适配器功能,绑定适配器既可以对已有的属性做拓展,也可以自定义出新的属性
二、环境配置
如果使用该功能的话需要添加kotlin-kapt
拓展
build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
三、拓展功能
1、对现有属性提供自定义逻辑
比如对现有的android:text
属性进行拓展
DataBindExtends.kt
@BindingAdapter("android:text")
fun setExtendContent(view: TextView, content: String) {
view.text = "拓展内容:$content"
}
class DataBindExtends {
}
那么在使用绑定属性更改的值,最终内容都会加上"拓展内容"进行显示
2、修改函数名
根据官网来说是用来修改一些属性具有名称不符的 setter 方法。该方式必须在使用的控件代码基础上进行修改,通常用在自定义控件上(毕竟官方控件TextView
之类的也没法修改)。如下
MyTextView.kt
@BindingMethods(value = [
BindingMethod(
type = MyTextView::class,
attribute = "android:text",
method = "toastContent")])//注意这个函数的参数要和android:text所使用的参数一致
class MyTextView: androidx.appcompat.widget.AppCompatTextView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
// override fun setText(text: CharSequence?) {
// throw RuntimeException("Stub!")
// }
fun toastContent(content: String){
Toast.makeText(context,content,Toast.LENGTH_SHORT).show()
}
}
activity_main.xml
<com.example.myapplication.MyTextView
android:id="@+id/update_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@{change}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="value" />
3、自定义多属性适配器
需要注意的是自定义适配器在xml里面必须和数据绑定一起使用
正确写法: app:imageUrl="@{change}"
错误写法: app:imageUrl="asbldbasldas"
DataBindExtends.kt
requireAll = false
表示并非为每一个属性都分配表达式,这样的话有多个属性时候,使用时候不要求全部使用
object DataBindExtends {
@BindingAdapter(value = ["app:imageUrl", "placeholder"], requireAll = false)
@JvmStatic fun setImageUrl(imageView: ImageView, url: String?, placeHolder: String) {
Log.e("YM-->","设置的内容url:$url--->placeHolder:$placeHolder")
}
@BindingAdapter("app:popularityIcon")
@JvmStatic fun popularityIcon(view: ImageView, popularity: String) {
Log.e("YM-->","--啦啦啦>$popularity")
}
}
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:imageUrl="@{change}"
placeholder="@{change}"
app:popularityIcon="@{change}"/>
4、对旧值的处理
在数据绑定中,允许对一个属性的旧值做一些处理,在一些特殊场景会有一些用处
@BindingAdapter("android:paddingStart")
fun setExtendContent(view: View, content: Int, newContent: Int) {
Log.e("YM-->","padding设置的内容:oldPadding-->$content--newContent:$newContent")
}
class DataBindExtends {
}
<View
android:id="@+id/ads"
android:layout_width="20dp"
android:layout_height="100dp"
android:paddingLeft="@{padding}"
android:paddingStart="@{padding}"
android:background="@color/black"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
5、多个监听器的问题
以下来自官方文档
当监听器有多个方法时,必须将它拆分为多个监听器。例如,
[View.OnAttachStateChangeListener](https://developer.android.google.cn/reference/android/view/View.OnAttachStateChangeListener)
有两个方法:[onViewAttachedToWindow(View)](https://developer.android.google.cn/reference/android/view/View.OnAttachStateChangeListener#onViewAttachedToWindow(android.view.View))
和[onViewDetachedFromWindow(View)](https://developer.android.google.cn/reference/android/view/View.OnAttachStateChangeListener#onViewDetachedFromWindow(android.view.View))
。该库提供了两个接口,用于区分它们的属性和处理脚本:
// Translation from provided interfaces in Java:
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewDetachedFromWindow {
fun onViewDetachedFromWindow(v: View)
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewAttachedToWindow {
fun onViewAttachedToWindow(v: View)
}
@BindingAdapter(
"android:onViewDetachedFromWindow",
"android:onViewAttachedToWindow",
requireAll = false
)
fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
val newListener: View.OnAttachStateChangeListener?
newListener = if (detach == null && attach == null) {
null
} else {
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
attach.onViewAttachedToWindow(v)
}
override fun onViewDetachedFromWindow(v: View) {
detach.onViewDetachedFromWindow(v)
}
}
}
val oldListener: View.OnAttachStateChangeListener? =
ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener)
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener)
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener)
}
}
}
6、对象转换
有时候某些属性使用的对象需要进行转换,这里进行演示,以下是每当赋值int
类型时候,将转换为 String
类型进行返回
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data >
<variable
name="dta"
type="java.util.Date" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/update_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@{dta}"
android:paddingStart="10dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="value" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
@BindingConversion
fun convertColorToDrawable(color: Date) = "=="
object DataBindExtends {
@BindingConversion
@JvmStatic fun booleanToVisibility(isNotVisible: Boolean): Int {
return if (isNotVisible) View.GONE else View.VISIBLE
}
}
}
需要注意的是转换的类型要和xml中使用的类型保持一致。
如果在kotlin里面使用Int
之类数据类型的话需要确认其包名,如果使用ObservableInt
之类的类型的话,暂时无法生效。