DataBinding实现数据驱动UI(Kotlin 版本)
-
概述:
- 本文与上一篇博客有密切联系,推荐进行对比阅读 DataBinding实现双向绑定,达到数据驱动UI效果(Java版本)
-
分层:
- Model层:User
- VM层:DataBinding实现
- View层:activity_main.xml
-
工程结构:
-
编写Model层:User
-
仿照java版本出错
-
继承BaseObservable类:需要写出完整类名后再导包,Kotlin没有提示的
class User : BaseObservable() { -
编写Model层字段:
var name : String ?= null; var pwd : String ?= null; -
尝试为Model层字段的get方法添加注解
-
Kotlin中为每个属性都是内置了get/set方法的,因此重写get,然后添加注解,发现报错:
因为:注解是给方法或者是字段使用,但此时的get只是一个表达式,所以这个注解就失效了,无法生成BR中的字段
-
-
尝试向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进行管理
-
实现思路
-
将布局拿给DataBinding进行管理:点一下那个转换的
-
为布局指定数据来源:补全data标签内容
-
完成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层单向绑定
-
实现思路
- 使用DataBindingUtils 拿到交给DataBinding管理的布局文件,binding
- 实例化Model层数据,绑定Model层与VM层
- 在子线程中更新UI
-
实现过程:
-
使用DataBindingUtils 拿到交给DataBinding管理的布局文件
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main) -
实例化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 -
在子线程中更新UI
thread { Thread.sleep(3000) user.nameF.set("wasbry") user.pwdF.set("2222222") }
-
-
实现细节:
-
细节一:发现没有DataBindingUtils这个类的
此时编译一下就有了(DataBinding依赖于APT技术,要先编译)
-
细节二:为什么需要指定泛型 < ActivityMainBinding >
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)-
源码角度分析:进入setContentView源码,明确规定了的需要传入一个ViewDataBinding的子类(ActivityMainBinding) ,其实里面还有一个ActivityMainBindingIml,写上去不会报错,一跑就错
-
语言角度分析:java语言在变量声明时就已经确定了类型,如果不指定泛型,那么在kotlin中类型推导机制无法工作
java代码示例
-
-
-
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") } } }
-
-
真机测试一波
实现双向绑定
-
概述:
- 在上述代码基础上仅作修改即可实现双向绑定
-
实现目标:数据驱动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>() } } -
运行截图:
-
-
总结:
-
在开发的时候一般都是单向的(View--->Model),因为双向绑定非常耗费性能
-
Databinding还有很多的好东西
- BindingAdapter等
- Model--->View:搞个事件就行了
-