欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MVVM实现数据双向绑定

程序员文章站 2024-02-01 14:25:16
...

Associate what you’ re trying to learn with what you already know. —— Daily English

这篇文章旨在通过一个Demo让我们对Android中的MVVM架构进行初步的认识。

MVVM与DataBinding的关系

很多同学会将这两者混为一谈,所以开始介绍之前,我们需要先理清楚这两者的关系。
MVVM和MVC、MVP一样,是项目中的架构设计思想;DataBinding是一种工具,它可以用于MVVM,也同样可以用于MVC和MVP。所以这两者是两回事,一个是架构设计思想,一个是工具。但是有一点,那就是Android中的MVVM一般都需要借助DataBinding来实现,这也是很多人将这两者混为一谈的原因。

MVVM简介

MVVM是更节省的设计模式,能实现双向的数据绑定。

须知

MVVM可以理解成M V VM。其中的M指的是Model层,也就是我们的JavaBean。V指的是VIew层,也就是我们具体的布局,如EditText等。VM指的是ViewModel层,它是Model层和View层的一个桥梁,也用来处理视图逻辑和业务逻辑。
简而言之,M还是Model,V还是View,VM就是ViewModel层。三者的关系大致如下图所示:
MVVM实现数据双向绑定
这个架构模式有如下两个特点

  1. 降低耦合:一个ViewModel层可以绑定不同的View层,当Model变化时View可以不变。
  2. 可重用性:可以把一些视图逻辑放在ViewModel层中,让很多View重用这些视图逻辑。

ViewModel相当于model层和View层的一个桥梁,当View层比如说一个EditText的值发生改变了,无需通过Activity,就直接可以改变JavaBean对应的属性值。Model层set一个值,也无需通过Activity,就可以直接改变页面上的值。

MVVM是有弊端的,一个是修改之后需要经常ReBuild,而且项目越大,ReBuild的时间也越长。另外也有三个原因会导致它的内存消耗比较大,这个会在介绍DataBinding的时候讲到。这也是有些公司不愿意用MVVM架构的原因。但是,MVVM为什么还会这么火呢,就是因为这种View和Model的双向绑定思想是值得我们学习的,也很可能是一种趋势。

什么是单向数据绑定,什么是双向数据绑定。

单向绑定是指View层(如EditText)上的数据改变会实时更新到Model层JavaBean中对应的属性值(如username)上。或者,Model层的数据改变会实时更新到View层上的显示,这样我们称之为单向的数据绑定。而双向绑定呢,是指Model层和View层,无论那层数据改变都会实时更新到对方,Model层数据改变会更新View,同样,View层数据改变会更新Model,这样就称之为双向的数据绑定。

项目实践

模拟一个登录的功能。

第一步,引入
在module的build.gradle文件中引入DataBinding

android {
	...
	// 添加DataBinding依赖
	dataBinding{
	    enabled = true
	}
)

第二步,定义实体类

public class UserInfo {
    // 被观察的属性(切记:必须是public修饰符,因为是DataBinding的规范)
    public ObservableField<String> name = new ObservableField<>();

    public ObservableField<String> pwd = new ObservableField<>();
}

实体类也可以定义成原始的那种格式,添加get(),set()方法,也可以定义成被观察者属性的格式。只要注意两点,一个是被观察者属性,一个是有刷新属性的方法。这里就不做解释,对格式有疑问的同学请参考我另一篇讲DataBinding的文章。

第三步,定义布局

布局界面很简单
MVVM实现数据双向绑定
对应xml文件代码如下

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <!--定义该布局需要绑定的数据名称和类型-->
    <data>
        <variable
            name="loginViewModel"
            type="pers.owen.my_mvvm.vm.LoginViewModel" />
    </data>

    <!-- 下部分内容和平时布局文件一样 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:hint="请输入账户"
            android:text="@={loginViewModel.userInfo.name}" />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:hint="请输入密码"
            android:text="@={loginViewModel.userInfo.pwd}" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:onClick="@{loginViewModel.loginClickListener}"
            android:text="登录" />

    </LinearLayout>
</layout>

布局格式参考上述XML即可。

这里我们发现<data>中的variable是一个ViewModel的名称和具体路径,当然也可以直接是一个实体类。这也就是我们为什么说DataBinding其实也可以用在MVC或MVP架构当中,因为这篇文章讲的是MVVM的架构,所以这里定义一个专门处理登录逻辑的ViewModel,叫做LoginViewModel类。

其中如android:onClick="@{loginViewModel.loginClickListener}"loginViewModel就是<data>标签下LoginViewModel的name。可以把这里的loginViewModel理解成一个对象,而loginViewModel.loginClickListener就是相当于调用这个对象中一个叫loginClickListener的public方法。

LoginViewModel的方法随即附上。

第四步,定义ViewModel

public class LoginViewModel {
    public UserInfo userInfo;

    public View.OnClickListener loginClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // 尝试1:view --> model单向绑定测试,改变EditText的值,查看bean中的对应属性值是否发生变化。
//            Log.e("owen >>> ", userInfo.name.get() + "--" + userInfo.pwd.get());

            // 尝试2:model --> view单向绑定测试,Model层属性的变更,也会改变View层的显示
//            userInfo.name.set("Owen");
//            userInfo.pwd.set("0410");

            // 尝试3:模拟网络请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if ("Owen".equals(userInfo.name.get()) && "123".equals(userInfo.pwd.get())) {
                        Log.e("Owen >>> ", "登录成功!");
                    } else {
                        Log.e("Owen >>> ", "登录失败!");
                    }
                }
            }).start();
        }
    };
}

注意LoginViewModel类中的属性名userInfologinClickListener是要和xml布局中如android:onClick="@{loginViewModel.loginClickListener}"的名称对应上。可以通过ctrl+左键点击跳转验证。

第五步,Rebuild Project
Rebuild完成后,会在data_binding_base_class_source_out目录下生成以[布局名]Binding.java文件,这里我们的布局叫activity_main,所以生成了一个名为ActivityMainBinding的Java类文件。目录的具体位置如下图所示:
MVVM实现数据双向绑定
有了它,我们就可以做绑定操作了。

最后一步,书写代码绑定

在Activity中创建ActivityMainBinding对象

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 1、必须先ReBuilder,2、书写代码绑定
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        new LoginViewModel(binding);
    }
}

通过构造方法传入LoginViewModel进行绑定

 public LoginViewModel(ActivityMainBinding binding) {
     userInfo = new UserInfo();
     // 将ViewModel和View进行绑定,通过DataBinding工具。
     binding.setLoginViewModel(this);
 }

最后LoginViewModel的完整代码如下:

public class LoginViewModel {
    public UserInfo userInfo;

    public LoginViewModel(ActivityMainBinding binding) {
        userInfo = new UserInfo();
        // 将ViewModel和View进行绑定,通过DataBinding工具。
        binding.setLoginViewModel(this);
    }

    public View.OnClickListener loginClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // 尝试1:view --> model单向绑定测试,改变EditText的值,查看bean中的对应属性值是否发生变化。
//            Log.e("owen >>> ", userInfo.name.get() + "--" + userInfo.pwd.get());

            // 尝试2:model --> view单向绑定测试,Model层属性的变更,也会改变View层的显示
//            userInfo.name.set("Owen");
//            userInfo.pwd.set("0410");

            // 尝试3:模拟网络请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if ("Owen".equals(userInfo.name.get()) && "123".equals(userInfo.pwd.get())) {
                        Log.e("Owen >>> ", "登录成功!");
                    } else {
                        Log.e("Owen >>> ", "登录失败!");
                    }
                }
            }).start();
        }
    };
}

尝试代码依次开启,做测试
尝试1:在界面上输入用户名,密码,点击登录,查看log发现UserInfo中的两个属性值已经被改变。证明View --> Mode单向绑定是Ok的。
尝试2:程序运行后,直接点击登录,发现界面上的EditText上的值已经更改。证明Model --> View的单向绑定也是成功的。
尝试3:模拟网络登录。

这就是我们在业务中用到的MVVM,我们发现在Activity中什么事情都不用干,但是我们必须有一个VM来作为V和M的桥梁来沟通。我们现在不需要在Activity中做很多复杂的东西,这就是架构MVVM的思想。你学到了吗?


文中Demo下载地址

Android 架构设计模式系列文章索引

相关标签: Android——架构师