JetPack使用记录之数据绑定库

关于数据绑定库,很多人的评价是难用,出问题难以排查,其实也就是的,哈哈,不过该用还得用,有问题解决问题。

概述

数据绑定库是一种支持库,借助该库,你可以使用声明性格式(而非程序化地)将布局中地界面组件绑定到应用中地数据源。

简单来说就是布局中的值再也不用使用通过findViewById找到这个组件再调用组件属性赋值了,可以直接绑定到某个数据源。

编译环境

哪个模块需要使用数据绑定功能,则在这个module的build.gradle中添加:

android {        
    ...        
    dataBinding {            
        enabled = true        
            }    
}    
复制代码

同时需要添加apt或者kapt:

plugins {   
        
    id 'kotlin-kapt'
}
复制代码

注意这里是按moudule来配置的,而不是在common模块里配置就可以了,是个坑的地方。

其他内容

  • 绑定表达式中使用default属性,使用default可以对比如text的值设置一个默认值:
<com.wayeal.common.view.ClearEditTextMain
    android:id="@+id/textUserId"
    android:layout_width="match_parent"
    android:layout_height="55dp"
    android:layout_marginTop="89dp"
    android:drawablePadding="8dp"
    android:allowUndo="false"
    android:drawableLeft="@mipmap/wy_cloud_user"
    android:ems="10"
    android:hint="@string/user_id"
    android:background="@null"
    android:inputType="textPersonName"
    android:textSize="15sp"
    android:textColor="@color/commonTextColor"
    android:textColorHint="@color/commonTextHint"
    android:text="@={viewModel.userName,default = 张三}"
    />
复制代码

比如这里的的EditText的值会根据viewModel变化,但是会有个默认值default。

布局和绑定表达式

设置完环境后,系统会为每个布局文件生成一个绑定类,这个生成的过程是系统做的,我们无法干预,这个绑定类包含从布局属性到布局视图的所有绑定,并且知道如何为绑定表达式指定值。
然后就是binding在代码里获取以及使用,一直是模糊不清的,这里总结几种方法:

  • 当在activity中使用,可以在onCreate中得到binding,同时可以调用setContentView方法,这个也是Activity在onCreate中必须要执行的方法,具体代码:
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(
                this, R.layout.activity_main)

        binding.user = User("Test", "User")
    }
复制代码

还有就是baseVMActivity也是这种方法:

abstract class BaseVMActivity<VM : BaseViewModel>(useDataBinding: Boolean = false) : AppCompatActivity() {

    private val _useBinding = useDataBinding
    protected lateinit var mBinding: ViewDataBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        startObserve()
        if (_useBinding) {
            mBinding = DataBindingUtil.setContentView(this, getLayoutResId())
            mBinding.lifecycleOwner = this
        } else setContentView(getLayoutResId())
        initView()
        initData()
    }

    open fun getLayoutResId(): Int = 0
    abstract fun initView()
    abstract fun initData()
    abstract fun startObserve()

}
复制代码
  • 当在Fragment、ListView或者RecyclerView适配器中使用数据绑定,那肯定也需要得到binding,这里以Fragment为例,是在onCreateView方法里获取,通过DataBindingUtil的inflate方法来获取,直接看BaseVMFragment中的写法:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return if (_useBinding) {
        mBinding = DataBindingUtil.inflate(inflater, getLayoutResId(), container, false)
        mBinding.root
    } else
        inflater.inflate(getLayoutResId(), container, false)
}
复制代码

然后就是sunflower中的写法:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val binding = FragmentGardenBinding.inflate(inflater, container, false)
    val adapter = GardenPlantingAdapter()
    binding.gardenList.adapter = adapter
    subscribeUi(adapter, binding)
    return binding.root
}
复制代码

其他的写法,我们用到的时候再说。

表达式语言

表达式语言这个是什么东西呢,就是在xml代码里通过@{}取值的表达式,这里的表达式能否丰富直接影响着这个控件还要不要额外处理,比如我一个控件的值需要是根据某个viewModel里的值进行复杂的运算,那如果表达式语言不支持,就完了,所以看一下常用的有哪些。

  • 首先是一些常见的运算符和关键字,比如算术运算符,逻辑运算符,三元运算符等等,直接看代码:
android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
    android:transitionName='@{"image_" + id}'
复制代码

这里万变不离其宗,都是取得是@{}括号中的值。

  • Null合并运算符,这个特别方便,如果左边不是null,则选择左边运算数,如果左边为null,则选择右边运算数,这个完全就是三元运算符的简写:
android:text="@{user.displayName ?? user.lastName}"
复制代码

等同于:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"   
复制代码
  • 属性引用,顾名思义就是表达式可以使用引用对象的属性,也正是因为这个,可以在ViewModel中定义引用类型,在xml里进行引用:
android:text="@{user.lastName}"   
复制代码
  • 避免出现Null指针异常,这个就比较好了,也就是上面说的引用时,当某个引用是null时,不会导致空指针异常,比如@{user.name}中,如果user为null,那默认值会是null。

  • 使用集合,集合在平时使用中用到的很多,比如List、Map等,那肯定在XML中的data部分也需要使用,但是这里使用的时候需要注意,首先是要import全名,然后使用泛型时的<>需要转义,直接看个代码:

<data>
        <import type="android.util.SparseArray"/>
        <import type="java.util.Map"/>
        <import type="java.util.List"/>
        <variable name="list" type="List&lt;String>"/>
        <variable name="sparse" type="SparseArray&lt;String>"/>
        <variable name="map" type="Map&lt;String, String>"/>
        <variable name="index" type="int"/>
        <variable name="key" type="String"/>
    </data>
    …
    android:text="@{list[index]}"
    …
    android:text="@{sparse[index]}"
    …
    android:text="@{map[key]}"
复制代码

比如上面中先在data中import出Map,然后在泛型中要把<转义。

  • 字符串字面量问题,为什么要说这个问题呢,在之前表达式中是以双引号””中@{}来取值,但是字符串字面量也是通过””来获取,所以这里就会出问题,解决的方案也非常暴力,直接使用单引号”,要不外面双引号里面字符串字面量用单引号,或者外面用单引号里面字符串字面量用双引号,直接看代码:
android:text='@{map["firstName"]}'
    
复制代码
android:text="@{map[`firstName`]}"
    
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享