Kotlin_MVVM(databinding实现) 计数实例*

本文详细介绍如何在Kotlin环境下利用DataBinding和MVVM模式,实现数据与UI的自动同步。通过实例展示如何配置DataBinding,创建ViewModel及Model,以及在布局文件中绑定数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Databinding:

让布局文件承担了部分原本属于页面的工作, 使页面与布局耦合度进一步降低

androidx.databinding

简单记录Kotlin 下使用MVVM的例子。

实现:模拟数据变化, 更新UI (两个TextView, 一个显示名字,一个显示计数)

一、app 目录build.grade 启动 dataBinding 

更新 2021/7/8  android gradle plugin 4.0.0 以上使用 buildFeatures,

android {
    //...
    buildFeatures {
        dataBinding = true

        // for view binding:
        // viewBinding = true
    }
}
以下方式为已抛弃
//    dataBinding {
//        enabled true
//    }

提示: DSL element 'android.dataBinding.enabled' is obsolete and has been replaced with 'android.buildFeatures.dataBinding'

参考:

https://stackoverflow.com/questions/59390492/dsl-element-android-databinding-enabled-is-obsolete-and-has-been-replaced-with

二、创建UserData (M) 和UserViewModel

class UserViewModel : BaseObservable {
    private var userData = UserData(this)

    constructor()

    //e: ����: Bindable must be on a member in an Observable class. UserViewModel is not Observable
    @Bindable
    fun getUserName(): String? {
        return userData.getCurUserName()
    }

    @Bindable
    fun getCount(): String {
        return userData.getCurrCount().toString()
    }


    fun changeUserName() {
        notifyPropertyChanged(BR.userName)
        notifyPropertyChanged(BR.count)
    }
}

1. UserViewModel: 主要提供两个Bindable 函数, 用于布局文件(V- view); 

                           changeUserName() 通知UI 数据已经变化,请更新UI

需要注意: Bindable 修饰函数(实际上为变量的get 方法), 需要所在的ViewModel 继承 BaseObservable,

否则,会报错:

 Bindable must be on a member in an Observable class. UserViewModel is not Observable

此外,也可以通过定义Observable 的变量,实现当数据变化时,自动更新UI, 则不需要手动调用notifyPropertyChanged 

    var userName = ObservableField<String>()
    userName.set(null)
    userName.set("Xiaoming")

 创建UserData:

class UserData {
    private var userNameList = mutableListOf("ZhangSan", "LiSi", "WangWu"
        , "ZhaoLiu", "YangBa", "SunJiu")
    private var curUserNameIndex = 0
    private var userViewModel: UserViewModel
    private var changeCount = 0
    private val maxCount = 20

    constructor(userViewModel: UserViewModel) {
        this.userViewModel = userViewModel
        //模拟数据变化
        simulateDataChange()
    }

    fun getCurUserName() : String ?{
        return userNameList[curUserNameIndex]
    }

    fun getCurrCount () : Int {
        return changeCount
    }

    private fun simulateDataChange() {
        Thread{
           for ( count in 0 until maxCount) {
               Thread.sleep(2000)
               changeCount++
               curUserNameIndex++
               if (curUserNameIndex >= userNameList.size) {
                   curUserNameIndex = 0
               }
               this.userViewModel.changeUserName()
           }
        }.start()
    }
}

 1. UserData:  提供用户名字(username) 和 当前计数(changeCount), 启动线程,每隔一秒更新数据(即curUserNameIndex),

并且调用 ViewModel 的 changeUserName() 告知数据变化了

其中,UserData / UserViewModel 相互持有引用,实际上,UserData(Model) 不需要持有ViewModel

三、布局文件 (V)

<?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="userViewModel"
            type="com.example.androidmvvmtest.UserViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/userNameTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{userViewModel.userName}"
            android:textSize="20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

        <TextView
            android:id="@+id/changeCountText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{userViewModel.count}"
            android:textSize="20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/userNameTextView"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

其中, 

1. 布局文件为  <layout >  </layout> 标签 

2. <data> </data> 标签定义引用的 ViewModel 类型和变量, 使用 variable 标签包裹,其中name 为变量名,type为类所在的包

        <variable
            name="userViewModel"
            type="com.example.androidmvvmtest.UserViewModel" />
如果想要引用Android 自带VIEW 的API, 例如 View.VISIBLE, 只需import 这个类
​​​​​​​<import
    type="android.view.View" />

3. 使用到viewmodel的地方:  android:text="@{userViewModel.userName}"

表示显示的文本,是引用 userViewModel 里面的 getUserName()的返回值 (省略了get)

四、 Activity 中调用

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var dataBinding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        var userViewModel = UserViewModel()
        dataBinding.userViewModel = userViewModel

    }
}

主要根据 DataBindingUtils 进行setContentView  (取代了之前的setContentView(R.layout.activity_main),

并且返回一个布局文件对应的 Binding 类型,  作用是设置布局文件里面的 ViewModel 变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值