dataBinding

目录

定义

使用步骤

XML 中常用的表达式语言

常见功能运算符

集合类型数据的设置或访问

资源文件的引入

XML 中事件处理

方式一:方法引用

方式二:监听器绑定

data标签中的操作

import

variable

include布局标签中的传值操作

可观察数据的使用 

可观察字段

可观察集合

可观察对象

生命周期感知型对象

双向数据绑定

binding 适配器

RecycleView中使用DataBinding


定义

将程序中的变量和方法通过声明的方式绑定到XML布局上。

(Android原文:数据绑定库是一个支持库,可让您使用声明性格式(而不是以程序化方式)将布局中的界面组件绑定到应用中的数据源)

使用步骤

一、在app模块的build.gradle文件中启用dataBinding构建选项。

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

二、编写数据绑定文件布局,以根标记layout开头,后跟data元素和view根元素。

例如:aty_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <!--    用于声明需要绑定的变量-->
    <data>
        <variable
            name="user"
            type="com.lifeidroid.databindingsimple.entity.User" />
    </data>
    <!--    用于声明需要绑定的布局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!--        用@{}语法来赋值属性-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"></TextView>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"></TextView>
    </LinearLayout>
</layout>

三、在Activity中使用视图绑定。

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.lifeidroid.databindingsimple.databinding.AtyMainBinding
import com.lifeidroid.databindingsimple.entity.User

class MainAcitivty : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //方式一 可以直接使用dataBinding自带的工具类DataBindingUtil直接进行绑定。也可以用下面两种方式,先获取视图然后进行绑定
        val binding:AtyMainBinding = DataBindingUtil.setContentView(this,R.layout.aty_main)
        //方式二 
//        val binding: AtyMainBinding = AtyMainBinding.inflate(layoutInflater)
//        setContentView(binding.root)
        //方式三
//        val binding: AtyMainBinding =
//            DataBindingUtil.inflate(layoutInflater, R.layout.aty_main, null, false)
//        setContentView(binding.root)

        //为布局变量关联数据
        binding.user = User("张","三丰")
    }
}

Fragment的视图绑定可以参考方式二或方式三,先获取视图,然后进行绑定。

XML 中常用的表达式语言

常见功能运算符

类型示例

数学:+ - / * %

android:progress="@{user.score +5}"

字符串串联:+

android:text="@{user.firstName+user.lastName}"

逻辑:&& || (&&需要转义为&amp;&amp;)

android:checked="@{user.age > 10 &amp;&amp; user.age &lt; 15}"

二进制文件:& | ^

一元组:+ - ! ~

位运算符:>> >>> <<

android:onClick="@{()-> viewModel.btnClick( 0x10 >> 2 ) }"

比较:== > < >= <=< 需要转义为 &lt;

instanceof

android:checked="@{ user instanceof User }"

分类:()

字面量,例如字符、字符串、数字、null

类型转换

方法调用

android:onClick="@{()-> viewModel.btnClick( 0x10 >> 2 ) }"

字段访问

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

数组访问:[]

android:text="@{userList[0].name}"

三元运算符:?:

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

Null 合并运算符

android:text="@{user.displayName ?? user.lastName}"
引用同一个布局控件的值<EditText android:id="@+id/example_text" .../>
<!--TextView引用EditText中的值-->
<TextView
    android:id="@+id/example_output"
    android:text="@{exampleText.text}"
.../>

在布局文件中不支持this、super、new、Explicit generic invocation 

集合类型数据的设置或访问

<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]}"
...    
表达式中的字符串可用以下两种方式
android:text='@{map["firstName"]}'  
android:text="@{map[`firstName`]}" 

资源文件的引入

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


<string name="nameFormat">%s -  %s</string>
android:text="@{@string/nameFormat(user.firstName , user.lastName)}"

<!--表达式中用到R文件中的值,需要在data中引入对应的R文件-->
<import type="com.example.android.databinding.basicsample.R"/>
app:srcCompat="@{user.likes > 4 ? R.drawable.ic_max : R.drawable.ic_min }"

XML 中事件处理

事件属性名称由监听器方法名称决定,也可以直接这只监听器中对应的方法,一般为on开头的方法,如下面示例:

ButtonSetOnClickListener->onClick

android:onClick="@{}"

setOnLongClickListener

->onLongClick
android:onLongClick="@{}"
CheckBox

setOnCheckedChangeListener->onCheckedChanged

android:onCheckedChanged="@{}"

方式一:方法引用

将事件处理的方法引用绑定到XML中对应的事件属性上。相当于事件属性在XML中不做任何业务处理。(注意:因为是方法的引用,所以被引用方法的参数和返回值必须与事件属性对应的方法相同。)

class MyHandlers {
    //参数和返回值必须与setOnClickListener中onClick方法的相同
    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}"/>
           <!--这里直接将方法的引用绑定到了onClick方法属性上-->
   </LinearLayout>
</layout>

方式二:监听器绑定

通过 lambda 表达式在XML中给事件属性设置一个监听器,事件触发带动相应的监听器。在监听器内可以做一些逻辑,但要避免复杂的监听器

class Presenter {
    fun onSaveClick(task: Task){}
    
    fun onSaveClick2(view: View, task: Task){}

    fun onCompletedChanged(task: Task, completed: Boolean){}
    
    //如果您正在监听的事件返回类型不是 void 的值,则您的表达式也必须返回相同类型的值。
    fun onLongClick(view: View, task: Task): Boolean { }
}
<?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)}" />
        
        <!--可以为参数重新命名-->
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onClick="@{(mView) -> presenter.onSaveClick2(mView, task)}" />

        <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
      android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
        
        <!--注意事件属性的返回值-->
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}" />
    </LinearLayout> 
</layout> 

data标签中的操作

import

在XML布局文件中导入引用类,比如用到的实体类、常量类、工具类等。

    <data>
        <!--表达式中用到View类的VISIBLE和GONE常量,需要对View类进行import导入。-->
        <import type="android.view.View" />
        <!--当引用类有冲突时,可以设置别名-->
        <import
            alias="CustomView"
            type="com.lifeidroid.databindingsimple.view.View" />
        <!--当变量中用到List或者其他集合类时需要导入对应的类-->
        <import type="java.util.List" />
        <!--当变量中用到实体类时需要导入对应的类-->
        <import type="com.lifeidroid.databindingsimple.entity.User" />
        <!--当表达式中用到工具类或自定义方法时时需要导入-->
        <import type="com.lifeidroid.databindingsimple.utils.StringUtils"/>
    </data>

variable

每一个variable元素就相当于是XML布局文件(aty_main.xml)对应的绑定类(AtyMainBinding)上创建的变量,在布局文件绑定的时候可以设置该变量。

布局文件中创建了user、image、note三个变量。系统会自动生成BR类,里面包含了生成的变量索引

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user" type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note" type="String"/>
</data>
....
</layout>

在Activity中对布局文件的变量进行绑定

class MainAcitivty : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: AtyMainBinding = DataBindingUtil.setContentView(this, R.layout.aty_main)
        // 为布局变量关联数据
        // 方式一、直接为变量赋值
        binding.user = User("张", "三丰", -20, 8, true)
        // 方式二、根据变量索引赋值
        // binding.setVariable(BR.user, User("张", "三丰", -20, 8, true))
        binding.image = getDrawable(R.drawable.ic_launcher_background)
        binding.note = "这是一个测试"
    }

变量在未赋值时会自动分配默认值(在生成的绑定类(AtyMainBinding)中,每个描述的变量都有一个对应的 setter 和 getter。在调用 setter 之前,这些变量会采用默认的托管代码值 - null 用于引用类型,0 用于 intfalse 用于 boolean,等等)。

include布局标签中的传值操作

以下示例展示了从main.xml布局中的user传递给 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}"/>
   </merge>
</layout>

可观察数据的使用 

以上介绍的都是传统对象在数据绑定中的使用,但是这些对象的变化不会通知UI(XML)更新。如果修改了数据源需要立即刷新UI,就需要用到可观察数据类型。

可观察字段

使某个字段变成可观察类型,只需要将字段替换成对应的可观察类型。

传统字段可观察字段
BooleanObservableBoolean
ByteObservableByte
Char

ObservableChar

ShortObservableShort
IntObservableInt
LongObservableLong
FloatObservableFloat
DoubleObservableDouble
ParcelableObservableParcelable
String

ObservableField<String>

示例:

class User {
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
}

...
//字段的设置和取值
user.firstName.set("张三丰")
user.firstName.get()

可观察集合

使用可变集合来保存数据,使集合具有可观察性。

传统集合可观察集合

ArrayList

ObservableArrayList

List

ObservableList

ArrayMap

ObservableArrayMap

MapObservableMap

示例:注意'<' 、'>'的转义

//集合定义
ObservableArrayMap<String, Any>().apply {
    put("firstName", "Google")
    put("lastName", "Inc.")
    put("age", 17)
}


//布局文件
<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
</data>
…
<TextView
    android:text="@{user.lastName}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<TextView
    android:text="@{String.valueOf(1 + (Integer)user.age)}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

可观察对象

使对象的属性更改时能及时刷新页面,需要将对象转为可观察对象。

步骤:

  1. 继承BaseObservable
  2. 给getter分配Bindable注解
  3. 在setter中调用notifyPropertyChanged()方法

示例:

import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import com.lifeidroid.databindingsimple.BR

class User2 : BaseObservable() {
    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            //Bindable 注解会在编译期间在 BR 类文件中生成一个条目。
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var age: Int = 0
        set(value) {
            field = value
            notifyPropertyChanged(BR.age)
        }
}

注:如遇到BR的字段不生成或者unresolved reference: BR的错误时,需要将app的build.gradle进行如下配置:

plugins {
    ...
    id("kotlin-kapt")
}

android {
    ...
    kapt {
        generateStubs = true
    }
}

dependencies {
    ...
    //版本与project的build.gradle中com.android.application版本保持一致
    kapt("androidx.databinding:databinding-compiler:8.2.1")
}

生命周期感知型对象

XML中可以绑定数据源StateFlow或LiveData,数据源发生变化会自动通知页面有关数据更新。这种绑定能够感知生命周期,在界面显示在屏幕上时才会触发。

StateFlow示例:

//XML
...
<data>
        <variable
          name="viewModel"
        type="com.lifeidroid.databindingsimple.ScheduleViewModel" />
</data>
<TextView
    android:id="@+id/name"
    android:text="@{viewmodel.username}" />


//Activity-------------------------------------------------------------
class MainActivity : AppCompatActivity() {
    val viewModel:MainViewModel = ScheduleViewModel()
    override fun onCreate(savedInstanceState: Bundle?) {
        val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.aty_main)
        // 将页面的生命周期绑定到Activity
        binding.lifecycleOwner = this
        // 设置页面的viewModel属性
        binding.viewModel = viewModel
    }
}

//ViewModel-------------------------------------------------------------

class ScheduleViewModel : ViewModel() {

    private val _username = MutableStateFlow<String>("")
    val username: StateFlow<String> = _username

    init {
        viewModelScope.launch {
            _username.value = Repository.loadUserName()
        }
    }
}

双向数据绑定

以上介绍的为单向数据绑定(数据变更—>页面更新)。但如果页面数据改变也会同步到数据源变化,这样就是双向数据绑定。

实现方式:

方式一:运用事件监听将页面状态通知数据源变化

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    <!--绑定数据源-->
    android:checked="@{viewmodel.rememberMe}"
    <!--通过对onCheckChanged方法监听同步数据源-->
    android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
/>

方式二:@={}表达式

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@={viewmodel.rememberMe}"
/>

binding 适配器

dataBinding扩展☞binding的适配器

RecycleView中使用DataBinding

adapter示例:

class ListAdapter(var context: Context,var datas:List<User3>) : RecyclerView.Adapter<ListAdapter.MyHolder>() {

    class MyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
        var binding = ItemListBinding.inflate(LayoutInflater.from(context),parent,false)
        return MyHolder(binding.root)
    }

    override fun getItemCount(): Int {
        return datas.size
    }

    override fun onBindViewHolder(holder: MyHolder, position: Int) {
        //通过getBinding方法返回绑定类
        var binding = DataBindingUtil.getBinding<ItemListBinding>(holder.itemView)
        //绑定变量方式一
        binding!!.user = datas[position]
        //绑定变量方式二
        //binding!!.setVariable(BR.user,datas[position])
        //通过binding可以获取视图控件
        binding!!.tvName.setOnClickListener {
            Log.d("TAG", "onBindViewHolder: "+datas[position].firstName)
        }
        //当变量发生改变后,需要调用下面方法强制立即绑定,让页面改变
        binding!!.executePendingBindings()
    }
}

「完」

以上为个人参考Android官网整理的基本用法,可能有疏漏,仅供参考。

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dinding是一种通过机制,将代码中的数据和XML(UI)进行绑定的技术。它允许双方都能对数据进行操作,并在数据发生变化时自动刷新数据。 在Android中,我们可以使用DataBindingUtil类的方法来创建Databinding实例,即通过DataBindingUtil.inflate(LayoutInflater, layoutId, parent, attachToParent)或DataBindingUtil.bindTo(viewRoot, layoutId)方法来得到绑定对象。 Databinding主要解决了Model层与View交互的问题,并且现在也支持双向绑定。它的优势在于可以简化代码,使得数据与UI更紧密地结合。我们可以通过一个登录的例子来进一步感受Databinding的优势。你可以下载并查看一个登录的Demo,或者自己编写相关的例子来深入理解Databinding的用法和好处。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [DataBinding详解](https://blog.csdn.net/peiyuWang_2015/article/details/76945081)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [DataBinding使用教程详解](https://blog.csdn.net/zhangwude2301/article/details/80069972)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值