前一篇博客我们介绍了DataBinding的使用。这一篇我们来看点更高级的东西—Binding Adapter。话不多说,开始吧!!!
配置
由于BindingAdapter是通过注解处理器来实现的。因此我们在之前的BindingAdapter的启用基础上还有在依赖中添加如下内容:
...
apply plugin: 'kotlin-kapt'
android {
...
dataBinding{
enabled = true
}
}
dependencies {
...
kapt "com.android.databinding:compiler:3.1.3"
}
这里用的是Kotlin因此用kapt来进行添加,而且还要用到kotlin-kapt插件。如果是Java的话用annotationProcessor来代替kapt。
自定义方法对应的属性名
在有些View中的某些setter方法也许没有对应的xml属性。但是我们有想在xml中进行定义怎么办!这是我们就需要为方法自定义属性名了。当然我可以为已存在的setter设置另外的属性名。下面举个例子:
@BindingMethods(value = [
(BindingMethod(type = android.widget.TextView::class,
attribute = "myBackgroundColor",
method = "setBackgroundColor"))
])
class MyTextView: TextView {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}
这里我们为TextView的setBackgroundColor方法设置了另外的一个属性名叫做myBackgroundColor,它的命名空间是app(也可以不添加命名空间)。如果想设置为android就需要在在前面显示的设置android命名空间。如:”android:myBackgroundColor”
下面是我们在xml中的使用:
<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>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.anriku.jetpackdemo.databinding.MyTextView
...
app:myBackgroundColor="@{@color/colorPrimary}"
... />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
可以看到在MyTextView的属性设置中我们使用了myBackgroundColor属性。效果图就不给了。自己实践下吧!这里需要注意的我们不能直接使用@color/colorPrimary而应该使用@{@color/colorPrimary}
自定义属性名及其逻辑
上面讲的@BindMethods都是建立在已有的方法之上的。我们通过@BindAdapter还可以自定义属性名以及它所做的逻辑。
举个例子:
object BindingAdapter {
@BindingAdapter("name")
@JvmStatic fun setName(view: View, name: String) {
Toast.makeText(view.context,name, Toast.LENGTH_SHORT).show()
}
}
可以看到我们自定义了一个name属性。对应的方法是Toast这个名字出来。这里要注意的是Kotlin中使用object并且将方法用@JvmStatic进行修饰。也就是Java中的静态方法
下面是xml的实现
<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="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
...
name="@{`anriku`}"
... />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
我们再具体的看下@BindingAdapter这个注解。下面的注解代码是Java代码
@Target(ElementType.METHOD)
public @interface BindingAdapter {
String[] value();
boolean requireAll() default true;
}
可以看到有两个值一个是value,另一个是requireAll并且它的默认值为true。value我们可以发现是String数组,因此我们可以设置多个属性来激化同一个方法。requireAll的话就是当我们的属性设置为多个的时候,是否需要一个控件设置了全部属性才能激活这个方法。默认的话就是要全部属性都设置了才行
下面我们来看一个value有两个值、requireAll设置为false的例子吧!
object BindingAdapter {
@BindingAdapter(value = ["firstName", "lastName"], requireAll = false)
@JvmStatic
fun setName(view: View, firstName: String?, lastName: String?) {
Toast.makeText(view.context, "${firstName?:""}${lastName?:""}",
Toast.LENGTH_SHORT).show()
}
}
这样我们就可以在xml中设置对这个两个属性任意的组合设置了。如果requireAll为true(默认就是),那么设置了要么全部设置,要么全不设置。
自定义属性值转换
情况如下:
<layout>
<data>
<variable
name="handlers"
type="com.anriku.jetpackdemo.MyHandlers"/>
</data>
...
<TextView
...
android:text="@{handlers}"
.../>
...
</layout>
当遇到如上情况,我们给text属性传递了一个MyHandlers对象(就是一个简单自定义对象)。它不能被接受这时候就会报错。那么我们如果偏要让text属性能接受MyHandlers对象呢?那么我们就要用自定义转换来进行实现了。
代码如下:
object BindingAdapter {
@BindingConversion
@JvmStatic fun convertToString(obj: MyHandlers): String{
Log.e("MyHandlers", "convertToString")
return obj.toString()
}
}
自定义转换用@BindingConversion注解来实现。这里我们在属性接受到了MyHandlers对象的时候,调用其toString方法。
现在在xml中你就可以直接想text属性传入MyHandlers对象了。
DataBindingComponent的使用
前面我们在写BindingAdapter的时候,那些方法都是静态方法,但是如果@BindingAdapter的注解到了是普通的方法时。我们就需要使用DataBindingComponent了。
**这个是DataBinding的Compiler自动生成的接口。当有控件使用了普通方法的BindingAdapter时候,就会在这个接口中生成get<含有这个BindinAdapter的类>的接口方法,然后在调用那些生产DataBinding的方法的时候就需要调用含有这个接口为参数的方法。自己实现接口在生成的方法中传回含有这个BindinAdapter的类。**含有这个BindinAdapter的类>
说着可能不太好理解。这里个给个例子:
首先,看一下含有普通方法上的BindinAdapter的类
class ComponentBindingAdapter {
private val prefixGenerate = PrefixGenerate()
@SuppressLint("SetTextI18n")
@BindingAdapter("component")
fun setString(textView: TextView, component: String) {
textView.text = component + prefixGenerate
}
}
PrefixGenerate是一个自定生成一个随机数字的类:
class PrefixGenerate {
private val random = Random()
fun generate(): String {
return random.nextInt(100).toString()
}
}
然后,在xml中使用对应的属性
<layout>
<data>
</data>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".databinding.MainActivity">
<TextView
android:id="@+id/tv_component"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:component="@{`Hello Anriku`}" />
</LinearLayout>
</layout>
最后,在Activity中的调用。
class MainActivity : AppCompatActivity() {
private lateinit var mBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this,
R.layout.activity_main) {
ComponentBindingAdapter()
}
}
}
在这里就需要调用DataBindingUtil含有DataBindingComponent的setContentView。然后,在实现的方法中返回了ComponentBindingAdapter类。
总结
这篇博客给大家讲了下BindingAdapter相关的东西。
主要有三点:
- 给已有的方法设置属性名或起别名。用@BindingMethods
- 自定义属性以及其实现逻辑。用@BindingAdapter
- 对属性参数值进行转换。用@BindingConversoin
参考
转载请注明链接