Android Jetpack-Databinding基本使用

news2025/7/19 20:21:53

文章目录

      • 让你的项目支持Databinding
      • 基本使用
      • 布局和绑定表达式
        • 常用运算符
        • 判空null
        • 获取对象属性
        • 避免空指针异常
        • 其他控件引用
        • 资源引用
      • 事件处理
      • import,variables,and includes
        • import
        • Variables
        • Includes
      • 数据更新->UI更新
        • 监听字段变化更新
        • 监听对象变化更新
      • UI更新->数据更新
      • 绑定类
        • 修改绑定类的名称
        • 获取到绑定类

自从有Android系统以来,我们绑定数据从findViewById开始,需要强转控件类型,随着SDK的升级,不再需要强转类型。顺带还有黄油刀,就是为了简化我们的开发。我们可以不再使用findViewById去找控件,然后再设置数据了。如果数据更新了,还得重新设置。
接下来我们就了解一下Databinding吧。
我们可以通过Databinding实现一般页面的数据绑定,列表的数据绑定,图片的数据绑定等常见的场景。

让你的项目支持Databinding

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

基本使用

先来一个布局,我们要显示用户的信息,包括姓名,年龄和性别
在这里插入图片描述
创建bean类,用于封装用户数据

data class User(var name: String, var age: Int, var gender: Gender)

enum class Gender {
    FEMALE, MALE
}

布局代码

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="user"
            type="com.example.databindingdemo.domain.User" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.10259918" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.2" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.3" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.3" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text_name"
            app:layout_constraintBottom_toTopOf="@+id/guideline"
            app:layout_constraintEnd_toStartOf="@+id/guideline4"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text_age"
            app:layout_constraintBottom_toTopOf="@+id/guideline2"
            app:layout_constraintEnd_toStartOf="@+id/guideline4"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline" />

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text_gender"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toStartOf="@+id/guideline4"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline2" />

        <TextView
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            app:layout_constraintBottom_toTopOf="@+id/guideline"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline4"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.gender.toString()}"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline4"
            app:layout_constraintTop_toTopOf="@+id/guideline2" />

        <TextView
            android:id="@+id/textView7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(user.age)}"
            app:layout_constraintBottom_toTopOf="@+id/guideline2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline4"
            app:layout_constraintTop_toTopOf="@+id/guideline" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

MainActivity.kt的代码

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //把布局交给DataBindingUtil
        val activityMainBinding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        //设置数据
        activityMainBinding.user = User("TrillGates", 30, Gender.MALE)
    }
}

运行结果
在这里插入图片描述
由此,我们就完成了数据的绑定,我们没有给控件起id,里面的id是自动生成的,我们不用也行。这是不是跟vue.js里一样呀,绑定数据就可以了,非常简单。
说明

  • 这里面我们布局最外层使用Layout标签进行包裹
  • layout你可以手动写,或者按alt+回车提示你自动改
    在这里插入图片描述
  • data标签
<data>
    <variable
              name="user"
              type="com.example.databindingdemo.domain.User" />
</data>

这里面我们用于置顶bean类中的User,也可以这样写:

<data>
    <import type="com.example.databindingdemo.domain.User" />
    <variable
              name="user"
              type="User" />
</data>
  • ActivityMainBinding是自动生成的类,大家可以看看以下代码,对应着以上的布局代码
// Generated by data binding compiler. Do not edit!
package com.example.databindingdemo.databinding;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Guideline;
import androidx.databinding.Bindable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import com.sunofbeaches.databindingdemo.R;
import com.sunofbeaches.databindingdemo.domain.User;
import java.lang.Deprecated;
import java.lang.Object;

public abstract class ActivityMainBinding extends ViewDataBinding {
  @NonNull
  public final Guideline guideline;

  @NonNull
  public final Guideline guideline2;

  @NonNull
  public final Guideline guideline3;

  @NonNull
  public final Guideline guideline4;

  @NonNull
  public final TextView textView;

  @NonNull
  public final TextView textView2;

  @NonNull
  public final TextView textView3;

  @NonNull
  public final TextView textView4;

  @NonNull
  public final TextView textView6;

  @NonNull
  public final TextView textView7;

  @Bindable
  protected User mUser;

  protected ActivityMainBinding(Object _bindingComponent, View _root, int _localFieldCount,
      Guideline guideline, Guideline guideline2, Guideline guideline3, Guideline guideline4,
      TextView textView, TextView textView2, TextView textView3, TextView textView4,
      TextView textView6, TextView textView7) {
    super(_bindingComponent, _root, _localFieldCount);
    this.guideline = guideline;
    this.guideline2 = guideline2;
    this.guideline3 = guideline3;
    this.guideline4 = guideline4;
    this.textView = textView;
    this.textView2 = textView2;
    this.textView3 = textView3;
    this.textView4 = textView4;
    this.textView6 = textView6;
    this.textView7 = textView7;
  }

  public abstract void setUser(@Nullable User user);

  @Nullable
  public User getUser() {
    return mUser;
  }

  @NonNull
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot) {
    return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
  }

  /**
   * This method receives DataBindingComponent instance as type Object instead of
   * type DataBindingComponent to avoid causing too many compilation errors if
   * compilation fails for another reason.
   * https://issuetracker.google.com/issues/116541301
   * @Deprecated Use DataBindingUtil.inflate(inflater, R.layout.activity_main, root, attachToRoot, component)
   */
  @NonNull
  @Deprecated
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot, @Nullable Object component) {
    return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, root, attachToRoot, component);
  }

  @NonNull
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, DataBindingUtil.getDefaultComponent());
  }

  /**
   * This method receives DataBindingComponent instance as type Object instead of
   * type DataBindingComponent to avoid causing too many compilation errors if
   * compilation fails for another reason.
   * https://issuetracker.google.com/issues/116541301
   * @Deprecated Use DataBindingUtil.inflate(inflater, R.layout.activity_main, null, false, component)
   */
  @NonNull
  @Deprecated
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable Object component) {
    return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, null, false, component);
  }

  public static ActivityMainBinding bind(@NonNull View view) {
    return bind(view, DataBindingUtil.getDefaultComponent());
  }

  /**
   * This method receives DataBindingComponent instance as type Object instead of
   * type DataBindingComponent to avoid causing too many compilation errors if
   * compilation fails for another reason.
   * https://issuetracker.google.com/issues/116541301
   * @Deprecated Use DataBindingUtil.bind(view, component)
   */
  @Deprecated
  public static ActivityMainBinding bind(@NonNull View view, @Nullable Object component) {
    return (ActivityMainBinding)bind(component, view, R.layout.activity_main);
  }
}

布局和绑定表达式

在布局中,我们可以通过@{}来绑定我们的控件与数据

常用运算符

在这里面,可以使用多种表达式,比如说:

算术运算符 + - / * %
字符串连接 +
逻辑 && ||
二元 & | ^
一元 + - ! ~
移位运算符 >> >>> <<
比较运算符 == > < >= <=
Instanceof
Grouping ()
文字 - character, String, numeric, null
Cast
方法调用
Field 访问字段
Array 访问数组
三元运算符 ?:

判空null

官方例子:

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

如果左边为null,那么会显示右边的内容
等价于这一行代码

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

获取对象属性

比如前面我们代码显示用户的名称
我们直接写:

<TextView
    android:id="@+id/textView4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user.name}"
    app:layout_constraintBottom_toTopOf="@+id/guideline"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="@+id/guideline4"
    app:layout_constraintTop_toTopOf="parent" />

android:text=“@{user.name}” 这里面访问对象的属性,就类似于我们的getter方法

避免空指针异常

databinding会自动地检查null,避免控制出异常。如果是为null的话则会显示默认值。
比如说前面的user.name,如果user为null,那么这个name是字符串类型,会显示null,如果前面的user.age,如果user为null,那么age是int类型,会显示默认值0

其他控件引用

比如说你在你的布局里有一个EditText,它的id为input_box,你还有一个TextView,如果它要显示EditText的内容,进行联动,可以这么做

<?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">

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


        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.3" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.1" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.2" />

        <TextView
            android:id="@+id/textView9"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text_account"
            app:layout_constraintBottom_toTopOf="@+id/guideline6"
            app:layout_constraintEnd_toStartOf="@+id/guideline5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView10"
            android:text="@{inputBox.text}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toTopOf="@+id/guideline7"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline5"
            app:layout_constraintTop_toTopOf="@+id/guideline6" />

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="10"
            android:id="@+id/input_box"
            android:hint="请输入用户名"
            android:inputType="textPersonName"
            app:layout_constraintBottom_toTopOf="@+id/guideline6"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline5"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

在这里插入图片描述

这里面注意一下名字,ID用下划线隔开,然后使用的是驼峰命名。

资源引用

也就是我们的表达式里也可以引用资源内容,例如引用String资源

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.sunofbeaches.databindingdemo.domain.User" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:textColor="@{@color/colorAccent}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{@string/app_name}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{@string/text_user_info(user.name,user.age)}" />
    </LinearLayout>
</layout>

ResourceActivity.kt的代码

class ResourcesActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val resourcesBinding = DataBindingUtil.setContentView<ActivityResourcesBinding>(
            this,
            R.layout.activity_resources
        )
        resourcesBinding.user = User("YiRan", 24, Gender.MALE)
    }
}

事件处理

事件处理主要有两种
一种是方法引用,另外一种则是监听绑定
官方的说法叫做:Method references 和 Listener bindings
这两种方式的不同是什么呢?创建时机不同
官方文档原话:

The major difference between method references and listener bindings is that the actual listener implementation is created when the data is bound, not when the event is triggered. If you prefer to evaluate the expression when the event happens, you should use listener binding.

数据准备

<data class="EventBinding">

    <variable
              name="textInfo"
              type="String" />

    <variable
              name="eventHandler"
              type="com.example.databindingdemo.EventHandlers" />
</data>

EventActivity.kt

class EventActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val eventBinding =
            DataBindingUtil.setContentView<EventBinding>(this, R.layout.activity_event)
        eventBinding.textInfo = "点击复制内容"
        eventBinding.eventHandler = EventHandlers()
    }
}

方法处理类

class EventHandlers {
    fun onFistButtonClick(view: View) {
        println("第一个按钮点击了...")
    }

    fun onTextClickToCopy(text: String) {
        println("复制的内容是:$text")
    }
}

方法引用
我们前面引入了EventHandlers,这其实是个类,类里面有方法,当然啦,你也可以传Activity进来,把方法写在Activity里即可。

<Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="@{eventHandler::onFistButtonClick}"
        android:text="第一个按钮点击" />

要注意的是方法里默认的参数是View,如果没有的话,会报错。
监听绑定
监听绑定,其实就是lambda表达式

<TextView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:onClick="@{()->eventHandler.onTextClickToCopy(textInfo)}"
          android:text="@{textInfo}" />

在这里插入图片描述

import,variables,and includes

导入[类],变量,包含(引入[布局])
import 我们一般用于导入类,比如说前面我们导入UserInfo,导入List类,而java.lang.*是默认导入的。
variables,变量的意思,也就是我们的数据bean类
include,包含,引入另外一个布局,子布局。达到某些布局可共用,减少重复代码。

import

官方的例子是这样

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

使用的时候就可以用值View里的常量了

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

别名
如果我们导入两个同名的类,不同包名嘛。
这个时候 我们可以通过起别名的方式来使用。
比如说,两个都是View

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

使用可以通过别名来使用

Variables

Variables是变量的意思,你可以引入多个变量,这些在前面的代码里已经显示过了。比如说:

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

Includes

使用场景,当我们当前这个xml布局include另外一个布局时,比如说:

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

        <import type="com.sunofbeaches.databindingdemo.domain.User" />

        <variable
            name="user"
            type="User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include
            layout="@layout/include_header_bar"
            bind:userInfo="@{user.name}" />

    </LinearLayout>

</layout>

这样子,它包含了一个子布局,Include的方式添加进来

所包含的内容是这样子的

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="userInfo"
            type="String" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#33ffaa">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="@{userInfo}" />

    </LinearLayout>
</layout>

Activity代码

class IncludeActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val includeBinding =
            DataBindingUtil.setContentView<ActivityIncludeBinding>(this, 
                R.layout.activity_include)
        includeBinding.user = User("TrillGates", 30, Gender.MALE)
    }
}

运行结果
在这里插入图片描述

数据更新->UI更新

以前我们刷新数据,去服务器获取新的数据,然后数据回来,接着重新设置数据,更新UI。那使用Databinding还需要那样更新UI吗?
不需要!通过使用实现Observable的数据,当数据更新的时候,自动更新UI。

监听字段变化更新

常用的Observable

ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
ObservableArrayMap
ObservableArrayList

比如说我们有一个User,里面有name,age。
我们可以这么写
布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="activity"
            type="com.sunofbeaches.databindingdemo.observable.PlainObjectActivity" />

        <variable
            name="simpleUser"
            type="com.sunofbeaches.databindingdemo.domain.SimpleUser" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{"姓名:"+simpleUser.userName}' />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{"年龄:"+simpleUser.age}' />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{()->activity.onButtonClick()}"
            android:text="修改年龄和名称" />
    </LinearLayout>
</layout>

数据Bean类

class SimpleUser {
    var userName = ObservableField<String>()
    var age = ObservableInt()
}

Activity

class PlainObjectActivity : AppCompatActivity() {
    private lateinit var dataBinding: ActivitySimpleObserableBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        dataBinding = DataBindingUtil.setContentView<ActivitySimpleObserableBinding>(
            this,
            R.layout.activity_simple_obserable
        )
        dataBinding.activity = this
        dataBinding.simpleUser = SimpleUser().apply {
            age.set(24)
            userName.set("YiRan2")
        }
    }

    fun onButtonClick() {
        Toast.makeText(this, "更新数据...", Toast.LENGTH_SHORT).show()
        dataBinding.simpleUser?.apply {
            age.set(18)
            userName.set("YiRan1")
        }
    }
}

这样当你点击按钮simpleUser数据发生变化监听到就会进行UI更新

监听对象变化更新

布局代码

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="activity"
            type="com.sunofbeaches.databindingdemo.observable.PlainObjectActivity" />

        <variable
            name="observableUser"
            type="com.sunofbeaches.databindingdemo.domain.ObservableObjectUser" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{"姓名:"+observableUser.userName}' />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{"年龄:"+observableUser.age}' />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{()->activity.onButtonClick()}"
            android:text="修改年龄和名称" />

    </LinearLayout>
</layout>

bean类代码

class ObservableObjectUser : BaseObservable() {

    @get:Bindable
    var userName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.userName)
        }

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

activity的代码

class PlainObjectActivity : AppCompatActivity() {
    private lateinit var dataBinding: ActivitySimpleObserableBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        dataBinding = DataBindingUtil.setContentView<ActivitySimpleObserableBinding>(
            this,
            R.layout.activity_simple_obserable
        )
        dataBinding.activity = this
        dataBinding.observableUser = ObservableObjectUser().apply {
            age = 30
            userName = "YiRan1"
        }
    }

    fun onButtonClick() {
        Toast.makeText(this, "更新数据...", Toast.LENGTH_SHORT).show()
        dataBinding.observableUser?.apply {
            age = 18
            userName = "YiRan2"
        }
    }
}

UI更新->数据更新

前面我们是数据更新了,UI跟着更新,接下来我们是UI更新,数据跟着更新。不过我们需要做一些处理。当我们输入框变化的时候,当我们数字选择器内容改变的时候,单选框状态改变的时候,我们的数据也要跟着变化了。
应用场景,比如说我们在交易股票的时候,你输入数量,会动态地计算你的成交以后的钱是多少。
使用传统的做法,我们得监听输入股票数量的输入框的内容变化,然后再进行计算,设置到最终成交金额的控件上。
如果使用DataBinding,这个怎么做呢?
UI布局

<?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="stock"
            type="com.sunofbeaches.databindingdemo.domain.ObservableStock" />

        <import
            type="com.sunofbeaches.databindingdemo.converter.Converter" />
    </data>

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


        <TextView
            android:id="@+id/textView11"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="出售数量"
            app:layout_constraintBottom_toTopOf="@+id/guideline8"
            app:layout_constraintEnd_toStartOf="@+id/guideline10"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline8"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="70dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline9"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="140dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline11"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="210dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline10"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="140dp" />

        <TextView
            android:id="@+id/textView12"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="当前股价"
            app:layout_constraintBottom_toTopOf="@+id/guideline9"
            app:layout_constraintEnd_toStartOf="@+id/guideline10"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline8" />

        <TextView
            android:id="@+id/textView13"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="预计成交总价"
            app:layout_constraintBottom_toTopOf="@+id/guideline11"
            app:layout_constraintEnd_toStartOf="@+id/guideline10"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline9" />

        <EditText
            android:id="@+id/editTextTextPersonName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:text="@={Converter.int2String(stock.sellCount)}"
            app:layout_constraintBottom_toTopOf="@+id/guideline8"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline10"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView14"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(stock.currentPrise)}"
            app:layout_constraintBottom_toTopOf="@+id/guideline9"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline10"
            app:layout_constraintTop_toTopOf="@+id/guideline8" />

        <TextView
            android:id="@+id/textView15"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(stock.totalPrise)}"
            app:layout_constraintBottom_toTopOf="@+id/guideline11"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline10"
            app:layout_constraintTop_toTopOf="@+id/guideline9" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

在这里插入图片描述
Activity代码

class TwoWayBindingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val contentView =
            DataBindingUtil.setContentView<ActivityTowWayBindingBinding>(
                this,
                R.layout.activity_tow_way_binding
            )
        val stock = ObservableStock()
        contentView.stock = stock
        stock.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
            override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
                if (sender is ObservableStock) {
                    sender.totalPrise = sender.sellCount * sender.currentPrise
                }
            }
        })
    }
}

相关的Bean类

class ObservableStock :
    BaseObservable() {
    var sellCount: Int = 0
        set(value) {
            if (value != field) {
                field = value
                notifyChange()
            }
        }
    var currentPrise: Float = 11.0f
    var totalPrise: Float = 0.0f

}

转换器,EditText只接受String,所以我们需要一个转换器,来把String转成int类型,把int类型转成String类型

object Converter {

    @InverseMethod("string2Int")
    @JvmStatic
    fun int2String(
        value: Int
    ): String {
        return value.toString()
    }

    @JvmStatic
    fun string2Int(value: String): Int {
        if (value.isEmpty()) {
            return 0
        }
        return value.toInt()
    }

}

运行结果:
在这里插入图片描述

绑定类

每一个xml可以对应生成一个binding类。比如说你的Activity里的布局 ,比如说列表的item,这些都可以生成对应的binding类。
前面的话,我们的绑定类都是自动生成的,比如说activity_main.xml的对应绑定类是ActivityMainBinding,也就是默认情况下以布局的名称改成驼峰命名法+Binding

修改绑定类的名称

在data标签上,修改class属性即可,比如说我们的main_activity.xml布局里

<data class="MainBinding">

    <import type="com.sunofbeaches.databindingdemo.domain.User" />

    <variable
              name="user"
              type="User" />
</data>

点击Build->Make Project一下,重新构建,就会生成MainBinding的类了

获取到绑定类

两种方式,一种是通过DataBindingUtil来绑定,一种是通过Binding类的静态方法来获取到绑定类实例

  • 手动方式来绑定
val inflate = ActivityMainBinding.inflate(layoutInflater)
setContentView(inflate.root)

这样子,返回的是ActivityMainBinding,并且,可以拿到View,设置到Activity里去。
当然啦,这个inflate也有重载的方法,其实跟我们以前使用的LayoutInflater差不多。

val binding = ActivityMainBinding.inflate(getLayoutInflater(), viewGroup, false)

可以直接bind到View上

val binding = ActivityMainBinding.bind(viewRoot)
  • 使用DataBindingUtil来绑定
//把布局交给DataBindingUtil
val activityMainBinding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)

我们在Activity里两种方法都可以,我们在RecyclerView的适配器,或者其他条目类的布局,可以通过Binding类的静态方法来获取到实例对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541928.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MPLS隧道——隧道迭代与MPLS高可靠性

目录 MPLS隧道迭代 什么情况下需要隧道迭代 解决方法 MPLS高可靠性 VPN FRR VPN GR MPLS隧道迭代 什么情况下需要隧道迭代 解决方法 两种解决方法 将IBGP邻居更改为Vpnv4的IBGP邻居&#xff08;为目标网段产生私网标签&#xff0c;然后此带标签的BGP路由直接进入标签隧道…

作为程序员的你,常用的工具软件有哪些?

不会还有程序员没用过Chatgpt吧&#xff1f; 我现在的工作日常&#xff1a;把需求提给Chatgpt&#xff0c;代码出来后再自行润色一下代码&#xff0c;然后到工业环境里跑一下&#xff0c;没问题就可以提交了。一来一回效率提高了好几倍&#xff0c;摸鱼的时间都变多了…… 除了…

自学黑客(网络安全)必学内容

随着时代的发展&#xff0c;经济、社会、生产、生活越来越依赖网络。而随着万物互联的物联网技术的兴起&#xff0c;线上线下已经打通&#xff0c;虚拟世界和现实世界的边界正变得模糊。这使得来自网络空间的攻击能够穿透虚拟世界的边界&#xff0c;直接影响现实世界的安全。 …

环环相扣,循环不止:深入解析循环队列

本盘博客会讲解力扣“622. 设计循环队列”的解题思路&#xff0c;这是题目链接。 先来审下题&#xff1a; 以下是示例&#xff1a; 以下是提示&#xff1a; 如何设计一个循环队列呢&#xff1f;这里我用数组来实现。结构的定义如下&#xff1a; typedef struct {int* a; …

又一神器开源!无需服务器支持!打通手机,浏览器的Web LLM!

大家好&#xff0c;我是千与千寻&#xff0c;大家可以叫我“千寻哥”&#xff0c;之前和大家分享了两篇关于ChatGPT的技术文章&#xff1a; 1.chatgpt 2.chatgpt ChatGPT毫无疑问是现在最大的风口&#xff0c;各个行业都在集成ChatGPT的API接口以及各类的应用插件&#xff0…

本地部署 GPT Academic

本地部署 GPT Academic GPT Academic 项目概述Github 地址部署 GPT Academic配置 GPT Academic 参数启动 GPT Academic访问 GPT AcademicNew Bing Cookie 的获取方法 GPT Academic 项目概述 GPT 学术优化 (GPT Academic)&#xff0c;为GPT/GLM提供图形交互界面&#xff0c;特别…

SSM整合(单元测试、结果封装、异常处理)

文章目录 1&#xff0c;SSM整合1.1 流程分析1.2 整合配置步骤1&#xff1a;创建Maven的web项目步骤2:添加依赖步骤3:创建项目包结构步骤4:创建SpringConfig配置类步骤5:创建JdbcConfig配置类步骤6:创建MybatisConfig配置类步骤7:创建jdbc.properties步骤8:创建SpringMVC配置类步…

【Leetcode刷题】算法:最长公共前缀

文章目录 一、题目描述二、解题思路2.1 解法12.2 解法22.3 解法32.4 解法4 三、结果提交 一、题目描述 二、解题思路 2.1 解法1 class Solution:def longestCommonPrefix(self, strs: List[str]) -> str:if not strs: # 如果字符串数组为空&#xff0c;则返回空字符串ret…

MIL-STD-1553B总线系统搭建指导

MIL-STD-1553B总线系统搭建指导 1.1553B总线协议 1.11553B总线介绍 MIL-STD-1553B&#xff08;GJB 289A&#xff09;是一种应用于机载电子设备间通信的共享式总线通信协议&#xff0c;以总线式拓扑结构连接最多31个终端设备互联&#xff0c;传输速率为1Mbps&#xff0c;在航…

任务跟踪器重要性探析:提升项目效率,实现管理优化

使用任务跟踪器完成项目的最显著好处之一是它们大大减少了开始新项目的初始阻力&#xff0c;尤其是当它们是大型、长期和复杂的项目时。任务跟踪器可用于将这些艰巨的项目分解为更小的、相互依赖的任务&#xff0c;这些任务有助于激发动力和行动以实现最终目标。使用项目任务跟…

Python: 让单元测试输出像GoogleTest一样

文章目录 1. 目的2. 原版 unittest 的输出3. 仿 GoogleTest 的输出效果4. 实现原理浅析传入 testRunner 参数testRunner 参数应该满足的条件颜色高亮&#xff1a; ASCII 转义字符的使用测试用例输出文本内容的格式调整&#xff1a;仿googletest 5. 完整实现代码6. 完整调用代码…

$‘\r‘: command not found syntax error near unexpected token `$‘do\r‘‘ 解决方案

问题描述 今天在执行代码时出现了这样的错误&#xff1a; bash xxx.sh xxx.sh: line 2: $\r: command not found xxx.sh: line 7: $\r: command not found xxx.sh: line 8: syntax error near unexpected token $do\r 经查阅&#xff0c;发现是.sh文件在windows下编辑&#xf…

Nevron Open Vision for .NET Crack

Nevron Open Vision for .NET Crack 增加了对Microsoft.NET 7.0的支持-NOV现在完全支持.NET Core 7.0&#xff0c;此外还支持Microsoft.NET Framework 4.7.2、.NET Core 5.0和.NET Core 6.0的内部版本。 用于.NET改进的NOV图表 添加了WPF和WinForms版本中提供的新3D渲染引擎。新…

展会回顾 | 2023元宇宙生态博览会圆满落幕,3DCAT荣获“元宇宙交互技术奖”

2023年5月10日-5月12日&#xff0c;一场涵盖了元宇宙终端头显、数字文娱、数字艺术、数字运动、数字多媒体展陈设计、数字展厅展馆、科技文旅、夜游演艺、沉浸式KTV/酒吧等多个领域的元宇宙商业盛会——2023第2届世界元宇宙生态博览会在广州广交会展馆A区3.2馆、4.2馆掀开帷幕。…

Python求balance_list【三】

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/130159648 很早之前&#xff0c;我写了2篇 如何求balance_list 的博客&#xff1a; https://blog.csdn.net/zyooooxie/article/detail…

DeepFM - 工业界经典baseline(哈工大 华为)

文章目录 1、模型结构如下:2、关键理解点:3、代码实现细节:DeepFM: A Factorization-Machine based Neural Network for CTR Prediction。dfm由哈工大和华为合作发表在IJCAI-2017;模型结构很简单,wide&deep结构。1、模型结构如下: 相比wide&deep [下文简称wd] 有…

就业内推 | 应届生校招、实习,上市公司有岗,最高18k*15薪

01 UCloud &#x1f537;招聘岗位&#xff1a;网络工程师 &#x1f537;职责描述&#xff1a; 1、负责UCloud全球骨干网或数据中心网络工作&#xff0c;包括设备技术选型、架构运营方案设计、日常运维支持 2、持续提升网络稳定性与性能。 &#x1f537;任职要求&#xff1a; …

Flowable-modeler可视化教程

Flowable-Modeler功能 提供可视化编辑器&#xff0c;编辑BPMN流程&#xff0c;编辑CASE模型&#xff0c;编辑Form表单&#xff0c;编辑App应用&#xff0c;编辑决策表提供可视化参数配置&#xff1a;每个流程可以配置详细的参数设置&#xff0c;按照流程对应的规范来设计。提供…

学Python的都在说爬虫容易进去,你还敢做爬虫吗?十分钟带你规避可拷风险

阅读文本大概需要 10 分钟&#xff0c;今天&#xff0c;不要面向监狱编程了。 序言 前段时间有一篇名为《只因写了一段爬虫&#xff0c;公司200多人被抓&#xff01;》的文章非常火&#xff0c;相信大家应该都看到了。 这篇文章火起来之后&#xff0c;本来经过了一个多月的时…

【Spring框架】--01.Spring概述、入门

文章目录 Spring1.概述1.1Spring是什么&#xff1f;1.2 Spring 的狭义和广义1.3 Spring Framework特点1.4 Spring模块组成1.5 Spring6特点1.5.1版本要求 2.入门2.1 构建模块2.2 程序开发2.2.1 引入依赖2.3.2 创建java类2.3.3 创建配置文件2.3.4 创建测试类测试2.3.5 运行测试程…