DataBinding 进阶用法

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. 效果图

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hanyang Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值