DataBinding 高级用法

本文详细介绍了DataBinding的高级用法,包括双向数据绑定、自定义View属性支持、转换器的应用以及自定义控件的绑定方法。通过实例演示了如何处理空值、非标准属性绑定和避免死循环,适合Android开发者深入理解并实践数据绑定技术。
摘要由CSDN通过智能技术生成

DataBinding的高级用法的演示:
 1、双向绑定
 2、自定义属性(todo 注意databinding支持view的属性,需要有对应setXXX,getXXX的格式的函数(或者boolean的isXXX),才能被框架感知。
 如果一个View的属性,不是规范的setXXX,getXXX的设置/获取函数,那么就不行。可以继承该函数,设置setXXX/getXXX,在xml中自定义的View,就可以支持。)
 3、转换器converters
 4、自定义控件支持dataBinding,@BindingMethods、@InverseBindingMethods等

DataBinding的高级用法的问题:
 1、@{user.name}如果user为null,是否运行崩溃
 2、DataBinding是否支持所有View的属性
 3、双向绑定时候,是否会数据陷入死循环。
问题解答:
 1、如果xml中的variable没有binding赋值,如myBg那一行如果注释掉。运行时就会崩溃。而如果只是name为空,就会显示null,而不会崩溃。
 2、上面已经知道,不是所有的View的属性都可以直接dataBinding的,需要满足标准setXXX/getXXX的函数方式,或者按照[MyImageView]中注释写的那三种方法,扩展view的属性binding支持
 3、双向绑定也不会死循环,因为实现类会做old==new的value值校验,并return,避免陷入双向刷新的死循环。

1. 项目 build.gradle 添加引用库

  //SwipeRefreshLayout
  implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
  //glide
  implementation 'com.github.bumptech.glide:glide:4.12.0'
  kapt 'com.github.bumptech.glide:compiler:4.12.0'//kotlin代码需要这个kapt注入进程

2. 布局文件 activity_advanced_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>

        <variable
            name="user"
            type="org.hanyang.jetpack.binding.bean.ObUser" />
        <!--用于加载图片的url-->
        <variable
            name="url"
            type="String" />

        <variable
            name="myBg"
            type="android.graphics.drawable.Drawable" />

        <!--用于演示双向绑定,刷新view的swipeRefreshLayout控件需要用到-->
        <variable
            name="activity"
            type="org.hanyang.jetpack.binding.activity.AdvancedUseActivity" />
    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <!--1、双向绑定,使用的是@={} 下面输入框的修改,就会同步修改user的name字段-->
        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{`Name ` + user.name }"
            tools:text="在输入框修改user的name,这里会变" />

        <androidx.appcompat.widget.AppCompatEditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="这里演示的输入名字,更改为user的name"
            android:singleLine="true"
            android:text="@={user.name}" />

        <!--2、转换,也就是将原有不支持的属性,转换为支持,兼容,如 background
         的属性不能设置普通 String,这里可以通过@BindingConversion的静态函数,来适配-->
        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@{`red`}"
            android:text="这个文案的背景色属性,就是通过converters转换才行的" />

        <!--3、添加或扩展已有控件的属性,或者给它的set支持dataBinding
         因为dataBinding只是控件属性set/get,需要标准的函数命名getXXX,setXXX才会识别该属性。
         如果不是这样命名的函数属性set,就不会支持dataBinding。这里我们可以扩展兼容-->

        <!--这里的src属性,并不支持网络加载url,或者uri,我们可以在静态函数中,适配兼容-->
        <androidx.appcompat.widget.AppCompatImageView
            imgSrc="@{url}"
            android:layout_width="wrap_content"
            android:layout_height="80dp"
            android:layout_marginTop="10dp"
            android:src="@drawable/img_banner1" />
        <!--这里是用的自定义的View,使用img属性,在view中添加set函数,这样和上面的@BindingAdapter对比,上面更方便,扩展性强-->
        <org.hanyang.jetpack.binding.view.MyImageView
            img="@{myBg}"
            android:layout_width="wrap_content"
            android:layout_height="80dp"
            android:layout_marginTop="10dp" />
        <!--这里的 image 属性,是原控件没有的,而是通过@BindingMethods来实现函数映射-->
        <androidx.appcompat.widget.AppCompatImageView
            image="@{myBg}"
            android:layout_width="wrap_content"
            android:layout_height="80dp"
            android:layout_marginTop="10dp" />

        <!--4.自定义View 双向绑定演示 记住双向绑定使用@={}-->
        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            app:sfl_refreshing="@={activity.refreshing}">

            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/tv_long_text_ad_binding"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="long text 长文本" />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </LinearLayout>
</layout>

3. 使用工具文件 BCTool.kt

/**
 * Binding高级用法中,辅助工具类,演示@BindingConversion,@bindadapter等
 */
object BCTool {

    /**
     * 兼容适配view的background的str转color属性,这里函数名 可以随意,而且不需要其他地方显式的调用。
     * 只需要在此 ,静态函数的声明即可。(java的写法就是public static,这里不写java版的了。)
     */
    @JvmStatic
    @BindingConversion
    fun conversionStr2Color(str: String): Drawable {
        return when (str) {
            "red" -> {
                ColorDrawable(Color.RED)
            }
            "blue" -> ColorDrawable(Color.BLUE)
            else -> {
                ColorDrawable(Color.YELLOW)
            }
        }
    }

    /**
     * 用于appCompatImageView的自定义属性,bind:imgSrc,命名空间bind:可以省略,也就是写作 imgSrc亦可。可以用于加载url的图片
     * 函数名也是随意,主要是value的声明,就是新加的属性名了,可以多个属性同用,并配置是否必须一起作用
     * 函数名随意,方法签名才重要,匹配对象控件,以及属性参数。这里还可以添加old 参数,获取修改新参数 之前对应的值。
     * todo 加载网络图片,需要网络权限,别忘了
     */
    @JvmStatic
    @BindingAdapter(value = ["imgSrc"], requireAll = false)
    fun urlImageSrc(view: AppCompatImageView, /*old: String?, */url: String?) {
        Glide.with(view)
            .load(url)
            .placeholder(R.drawable.img_banner1)
            .centerInside()
            .into(view)
    }

    /**
     * 这个是 databinding高级用法中,配合演示swipeRefreshLayout的刷新状态的感知
     * 第一步:单向的,数据变化,刷新UI
     */
    @JvmStatic
    @BindingAdapter("sfl_refreshing", requireAll = false)
    fun setSwipeRefreshing(view: SwipeRefreshLayout, oldValue: Boolean, newValue: Boolean) {
        //判断是否是新的值,避免陷入死循环
        if (oldValue != newValue)
            view.isRefreshing = newValue
    }

    /**
     * 第二步:ui的状态,反向绑定给数据变化
     */
    @JvmStatic
    @BindingAdapter("sfl_refreshingAttrChanged", requireAll = false)
    fun setRefreshCallback(view: SwipeRefreshLayout, listener: InverseBindingListener?) {
        listener ?: return
        view.setOnRefreshListener {
            //由ui层的刷新状态变化,反向通知数据层的变化
            listener.onChange()
        }
    }

    /**
     * 反向绑定的实现,将UI的变化,回调给bindingListener,listener就会onChange,通知数据变化
     * 注意这里的attr和event,是跟上面两步配合一致才有效
     */
    @JvmStatic
    @InverseBindingAdapter(attribute = "sfl_refreshing", event = "sfl_refreshingAttrChanged")
    fun isSwipeRefreshing(view: SwipeRefreshLayout): Boolean {
        return view.isRefreshing
    }
}

4. 测试页面 AdvancedUseActivity.kt

/**
 * DataBinding的高级用法的演示
 * 1、双向绑定
 * 2、自定义属性(todo 注意databinding支持view的属性,需要有对应setXXX,getXXX的格式的函数(或者boolean的isXXX),才能被框架感知。
 * 如果一个View的属性,不是规范的setXXX,getXXX的设置/获取函数,那么就不行。可以继承该函数,设置setXXX/getXXX,在xml中自定义的View,就可以支持。)
 * 3、转换器converters
 * 4、自定义控件支持dataBinding,@BindingMethods、@InverseBindingMethods等
 */
class AdvancedUseActivity : AppCompatActivity() {
    val TAG = AdvancedUseActivity::class.java.simpleName

    //标记是否刷新liveData中的对象。不能 private,因为要用在xml中
    val refreshing = MutableLiveData<Boolean>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //Glide 初始化
        //Glide.init(this, GlideBuilder())
        val binding = DataBindingUtil.setContentView<ActivityAdvancedUseBinding>(
            this,
            R.layout.activity_advanced_use
        )

        //user 设置
        val user = ObUser("张三", 30, 1, "没有修改名字前的数据")
        binding.user = user

        //url
        val url = "https://t7.baidu.com/it/u=2931491413,1199396761&fm=193&f=GIF"
        binding.url = url

        //设置myView的 img 参数
        binding.myBg = getDrawable(R.drawable.img_banner1)

        //演示自定义View实现双向绑定
        // 1、先单向绑定 也就是将viewModel的数据变化,通知UI来刷新;(这里也就是在[BCTool]中static声明自定义属性,refreshing)
        // 2、将UI的变化,反向绑定,来通知数据模型的状态变化。
        // 3、完成双向绑定,避免死循环。
        //这里是个长文本,配合演示swipeRefreshLayout的状态感知
        binding.tvLongTextAdBinding.text = strText
        //
        binding.activity = this
        //这里记录log, liveData感知,也就证明,UI的刷新,将状态反响绑定给了data
        refreshing.observe(this, Observer<Boolean> {
            Log.i(TAG, "refreshing $it")
        })
    }

}

const val strText = """
James Ray edited this page on 2 Apr · 241 revisions
Welcome to the Ethereum Wiki!
Documentation chat standard-readme compliant
Ethereum wiki covering all things related to Ethereum
Contents
Issues and pull requests
Contribution guidelines
Introduction
Fixing vandalism
Page titles
Wikipedia pillars
Translating
License and contributor license agreement
Editing locally (requires access permission)
Setup
Usage
Getting started
"""

5. 效果图

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hanyang Li

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

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

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

打赏作者

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

抵扣说明:

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

余额充值