JetPack——databing
databing是什么?
数据绑定库是一个支持库,它允许你使用声明式格式而不是编程方式将布局中的UI组件绑定到应用程序中的数据源
databing解决什么问题?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
当数据变化时,需要先找到控件才能设置数据
class MainActivity : AppCompatActivity() {
data class User(val name: String, val age: Int)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val user = User("AAA", 18)
findViewById<TextView>(R.id.tv).apply {
text = user.name
}
}
当控件数量增大时,会出现大量样板代码
databing实现
在应用模块的build.gradle中添加配置
android {
...
buildFeatures {
dataBinding true
}
}
根布局改为<layout>,可在<data>中创建<variable>指定变量和类型,通过@{变量.数据}引用数据,实现数据变化自动更新到UI
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.demo2_databinding2.MainActivity.User" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:text="@{user.name}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
系统会自动生成以布局为名的Binding类,如activity_main.xml生成ActivityMainBinding,通过其加载布局
class MainActivity : AppCompatActivity() {
data class User(var name: String, var age: Int)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)*/
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val user = User("AAA", 18)
binding.user = user
}
}
如果要在Fragment、ListView、RecycleView中加载布局,可以使用xxxBinding或DataBindingUtil类中的inflate()方法
databing扩展
事件处理
- 方法引用:在编译时生成,需要与被处理事件签名一致,否则会报错
- 监听器绑定:在事件实际触发时才会生成
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="eventHandler"
type="com.example.demo2_databinding2.MainActivity.EventHandle" />
<variable
name="user"
type="com.example.demo2_databinding2.MainActivity.User" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{eventHandler::onTextClick}"
android:onLongClick="@{(view)->eventHandler.onTextLongClick(view,user)}"
android:text="@{user.name}" />
</LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
data class User(var name: String, var age: Int)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val user = User("AAA", 18)
binding.user = user
binding.eventHandler = EventHandle(user)
}
inner class EventHandle(private val user: User) {
fun onTextClick(view: View) { //方法引用,签名需要和onclick()一致,若需要数据可以从外面传进来
println("onImageClick ${user.name}")
}
fun onTextLongClick(
view: View,
user: User
): Boolean { //监听器绑定,返回值需要和onLongClick()一致,可以从参数中获取数据
println("onLongClick ${user.name}")
return true
}
}
}
include布局处理
xml中的变量可以通过namespace传递给<include>布局,如下声明my:username传递user.name
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:my="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="user"
type="com.example.demo2_databinding2.MainActivity.User" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/sub_layout"
layout="@layout/sub_item"
my:username="@{user.name}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
sub_item.xml中接收变量username
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="username"
type="String" />
</data>
<TextView
android:text="@{username}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</layout>
class MainActivity : AppCompatActivity() {
data class User(var name: String, var age: Int)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val user = User("AAA", 18)
binding.user = user
}
}
可观察的数据对象
上面例子中的只有user改变时才会更新UI,但user中的域改变不会更新UI,要想要让域更新时触发UI修改,需要
- 继承BaseObservable()
- get()加上@Bindable注解
- set()改变值后调用notifyPropertyChanged(BR.xxx),BR为自动生成的类(若报错则在build.gradle中添加apply plugin: ‘kotlin-kapt’)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.demo2_databinding2.MainActivity.User" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/sub_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}" />
</LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
class User(name: String, age: Int) : BaseObservable() {
@get:Bindable
var name: String = name
set(value) {
field = value
notifyPropertyChanged(BR.name)
}
var age: Int = age
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val user = User("AAA", 18)
binding.user = user
Thread {
Thread.sleep(3000)
println("change")
user.name = "BBB"
}.start()
}
}
或将域声明为ObservableField<T>,此时修改应调用内部的set()方法
class User(name: String, age: Int) {
var name = ObservableField<String>(name)
var age = age
}
自定义属性绑定
class MainActivity : AppCompatActivity() {
companion object {
@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
view.setPadding(
padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom()
)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
}
}
如上,使用@BindingAdapter声明android:paddingLeft,可以直接设置左边距
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="100dp"
android:text="AAA" />
</LinearLayout>
</layout>
自定义类型转换
如下android:background接收一个Drawable,但传递的是int
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="isError"
type="Boolean" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@{isError ? @color/black : @color/white}" />
</LinearLayout>
</layout>
通过@BindingConversion注解类型转换方法,其会自动调用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.isError = false
}
}
@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)
和ViewModel、LiveData一起使用
class UserViewModel : ViewModel() {
val name = MutableLiveData<String>()
}
布局引用UserViewModel的name
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewmodel"
type="com.example.demo2_databinding2.UserViewModel" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:text="@{viewmodel.name}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
当LiveData改变时,会更新到UI,注意需要设置binding.lifecycleOwner = this才能生效
class MainActivity : AppCompatActivity() {
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewmodel = userViewModel
binding.lifecycleOwner = this
userViewModel.name.observe(this, Observer {
println("${it}")
})
Thread{
Thread.sleep(2000)
userViewModel.name.postValue("AAA")
}.start()
}
}
双向数据绑定
如下代码针对CheckBox,使用LiveData实现数据到UI的更新,使用监听器实现UI到数据的更新
class MyViewModel : ViewModel() {
val rememberMe = MutableLiveData<Boolean>(false)
fun rememberMeChanged(view: CompoundButton, change: Boolean) {
if (rememberMe.value == change) { //数据更新触发UI更新,UI更新又会触发数据更新,故若是数据更新导致的UI更新则跳过
return
}
rememberMe.postValue(change)
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewmodel"
type="com.example.demo2_databinding2.MyViewModel" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<CheckBox
android:id="@+id/rememberMeCheckBox"
android:layout_width="50dp"
android:layout_height="50dp"
android:checked="@{viewmodel.rememberMe}"
android:onCheckedChanged="@{(view,change)->viewmodel.rememberMeChanged(view,change)}" />
</LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewmodel = myViewModel
binding.lifecycleOwner = this
myViewModel.rememberMe.observe(this, Observer {
println("${it}")
})
Thread {
Thread.sleep(2000)
myViewModel.rememberMe.postValue(true)
}.start()
}
}
若使用双向数据绑定,可以省略监听器,将@{viewmodel.rememberMe}改为@={viewmodel.rememberMe},即多加一个=
class MyMutableLiveData<T>(value: T) : MutableLiveData<T>(value) {
override fun postValue(value: T) {
if (value != getValue()) {
super.postValue(value)
}
}
override fun setValue(value: T) {
if (value != getValue()) { //数据更新触发UI更新,UI更新又会触发数据更新,故若是数据更新导致的UI更新则跳过
super.setValue(value)
}
}
}
class MyViewModel : ViewModel() {
val rememberMe = MyMutableLiveData<Boolean>(false)
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewmodel"
type="com.example.demo2_databinding2.MyViewModel" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<CheckBox
android:id="@+id/rememberMeCheckBox"
android:layout_width="50dp"
android:layout_height="50dp"
android:checked="@={viewmodel.rememberMe}" />
</LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewmodel = myViewModel
binding.lifecycleOwner = this
myViewModel.rememberMe.observe(this, Observer {
println("${it}")
})
Thread {
Thread.sleep(2000)
myViewModel.rememberMe.postValue(true)
}.start()
}
}
自定义双向数据绑定
在res/value下新建attrs.xml,自定义属性numberValue
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="InputNumberView">
<attr name="numberValue" format="integer" />
</declare-styleable>
</resources>
自定义控件布局如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#44000000">
<Button
android:id="@+id/minBtn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="-" />
<EditText
android:id="@+id/numberValueEt"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="0" />
<Button
android:id="@+id/addBtn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="+" />
</LinearLayout>
通过如下自定义控件模拟购物车加减功能
class InputNumberView : LinearLayout {
private var listener: OnNumberValueChangeListener? = null
private lateinit var minBtn: Button
private lateinit var numberValueEt: TextView
private lateinit var addBtn: Button
private var currentNumberValue: Int = 0
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
) {
initAttr(context, attrs)
initView()
initEvent()
}
fun setNumber(value: Int) {
post(object : Runnable {
override fun run() {
serCurrentNumberValue(value)
}
})
}
fun getNumber(): Int {
return currentNumberValue
}
private fun initEvent() {
minBtn.setOnClickListener(object : OnClickListener {
override fun onClick(v: View?) {
serCurrentNumberValue(--currentNumberValue)
}
})
numberValueEt.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
val text = s.toString().toInt()
if (text != currentNumberValue) {
currentNumberValue = text
listener?.onNumberValueChange(currentNumberValue)
}
}
})
addBtn.setOnClickListener(object : OnClickListener {
override fun onClick(v: View?) {
serCurrentNumberValue(++currentNumberValue)
}
})
}
private fun serCurrentNumberValue(value: Int) {
currentNumberValue = value
numberValueEt.text = String.format("%s", currentNumberValue)
listener?.onNumberValueChange(currentNumberValue)
}
private fun initView() {
LayoutInflater.from(context).inflate(R.layout.input_number_layout, this)
minBtn = findViewById<Button>(R.id.minBtn)
numberValueEt = findViewById<EditText>(R.id.numberValueEt)
addBtn = findViewById<Button>(R.id.addBtn)
numberValueEt.text = String.format("%s", currentNumberValue)
}
private fun initAttr(context: Context, attrs: AttributeSet?) {
val ta: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.InputNumberView)
currentNumberValue = ta.getInt(R.styleable.InputNumberView_numberValue, 0)
ta.recycle()
}
interface OnNumberValueChangeListener {
fun onNumberValueChange(currentNumber: Int)
}
fun setOnNumberChangeListener(listener: OnNumberValueChangeListener) {
this.listener = listener
}
}
常规用法
<?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">
<data>
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.demo2_databinding2.InputNumberView
android:id="@+id/input_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:numberValue="10" />
</LinearLayout>
</layout>
Goods的number更改时通知UI,UI更改时通过监听器通知number
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG: String = "MainActivity"
}
class Goods(var number: Int)
private var goods = Goods(10)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.inputNumber.setOnNumberChangeListener(object :
InputNumberView.OnNumberValueChangeListener {
override fun onNumberValueChange(currentNumber: Int) {
Log.d(TAG, "onNumberChange: $currentNumber")
goods.number = currentNumber
}
})
Thread {
Thread.sleep(2000)
goods.number = 20
binding.inputNumber.setNumber(goods.number)
}.start()
}
}
databing单向数据绑定
对属性numberValue和numberValueChangeListener新增单项绑定
object InputNumberViewBindingAdapter {
@JvmStatic
@BindingAdapter("numberValue")
fun setValue(view: InputNumberView, value: Int) {
if (view.getNumber() != value) {
view.setNumber(value)
}
}
@JvmStatic
@BindingAdapter("numberValueChangeListener")
fun setListener(view: InputNumberView, listener: InputNumberView.OnNumberValueChangeListener) {
view.setOnNumberChangeListener(listener)
}
}
使用ViewModel和LiveData处理逻辑,此时number更改时通知UI,UI更改时还是通过监听器通知number
class GoodsViewModel : ViewModel() {
val number = MutableLiveData<Int>(10)
fun getNumberValueChangeListener(): InputNumberView.OnNumberValueChangeListener {
return object : InputNumberView.OnNumberValueChangeListener {
override fun onNumberValueChange(currentNumber: Int) {
if (currentNumber != number.value) {
Log.d("song", "onNumberValueChange: $currentNumber")
number.postValue(currentNumber)
}
}
}
}
}
修改布局让属性numberValue和NumberValueChangeListener和ViewModel绑定
<?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">
<data>
<variable
name="goodsVM"
type="com.example.demo2_databinding2.GoodsViewModel" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.demo2_databinding2.InputNumberView
android:id="@+id/input_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:numberValue="@{goodsVM.number}"
app:NumberValueChangeListener="@{goodsVM.numberValueChangeListener}"/>
</LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
private val goodsViewModel: GoodsViewModel by viewModels()
companion object {
private const val TAG: String = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.goodsVM = goodsViewModel
binding.lifecycleOwner = this
goodsViewModel.number.observe(this, Observer {
Log.d(TAG, "onCreate: $it")
})
Thread {
Thread.sleep(2000)
goodsViewModel.number.postValue(1000)
}.start()
}
}
databing双向数据绑定
新增双向数据绑定,需要注意避免死循环
- @InverseBindingAdapter(attribute = “numberValue”)定义获取数据方法
- @BindingAdapter(“numberValueAttrChanged”)定义何时触发数据监听
object InputNumberViewBindingAdapter {
@JvmStatic
@BindingAdapter("numberValue")
fun setValue(view: InputNumberView, value: Int) {
if (view.getNumber() != value) {
Log.d("song", "setValue: $value")
view.setNumber(value)
}
}
@JvmStatic
@InverseBindingAdapter(attribute = "numberValue")
fun getValue(view: InputNumberView): Int {
Log.d("song", "getValue: " + view.getNumber())
return view.getNumber()
}
@JvmStatic
@BindingAdapter("numberValueAttrChanged")
fun setListeners(view: InputNumberView, inverseBindingListener: InverseBindingListener) {
view.setOnNumberChangeListener(object : InputNumberView.OnNumberValueChangeListener {
override fun onNumberValueChange(currentNumber: Int) {
inverseBindingListener.onChange()
}
})
}
}
ViewModel中number更改时通知UI,UI更改时由内部的InverseBindingListener通知number
class GoodsViewModel : ViewModel() {
val number = MutableLiveData<Int>(10)
}
修改布局为双向监听
<?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">
<data>
<variable
name="goodsVM"
type="com.example.demo2_databinding2.GoodsViewModel" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.demo2_databinding2.InputNumberView
android:id="@+id/input_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:numberValue="@={goodsVM.number}" />
</LinearLayout>
</layout>
databing源码解析
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="eventHandler"
type="com.example.demo2_databinding2.MainActivity.EventHandle" />
<variable
name="user"
type="com.example.demo2_databinding2.MainActivity.User" />
</data>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{eventHandler::onTextClick}"
android:onLongClick="@{(view)->eventHandler.onTextLongClick(view,user)}"
android:text="@{user.name}" />
</LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
data class User(var name: String, var age: Int)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val user = User("AAA", 18)
binding.user = user
binding.eventHandler = EventHandle(user)
}
inner class EventHandle(private val user: User) {
fun onTextClick(view: View) { //方法引用,签名需要和onclick()一致,若需要数据可以从外面传进来
println("onImageClick ${user.name}")
}
fun onTextLongClick(
view: View,
user: User
): Boolean { //监听器绑定,返回值需要和onLongClick()一致,可以从参数中获取数据
println("onLongClick ${user.name}")
return true
}
}
}
以上面的布局和代码为例,系统在build目录会自动生成相关类,ActivityMainBinding类如下
- 会为布局中每个带id的控件生成实例
- 创建布局中的变量
- 将inflate()和bind()转发给父类ViewDataBinding
public abstract class ActivityMainBinding extends ViewDataBinding {
@NonNull
public final LinearLayout main;
@NonNull
public final TextView tv;
@Bindable
protected MainActivity.EventHandle mEventHandler;
@Bindable
protected MainActivity.User mUser;
protected ActivityMainBinding(Object _bindingComponent, View _root, int _localFieldCount,
LinearLayout main, TextView tv) {
super(_bindingComponent, _root, _localFieldCount);
this.main = main;
this.tv = tv;
}
public abstract void setEventHandler(@Nullable MainActivity.EventHandle eventHandler);
@Nullable
public MainActivity.EventHandle getEventHandler() {
return mEventHandler;
}
public abstract void setUser(@Nullable MainActivity.User user);
@Nullable
public MainActivity.User getUser() {
return mUser;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
}
@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());
}
@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());
}
@Deprecated
public static ActivityMainBinding bind(@NonNull View view, @Nullable Object component) {
return (ActivityMainBinding)bind(component, view, R.layout.activity_main);
}
}
ActivityMainBinding的实现类ActivityMainBindingImpl
- 为布局中变量实现set方法
- 通过Flag判断当前调用的方法
public class ActivityMainBindingImpl extends ActivityMainBinding implements com.example.demo2_databinding2.generated.callback.OnLongClickListener.Listener {
@Nullable
private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = null;
}
// views
// variables
@Nullable
private final android.view.View.OnLongClickListener mCallback1;
// values
// listeners
private OnClickListenerImpl mEventHandlerOnTextClickAndroidViewViewOnClickListener;
// Inverse Binding Event Handlers
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 0
, (android.widget.LinearLayout) bindings[0]
, (android.widget.TextView) bindings[1]
);
this.main.setTag(null);
this.tv.setTag(null);
setRootTag(root);
// listeners
mCallback1 = new com.example.demo2_databinding2.generated.callback.OnLongClickListener(this, 1);
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
@Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.user == variableId) {
setUser((com.example.demo2_databinding2.MainActivity.User) variable);
}
else if (BR.eventHandler == variableId) {
setEventHandler((com.example.demo2_databinding2.MainActivity.EventHandle) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setUser(@Nullable com.example.demo2_databinding2.MainActivity.User User) {
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
public void setEventHandler(@Nullable com.example.demo2_databinding2.MainActivity.EventHandle EventHandler) {
this.mEventHandler = EventHandler;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.eventHandler);
super.requestRebind();
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
}
return false;
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.example.demo2_databinding2.MainActivity.User user = mUser;
android.view.View.OnClickListener eventHandlerOnTextClickAndroidViewViewOnClickListener = null;
com.example.demo2_databinding2.MainActivity.EventHandle eventHandler = mEventHandler;
if ((dirtyFlags & 0x5L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
}
if ((dirtyFlags & 0x6L) != 0) {
if (eventHandler != null) {
// read eventHandler::onTextClick
eventHandlerOnTextClickAndroidViewViewOnClickListener = (((mEventHandlerOnTextClickAndroidViewViewOnClickListener == null) ? (mEventHandlerOnTextClickAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mEventHandlerOnTextClickAndroidViewViewOnClickListener).setValue(eventHandler));
}
}
// batch finished
if ((dirtyFlags & 0x6L) != 0) {
// api target 1
this.tv.setOnClickListener(eventHandlerOnTextClickAndroidViewViewOnClickListener);
}
if ((dirtyFlags & 0x4L) != 0) {
// api target 1
this.tv.setOnLongClickListener(mCallback1);
}
if ((dirtyFlags & 0x5L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv, userName);
}
}
// Listener Stub Implementations
public static class OnClickListenerImpl implements android.view.View.OnClickListener{
private com.example.demo2_databinding2.MainActivity.EventHandle value;
public OnClickListenerImpl setValue(com.example.demo2_databinding2.MainActivity.EventHandle value) {
this.value = value;
return value == null ? null : this;
}
@Override
public void onClick(android.view.View arg0) {
this.value.onTextClick(arg0);
}
}
// callback impls
public final boolean _internalCallbackOnLongClick(int sourceId , android.view.View callbackArg_0) {
// localize variables for thread safety
// user
com.example.demo2_databinding2.MainActivity.User user = mUser;
// eventHandler
com.example.demo2_databinding2.MainActivity.EventHandle eventHandler = mEventHandler;
// eventHandler != null
boolean eventHandlerJavaLangObjectNull = false;
// eventHandler.onTextLongClick(view, user)
boolean eventHandlerOnTextLongClickCallbackArg0User = false;
eventHandlerJavaLangObjectNull = (eventHandler) != (null);
if (eventHandlerJavaLangObjectNull) {
eventHandlerOnTextLongClickCallbackArg0User = eventHandler.onTextLongClick(callbackArg_0, user);
}
return eventHandlerOnTextLongClickCallbackArg0User;
}
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user
flag 1 (0x2L): eventHandler
flag 2 (0x3L): null
flag mapping end*/
//end
}