• DataBinding 高级用法


    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 添加引用库

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

    2. 布局文件 activity_advanced_use.xml

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <layout xmlns:app="http://schemas.android.com/apk/res-auto"
    3. xmlns:tools="http://schemas.android.com/tools">
    4. <data>
    5. <variable
    6. name="user"
    7. type="org.hanyang.jetpack.binding.bean.ObUser" />
    8. <!--用于加载图片的url-->
    9. <variable
    10. name="url"
    11. type="String" />
    12. <variable
    13. name="myBg"
    14. type="android.graphics.drawable.Drawable" />
    15. <!--用于演示双向绑定,刷新view的swipeRefreshLayout控件需要用到-->
    16. <variable
    17. name="activity"
    18. type="org.hanyang.jetpack.binding.activity.AdvancedUseActivity" />
    19. </data>
    20. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    21. android:layout_width="match_parent"
    22. android:layout_height="match_parent"
    23. android:gravity="center_horizontal"
    24. android:orientation="vertical">
    25. <!--1、双向绑定,使用的是@={} 下面输入框的修改,就会同步修改user的name字段-->
    26. <androidx.appcompat.widget.AppCompatTextView
    27. android:layout_width="wrap_content"
    28. android:layout_height="wrap_content"
    29. android:text="@{`Name ` + user.name }"
    30. tools:text="在输入框修改user的name,这里会变" />
    31. <androidx.appcompat.widget.AppCompatEditText
    32. android:layout_width="wrap_content"
    33. android:layout_height="wrap_content"
    34. android:hint="这里演示的输入名字,更改为user的name"
    35. android:singleLine="true"
    36. android:text="@={user.name}" />
    37. <!--2、转换,也就是将原有不支持的属性,转换为支持,兼容,如 background
    38. 的属性不能设置普通 String,这里可以通过@BindingConversion的静态函数,来适配-->
    39. <androidx.appcompat.widget.AppCompatTextView
    40. android:layout_width="wrap_content"
    41. android:layout_height="wrap_content"
    42. android:background="@{`red`}"
    43. android:text="这个文案的背景色属性,就是通过converters转换才行的" />
    44. <!--3、添加或扩展已有控件的属性,或者给它的set支持dataBinding
    45. 因为dataBinding只是控件属性set/get,需要标准的函数命名getXXX,setXXX才会识别该属性。
    46. 如果不是这样命名的函数属性set,就不会支持dataBinding。这里我们可以扩展兼容-->
    47. <!--这里的src属性,并不支持网络加载url,或者uri,我们可以在静态函数中,适配兼容-->
    48. <androidx.appcompat.widget.AppCompatImageView
    49. imgSrc="@{url}"
    50. android:layout_width="wrap_content"
    51. android:layout_height="80dp"
    52. android:layout_marginTop="10dp"
    53. android:src="@drawable/img_banner1" />
    54. <!--这里是用的自定义的View,使用img属性,在view中添加set函数,这样和上面的@BindingAdapter对比,上面更方便,扩展性强-->
    55. <org.hanyang.jetpack.binding.view.MyImageView
    56. img="@{myBg}"
    57. android:layout_width="wrap_content"
    58. android:layout_height="80dp"
    59. android:layout_marginTop="10dp" />
    60. <!--这里的 image 属性,是原控件没有的,而是通过@BindingMethods来实现函数映射-->
    61. <androidx.appcompat.widget.AppCompatImageView
    62. image="@{myBg}"
    63. android:layout_width="wrap_content"
    64. android:layout_height="80dp"
    65. android:layout_marginTop="10dp" />
    66. <!--4.自定义View 双向绑定演示 记住双向绑定使用@={}-->
    67. <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    68. android:layout_width="match_parent"
    69. android:layout_height="match_parent"
    70. android:layout_marginTop="10dp"
    71. app:sfl_refreshing="@={activity.refreshing}">
    72. <androidx.appcompat.widget.AppCompatTextView
    73. android:id="@+id/tv_long_text_ad_binding"
    74. android:layout_width="match_parent"
    75. android:layout_height="match_parent"
    76. android:text="long text 长文本" />
    77. </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    78. </LinearLayout>
    79. </layout>

    3. 使用工具文件 BCTool.kt

    1. /**
    2. * Binding高级用法中,辅助工具类,演示@BindingConversion@bindadapter
    3. */
    4. object BCTool {
    5. /**
    6. * 兼容适配view的background的str转color属性,这里函数名 可以随意,而且不需要其他地方显式的调用。
    7. * 只需要在此 ,静态函数的声明即可。(java的写法就是public static,这里不写java版的了。)
    8. */
    9. @JvmStatic
    10. @BindingConversion
    11. fun conversionStr2Color(str: String): Drawable {
    12. return when (str) {
    13. "red" -> {
    14. ColorDrawable(Color.RED)
    15. }
    16. "blue" -> ColorDrawable(Color.BLUE)
    17. else -> {
    18. ColorDrawable(Color.YELLOW)
    19. }
    20. }
    21. }
    22. /**
    23. * 用于appCompatImageView的自定义属性,bind:imgSrc,命名空间bind:可以省略,也就是写作 imgSrc亦可。可以用于加载url的图片
    24. * 函数名也是随意,主要是value的声明,就是新加的属性名了,可以多个属性同用,并配置是否必须一起作用
    25. * 函数名随意,方法签名才重要,匹配对象控件,以及属性参数。这里还可以添加old 参数,获取修改新参数 之前对应的值。
    26. * todo 加载网络图片,需要网络权限,别忘了
    27. */
    28. @JvmStatic
    29. @BindingAdapter(value = ["imgSrc"], requireAll = false)
    30. fun urlImageSrc(view: AppCompatImageView, /*old: String?, */url: String?) {
    31. Glide.with(view)
    32. .load(url)
    33. .placeholder(R.drawable.img_banner1)
    34. .centerInside()
    35. .into(view)
    36. }
    37. /**
    38. * 这个是 databinding高级用法中,配合演示swipeRefreshLayout的刷新状态的感知
    39. * 第一步:单向的,数据变化,刷新UI
    40. */
    41. @JvmStatic
    42. @BindingAdapter("sfl_refreshing", requireAll = false)
    43. fun setSwipeRefreshing(view: SwipeRefreshLayout, oldValue: Boolean, newValue: Boolean) {
    44. //判断是否是新的值,避免陷入死循环
    45. if (oldValue != newValue)
    46. view.isRefreshing = newValue
    47. }
    48. /**
    49. * 第二步:ui的状态,反向绑定给数据变化
    50. */
    51. @JvmStatic
    52. @BindingAdapter("sfl_refreshingAttrChanged", requireAll = false)
    53. fun setRefreshCallback(view: SwipeRefreshLayout, listener: InverseBindingListener?) {
    54. listener ?: return
    55. view.setOnRefreshListener {
    56. //由ui层的刷新状态变化,反向通知数据层的变化
    57. listener.onChange()
    58. }
    59. }
    60. /**
    61. * 反向绑定的实现,将UI的变化,回调给bindingListener,listener就会onChange,通知数据变化
    62. * 注意这里的attr和event,是跟上面两步配合一致才有效
    63. */
    64. @JvmStatic
    65. @InverseBindingAdapter(attribute = "sfl_refreshing", event = "sfl_refreshingAttrChanged")
    66. fun isSwipeRefreshing(view: SwipeRefreshLayout): Boolean {
    67. return view.isRefreshing
    68. }
    69. }

    4. 测试页面 AdvancedUseActivity.kt

    1. /**
    2. * DataBinding的高级用法的演示
    3. * 1、双向绑定
    4. * 2、自定义属性(todo 注意databinding支持view的属性,需要有对应setXXX,getXXX的格式的函数(或者boolean的isXXX),才能被框架感知。
    5. * 如果一个View的属性,不是规范的setXXX,getXXX的设置/获取函数,那么就不行。可以继承该函数,设置setXXX/getXXX,在xml中自定义的View,就可以支持。)
    6. * 3、转换器converters
    7. * 4、自定义控件支持dataBinding,@BindingMethods、@InverseBindingMethods等
    8. */
    9. class AdvancedUseActivity : AppCompatActivity() {
    10. val TAG = AdvancedUseActivity::class.java.simpleName
    11. //标记是否刷新liveData中的对象。不能 private,因为要用在xml中
    12. val refreshing = MutableLiveData<Boolean>()
    13. override fun onCreate(savedInstanceState: Bundle?) {
    14. super.onCreate(savedInstanceState)
    15. //Glide 初始化
    16. //Glide.init(this, GlideBuilder())
    17. val binding = DataBindingUtil.setContentView<ActivityAdvancedUseBinding>(
    18. this,
    19. R.layout.activity_advanced_use
    20. )
    21. //user 设置
    22. val user = ObUser("张三", 30, 1, "没有修改名字前的数据")
    23. binding.user = user
    24. //url
    25. val url = "https://t7.baidu.com/it/u=2931491413,1199396761&fm=193&f=GIF"
    26. binding.url = url
    27. //设置myView的 img 参数
    28. binding.myBg = getDrawable(R.drawable.img_banner1)
    29. //演示自定义View实现双向绑定
    30. // 1、先单向绑定 也就是将viewModel的数据变化,通知UI来刷新;(这里也就是在[BCTool]中static声明自定义属性,refreshing)
    31. // 2、将UI的变化,反向绑定,来通知数据模型的状态变化。
    32. // 3、完成双向绑定,避免死循环。
    33. //这里是个长文本,配合演示swipeRefreshLayout的状态感知
    34. binding.tvLongTextAdBinding.text = strText
    35. //
    36. binding.activity = this
    37. //这里记录log, liveData感知,也就证明,UI的刷新,将状态反响绑定给了data
    38. refreshing.observe(this, Observer<Boolean> {
    39. Log.i(TAG, "refreshing $it")
    40. })
    41. }
    42. }
    43. const val strText = """
    44. James Ray edited this page on 2 Apr · 241 revisions
    45. Welcome to the Ethereum Wiki!
    46. Documentation chat standard-readme compliant
    47. Ethereum wiki covering all things related to Ethereum
    48. Contents
    49. Issues and pull requests
    50. Contribution guidelines
    51. Introduction
    52. Fixing vandalism
    53. Page titles
    54. Wikipedia pillars
    55. Translating
    56. License and contributor license agreement
    57. Editing locally (requires access permission)
    58. Setup
    59. Usage
    60. Getting started
    61. """

    5. 效果图

     

  • 相关阅读:
    「纯干货」接口项目/接口框架通通都有,都给你
    【算法练习Day43】最佳买卖股票时机含冷冻期&&买卖股票的最佳时机含手续费
    多线程常识相关
    全影集团呀
    Unity求物体关于平面镜像对称后坐标以及旋转
    【栈、树、字符串、二叉树-中等】331. 验证二叉树的前序序列化
    深入理解计算机系统——Midterm Exam 2012
    【C++】父类与子类的那些事
    2022第四季度的机遇,Genesis Universe限量卡牌争夺战开启
    qt判断当前日期的当月的最后一天是几号
  • 原文地址:https://blog.csdn.net/u011193452/article/details/126385645