- 一、前言
- 二、属性的定义
- 三、使用自定义特性的双向数据绑定
- 四、转换器
- 五、其余内容
- 使用双向数据绑定的无限循环
- 双向特性
- 六、参考链接
本篇记录下关于DataBinding
双向绑定的问题。双向绑定的意思是Modle的数据变化会引起UI刷新,UI的刷新也会反向引起Modle的改变。
以下是简单的示例
ObservableUserModel.kt
class ObservableUserModel : ViewModel(), Observable {
private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()
var isCheck = false
@Bindable
fun getRememberMe(): Boolean {
return isCheck
}
fun setRememberMe(value: Boolean) {
// Avoids infinite loops.
if (isCheck != value) {
isCheck = value
// React to the change.
// saveData()
Log.e("YM--","---->rememberMe更改后的值:$value")
// Notify observers of a new value.
notifyPropertyChanged(BR.rememberMe)
}
}
private var state = false
var timerRunning: Boolean
@Bindable get() {
return state
}
set(value) {
// These methods take care of calling notifyPropertyChanged()
Log.e("YM--","---->timerRunning更改后的值:$value")
state = value
notifyPropertyChanged(BR.timerRunning)
}
@get:Bindable
var newCheck = false
set(value) {
field = value
Log.e("YM--","---->newCheck更改后的值:$value")
// notifyPropertyChanged(BR.newCheck)
}
@Bindable
var newCheck2 = false
set(value) {
field = value
Log.e("YM--","---->newCheck2更改后的值:$value")
}
fun updateName(newName: String) {
name = newName
notifyPropertyChanged(BR.name)
}
override fun addOnPropertyChangedCallback(
callback: Observable.OnPropertyChangedCallback
) {
callbacks.add(callback)
}
override fun removeOnPropertyChangedCallback(
callback: Observable.OnPropertyChangedCallback
) {
callbacks.remove(callback)
}
/**
* Notifies observers that all properties of this instance have changed.
*/
fun notifyChange() {
callbacks.notifyCallbacks(this, 0, null)
}
/**
* Notifies observers that a specific property has changed. The getter for the
* property that changes should be marked with the @Bindable annotation to
* generate a field in the BR class to be used as the fieldId parameter.
*
* @param fieldId The generated BR id for the Bindable field.
*/
fun notifyPropertyChanged(fieldId: Int) {
callbacks.notifyCallbacks(this, fieldId, null)
}
}
activity_main.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="observableModel"
type="com.example.myapplication.ObservableUserModel" />
data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatCheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择"
android:checked="@={observableModel.newCheck2}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/update_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
androidx.constraintlayout.widget.ConstraintLayout>
layout>
以上ObservableUserModel
中凡是包含Bindable
定义的属性,均可使用双向绑定,在set
函数里面可以监听数据改变并执行下一步 *** 作。里面的notifyPropertyChanged()
函数视情况调用,比如该值改变后触发其它属性的UI刷新。
之前讲过自定义属性的用法和双向绑定的基本用法。这里将两者进行结合起来进行使用。
InverseObservableUserModel.kt
class InverseObservableUserModel : ViewModel(), Observable {
private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()
@Bindable
var inverseContent = "默认值Inverse"
fun updateInverseContent(newValue: String) {
inverseContent = newValue
notifyPropertyChanged(BR.inverseContent)
}
override fun addOnPropertyChangedCallback(
callback: Observable.OnPropertyChangedCallback
) {
callbacks.add(callback)
}
override fun removeOnPropertyChangedCallback(
callback: Observable.OnPropertyChangedCallback
) {
callbacks.remove(callback)
}
/**
* Notifies observers that all properties of this instance have changed.
*/
fun notifyChange() {
callbacks.notifyCallbacks(this, 0, null)
}
/**
* Notifies observers that a specific property has changed. The getter for the
* property that changes should be marked with the @Bindable annotation to
* generate a field in the BR class to be used as the fieldId parameter.
*
* @param fieldId The generated BR id for the Bindable field.
*/
fun notifyPropertyChanged(fieldId: Int) {
callbacks.notifyCallbacks(this, fieldId, null)
}
}
InverseBindAdapter.kt
//双向绑定的自定义属性拓展
object InverseBindAdapter {
@BindingAdapter("time")
@JvmStatic fun setTime(view: AppCompatEditText, newValue: String) {
// Important to break potential infinite loops.
if (view.text?.toString() != newValue) {
view.setText(newValue)
}
}
//该函数会在使用InverseBindingListener进行调用
// 而且在使用时候绑定的名字必须为timeAttrChanged,命名规则用AttrChanged做后缀,除非使用event进行指定,
// 例如 @InverseBindingAdapter(attribute = "time", event = "timeAttrChanged")
// InverseBindingAdapter必须与InverseBindingListener配套使用,不可单独使用
// 如果不使用双向绑定的话,如"@={inverseObservableModel.inverseContent}"形式的话则不会调用该函数
@InverseBindingAdapter(attribute = "time", event = "timeAttrChanged")
@JvmStatic fun getTime(view: AppCompatEditText) : String {
Log.e("YM","--->接收返回值")
return view.text?.toString() ?: ""
}
@BindingAdapter("timeAttrChanged")
@JvmStatic fun setListeners(
view: AppCompatEditText,
attrChange: InverseBindingListener
) {
// Set a listener for click, focus, touch, etc.
view.onFocusChangeListener = View.OnFocusChangeListener { focusedView, hasFocus ->
val textView = focusedView as TextView
Log.e("YM","--->焦点更改:$hasFocus")
if (hasFocus) {
// Delete contents of the EditText if the focus entered.
textView.text = ""
} else {
// If the focus left, update the listener
attrChange.onChange()
}
}
}
}
activity_main.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="inverseObservableModel"
type="com.example.myapplication.InverseObservableUserModel" />
data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTop="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
time="@={inverseObservableModel.inverseContent}"/>
androidx.constraintlayout.widget.ConstraintLayout>
layout>
四、转换器
有时候使用的数据需要根据情况进行转换,跟前文使用的转换器需求一样,不过代码略有差异,之前使用的转换器为@BindingConversion
,现在使用@InverseMethod
进行转换,代码如下
InverseObservableUserModel.kt
class InverseObservableUserModel : ViewModel(), Observable {
private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()
@Bindable
var inverseContent = "默认值Inverse"
@Bindable
var inverseDate = Date().time
fun updateInverseContent(newValue: String) {
inverseContent = newValue
notifyPropertyChanged(BR.inverseContent)
}
override fun addOnPropertyChangedCallback(
callback: Observable.OnPropertyChangedCallback
) {
callbacks.add(callback)
}
override fun removeOnPropertyChangedCallback(
callback: Observable.OnPropertyChangedCallback
) {
callbacks.remove(callback)
}
/**
* Notifies observers that all properties of this instance have changed.
*/
fun notifyChange() {
callbacks.notifyCallbacks(this, 0, null)
}
/**
* Notifies observers that a specific property has changed. The getter for the
* property that changes should be marked with the @Bindable annotation to
* generate a field in the BR class to be used as the fieldId parameter.
*
* @param fieldId The generated BR id for the Bindable field.
*/
fun notifyPropertyChanged(fieldId: Int) {
callbacks.notifyCallbacks(this, fieldId, null)
}
}
ConverterAdapter.java
这里为什么使用java,是因为DataBinding
编译后的代码是Java类型,如果使用kotlin
,kotlin
的基本类型是kotlin
的,不是Java
的,所以会导致类型不匹配,一直编译不通过
public class ConverterAdapter{
//这里需要注意@InverseMethod("stringToDate")中的参数需要创建一个同名的函数,其中入参,返回参数要相互换下位置
@InverseMethod("stringToDate")
public static String dateToString(AppCompatEditText editText,long newValue){
Log.e("YM"," dateToString类型转换");
return "新的日期";
}
public static long stringToDate(AppCompatEditText editText,String newValue){
Log.e("YM"," stringToDate类型转换");
return 0;
}
}
如果实在想使用kotlin
写代码,需要使用以下方式
需要注意的是 java.lang.String
需要直接写,不需要联想,联想功能出现的列表里面没有String
类。其字符串类型不能直接使用" 内容 "
的方式生成,需要使用以下方式
java.lang.String("新的日期")
Converter.kt
object Converter {
@InverseMethod("stringToDate")
@JvmStatic fun dateToString(
view: AppCompatEditText, oldValue: Long,
value: Long
): java.lang.String {
// Converts long to String.
Log.e("YM"," dateToString类型转换")
return java.lang.String("新的日期")
}
@JvmStatic fun stringToDater(
view: AppCompatEditText, oldValue: Long,
value: java.lang.String
): Long {
// Converts String to long.
Log.e("YM"," stringToDate类型转换")
return 0L
}
}
activity_main.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>
<import type="com.example.myapplication.ConverterAdapter"/>
<variable
name="inverseObservableModel"
type="com.example.myapplication.InverseObservableUserModel" />
data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/update_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
time="@={ConverterAdapter.dateToString(input,inverseObservableModel.inverseDate)}"/>
androidx.constraintlayout.widget.ConstraintLayout>
layout>
五、其余内容
以下内容主要是引用的官方资料,略微注意一下就行,另外就是在xml中是可以使用context
属性的,类型为Context
。无需引入额外的库,直接使用对象就行
使用双向数据绑定的无限循环六、参考链接使用双向数据绑定时,请注意不要引入无限循环。当用户更改特性时,系统会调用使用
@InverseBindingAdapter
注释的方法,并且该值将分配给后备属性。继而调用使用@BindingAdapter
注释的方法,从而触发对使用@InverseBindingAdapter
注释的方法的另一个调用,依此类推。因此,通过比较使用
双向特性@BindingAdapter
注释的方法中的新值和旧值,可以打破可能出现的无限循环。当您使用下表中的特性时,该平台提供对双向数据绑定的内置支持。有关平台如何提供此类支持的详细信息,请参阅相应绑定适配器的实现:
类 特性 绑定适配器 AdapterView
android:selectedItemPosition
android:selection
AdapterViewBindingAdapter
CalendarView
android:date
CalendarViewBindingAdapter
CompoundButton
android:checked
CompoundButtonBindingAdapter
DatePicker
android:year
android:month
android:day
DatePickerBindingAdapter
NumberPicker
android:value
NumberPickerBindingAdapter
RadioButton
android:checkedButton
RadioGroupBindingAdapter
RatingBar
android:rating
RatingBarBindingAdapter
SeekBar
android:progress
SeekBarBindingAdapter
TabHost
android:currentTab
TabHostBindingAdapter
TextView
android:text
TextViewBindingAdapter
TimePicker
android:hour
android:minute
TimePickerBindingAdapter
-
双向数据绑定 | Android 开发者 | Android Developers
-
https://github.com/android/databinding-samples
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)