您现在的位置是:首页 >其他 >Android Jetpack-Databinding基本使用网站首页其他
Android Jetpack-Databinding基本使用
文章目录
自从有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类的静态方法来获取到实例对象。