Android DataBinding笔记
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android DataBinding笔记相关的知识,希望对你有一定的参考价值。
官网介绍,DataBinding是用于将数据的和视图绑定在一起的支持库,特点是只需要在视图定义的xml中编写绑定信息,减少在代码中的视图于数据的交互逻辑。
这样可以分离数据和视图,减少这两个模块在代码层面上的耦合。
启用功能
在项目的build.gradle中添加如下代码即可:
android
...
dataBinding
enabled = true
常见问题:
1. DSL element ‘android.dataBinding.enabled’ is obsolete and has been replaced with ‘android.buildFeatures.dataBinding’.
参考:https://blog.csdn.net/qq_40533422/article/details/106838495
android
...
buildFeatures
dataBinding = true
2. If you plan to use data binding in a Kotlin project, you should apply the kotlin-kapt plugin. 如果是使用Kotlin开发需要启用Kotlin插件,不然可能会有莫名其妙的问题。
plugins
...
id 'kotlin-kapt'
基础的绑定用法
更详细的用法参考官网的布局和绑定表达式,基础绑定用法示例:
const val TAG = "databinding"
class MainActivity : AppCompatActivity() , View.OnClickListener
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
activityMainBinding.aty = this
activityMainBinding.data = SimpleData("Jack")
override fun onClick(v: View?)
Log.d(TAG, "onClick: v = $v")
fun doClick()
Log.d(TAG, "doClick: called")
其中 MainActivity 的 ActivityMainBinding (这个叫做绑定类)是根据布局文件和其名称自动生成的,需要注意的是,任何情况下都不应该自己实现绑定类,这个一定要交给DataBinding来生成。对应的布局文件如下,一共两个:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="aty" type="com.im_hero.databinding.MainActivity" />
<variable name="data" type="com.im_hero.databinding.data.SimpleData" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<!-- Data Binding -->
<TextView
android:id="@+id/tvDataName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@data.name" />
<!-- Reference of id -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@tvDataName.text" />
<!-- Plurals usage -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@@plurals/banana(1, 1)" />
<!-- format string -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@@string/format_name(`Jason`)" />
<!-- Event Listener -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#99FF22"
android:onClick="@aty::onClick"
android:text="@string/event_listener" />
<!-- Lambda Event Listener -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#99FF22"
android:onClick="@(v) -> aty.doClick()"
android:text="@string/lambda_event_listener" />
<!-- Include element data binding -->
<include
layout="@layout/include_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
bind:aty="@aty"
bind:data="@data" />
</LinearLayout>
</layout>
第二个布局是被 include 标签引用的,include_data 布局,可以用过 bind:aty="@aty 和 bind:data="@data" 这种语法将数据传递给被引用的布局:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="aty" type="com.im_hero.databinding.MainActivity" />
<variable name="data" type="com.im_hero.databinding.data.SimpleData" />
</data>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@`aty = ` + aty + `, data = ` + data" />
</layout>
运行结果如下图:
下面来介绍可被观察的数据,用来实现界面当数据改变时自动更新。
可被观察的数据
参考官网:使用可观察的数据对象。基础的可被观察的数据:
- ObservableBoolean
- ObservableChar
- ObservableShort
等等……都是继承自androidx.databinding.BaseObservable,看下核心源码:
public class BaseObservable implements Observable
private transient PropertyChangeRegistry mCallbacks;
...
public void notifyChange()
synchronized (this)
if (mCallbacks == null)
return;
mCallbacks.notifyCallbacks(this, 0, null);
...
public void notifyPropertyChanged(int fieldId)
synchronized (this)
if (mCallbacks == null)
return;
mCallbacks.notifyCallbacks(this, fieldId, null);
public class CallbackRegistry<C, T, A> implements Cloneable
...
private List<C> mCallbacks = new ArrayList<C>();
...
private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
final int endIndex, final long bits)
long bitMask = 1;
for (int i = startIndex; i < endIndex; i++)
if ((bits & bitMask) == 0)
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
bitMask <<= 1;
...
public abstract static class NotifierCallback<C, T, A>
public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
就是普通的观察者模式。只是Google自动生成了特别多的代码,大幅减少了我们的工作量。
基本使用方法
首先我们需要有个实现了BaseObservable的类:
class ObservableData() : BaseObservable()
@get:Bindable
var name: String = ""
set(value)
field = value
notifyPropertyChanged(BR.name)
@get:Bindable
var age: Int = 0
set(value)
field = value
notifyPropertyChanged(BR.age)
override fun toString(): String = "ObservableData(name='$name', age=$age)"
让后将其声明在布局中:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.content.Intent"/>
<variable name="oBoolean" type="androidx.databinding.ObservableBoolean"/>
<variable name="oByte" type="androidx.databinding.ObservableByte"/>
<variable name="oChar" type="androidx.databinding.ObservableChar"/>
<variable name="oShort" type="androidx.databinding.ObservableShort"/>
<variable name="oInt" type="androidx.databinding.ObservableInt"/>
<variable name="oLong" type="androidx.databinding.ObservableLong"/>
<variable name="oFloat" type="androidx.databinding.ObservableFloat"/>
<variable name="oDouble" type="androidx.databinding.ObservableDouble"/>
<variable name="oParcelable" type="androidx.databinding.ObservableParcelable<Intent>"/>
<variable name="oData" type="com.im_hero.databinding.data.ObservableData" />
<variable name="aty" type="com.im_hero.databinding.ObservableActivity" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ObservableActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@oBoolean ? `true` : `false`" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oByte)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oChar)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oShort)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oInt)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oLong)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oFloat)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oDouble)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@String.valueOf(oParcelable.action)" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@oData.name+`: `+oData.age" />
</LinearLayout>
</layout>
有一些DataBinding库中自带的一些基础类型实现在其中,就一起测试了一下,需要注意的是不能通过aty.observableBoolean这种方式,因为DataBinding会去监听aty这个对象,而MainActivity是没有实现BaseObservable的!。Activity代码如下:
class ObservableActivity : AppCompatActivity()
val observableBoolean: ObservableBoolean = ObservableBoolean()
val observableByte: ObservableByte = ObservableByte()
val observableChar: ObservableChar = ObservableChar('a')
val observableShort: ObservableShort = ObservableShort()
val observableInt: ObservableInt = ObservableInt()
val observableLong: ObservableLong = ObservableLong()
val observableFloat: ObservableFloat = ObservableFloat()
val observableDouble: ObservableDouble = ObservableDouble()
val observableParcelable: ObservableParcelable<Intent> = ObservableParcelable<Intent>(Intent("action"))
val observableData: ObservableData = ObservableData().apply name = "Jason"; age = 22
lateinit var binding: ActivityObservableBinding
var handler = Handler(Looper.getMainLooper())
var changeData = Runnable
observableBoolean.set(!observableBoolean.get())
observableByte.set((observableByte.get() + 1).toByte())
observableChar.set('b')
observableShort.set((observableShort.get() + 1).toShort())
observableInt.set(observableInt.get() + 1)
observableLong.set(observableLong.get() + 1)
observableFloat.set(observableFloat.get() + 1)
observableDouble.set(observableDouble.get() + 1)
observableParcelable.set(Intent("new $observableParcelable.get()!!.action"))
observableData.age = observableData.age + 1
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate: new")
binding = DataBindingUtil.setContentView(this, R.layout.activity_observable)
binding.oBoolean = observableBoolean
binding.oByte = observableByte
binding.oChar = observableChar
binding.oShort = observableShort
binding.oInt = observableInt
binding.oLong = observableLong
binding.oFloat = observableFloat
binding.oDouble = observableDouble
binding.oParcelable = observableParcelable
binding.oData = observableData
handler.postDelayed(changeData, TimeUnit.SECONDS.toMillis(5))
启动时界面:
5秒后改变数据的界面:
这里记录常见的应用场景和一些基础知识点
Activity和Fragment之间的交互(它们都有使用DataBinding)
- 定义自动生成的绑定类的名称
- 和ViewStub的搭配方法
- View的ID会在绑定类中自动生成控件的成员变量
- Activity视图内包含Fragment和ViewStub
在Activity中包含Fragment和ViewStub是很常见的操作,然而DataBinding可以让他们之间的关联代码减少很多。下面的代码用来演示他们之间通过DataBinding是如何来进行交互的。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewStubData"
type="androidx.databinding.ObservableField<String>" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".CustomBindingActivity">
<fragment
android:id="@+id/fCustomFragment"
class="com.im_hero.databinding.CustomBindingFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#2299FF"
android:text="@viewStubData" />
<ViewStub
android:id="@+id/viewStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/custom_view_stub" />
</LinearLayout>
</layout>
这是Activity的布局文件,其中“<”符号被替换成了“<”,参考表达式语言-常见功能。它包含了一个Fragment和一个ViewStub,其中viewStubData是一个可被观察的数据,用来标识viewStub的显示状态。如下,是对应Activity的代码:
class CustomBindingActivity : AppCompatActivity(), CustomBindingFragment.CustomBindingFragmentListener
lateinit var activityCustomBindingBinding: ActivityCustomBindingBinding
lateinit var fCustomFragment: CustomBindingFragment
lateinit var tvViewStub: TextView
lateinit var customViewStubBinding: CustomViewStubBinding
val oFieldViewStub = ObservableField(INVISIBLE)
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
activityCustomBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_custom_binding)
activityCustomBindingBinding.viewStubData = oFieldViewStub
fCustomFragment = supportFragmentManager.findFragmentById(R.id.fCustomFragment) as CustomBindingFragment
/**
* [CustomBindingFragment.CustomBindingFragmentListener.showViewStub]用于让CustomBindingFragment控制ViewStub的显示状态
*/
override fun showViewStub(checked: Boolean)
if (::tvViewStub.isInitialized)
oFieldViewStub.set(if (checked) VISIBLE else INVISIBLE)
else
tvViewStub = activityCustomBindingBinding.viewStub.viewStub!!.inflate() as TextView
// 运行时绑定,因为ViewStub是在运行时并且需要了才会填充到布局内部。
customViewStubBinding = DataBindingUtil.bind(tvViewStub)!!
customViewStubBinding.viewStubData = oFieldViewStub
oFieldViewStub.set(VISIBLE)
运行时绑定,比较实用给ViewStub这种在运气期间加入到布局的内部的视图。
有个细节,如下代码中第一个viewStub是activity_custom_binding布局中ViewStub控件的ID在绑定类中生成的属性,其真实类名是androidx.databinding.ViewStubProxy,第二个viewStub才是布局中的ViewStub:
activityCustomBindingBinding.viewStub.viewStub
ViewStub布局文件:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<import type="com.im_hero.databinding.ConstantsKt"/>
<variable
name="viewStubData"
type="androidx.databinding.ObservableField<String>" />
</data>
<TextView
android:id="@+id/tvViewStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@`ViewStub:`+viewStubData"
android:visibility="@ConstantsKt.INVISIBLE.equals(viewStubData) ? View.GONE : View.VISIBLE" />
</layout>
如下是ViewStub的布局文件和常量定义:
// Constants.kt 文件中
const val INVISIBLE = "Invisible"
const val VISIBLE = "Visible""
下面是CustomBindingFragment的布局文件和实现代码:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data class="CustomDataBinding">
<import type="com.im_hero.databinding.ConstantsKt"/>
<variable
name="showViewStubStatus"
type="androidx.databinding.ObservableBoolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CustomBindingFragment">
<ToggleButton
android:id="@+id/btnShowViewStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@showViewStubStatus"
android:textOn="@ConstantsKt.VISIBLE"
android:textOff="@ConstantsKt.INVISIBLE" />
</LinearLayout>
</layout>
其中 <data class=“CustomDataBinding”> 就是用来指定布局自动生成的绑定类名。
class CustomBindingFragment : Fragment(), CompoundButton.OnCheckedChangeListener
lateinit var customDataBinding: CustomDataBinding
lateinit var customBindingFragmentListener: CustomBindingFragmentListener
val oBoolean = ObservableBoolean(false)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View
customDataBinding = CustomDataBinding.inflate(inflater, container, false)
customDataBinding.showViewStubStatus = oBoolean
customDataBinding.btnShowViewStub.setOnCheckedChangeListener(this)
return customDataBinding.root
override fun onAttach(context: Context)
super.onAttach(context)
if (context is CustomBindingFragmentListener)
customBindingFragmentListener = context
else
throw AndroidRuntimeException("Context must implements CustomBindingFragmentListener")
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean)
customBindingFragmentListener.showViewStub(isChecked)
interface CustomBindingFragmentListener
fun showViewStub(checked: Boolean)
至此通过Fragment上的按钮控制Activity上的ViewStub的填充和显示隐藏的功能就完成了。这个例子主要是用于演示开头的那几个知识点,特别简单哈,主要是用于演示DataBinding可以给我们减少很多的关联代码。这样我们就主要关注与数据的更新和界面的逻辑,关联代码就完全交给了DataBinding。贴上运行结果图,ViweStub未显示的界面:
显示ViewStub的界面:
下面主要记录绑定适配器和双向绑定的基本用法,这篇文章会包含完整的代码和运行效果。
绑定适配器
绑定适配器的作用就是给布局文件(xml)和特定方法之间起到相互绑定的作用的注解。 如下示例(其中invertColorBackground这个app的前缀是可以省略的,而且不省略AndroidStudio还会报警告:Application namespace for attribute app:colorValue will be ignored.):
@BindingAdapter("invertColorBackground")
fun setupInvertColorBackground(invertBackgroundColorView: View, colorValue: Int)
...
@BindingAdapter("android:text")
fun setText(view: TextView, text: CharSequence)
...
对应的xml应用方式:
<TextView
...
android:text="..."
app:invertColorBackground="@color" />
转换器
转换器用于将xml中其他的数据类型转换成绑定适配器需要的数据类型。如下定义:
@BindingConversion
fun convertColorToDrawable(color: ObservableInt) = ColorDrawable(color.get())
这个就是将xml中为ObservableInt的数据类型转换为ColorDrawable的数据类型,示例如下:
<variable name="color" type="androidx.databinding.ObservableInt" />
...
<View
...
android:background="@color" />
这样就可以将ObservableInt直接设置为背景色了。
双向绑定
双向绑定的作用是在数据绑定的基础上,增加视图的变化时更新数据的功能。 如下示例:
<TextView
...
app:colorValue="@=color" />
是的没错,就是多了一个‘=’号。这样就是双向绑定的使用方法。那么如何定义呢?一共有三个步骤:
1. 定义绑定适配器(BindingAdapter),数据改变时通知视图更新
@BindingAdapter(value = ["colorValue"])
fun setupColorValue(tvColorValue: TextView, colorValue: Int)
tvColorValue.setTextColor(colorValue)
tvColorValue.text = String.format("0x%08x", colorValue).toUpperCase(Locale.getDefault())
2. 定义反向绑定适配器(InverseBindingAdapter),定义视图更新时如何更新数据
@InverseBindingAdapter(attribute = "colorValue")
fun onColorValueChanged(tvColorValue: TextView): Int
return tvColorValue.currentTextColor
3. 定义何时需要通知DataBinding去更新数据(注意,这里用的也是BindingAdapter注解,而且一定需要 AttrChanged 作为适配器中 colorValue 的后缀,不然会报错!)
@BindingAdapter(value = ["colorValueAttrChanged"])
fun setOnColorValueChanged(tvColorValue: TextView, attrChanged: InverseBindingListener)
tvColorValue.addTextChangedListener(object : TextWatcher
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int)
Log.d(TAG, "beforeTextChanged: s = $s")
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int)
Log.d(TAG, "onTextChanged: s = $s")
override fun afterTextChanged(s: Editable?)
Log.d(TAG, "afterTextChanged: s = $s")
attrChanged.onChange()
)
这个示例实现了如下双向更新的功能:
- 当颜色值(colorValue)更新时,更新字体颜色和文字显示
- 当前字体颜色更新时,更新颜色值(colorValue)
完整代码示例
下面的代码会实现一个包含四个控件的视图:
- 功能开关按钮:用于开启和关闭自动变色,以及控制是否可以变色
- 手动变色按钮:用于直接让变色控件变色
- 变色控件:用于显示当前颜色
- 颜色提示控件:用于显示当前颜色值是多少
运行效果如下图:
用到了以下知识点:
- 绑定适配器
- 双向绑定适配器
- DataBinding中如何应用merge布局
绑定适配器的定义
- 实现了TextView的文字于颜色值的双向绑定
- 根据颜色值显示其反色的背景
@BindingAdapter(value = ["colorValue"])
fun setupColorValue(tvColorValue: TextView, colorValue: Int)
tvColorValue.setTextColor(colorValue)
tvColorValue.text = String.format("0x%08x", colorValue).toUpperCase(Locale.getDefault())
@InverseBindingAdapter(attribute = "colorValue")
fun onColorValueChanged(tvColorValue: TextView): Int
return tvColorValue.currentTextColor
@BindingAdapter(value = ["colorValueAttrChanged"])
fun setOnColorValueChanged(tvColorValue: TextView, attrChanged: InverseBindingListener)
tvColorValue.addTextChangedListener(object : TextWatcher
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int)
Log.d(TAG, "beforeTextChanged: s = $s")
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int)
Log.d(TAG, "onTextChanged: s = $s")
override fun afterTextChanged(s: Editable?)
Log.d(TAG, "afterTextChanged: s = $s")
attrChanged.onChange()
)
@BindingAdapter(value = ["invertColorBackground"])
fun setupInvertColorBackground(invertBackgroundColorView: View, colorValue: Int)
val invertColor = (0xFF0000 - (colorValue and 0xFF0000))
.or(0xFF00 - (colorValue and 0xFF00))
.or(0xFF - (colorValue and 0xFF))
Log.d(TAG, "setupInvertColorBackground: invertColor = $invertColor")
invertBackgroundColorView.setBackgroundColor(Color.BLACK or invertColor)
@BindingConversion
fun convertColorToDrawable(color: ObservableInt) = ColorDrawable(color.get())
布局的实现
- activity_binding_adapter.xml 显示控制按钮
- merge_layout.xml 显示颜色控件
activity_binding_adapter.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="btnColorSwitch" type="androidx.databinding.ObservableBoolean" />
<variable name="color" type="androidx.databinding.ObservableInt" />
<variable name="onCheckedChangeListener" type="android.widget.CompoundButton.OnCheckedChangeListener" />
<variable name="onClickListener" type="android.view.View.OnClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BindingAdapterActivity">
<ToggleButton
android:id="@+id/btnSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@=btnColorSwitch"
android:onCheckedChanged="@onCheckedChangeListener"
app:onCheckedChangeListener="@onCheckedChangeListener"/>
<Button
android:id="@+id/btnChangeColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/change_color"
app:onClickListener="@onClickListener"/>
<include
layout="@layout/merge_layout"
android:id="@+id/includeMergeLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
bind:color="@color" />
</LinearLayout>
</layout>
merge_layout.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="color" type="androidx.databinding.ObservableInt" />
</data>
<merge>
<View
android:id="@+id/vColor"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color" />
<TextView
android:id="@+id/tvColorValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textAlignment="center"
app:invertColorBackground="@color"
app:colorValue="@=color" />
</merge>
</layout>
Activity的实现
- 每隔1.5秒自动改变color的颜色值
- 实现开关控制
- 实现单击变色
class BindingAdapterActivity : AppCompatActivity(), CompoundButton.OnCheckedChangeListener, View.OnClickListener
lateinit var activityBindingAdapterBinding: ActivityBindingAdapterBinding
val color = ObservableInt(Color.WHITE)
val btnColorSwitch = ObservableBoolean(false)
val random = Random()
var lastRandom = 0
val DELAY_TO_CHANGE_COLOR = 1500L
val mHandler = Handler(Looper.getMainLooper())
val colorChanger = object : Runnable
override fun run()
color.set(randomColor())
mHandler.postDelayed(this, DELAY_TO_CHANGE_COLOR)
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
activityBindingAdapterBinding = DataBindingUtil.setContentView(this, R.layout.activity_binding_adapter)
activityBindingAdapterBinding.onCheckedChangeListener = this
activityBindingAdapterBinding.onClickListener = this
activityBindingAdapterBinding.color = color
activityBindingAdapterBinding.btnColorSwitch = btnColorSwitch
override fun onResume()
super.onResume()
if (btnColorSwitch.get()) startChangeColor()
override fun onPause()
if (btnColorSwitch.get()) stopChangeColor()
super.onPause()
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean)
if (isChecked)
startChangeColor()
else
stopChangeColor()
override fun onClick(v: View)
when (v.id)
R.id.btnChangeColor ->
if (btnColorSwitch.get())
mHandler.removeCallbacks(colorChanger)
setupColorValue(activityBindingAdapterBinding.includeMergeLayout.tvColorValue, randomColor())
mHandler.postDelayed(colorChanger, DELAY_TO_CHANGE_COLOR)
else
Log.d(TAG, "onCreate: btnColorSwitch = false")
private fun startChangeColor()
mHandler.removeCallbacks(colorChanger)
mHandler.postDelayed(colorChanger, DELAY_TO_CHANGE_COLOR)
private fun stopChangeColor()
mHandler.removeCallbacks(colorChanger)
private fun randomColor(): Int
val nextInt = random.nextInt(7)
lastRandom = if (lastRandom == nextInt) (nextInt + 1) % 7 else nextInt
return when (lastRandom)
0 -> Color.WHITE
1 -> Color.RED
2 -> Color.BLUE
3 -> Color.YELLOW
4 -> Color.CYAN
5 -> Color.MAGENTA
else -> Color.BLACK
可以看到配置视图数据和状态等操作全部都交给了DataBinding,而这里只需要负责给数据赋值,实现变更逻辑。这样我们可以将流程设计和具体实现给分开,让代码逻辑更加清晰,代码管理更加方便。
以上是关于Android DataBinding笔记的主要内容,如果未能解决你的问题,请参考以下文章
设计模式笔记之三:Android DataBinding库(MVVM设计模式)
Android:延迟加载 DataBinding 视图会引发异常
DSL 元素 'android.dataBinding.enabled' 已过时,已替换为 'android.buildFeatures.dataBinding'