dataBinding数据绑定(一)

编译环境

要将应用配置为使用数据绑定,请在应用模块的build.gradle文件中添加dataBinding元素:

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

使用数据绑定库

数据绑定的布局文件以根标记layout开头,后跟data元素和view根元素:

<?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="user"
            type="com.jetpackdemo.User" />
    </data>

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

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

data中使用了包名为com.jetpackdemoUser文件,并为此对象设置了name:user
TextView中:android:text="@{user.name}"表示为TextView设置username属性值。
完成布局文件后,系统会为每一个布局文件生成一个绑定类,默认情况下,类名基于布局文件的名称,将xml文件的名称转换为驼峰大小写,并在末尾添加Binding一词。

lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.user = User("hello")
    }

如果在FragmentListViewRecyclerView适配器中使用数据绑定,则可以使用DataBindingUtilinflate方法:

val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)

布局表达式

可以在布局文件的使用一下运算符和关键字:

  • 算术运算符+ - * / %
  • 字符串连接运算符 +
  • 逻辑运算符 && ||
  • 二元运算符 & | ^
  • 一元运算符 + - ! ~
  • 移位运算符 >> >>> <<
  • 比较运算符 == > < >= <=(<需要转义为&lt;)
  • instanceof
  • 分组运算符 ()
  • 字面量运算符-字符、字符串、数字、null
  • 类型转换
  • 方法调用
  • 字段访问
  • 数组访问[ ]
  • 三元运算符 ?:
缺少的布局表达式
  • this
  • super
  • new
  • 显式泛型调用
Null合并运算符
android:text="@{user.name ?? user.sex}"

如果左边运算不为null则选择左边运算,否则选择右边运算。

属性引用
android:text="@{user.name}"
避免出现Null指针异常

生成的数据绑定代码会自动检查有没有null值并避免出现null指针异常。如果在表达式中Stringnull则分配默认值null

集合表达式

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

<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]}"

在集合中,必须使用转义<字符。例如:不要写成List<String>形式,而是必须写成List&lt;String>

获取map中的值
android:text='@{map.get("name")}'

事件处理

通过数据绑定,您可以编写从视图分派的表达式处理事件(例如:onClick()方法)。事件名称由监听器方法的名称确定。
可以使用以下机制处理事件:

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

方法引用

一个主要优点是表达式在编译时进行处理,因此,如果该方法不存在或其签名不正确,则会收到编译时错误。

    class MyHandlers {
        fun onClickFriend(view: View) { ... }
    }
<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="handlers" type="com.example.MyHandlers"/>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"
               android:onClick="@{handlers::onClickFriend}"/>
       </LinearLayout>
    </layout>

方法引用中的签名必须和方法中的签名保持一致,并且参数也保持一致,不能自定义参数。

监听器绑定

监听器绑定是在事件发生时运行的绑定表达式。它们类似于方法引用,但允许您运行任意数据绑定表达式。此功能适用于 Gradle 2.0 版及更高版本的 Android Gradle 插件。
在方法引用中,方法的参数必须与事件监听器的参数匹配。在监听器绑定中,只有您的返回值必须与监听器的预期返回值相匹配(预期返回值无效除外)。

    class Presenter {
        fun onSaveClick(task: Task){}
    }

然后,您可以将点击事件绑定到 onSaveClick() 方法

<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            <variable name="task" type="com.android.example.Task" />
            <variable name="presenter" type="com.android.example.Presenter" />
        </data>
        <LinearLayout 
               android:layout_width="match_parent" 
               android:layout_height="match_parent">
               <Button 
                    android:layout_width="wrap_content" 
                    android:layout_height="wrap_content"
                    android:onClick="@{() -> presenter.onSaveClick(task)}" />
        </LinearLayout>
    </layout>

如果监听的事件返回类型不是void的值,那么表达式也必须返回相同类型的值(比如长按事件返回值是boolean)。

fun onSaveClick(task: Task): Boolean{}

从以上说明可以看出,方法引用更适合简单的事件处理,并且方法不需要自定义参数。监听器则更适合复杂的事件处理,方法可以自定义参数。

避免使用复杂的监听器

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

导入、变量和包含

导入

通过导入功能,您可以轻松地在布局文件中引用类,就像在托管代码中一样。您可以在 data 元素使用多个 import 元素,也可以不使用。以下代码示例将View类导入到布局文件中:

<data>
        <import type="android.view.View"/>
    </data>

上述方法中导入了View类,可以引用View的属性,比如设置显示:

android:visibility="@{user.man? View.VISIBLE : View.GONE}"
类型别名

当类名有冲突时,其中一个类可使用别名重命名。以下示例将com.example.real.estate软件包中的View类重命名为Vista

<import type="android.view.View"/>
    <import type="com.example.real.estate.View"
            alias="Vista"/>
导入其他类
<data>
        <import type="com.example.User"/>
        <import type="java.util.List"/>
        <variable name="user" type="User"/>
        <variable name="userList" type="List&lt;User>"/>
    </data>

还可以通过使用导入的类型来对表达式的一部分进行类型转换。以下示例将connection属性强制转换为类型User

<TextView
       android:text="@{((User)(user.connection)).lastName}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

在表达式中引用静态字段和方法时,也可以使用导入的类型。以下代码会导入MyStringUtils类,并引用其 capitalize方法:

<data>
        <import type="com.example.MyStringUtils"/>
        <variable name="user" type="com.example.User"/>
    </data>
    …
    <TextView
       android:text="@{MyStringUtils.capitalize(user.lastName)}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
包含

通过使用应用命名空间和特性中的变量名称,变量可以从包含的布局传递到被包含布局的绑定。以下示例展示了来自 name.xml 和 contact.xml 布局文件的被包含 user 变量:

<?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 元素的直接子元素。例如,以下布局不受支持:

<?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>
       <merge><!-- Doesn't work -->
           <include layout="@layout/name"
               bind:user="@{user}"/>
           <include layout="@layout/contact"
               bind:user="@{user}"/>
       </merge>
    </layout>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,124评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,453评论 3 404
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,386评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,136评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,142评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,593评论 1 315
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,958评论 3 429
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,944评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,477评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,512评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,639评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,227评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,971评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,397评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,550评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,203评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,713评论 2 366