DataBinding全解析1-简单介绍和使用

763 阅读5分钟

前言

数据绑定库就是一种支持库,通过该库,可以使用声明性格式将布局中的界面组件绑定到应用中的数据源。

可以概况以下优点:

  • 数据源直接和控件绑定,无需再使用findViewById。
  • 配合其他JectPack组件,形成观察者设计模型,数据源变化直接显示在XML上变化,省去一些操作。
  • 有助于防止内存泄漏以及避免Null指针异常。
  • 可以双向绑定,控件变化,值直接通知数据源变化。

很多同学没有用DataBinding,觉得它出错很难排查,但是一旦使用了,会发现这是一个真香的库。

正文

配置环境

首先就是dataBinding的依赖,不同于使用其他库我们都是添加依赖,数据绑定直接在模块的build.gradle中配置即可,哪个模块需要使用数据绑定功能,则在这个module的build.gradle中添加:

android {        
    ...        
    dataBinding {            
        enabled = true        
            }    
}    

数据绑定功能需要生成代码,所以apt是必须的:

plugins {     
    id 'kotlin-kapt'
}

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

默认值属性

在使用数据绑定库时,我们想监听或者观察数据源变化就直接使用 @={} 这种方式即可,但是假如数据源为空,这时可以设置个默认值,使用 default 来表示。

<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。

布局和绑定表达式

前面也说了,数据绑定库会为我们自动生成一些代码,也就是绑定类,这个绑定类很关键,包含了xml里写的数据源和布局文件,所以获取到这个绑定类至关重要,下面列举几种获取这个绑定类的方法。

在Activity中使用

在Activity中,由于需要通过setContentView来设置Activity的内容,所以官方的API中,把获取binding和setContentView这2步合在了一起,如下所示:

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

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

        binding.user = User("Test", "User")
    }

既然我们知道了在Activity中是如何使用的,那我们的基类Activity就可以使用了,代码如下:

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()

}

在非Activity中使用

这种情况更常见,比如在Fragment、RecyclerView和ListView中等等,这时就可以老老实实的使用DataBdingUtil的inflate方法,其实这个和普通的inflater的方法是一样的,这里也就可以拿到binding了。

在拿到binding后,里面的root就是布局,比如下面代码是Fragment中的onCreateView方法中使用,返回创建的布局view:

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)
}

上面是直接通过DataBindingUtil类来获取,使用更为广泛,当然有个简化的效果,直接使用生成的类的inflate方法是一样的,比如下面代码:

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
}

表达式语言

表达式是啥 它和语句的区别是什么 相信大家都知道,表达式是有值的,所以这里的表达式指的就是数据绑定时控件的值。

所以如果表达式能计算或者有着丰富的运算符,那数据绑定将会简单很多,下面我们来看一下表达式语言都有哪些语法。

常见的运算符和关键字

首先是一些常见的运算符和关键字,比如算术运算符,逻辑运算符,三元运算符等等,直接看代码:

    android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
    android:transitionName='@{"image_" + id}'

Null合并运算符

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指针异常,这个就比较好了,也就是上面说的引用时,当某个引用是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`]}"
    

总结

本篇文章主要也是介绍为主,数据绑定库更多的用法,可以查看后续文章。

# 绑定适配器

# 事件处理