JetPack-(7)-ViewModel + LiveData + DataBinding综合使用

理论

ViewModel + LiveData + DataBinding可以有效的将数据与UI组件分离,提高代码的可维护性和可读性

1.ViewModel使用要点

ViewModel是视图View和数据模型Model之间的沟通桥梁,借助ViewModel,视图 与 数据模型实现了解耦,同时还能保证视图 与 数据模型之间 保持通信。这样Activity的代码量减少了,只需要维护视图View相关内容,增加了代码的可维护性。

并且我们不需要再关心屏幕旋转导致数据丢失的问题,因为ViewModel的生命周期与Activity或Fragment的生命周期相互独立,ViewModel不会受到Activity组件销毁的影响。如果由于屏幕旋转导致的Activity销毁重建,与之绑定的ViewModel会在销毁的时候解绑,Activity重建时重新绑定。

ViewModel使用步骤:

  • 首先,创建自定义ViewModel视图模型类,继承androidx.lifecycle.ViewModel类,该类就是要设置到视图(也就是xml)中的数据模型。
  • 与DataBinding结合使用时,在DataBinding布局中设置的就是该视图模型类对象
  • 该ViewModel类中,还使用了LiveData,可以实时监听数据改变,以更新UI组件
scala 复制代码
public class MyViewModel extends ViewModel {
    private MutableLiveData<Integer> number;

    public MyViewModel() {
        number = new MutableLiveData<>();
        number.setValue(0);
    }

    public MutableLiveData<Integer> getNumber() {
        return number;
    }
}

然后在Activity中,获取视图模型:

  1. 创建ViewModelProvider对象,传入如下两个参数:
  • ViewModelStoreOwner对象,就是本Activity组件
  • ViewModelProvider.Factory对象,通过调用ViewModelProvider.AndroidViewModelFactory方法获取
  1. 调用ViewModelProvider的get方法,获取自定义ViewModel类
csharp 复制代码
MyViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
  1. 最后,将视图模型中的数据设置到视图组件中: 与DataBinding结合使用时,将ViewModel对象设置到DataBinding布局中

2.LiveData使用要点

LiveData是一个可被观察的数据容器类,它可以包含任何类型的数据。它将数据包装起来,使数据成为观察者,当数据发生变化时,观察者能够获得通知。

LiveData是基于ViewModel的,是对ViewModel数据维护的一个补充:

在Activity中使用代码可以将ViewModel初始数据 设置给 视图组件,进行 初始状态显示,如果 在运行过程中,ViewModel中的数据发生了改变,如何将变化应用到视图组件中,在视图中显示最新的数据内容,此时就用到了LiveData组件

在ViewModel的基础上,通过引入LiveData,可以将 运行过程中ViewModel中的Model模型数据改变 通知 视图View,让视图组件显示最新的数据内容

在ViewModel中使用了LiveData后,必须调用LiveData的observer方法为LiveData设置androidx.lifecycle.Observer监听器,如果监听到了LiveData数据变化,直接回调监听器的 Observer监听器的onChanged方法,在该方法中执行更新视图的操作

LiveData使用:

  • 1.首先,在自定义ViewModel类中,定义MutableLiveData成员:
    1. 然后,在DataBinding布局中,设置该自定义ViewModel类型实例对象作为绑定的数据
  • 3.最后,在Activity组件中 获取DataBinding布局对应的ActivityMainBinding实例对象,为Activity设置布局文件 以及 设置LiveData在DataBinding中观察者生命周期所有者

在最后调用的activityMainBinding.setLifecycleOwner()方法,设置LiveData在DataBinding布局中的观察者的生命周期所有者

LiveData如果要生效,需要为其设置 androidx.lifecycle.Observer观察者,如果监听到了LiveData数据变化,直接回调监听器的 onChanged函数

DataBinding布局生成对应的ViewDataBinding类,调用 activityMainBinding.setLifecycleOwner()方法,传入的LifecycleOwner实例对象,该对象就算LiveData的观察者,如果不设置,LiveData发生数据改变后,则不会通知UI组件进行数据更新

Activity继承了AppCompatActivity,最底层的ComponentActivity实现了LifecycleOwner接口,因此可以将Activity作为LifecycleOwner设置给ViewDataBinding数据绑定类

3.DataBinding使用要点

DataBinding的主要作用就是 绑定下面两个元素:

  • 数据模型model/视图模型viewModel

  • 视图View DataBinding中除了绑定数据模型Model之外,还可以直接绑定视图模型ViewModel,这是DataBinding+ViewModel组合用法。

  • 如果ViewModel中使用了LiveData变量,则变成了DataBinding+ViewModel+LiveData组合用法

使用步骤:

  1. 将普通布局转换为DataBinding布局文件
  2. 使用 < data > 标签配置数据模型对象
  3. 加载数据绑定布局 获取数据模型 设置数据模型对象 设置LiveData在DataBinding中观察者生命周期所有者

实践

1.ViewModel + LiveData代码

定义ViewModel视图模型类,该类继承了 androidx.lifecycle.ViewModel类

在该自定义ViewModel类中,定义了MutableLiveData成员,这是LiveData实现,用于在运行过程中,一旦ViewModel数据发生改变,就通知View视图组件,更新数据显示。

LiveData生效 需要为MutableLiveData设置androidx.lifecycle.Observer监听器,当数据发生改变时,就会回调监听器中的androidx.lifecycle.Observer的onChanged回调函数

typescript 复制代码
public class MainViewModel extends ViewModel {
    private MutableLiveData<Integer> number;

    public MainViewModel() {
        number = new MutableLiveData<>();
        number.setValue(0);
    }

    public MutableLiveData<Integer> getNumber() {
        return number;
    }

    public void setNumber(MutableLiveData<Integer> number) {
        this.number = number;
    }

    public void plus() {
        number.setValue(number.getValue() + 1);
    }

    public void minus() {
        number.setValue(number.getValue() - 1);
    }

    public void reset() {
        number.setValue(0);
    }
}

2.DataBinding代码

DataBinding布局文件代码:

ini 复制代码
<?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="viewmodel"
            type="com.wzj.databindingdemo.MainViewModel" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewmodel.number}"
            android:textSize="16sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/btn_plus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->viewmodel.plus()}"
            android:text="+1"
            app:layout_constraintRight_toLeftOf="@id/btn_minus"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView" />

        <Button
            android:id="@+id/btn_minus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->viewmodel.minus()}"
            android:text="-1"
            app:layout_constraintLeft_toRightOf="@id/btn_plus"
            app:layout_constraintRight_toLeftOf="@id/btn_reset"
            app:layout_constraintTop_toBottomOf="@id/textView" />

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->viewmodel.reset()}"
            android:text="reset"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintLeft_toRightOf="@id/btn_minus"
            app:layout_constraintTop_toBottomOf="@id/textView" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3.Activity代码

在Activity中,需要完成三个任务:

  • 加载DataBinding布局,需要获取DataBinding布局对应的ViewDataBinding类,
  • 为DataBinding布局配置ViewModel实例对象,先获取ViewModel实例,然后设置给DataBinding布局
  • 设置LiveData观察者,在ViewDataBinding类中,可以直接调用ViewDataBinding的setLifecycleOwner函数将本Activity组件设置为LiveData的观察者即可
scala 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        MainViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MainViewModel.class);
        activityMainBinding.setViewmodel(viewModel);
        //设置LiveData在DataBinding中观察者生命周期所有者
        activityMainBinding.setLifecycleOwner(this);
    }
}

执行结果:

相关推荐
NiNg_1_23416 分钟前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河18 分钟前
CSS总结
前端·css
BigYe程普40 分钟前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H1 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍1 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai1 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端
流烟默1 小时前
Vue中watch监听属性的一些应用总结
前端·javascript·vue.js·watch
2401_857297911 小时前
招联金融2025校招内推
java·前端·算法·金融·求职招聘
茶卡盐佑星_2 小时前
meta标签作用/SEO优化
前端·javascript·html
Ink2 小时前
从底层看 path.resolve 实现
前端·node.js