DataBinding数据驱动UI与双向绑定(Kotlin 版本)

572 阅读3分钟

DataBinding实现数据驱动UI(Kotlin 版本)

图片.png

  • 编写Model层:User

    • 仿照java版本出错

      • 继承BaseObservable类:需要写出完整类名后再导包,Kotlin没有提示的

         class User : BaseObservable() {
        
      • 编写Model层字段:

         var name : String ?= null;
         var pwd : String ?= null;
        
      • 尝试为Model层字段的get方法添加注解

        • Kotlin中为每个属性都是内置了get/set方法的,因此重写get,然后添加注解,发现报错:

        图片.png

        因为:注解是给方法或者是字段使用,但此时的get只是一个表达式,所以这个注解就失效了,无法生成BR中的字段

      图片.png

      • 尝试向set方法内添加:notifyPropertyChanged(BR.name)

        • 不行的,注解都加不上去,BR文件都无法生成
    • Model层完整代码:使用ObservableField维护数据

       package com.wasbry.myapplication
       ​
       import androidx.databinding.Bindable
       import androidx.databinding.ObservableField
       ​
       class User{
           //继承BaseObservable()失效,这个在java里面可以,但是在kotlin中就不行了
           //在kotlin中直接使用ObservableField维护具体字段
           val nameF : ObservableField<String> by lazy { ObservableField<String>() }
           val pwdF : ObservableField<String> by lazy { ObservableField<String>() }
       }
      
  • View 层编写:将布局交由DataBinding进行管理

    • 实现思路

      1. 将布局拿给DataBinding进行管理:点一下那个转换的

      2. 为布局指定数据来源:补全data标签内容

      3. 完成View层---->Model层单向绑定

        //注意此处用的是没有等号,双向绑定实现时需要加上等号
         android:text="@{user.nameF}"
        
    • View层完整代码:

       <?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"
           xmlns:tools="http://schemas.android.com/tools">
       <!--布局的数据来源-->
           <data>
               <variable
                   name="user"
                   type="com.wasbry.myapplication.User" />
           </data>
       ​
           <LinearLayout
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               tools:context=".MainActivity"
               android:orientation="vertical">
       ​
               <EditText
                   android:id="@+id/et_name2"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:text="@{user.nameF}"
                   />
           
               <EditText
                   android:id="@+id/et_pwd2"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:text="@{user.pwdF}"
                   android:layout_marginBottom="60dp"
                   />
           </LinearLayout>
       </layout>
      
  • 编写VM层:在MainActivity使用DataBinding,完成Model层--->View层单向绑定

    • 实现思路

      1. 使用DataBindingUtils 拿到交给DataBinding管理的布局文件,binding
      2. 实例化Model层数据,绑定Model层与VM层
      3. 在子线程中更新UI
    • 实现过程:

      1. 使用DataBindingUtils 拿到交给DataBinding管理的布局文件

         val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
        
      2. 实例化Model层数据,binding.user = user

         //模拟Model层数据(json解析后得到的javaBean)
             private val user = User()
         ​
         //绑定Model层与VM层
         val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
                 
         binding.user = user
        
      3. 在子线程中更新UI

             thread {
                     Thread.sleep(3000)
                     user.nameF.set("wasbry")
                     user.pwdF.set("2222222")
                 }
        
    • 实现细节:

      • 细节一:发现没有DataBindingUtils这个类的

        图片.png

        此时编译一下就有了(DataBinding依赖于APT技术,要先编译)

        图片.png

      • 细节二:为什么需要指定泛型 < ActivityMainBinding >

          val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
        
        • 源码角度分析:进入setContentView源码,明确规定了的需要传入一个ViewDataBinding的子类(ActivityMainBinding) ,其实里面还有一个ActivityMainBindingIml,写上去不会报错,一跑就错

        图片.png

        • 语言角度分析:java语言在变量声明时就已经确定了类型,如果不指定泛型,那么在kotlin中类型推导机制无法工作

          java代码示例

          image-20211218171308161

    • VM层完整代码:

       package com.wasbry.myapplication
       ​
       import androidx.appcompat.app.AppCompatActivity
       import android.os.Bundle
       import androidx.databinding.DataBindingUtil
       import com.wasbry.myapplication.databinding.ActivityMainBinding
       import kotlin.concurrent.thread
       ​
       class MainActivity : AppCompatActivity() {
       ​
           //模拟Model层数据(json解析后得到的javaBean)
           private val user = User()
       ​
           override fun onCreate(savedInstanceState: Bundle?) {
               super.onCreate(savedInstanceState)
       //        setContentView(R.layout.activity_main)
               val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
               binding.user = user
       ​
               thread {
                   Thread.sleep(3000)
                   user.nameF.set("wasbry")
                   user.pwdF.set("2222222")
               }
           }
       }
      
  • 真机测试一波

    image-20220305133528675

实现双向绑定

  • 概述:

    • 在上述代码基础上仅作修改即可实现双向绑定
  • 实现目标:数据驱动UI+UI回馈数据

    • 数据驱动UI

      • 修改字段数据后,UI更新
    • UI回馈数据

      • 修改UI,在输入框内输入内容后通过日志拿到用户输入的数据
  • 实现细节:在上述版本上做修改

    • View层核心代码

           <EditText
                  ……
                   android:text="@={user.nameF}"
                   />
       ​
               <EditText
                  ……
                   android:text="@={user.pwdF}"
                   />
      
    • VM层核心代码:

       class MainActivity : AppCompatActivity() {
       ​
           //模拟Model层数据(json解析后得到的javaBean)
           private val user = User()
       ​
           override fun onCreate(savedInstanceState: Bundle?) {
               super.onCreate(savedInstanceState)
       ​
               val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
               binding.user = user
       ​
               thread {
                   Thread.sleep(3000)
                   user.nameF.set("wasbry")
                   user.pwdF.set("2222222")
       ​
                   Thread.sleep(10000)
                   Log.d("MainActivity","用户输入第一个字段为${user.nameF.get()}," +
                           "用户输入第二个字段为${user.pwdF.get()}")
               }
           }
       }
      
    • Model层核心代码:

       class User{
       ​
           //继承BaseObservable()失效,这个在java里面可以,但是在kotlin中就不行了
           val nameF : ObservableField<String> by lazy { ObservableField<String>() }
           val pwdF : ObservableField<String> by lazy { ObservableField<String>() }
       }
      
    • 运行截图:

      image-20220305135041243

  • 总结:

    • 在开发的时候一般都是单向的(View--->Model),因为双向绑定非常耗费性能

    • Databinding还有很多的好东西

      • BindingAdapter等
      • Model--->View:搞个事件就行了