Android 架构组件 DataBinding(一)

简介

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

借助布局文件中的绑定组件,您可以移除 Activity 中的许多界面框架调用,使其维护起来更简单、方便。还可以提高应用性能,并且有助于防止内存泄漏以及避免发生 Null 指针异常。

开始使用:

DataBinding 不但灵活而且兼容性广。因此大家可以在Android 4.0(API 级别 14 )的时候进行使用。
如果想要使用 DataBinding,那么大家就需要在Module 的build.gradle 中添加

android {
        dataBinding {
            enabled = true
        }
    }
    

或者

android {
        dataBinding.enabled = true
    }
    

如何使用(介绍)

如果需要使用DataBinding ,那么大家就需要在xml 布局中符合如下的规则:

  • 使用 <layout> 包裹整体的内容
  • 使用<data>包裹自己所需要的数据
    如:
<?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>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

在代码中生成绑定类绑定布局的方式有两种:

  • 使用 DataBindingUtil 的方式
  • 使用 XMLBinding 方式

布局和绑定表达式

布局

与普通的xml不同,DataBinding布局文件以根标记 layout 开头,后跟 data 元素和 view 根元素。

<layout> 标签的内容

<layout> 标签中盛放xml文件中的命名空间。如:

  • 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> 标签中盛放数据类型。如:
string 数据类型可以使用如下方式导入

  • <import type=“String” alias=“str”/>
  • <variable name=“strs” type=“String” />
<data> 标签中内容详解

<import> 中的type 属于 要导入的名称, alias 属于自己定义的别名,可以在布局中进行使用和类目相同的类进行区分。
<variable> 中的 name 表示在要在布局中使用的名称,和上面的别名相同。
当然 <import> 仅仅是导入使用的类型,如果想要色湖之数据还是需要在 <variable> 进行设置。

view 根元素

这里使用的View 根标签和我们使用的正常布局一样。

生成绑定类绑定布局

首先我们要知道,系统会为所有使用<layout>的布局生成一个绑定类,格式为布局文件的名称转换为 Pascal 大小写形式并在末尾添加 Binding 后缀。如activity_main.xml 生成的类就是ActivityMainBinding。
生成的绑定类将布局与布局中的视图关联起来。这些类都是从ViewDataBinding 继承而来的。

在上面我们介绍了代码中绑定布局的方式有两种,现在我们就介绍一下。

使用DataBindingUtil 的方式

如图我们可以看见DataBindingUtil 中可以使用 inflate 、bind 、setContentVIew 的方式绑定布局。
其中:
inflate :可以在任意地方进行使用,一般在Fragment / recyclerview 等地方进行使用
bind : 可以在任意地方进行使用。
setContentVIew : 只可以在Activity 中进行使用。
在这里插入图片描述

使用 xmlBinding 的方式

xmlBinding 和上面的DataBindingUtil 相似。

只不过如果使用 xmlBinding 之后必须要使用 setContentView (Activity 中)重新设置数据。

开发中使用哪种?

我个人推荐使用 DataBIndingUtil 的方式。大家都知道如果要写项目,那么就会抽取出Base 类,所以我们为了使用的方便一般就自己对于setCOntentView方法进行各种设置。比如我的BaseActviity。

abstract class BaseActviity<T : ViewDataBinding> : AppCompatActivity() {
    var mBInding: T? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBInding =  DataBindingUtil.setContentView(this,getLayoutIds())
    }
    abstract  fun  getLayoutIds(): Int
}

这样我们就可以在自己的类中使用统一的方式进行设置。

绑定表达式

在布局中设置动态属性

如果有一个 User 类,类中有name 和 age 两个属性,那么我们在布局中设置一个textview 的name 为user的name我们可以在布局中直接使用。

<?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>
        <import type="com.chaunzhi.test.User"/>
        <variable
            name="user"
            type="User" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            app:layout_constraintTop_toTopOf="parent"
            android:text="@{user.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

当然,我们这里只是将数据类型添加到了布局中,但是布局仍然不知道内部的数据是上面,所以我们需要对于数据和布局进行绑定。
如,下面的例子:

        val mBinding = setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        mBinding.user = User("蔡依林",22)

表达式语言

常见的表达式语法

表达式语言与托管代码中的表达式非常相似。您可以在表达式语言中使用以下运算符和关键字:

  • 算术运算符 + - / * %
  • 逻辑运算符 && ||
  • 移位运算符 >> >>> <<
  • 一元运算符 + - ! ~
  • 二元运算符 & | ^
  • 三元运算符 ?:
  • instanceof
  • 分组运算符 ()
  • 字符串连接运算符 +
  • 字面量运算符 - 字符、字符串、数字、null
  • 类型转换
  • 方法调用
  • 字段访问
  • 数组访问 []
缺少的运算
  • this
  • super
  • new
  • 显式泛型调用
Null 合并运算符

如果左边运算数不是 null,则 Null 合并运算符 (??) 选择左边运算数,如果左边运算数为 ,则选择右边运算数

android:text="@{user.displayName ?? user.lastName}"

这在功能上 等效于 三元运算符

android:text="@{user.displayName != null ? user.displayName : user.lastName}"
属性引用

这个功能就是在上面的时候进行使用的@{}的方式,这里不再次进行介绍。

避免出现NULL指针异常

生成的数据绑定类会自动检查是否有null 值,并且避免空指针异常。例如,在表达式 @{user.name} 中,如果 user 为 Null,则为 user.name 分配默认值 null。如果您引用 user.age,其中 age 的类型为 int,则数据绑定使用默认值 0。

集合

为方便起见,可使用 [] 运算符访问常见集合,例如数组、列表、稀疏列表和映射。

注意:要使 XML 不含语法错误,您必须转义 < 字符。例如:不要写成 List<String> 形式,而是必须写成 List &lt;String>。

您还可以使用 object.key 表示法在映射中引用值。例如, @{map[key]} 可替换为 @{map.key}。

字符串字面量

您可以使用单引号括住特性值,这样就可以在表达式中使用双引号,如以下示例所示

android:text='@{map["firstName"]}'

也可以使用双引号括住特性值。如果这样做,则还应使用反单引号 ` 将字符串字面量括起来:

android:text="@{map[`firstName`]}"
资源

您可以使用以下语法访问表达式中的资源:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

格式字符串和复数形式可通过提供参数进行求值:

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

当一个复数带有多个参数时,应传递所有参数:


      Have an orange
      Have %d oranges
      
    android:text="@{@plurals/orange(orangeCount, orangeCount)}"
    

某些资源需要显式类型求值,如下表所示:

类型常规引用表达式引用
String[]@array@stringArray
int[]@array@intArray
TypedArray@array@typedArray
Animator@animator@animator
StateListAnimator@animator@stateListAnimator
color int@color@color
ColorStateList@color@colorStateList

事件处理

通过数据绑定,您可以编写从视图分派的表达式处理事件(例如,onClick() 方法)。事件特性名称由监听器方法的名称确定,但有一些例外情况。例如,View.OnClickListener 有一个 onClick() 方法,所以该事件的特性为 android:onClick。

有一些专门针对点击事件的事件处理脚本,这些处理脚本需要使用除 android:onClick 以外的特性来避免冲突。您可以使用以下特性来避免这些类型的冲突

Listener setter特性
SearchViewsetOnSearchClickListener(View.OnClickListener)android:onSearchClick
ZoomControlssetOnZoomInClickListener(View.OnClickListener)android:onZoomIn
ZoomControlssetOnZoomOutClickListener(View.OnClickListener)android:onZoomOut

当然这些事件我们也可以自己进行定义,我们之后再说。

处理事件的方式

**方法引用:**在表达式中,您可以引用符合监听器方法签名的方法。当表达式求值结果为方法引用时,数据绑定会将方法引用和所有者对象封装到监听器中,并在目标视图上设置该监听器。如果表达式的求值结果为 null,则数据绑定不会创建监听器,而是设置 null 监听器。
**监听器绑定:**这些是在事件发生时进行求值的 lambda 表达式。数据绑定始终会创建一个要在视图上设置的监听器。事件被分派后,监听器会对 lambda 表达式进行求值。

方法引用

事件可以直接绑定到处理程序方法,类似于android:onClick可以分配给活动中的方法…
使用方式:
1… 定有一个类,类中有一个需要传递view 的方法

open class Click {
    fun ztoastClcik(view : View){
    }
}
  1. 在xml 中进行设置:在这里通过 @{类名(自己设置的variable 中的name):: 方法名称}
<?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="click"
            type="com.chaunzhi.test.Click" />
        <import type="com.chaunzhi.test.Utils" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btns"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{click::toastClcik}"
            android:text="click"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  1. 在代码中进行设置
    mBinding.click =  Click()

方法引用大家可以认为是一个官方原生的监听器,里面只可以传递view,不可以传递其他参数。我个人认为局限性太大。

监听器绑定

监听器绑定是在事件发生时运行的绑定表达式。它们类似于方法引用,但允许您运行任意数据绑定表达式。此功能适用于 Gradle 2.0 版及更高版本的 Android Gradle 插件。
使用方式:

  1. 定义一个类,类中定义一个方法
open class Click {
    fun toastClcik(context:Context, msg: String) {
        Toast.makeText(context,msg,Toast.LENGTH_SHORT).show()
    }
}
  1. 在xml 文件中进行使用
    这里我们使用的方式是 @{(view)->类名.方法名(所传递的参数)}. 我们可一点击进入(view)中发现他调用的其实是
 public interface OnClickListener {
        void onClick(View var1);
    }

当然了,在这里也可以不传递。所传递的参数通过 <variable>进行设置。

<?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="msg"
            type="String" />
        <variable
            name="contexts"
            type="android.content.Context" />
        <variable
            name="click"
            type="com.chaunzhi.test.Click" />
        <import type="com.chaunzhi.test.Utils" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btns"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{(view)->click.toastClcik(contexts,msg)}"
            android:text="click"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3.在代码中进行设置

        mBinding.msg = "按钮被点击了"
        mBinding.contexts = this
        mBinding.click =  Click()

需要注意的是,如果您监听的事件返回类型不是 void 的值,则您的表达式也必须返回相同类型的值。

监听器表达式功能非常强大,可以使您的代码非常易于阅读。另一方面,包含复杂表达式的监听器会使您的布局难以阅读和维护。这些表达式应该像将可用数据从界面传递到回调方法一样简单。您应该在从监听器表达式调用的回调方法中实现任何业务逻辑。

方法引用和监听器绑定之间的区别

监听器实现是在绑定数据时创建的,而不是在事件触发时创建的。

导入、变量和包含
导入

通过导入功能,您可以轻松地在布局文件中引用类,就像在托管代码中一样。您可以在 data 元素使用多个 import 元素,也可以不使用。
当类名有冲突时,其中一个类可使用别名重命名。以下示例将 com.example.real.estate 软件包中的 View 类重命名为 Vista:

变量

您可以在 data 元素中使用多个 variable 元素。每个 variable 元素都描述了一个可以在布局上设置、并将在布局文件中的绑定表达式中使用的属性。
变量类型在编译时进行检查,因此,如果变量实现 Observable 或者是可观察集合,则应反映在类型中。如果该变量是不实现 Observable 接口的基类或接口,则变量是“不可观察的”。

如果不同配置(例如横向或纵向)有不同的布局文件,则变量会合并在一起。这些布局文件之间不得存在有冲突的变量定义。

在生成的绑定类中,每个描述的变量都有一个对应的 setter 和 getter。在调用 setter 之前,这些变量一直采用默认的托管代码值,例如引用类型采用 null,int 采用 0,boolean 采用 false,等等。

系统会根据需要生成名为 context 的特殊变量,用于绑定表达式。context 的值是根视图的 getContext() 方法中的 Context 对象。context 变量会被具有该名称的显式变量声明替换。

包含

通过使用应用命名空间和特性中的变量名称,变量可以从包含的布局传递到被包含布局的绑定。

<?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:user="@{user}"/>
           <include layout="@layout/contact"
               bind:user="@{user}"/>
       </LinearLayout>
    </layout> 

数据绑定不支持 include 作为 merge 元素的直接子元素。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Jetpack是Google提供的一套用于加速Android应用开发的工具包,其包括了许多架构组件,其之一就是ViewModel。 ViewModel是一种设计模式,用于保存和管理与UI相关的数据。在传统的Android开发,当屏幕旋转或者因为其他原因导致Activity或Fragment重建时,之前保存的临时数据就会丢失。而ViewModel的出现解决了这个问题。 ViewModel的主要作用是将数据与UI组件分离。它的工作方式是创建一个ViewModel类,并在其保存需要与UI组件交互的数据。这样,当屏幕旋转或重建时,ViewModel实例不会销毁,数据也会得到保留。然后,在Activity或Fragment,通过获取ViewModel实例,可以轻松地访问这些数据。 使用ViewModel的好处有很多。首先,它可以避免内存泄漏,因为ViewModel的生命周期与Activity或Fragment无关。其次,它可以节省资源,因为当Activity或Fragment销毁时,ViewModel实例可以被系统缓存起来,下次再创建时可以直接返回该实例。另外,由于ViewModel保存了与UI相关的数据,可以减少因为屏幕旋转导致的数据重复加载的问题。 在使用ViewModel时,你可以选择使用Android Jetpack的其他架构组件来进一步提高开发效率,比如通过LiveData实现数据的观察和通知,或者通过DataBinding来实现UI与数据的自动绑定。 总之,ViewModel是Android Jetpack非常重要的一个架构组件,它的出现实现了数据与UI的解耦,提高了开发效率,并且解决了数据丢失的问题。希望通过这篇文档的详解,你对ViewModel有了更深入的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值