Android入门第59天-进入MVVM

Posted TGITCIC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android入门第59天-进入MVVM相关的知识,希望对你有一定的参考价值。

什么是MVVM

用“某大文豪亲”的话说:MVVM并不存在,只是xml里找控件找了太多了,自然而然就“找”出了一套共性

所以,MVVM只是包括了以下这些技术:

  1. DataBind;

  1. ViewModel双向绑定;

  1. Okhttp3+retrofit+rxjava(时下最流行,我们后续教程会让学这个东西变得简单到极致);

  1. 其它非android Studio内提供的一些控件、工具类的使用;

以上这一套又有人称为:Google Android Jetpack

为什么传统的开发不用要去用MVVM?

首先让我们来看一下,传统的android开发我们会经常干一些什么样的事呢?

先定义一个layout布局,然后对控件设定android:id,然后在Activity的java端去做findViewById(R.id.myButton)这样找到这个控件,再然后。。。再然后。。。

再来看我们使用DataAdapter(MVC)的设计模式,动不动就是一堆的ViewHolder,getView还要避免二次重复加载......

这一切其实在Android的底层是根据一个xml格式的layout加载然后使用xml path路径查询去定位和绑定到我们的控件的。各位有Java Xml编程经验的都知道,xml的dom查找费时又费力,有时xml的层次嵌套一多还会出现性能问题

因此呢,google就想在编译时就把这个xml一次转成一个Java的Object然后使用Java的内部支持的LinkList、ArrayList、HashMap等数据结构去实现这些定位,不是更优雅、更便捷、更“程序员”么?于是就产生了Data Binding。

那么有了Data Binding后,我们还要考虑诸如此类场景:譬如说我在界面有一个EditText,往里面录入了值,然后在点击一个【提交】按钮时,要把这个刚录进去的文字内容给取出来?那么换了平时我们需要先定位到这个控件->再把这个控件当前的内容取出来->赋给一个局部变量。有时如果碰到横坚屏切换,如果我们要保持输入的EditText内的内容保持不被clean掉还需要做二次赋值、二次查找控件,是不是?

那么现在假设我们可以让控件和这个临时局部变量和这个EditText的onChanged事件对开发时来说变得“透明”,每次set/get就可以了,让底层框架去实现这些控件定位、查找、二次赋值等工作,于是就有了ViewModel形式。

所以MVVM(Model View ViewModel)就这么来了!

当然,有了上述这些我们还要考虑如:Fragment里怎么MVVM、Activity里怎么MVVM、ImageView怎么MVVM、最常用的HttpJson Request怎么MVVM?

于是为了隐藏、掩盖极大便利开发者,把一些对“底层”的协议部分的转换、重复的劳动去除掉,这就有了什么Glide、Okhttp3+retrofix+rxjava这些东西了。

把这些东西打包在一起就成了google jetpack。

因此,一切都是源于“减少重复、无意义的劳动”而诞生的。大家可以认为MVVM就是J2ee->struts->this is not j2ee(spring)->spring+hibernate->jfx->spring+mybatis->spring boot->spring boot2这么一个演变过程中的产物而己。它并不是什么新的理念,也没有什么高的技术含量,它只是“因为开发的人多了、平时碰到的一些重复性劳动成为了共通的痛点后人们进行了总结、抽象”后的一个产物而己。

Android的学习路径

在进入正文前,我们的Android到今天为此算是一个里程碑,因为如果你不学后面的40多天,你其实已经自己可以开发点东西了,自己照着微博留言做一个小论坛、小商城,前面我们学习到的这些知识足够用了。但是,如果你想去正规化团队、好点的团队、公司工作肯定不能这么“作坊”,那么后面我们就会集中火力讲在jetpack即MVVM模式下的各种开发了。

所以,有必要先列一下什么称为学好Android需要经历哪些核心知识点。你也可以跳过这一节直接进入后续篇章,我是按照一个小白以及教学上从易入难、一个程序员是怎么培养的(从感性到理性)的一惯手法列出了下面共计:16大类、63个技术点,这也是我们这个系列遵照的一条写作线索。

当一个人把这些内容全学完了,Android才可以算是入门了(对不起,我从不说高手一类,因为高手是指哪些发明了Android的人,因此我到现在为止还一直称自己是合格的程序员,高级也不算。只有发明开发了那些mysql、linux、java语言的人才称得上真正的高级程序员)说白了就是可以找个好点的厂子了

Android学习路线一览

一、开始阶段

  1. 新建工程

  1. 环境相关知识

二、基本布局

  1. LinearLayout

  1. TableLayout

  1. RelativeLayout

  1. 几个基本布局的混用

三、基本组件与Android开发基础知识

  1. TextView

  1. EditText

  • 监听回车

  • 光标移动和选择

  1. Button

  1. ImageView

  1. ProgressBar

  1. RadioButton

  1. CheckBox

  1. SwitchButton

  1. SeekBar

  1. StatusBar

  1. WebView

  1. RecycleViews

  1. DrawerLaout

  1. 自定义View

  1. 模块化基本知识

  1. Gradle

  1. NDK

  1. 调试机巧

  1. 一些常用第三方库

  1. 多线程

  1. IO

  1. Network相关基本知识

四、Activity相关

  1. Android Activity是干什么的

  1. Activity生命周期

  1. Activity的启动以及携带参数启动

五、Service

  1. Service是干什么的

  1. 后台Service

  1. 前台Service

  1. IntentService用法

六、BroadCast

  1. 广播机制BroadCast的介绍

  1. 监听屏幕亮灭

七、Fragment

  1. Fragment基本概念

  1. Fragment的使用

八、res的应用

  1. 资源目录的使用

  1. Shape的绘制

  1. Android组件的一些自定义图片、背景

九、Android权限

十、字体

  1. Text Style

  1. Android的字库

十一、数据库

十二、设计模式

  1. MVC

  1. MVVM

十三、Android JetPack全家桶

  1. 介绍

  1. Fragment间共享数据

  1. DataBind

  • 使用

  • 数据绑定

  • 对于ObserableFiled以及可观察对象的使用

  1. Live Data

  • 数据准备

  • Layout

  • Activity设计

  1. LifeCycle

  • 活动

  • 状态转换

  1. WorkManager

  • 入门

  • Work Manager工作约束、延迟与查询

  • Work Manager定时任务

  1. View Model

  • 概览

  • Activity使用View Model

十四、编译打包CICD以及监控相关

十五、动画基础

十六、与设备相关开发

  1. 像机

  1. 蓝牙

  1. Wifi

  1. 获取手机的角度、姿势

  1. AIDL

  1. ZIP

  1. Binder

  1. Notification

  1. RTFSC

  1. PackManager

MVVM-之从DataBind入门

我们假设我们有一个Login界面,里面就3个内容,如下图中所示。

  • user.name

  • user.password

  • user.header

然后点击一下【CLICK]按钮,把user.name和user.password里的内容变换成我们预设的另一个人的信息。

从传统的开发来说我们需要有两个TextView+一个ImageView,是不是?

现在我们用MVVM的DataBind是怎么实现的呢?

DataBind第一步-工程需要变成可以DataBind

在build.gradle里加入如下内容,一定不要忘了,记得它是加在android块内的

    dataBinding 
        enabled = true
    

DataBind第二步-build.properties内要启用google jetpack

在gradle.properties里加入以下两句话

android.useAndroidX=true
android.enableJetifier=true

还没完,接着把这个工程->Migrate to AndroidX,切记,否则后面你在学ViewModel时会碰到一堆很“怪”的问题,其实归根到底是因为我们前面一直用的都是android.v4.support的package,而MVVM里用到的都是androidx包内的内容,就算具体的类名相同其属性和相关的成员函数还是有不少区别的。从现在开始我们就要开始习惯于androidx了,如下截图操作:

选中你的工程->File->Refactor->Migrate to AndroidX。在Migrate时android studio会提示你备份好原有的项目,你可以选“Y”也可以选“Ignore”,看个人喜好吧。

DataBinding第三步-什么都正常的情况下出现了一堆红色的problem的处理手段

这一步后会提示项目重启,反正在新建项目时把这一步纳入第三步,这样一次干净的重启后顺达便项目进行了重编译、顺达便也就把一堆的gradle需要的依赖包给下下来了。

DataBinding第四步-Layout或者是activity的布局文件里要使用<layout>标签

如果我们已经有一个layout布局了,我们需要把我们的最最外层的xml的根结点,用layout替换成如下样例中的内容:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >

改完后的一个标准的xml的layout内容如下所示:

<?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"
    >
    <!-- 设置数据源 data中可以添加多个数据源 -->
    <data>
        <variable
            name="user"
            type="com.mkyuan.android.demo.simplemvvm.User" />
    </data>
    <!-- 我们自己的布局 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!--
         数据设置采用上面设置的name.属性的方式
         这里是 user.name
         如果想拼接字符串使用的是键盘左上角数字1旁边的那个符号
         "@`拼接字符串`+user.name"
         -->
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@`姓名:`+user.name" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@`密码:`+user.password" />
        <ImageView
            android:adjustViewBounds="true"
            android:layout_width="150dp"
            android:layout_height="200dp"
            app:headId="@user.header" />

        <Button
            android:id="@+id/buttonChangeUserInfor"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:text="click" />

    </LinearLayout>
</layout>

DataBinding第五步-定义一个extends BaseObservable的Java POJO

拿我们的例子,这个界面有3个属性,一个是user.name,一个是user.password,一个是user.header。

package com.mkyuan.android.demo.simplemvvm;
/**
 * User 实体类
 * mvvm 绑定的步骤如下:
 * <p>
 * 1.User类继承被观察者的一个类 BaseObservable androidx.databinding包下面的一个类(我是使用的androidx)
 * 2.get方法添加  @Bindable 注解,这是一个运行时注解。
 * 3.在需要同步属性的set方法中设置对应的值 如setName中:notifyPropertyChanged(BR.name); 其中 BR是编译时生成的一个类,没有的话可以rebuild一下
 * 这样一个基本的数据绑定就结束了
 */

import android.widget.ImageView;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;

import com.bumptech.glide.Glide;

import java.time.Instant;

public class User extends BaseObservable 
    public User(String name, String password,int header) 
        this.name = name;
        this.password = password;
        this.header=header;
    

    @Bindable
    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
        notifyPropertyChanged(BR.name);
    

    @Bindable
    public String getPassword() 
        return password;
    

    public void setPassword(String password) 
        this.password = password;
        notifyPropertyChanged(BR.password);
    

    private int header;
    @Bindable
    public int getHeader() 
        return header;
    

    public void setHeader(int header) 
        this.header = header;
        notifyPropertyChanged(BR.header);
    
    //自定义属性  headUrl 是自定义的,在xml的imageView中引用
    @BindingAdapter("headId")
    public static void getImage(ImageView view, int headerId) 
        Glide.with(view.getContext()).load(headerId).into(view);
    

    private String name = "";
    private String password = "";

此处我们使用了Glid,这是一个优化过的并且简化了的把远程/本地图片塞到ImageView的组件。要使用它你需要在gradle中加入以下的依赖

    implementation 'com.github.bumptech.glide:glide:4.9.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

核心代码解读

  1. @Bindable对象,它必须定义在getXXX方法上,它的作用等同于findViewById、find到后取控件、得到控件后取控件的相关属性、把控件相关属性里的value赋到一个临时变量这么一个作用;

  1. notifyPropertyChanged(),它就相关于findViewById、找到控件后获取用户刚输入的那个内容、监听这个控件的onChanged事件,并且把这个刚输入的内容再替换之前get出来的内容赋给到的临时变量里的值的作用;

  1. @BindingAdapter("headId"),介个就是对ImageView的databinding的用法了。它的作用就是绑定你的activity_main.xml文件里的ImageView对象,并使用Glide组件优雅的把一个本地(也可以是远程)Image(的ID)喂入ImageView里并显示成图片;

DataBinding正式使用

没有第六步了,第六步就是使用了,我们来看我们的Activity的交互端代码MainActivity.java

package com.mkyuan.android.demo.simplemvvm;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;
import android.view.View;


import com.mkyuan.android.demo.simplemvvm.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity 
    private static final String TAG = "DemoSimpleMVVM";
    private ActivityMainBinding activityMainBinding;

    public void click(View view) 
        int imageId=R.drawable.qq;
        activityMainBinding.getUser().setName("李四");
        activityMainBinding.getUser().setPassword("password123");
        activityMainBinding.getUser().setHeader(imageId);
    

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        // 通过 DataBindingUtil 的 setContentView 方法替代 activity 的 setContentView 方法,返回
        // ActivityMainBinding
        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        int imageId=R.drawable.header;
        User user = new User("张三", "123456",imageId);
        // 通过 ActivityMainBinding 将数据源和view绑定
        activityMainBinding.setUser(user);
    

看这个代码,从头到位有没有findViewById?

另外代码中我们可以看到有一个叫ActivityMainBinding的类,这个是哪来的?

我们看我们的项目中是不是一个activity就有一个叫activity_名字.xml的文件。那么我们在第四步中,在activity_main.xml文件里不是把最顶层的标签加了<Layout>以及相应的data的绑定了是不是?于是,AndroidStudio就自动会把这个activity_main.xml做以下操作:

  1. 去掉_xml;

  1. 把.xml前的用java驼峰命名方式连接_(下划线)前后的单词把这些单词大写+Binding并生成这个类(如果有一些开发者在AndroidStudio里没有生成这个类,那么你可以:Build->Rebuild Project一下)

然后你自己运行一下试试看这个效果,一个click按钮点下去,看似你在替换的是User这个类里的值实际Databind里把你的主界面里的user.name、user.password、user.header相binding的控件的值也给改变了。

是不是?嘿嘿!很好看吧?不妨自己动一下手试试看吧。

附、项目工程全结构

风雨虽大、但有你我携手“共撑一把伞”才能安然渡过。分享知识、便利你我、创造共赢!

以上是关于Android入门第59天-进入MVVM的主要内容,如果未能解决你的问题,请参考以下文章

Android入门第60天-MVVM中的Databinding与ListView结合使用

Android入门第60天-MVVM中的Databinding与ListView结合使用

Android入门第62天-Glide显示网络图片高版本的使用

Android入门第64天-MVVM下瀑布流界面的完美实现-使用RecyclerView

Android入门第65天-mvvm模式下的retrofit2+okhttp3+rxjava

Android入门第55天-在Android里使用OKHttp组件访问网络资源