DataBinding的进阶用法的演示:
1、响应式user对象的演示
2、list、recyclerView的演示使用
3、kotlin中binding的写法的优势
1. 布局文件
1.1 activity_common_use.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!--普通的 user 对象-->
<variable
name="user"
type="org.hanyang.jetpack.binding.bean.CommonUser" />
<!--包含部分 observable 属性的 user 对象-->
<variable
name="fuser"
type="org.hanyang.jetpack.binding.bean.FieldUser" />
<!-- Observable的user对象 -->
<variable
name="ouser"
type="org.hanyang.jetpack.binding.bean.ObUser" />
<import type="android.view.View" />
<!--单独的ObservableBoolean-->
<variable
name="show"
type="Boolean" />
<variable
name="ladapter"
type="org.hanyang.jetpack.binding.adapter.LAdapter" />
<variable
name="radapter"
type="org.hanyang.jetpack.binding.adapter.RAdapter" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<!--普通user对象,不会响应dataBinding的变化-->
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_common_user_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{user.name + user.age + user.sex + user.desc}"
tools:text="普通user的数据信息" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_change_common"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通改变"
android:textAllCaps="false" />
<!--部分属性响应的 user 对象在代码中 desc 属性需要get(), xml里可以不用-->
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_field_user_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{fuser.name+fuser.age+fuser.sex+fuser.desc}"
android:textColor="@android:color/holo_blue_light"
tools:text="Field的user的数据信息" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_change_field"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="改变Field"
android:textAllCaps="false" />
<!--Observable的 user对象-->
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_ob_user_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{ouser.toString()}"
android:textColor="@android:color/holo_green_dark"
tools:text="Observable user的数据信息" />
<!--注意这里点击改变Observable user的数据,上面以及下面include的user数据会同步响应变化-->
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_change_ob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="改变Observable"
android:textAllCaps="false" />
<!--直观的数据绑定view的显示隐藏-->
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个显示/隐藏文案,根据checkbox的状态"
android:visibility="@{show?View.VISIBLE:View.INVISIBLE}" />
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cb_common"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控制显示文案" />
<!--include 导入layout的使用 在这里声明内部layout用到的对象,变量。-->
<include
layout="@layout/layout_user"
ouser="@{ouser}" />
<!--list列表,recyclerView的列表使用 app:adapter="@{ladapter}"-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ListView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:adapter="@{ladapter}"
tools:listitem="@layout/item_lv" />
<!--RecyclerView可以在xml中写好layoutManager以及adapter的引用,这些有时候xml不会提示和补全-->
<androidx.recyclerview.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:adapter="@{radapter}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_lv" />
</LinearLayout>
</LinearLayout>
</layout>
1.2 layout_user.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="ouser"
type="org.hanyang.jetpack.binding.bean.ObUser" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--简单的演示,这里就是用了 observable 的user -->
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ouser.toString()}" />
</LinearLayout>
</layout>
1.3 item_lv.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="org.hanyang.jetpack.binding.bean.ObUser" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="30dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_purple"
android:gravity="center"
android:text="@{user.name}"
tools:text="姓名" />
<!--注意:这里的age,sex是Int类型,所以要转为string,而且这类错误,不易发现-->
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="30dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:text="@{user.age+``}"
tools:text="年龄" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="30dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_dark"
android:gravity="center"
android:text="@{user.sex +``}"
tools:text="性别" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:text="@{user.desc}"
tools:text="简述" />
</LinearLayout>
</layout>
2. 模型bean文件 User.kt
/**
* ----------------------------------------------------------------
* 用于DataBinding的数据对象类,user,这里在kotlin中,多个public的class可以放在一个kt文件中
*/
/**
* 普通的数据类,这里用了kotlin的数据类 data class 也就是一个特殊的class而已。
* val 表示不可变量,var 可变。这些在kotlin的代码演示中会有描述
*/
data class CommonUser(
val name: String, //姓名
var age: Int, //年龄
var sex: Int, //性别 0 女 1 男
var desc: String //描述
)
/**
* 这是部分属性可以databinding响应的user类,注意observable的属性需要public权限,否则dataBinding则无法通过反射处理数据响应
*/
data class FieldUser(
var name: String, //姓名
var age: ObservableInt,//可响应的Int类型
var sex: Int,
var desc: ObservableField<String>//可响应的String类型
)
/**
* 继承dataBinding的baseObservable的user对象类
* 这里是kotlin的写法,类似于java中,继承BaseObservable的对象类,
* 需要响应变化的字段,就在对应变量的get函数上加 @Bindable 。然后set中notifyChange kotlin的写法,免去了java的getter setter的方式
* 成员属性需要响应变化的,就在其set函数中,notify一下属性变化,那么set的时候,databinding就会感知到。
*/
class ObUser() : BaseObservable() {
//kotlin中类的构造函数可以多个,有主次之分,且次级构造函数必须调用主构造函数,如这里的this()
constructor(name: String, age: Int, sex: Int, desc: String) : this() {
this.name = name
this.age = age
// this.sex = sex
this.desc = desc
}
//这是单独在set上@bindable,name可以声明private
var name: String = ""
//kotlin的成员属性必须初始化(或者lateinit)
set(value) {
//BR.name表示通知name这个属性的变化。 notifyChange() 通知所有变化
notifyPropertyChanged(BR.name)
field = value
}
@Bindable
get() = field
//这是在整个变量上声明@bindable,所以必须是public的
@Bindable
var age = 18
set(value) {
notifyPropertyChanged(BR.age)
field = value
}
get() = field
val sex = 1
var desc: String = ""
set(value) {
field = "$value\n set中多余的拼接"//描述
notifyPropertyChanged(BR.desc)
}
@Bindable
get() = field
override fun toString(): String {
notifyChange()
return "ObUser(name='$name', age=$age, sex=$sex, desc='$desc')"
}
}
3. 适配器文件
3.1 LAdapter.kt
/**
* ListView的adapter,简单演示,所以也不写viewHolder了
*/
class LAdapter : BaseAdapter() {
private var users: MutableList<ObUser> = arrayListOf()
init {
//初始化三个数据
for (i in 0..2) {
users.add(ObUser("小小$i", 20 + i, i % 2, "小小的说明 $i"))
}
}
override fun getCount(): Int {
return users.size
}
override fun getItem(position: Int): ObUser {
return users[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
//简单演示,就不用 viewHolder,实际请使用
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val inflater = LayoutInflater.from(parent?.context)
var binding = ItemLvBinding.inflate(inflater)
// Java 写法
// if (convertView == null) {
// binding = DataBindingUtil.inflate<ItemLvBinding>(inflater, R.layout.item_lv, parent, false)
// } else {
// binding = DataBindingUtil.getBinding<ItemLvBinding>(convertView)!!
// }
// binding.setVariable(BR.user, users.get(position))
binding.user = users[position]
return binding.root
}
}
3.2 RAdapter.kt
/**
* 简单演示的RecyclerView的adapter
*/
class RAdapter : Adapter<RAdapter.MyHolder>() {
private var users: MutableList<ObUser> = arrayListOf()
init {
//初始化三个数据
for (i in 0..2) {
users.add(ObUser("小艾 $i", 20 + i, i % 2, "小艾的说明 $i"))
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemLvBinding.inflate(inflater)
return MyHolder(binding)
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
//java 写法可以setVariable
holder.binding.user = users[position]
holder.binding.executePendingBindings()
}
//kotlin,return的方式,可以简写
override fun getItemCount() = users.size
//在构造函数中声明binding变量,这样上面的holder才能引用到,如果不加val/var,就引用不到,就需要在class的{}内写get函数
class MyHolder(val binding: ItemLvBinding) : ViewHolder(binding.root)
}
4. 测试页面 CommonUseActivity.kt
/**
* DataBinding的进阶用法的演示界面
* 1、响应式user对象的演示
* 2、list、recyclerView的演示使用
* 3、kotlin中binding的写法的优势
*/
class CommonUseActivity : AppCompatActivity(), View.OnClickListener {
//一种懒加载的初始化控件的方式
private val tvCommon: AppCompatTextView by lazy { findViewById<AppCompatTextView>(R.id.tv_common_user_info) }
//这里由于使用了 apply plugin: 'org.jetbrains.kotlin.android.extensions' 会自动将xml中的id,关联为控件(也可以在代码中直接用tv_field_user_info)
private val tvField: AppCompatTextView by lazy { tv_field_user_info }
//这是稍后初始化的方式
private lateinit var tvObservable: AppCompatTextView
private lateinit var user: CommonUser //普通user对象
private lateinit var fuser: FieldUser //部分属性可变的user
private lateinit var obUser: ObUser //BaseObservable 的user
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//关联布局,binding 对象
val binding = DataBindingUtil.setContentView<ActivityCommonUseBinding>(
this,
R.layout.activity_common_use
)
//初始化user,fuser,obuser的对象,并赋值到binding中去
user = CommonUser("张三", 31, 1, "排行第三")
binding.user = user
//普通点类似java点方式,关联控件和注册事件
binding.btnChangeCommon.setOnClickListener(this)
//ObservableInt(
fuser = FieldUser("李四", ObservableInt(20), 0, ObservableField<String>("李四名字的由来"))
//在代码中要获取desc这样的observable的属性,就需要用get(),而且get()的是可null的返回,
//xml中直接用 user.desc即可,不需要写 .get(). 写了也无妨
//fuser.desc.get()
binding.fuser = fuser
binding.btnChangeField.setOnClickListener(this)
obUser = ObUser("王二", 22, 0, " 性别是改不了的,因为内部val声明了不可变量")
binding.ouser = obUser
//lateinit 延迟初始化
tvObservable = tv_ob_user_info
binding.btnChangeOb.setOnClickListener(this)
//checkbox的响应
cb_common.setOnCheckedChangeListener { compoundButton, checked ->
//设置变量
binding.show = checked
}
//adapter 设置
binding.ladapter = LAdapter()
binding.radapter = RAdapter()
}
//点击事件点处理
override fun onClick(view: View?) {
when (view?.id) {
R.id.btn_change_common -> {
user.age = 10; user.desc = "张三的改变desc,普通user对象不会影响binding"
Toast.makeText(this, "注意上面的common user的info信息不会变化", Toast.LENGTH_SHORT).show()
//当然,如果在此处,显示的代码设置Text,会变化,但是这不是数据绑定,因为你每次改变都要setText才生效
//tvCommon.text = user.desc
}
R.id.btn_change_field -> {
//fuser.name = "李四/李斯 "
fuser.age.set(23)
//值都会被改变
fuser.desc.set("这两个field可以改变,你会发现名字的name是不会改变的")
}
R.id.btn_change_ob -> {
obUser.name = "王二二"
obUser.age = 222
obUser.desc = "王二的名字,年龄,描述都会响应binding的变化"
}
}
}
}
5. 效果图