MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开

MVVM的优点

可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。 在Android中,布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化

低耦合:以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成,甚至都不需要再Activity、Fragment去findViewById。这时候Activity、Fragment只需要做好的逻辑处理就可以了

MVVM

角色

View: 对应于Activity和XML,负责View的绘制以及与用户交互

Model: 实体模型

ViewModel: 负责完成View与Model间的交互,负责业务逻辑

数据驱动

在常规的开发模式中,数据变化需要更新UI的时候,需要先获取UI控件的引用,然后再更新UI。获取用户的输入和操作也需要通过UI控件的引用。在MVVM中,这些都是通过数据驱动来自动完成的,数据变化后会自动更新UI,UI的改变也能自动反馈到数据层,数据成为主导因素。这样MVVM层在业务逻辑处理中只要关心数据,不需要直接和UI打交道,在业务处理过程中简单方便很多

MVVM是一种架构模式,而DataBinding是一个实现数据和UI绑定的框架,是构建MVVM模式的一个工具

DataBinding支持的表达式

  • 数学表达式:+ - / * %
  • 字符串拼接:+
  • 逻辑表达式:&& ||
  • 位操作符:& | ^
  • 一元操作符:+ - ! ~
  • 位移操作符:>> >>> <<
  • 比较操作符:== > < >= <=
  • instanceof
  • 分组操作符:()
  • 字面量:character, String, numeric, null
  • 强转、方法调用
  • 数组访问:[]
  • 三元操作符:?:

    android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

    支持dimen,还支持colorstringdrawableanim
  • 聚合判断(Null Coalescing Operator)语法??

    android:text="@{user.userName ?? user.realName}"

    意思是如果userName为null,则显示realName
  • 字符拼接
  1. <TextView
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content"
  4. android:text="@{`username is :`+user.username}"/>

这里的字符拼接不是用单引号哦,用的是ESC按键下面那个按键按出来的。目前DataBinding中的字符拼接还不支持中文

官方手册:https://developer.android.com/tools/data-binding/guide.html

MVVM用法

模板写法

  1. <layout xmlns:android="http://schemas.android.com/apk/res/android" >
  2. <data>
  3. <!--此处定义该布局要用到的数据的名称及类型-->
  4. </data>
  5. <!--此处按照常规方式定义要使用的布局,其中可以使用binding表达式代表属性值,所谓binding表达式,指形如"@{user.firstName}"的表达式-->
  6. </layout>

自定义Binding类名(Custom Binding Class Names)

  • 以为根节点布局,android studio默认会自动产生一个Binding类。类名为根据布局名产生,如一个名为activity_simple的布局,它的Binding类为ActivitySimpleBinding,所在包为app_package/databinding
  • 也可以自定义Binding类的名称和包名
  1. <data class="CustomBinding"></data> 在app_package/databinding下生成CustomBinding
  2. <data class=".CustomBinding"></data> 在app_package下生成CustomBinding
  3. <data class="com.example.CustomBinding"></data> 明确指定包名和类名

Layout布局中Includes 标签使用

  1. <include layout="@layout/name"
  2. bind:user="@{user}"/>

BaseObservable的方式

使User继承BaseObservable,在get方法上加上注解@Bindable,会在BR(BR类自动生成的)生成该字段标识(int)

set方法里notifyPropertyChanged(BR.field);

  1. @Bindable
  2. public String getUserName() {
  3. return userName;
  4. }

理解实例

在项目gradle的android标签下中添加

  1. dataBinding{
  2. enabled true
  3. }

编写测试类

  1. public class User {
  2. private String name;
  3. private String password;
  4. public User(String name, String password) {
  5. this.name = name;
  6. this.password = password;
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public String getPassword() {
  15. return password;
  16. }
  17. public void setPassword(String password) {
  18. this.password = password;
  19. }
  20. }

编写布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  3. <data>
  4. <!--第一种引入方式-->
  5. <variable
  6. name="user"
  7. type="com.cj5785.mvvmdesign.User" />
  8. <!--第二种引入方式-->
  9. <!--<import type="com.cj5785.mvvmdesign.User" />-->
  10. <!--<variable-->
  11. <!--name="user"-->
  12. <!--type="User" />-->
  13. <!--如果有多个相同model,可以取别名-->
  14. <!--<import alias="typeUser" type="com.cj5785.mvvmdesign.User" />-->
  15. <!--<variable name="user" type="typeUser" />-->
  16. </data>
  17. <LinearLayout
  18. android:layout_width="match_parent"
  19. android:layout_height="match_parent"
  20. android:orientation="vertical">
  21. <TextView
  22. android:layout_width="match_parent"
  23. android:layout_height="wrap_content"
  24. android:gravity="center"
  25. android:textSize="18sp"
  26. android:text="@{user.name}" /><!--显示字段-->
  27. <TextView
  28. android:layout_width="match_parent"
  29. android:layout_height="wrap_content"
  30. android:gravity="center"
  31. android:textSize="18sp"
  32. android:text="@{user.password}" /><!--显示字段-->
  33. </LinearLayout>
  34. </layout>

在Activity中调用测试

  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. // 使用MVVM时候,这里不能使用setContentView
  6. // setContentView(R.layout.activity_main);
  7. //ActivityMainBinding在编译时自动生成
  8. ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
  9. User user = new User("jack","123456");
  10. binding.setUser(user);
  11. }
  12. }

这样就完成了MVVM模型最简单的运用

此时就显示出了设置的name和password

接下来测试数据变化更新UI

修改测试类

  1. public class User extends BaseObservable{
  2. private String name;
  3. private String password;
  4. public User(String name, String password) {
  5. this.name = name;
  6. this.password = password;
  7. }
  8. @Bindable
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. notifyPropertyChanged(BR.name);
  15. }
  16. @Bindable
  17. public String getPassword() {
  18. return password;
  19. }
  20. public void setPassword(String password) {
  21. this.password = password;
  22. notifyPropertyChanged(BR.password);
  23. }
  24. }

在Activity中使用延迟设置

  1. public class MainActivity extends AppCompatActivity {
  2. User user;
  3. Handler handler = new Handler();
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. // 使用MVVM时候,这里不能使用setContentView
  8. // setContentView(R.layout.activity_main);
  9. ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
  10. user = new User("jack", "123456");
  11. binding.setUser(user);
  12. handler.postDelayed(new Runnable() {
  13. @Override
  14. public void run() {
  15. user.setName("Bob");
  16. user.setPassword("654321");
  17. }
  18. }, 5000);
  19. }
  20. }

然后可以看到在经历5秒以后,文字被改变了

使用MVVM框架加载网络图片又怎么做呢

首先导入com.squareup.picasso:picasso依赖

在User中添加相应的图片获取方法

  1. public class User extends BaseObservable{
  2. private String name;
  3. private String password;
  4. private String header;
  5. public User(String name, String password,String header) {
  6. this.name = name;
  7. this.password = password;
  8. this.header = header;
  9. }
  10. @BindingAdapter("bind:header")
  11. public static void getImage(ImageView imageView,String url){
  12. Picasso.with(imageView.getContext()).load(url).into(imageView);
  13. }
  14. @Bindable
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. notifyPropertyChanged(BR.name);
  21. }
  22. @Bindable
  23. public String getPassword() {
  24. return password;
  25. }
  26. public void setPassword(String password) {
  27. this.password = password;
  28. notifyPropertyChanged(BR.password);
  29. }
  30. public String getHeader() {
  31. return header;
  32. }
  33. public void setHeader(String header) {
  34. this.header = header;
  35. }
  36. }

修改布局文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto">
  4. <data>
  5. <!--第一种引入方式-->
  6. <variable
  7. name="user"
  8. type="com.cj5785.mvvmdesign.User" />
  9. <!--第二种引入方式-->
  10. <!--<import type="com.cj5785.mvvmdesign.User" />-->
  11. <!--<variable-->
  12. <!--name="user"-->
  13. <!--type="User" />-->
  14. <!--如果有多个相同model,可以取别名-->
  15. <!--<import alias="typeUser" type="com.cj5785.mvvmdesign.User" />-->
  16. <!--<variable name="user" type="typeUser" />-->
  17. </data>
  18. <LinearLayout
  19. android:layout_width="match_parent"
  20. android:layout_height="match_parent"
  21. android:orientation="vertical">
  22. <ImageView
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. app:header="@{user.header}"/>
  26. <TextView
  27. android:layout_width="match_parent"
  28. android:layout_height="wrap_content"
  29. android:gravity="center"
  30. android:textSize="18sp"
  31. android:text="@{user.name}" /><!--显示字段-->
  32. <TextView
  33. android:layout_width="match_parent"
  34. android:layout_height="wrap_content"
  35. android:gravity="center"
  36. android:textSize="18sp"
  37. android:text="@{user.password}" /><!--显示字段-->
  38. </LinearLayout>
  39. </layout>

在Activity中直接调用

  1. public class MainActivity extends AppCompatActivity {
  2. User user;
  3. Handler handler = new Handler();
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. // 使用MVVM时候,这里不能使用setContentView
  8. // setContentView(R.layout.activity_main);
  9. ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
  10. user = new User("jack", "123456","http://i0.hdslb.com/bfs/archive/42172b20c899bcbbad0d46868af81b8081e999f1.jpg");
  11. binding.setUser(user);
  12. handler.postDelayed(new Runnable() {
  13. @Override
  14. public void run() {
  15. user.setName("Bob");
  16. user.setPassword("654321");
  17. }
  18. }, 5000);
  19. }
  20. }

接下来看看除了使用BaseObservable的另一种方式

  1. public class UserField {
  2. public ObservableField<String> name = new ObservableField<>();
  3. public ObservableField<String> password = new ObservableField<>();
  4. }

布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  3. <data>
  4. <variable
  5. name="field"
  6. type="com.cj5785.mvvmdesign.UserField"/>
  7. </data>
  8. <LinearLayout
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"
  11. android:orientation="vertical">
  12. <TextView
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:gravity="center"
  16. android:textSize="18sp"
  17. android:text="@{field.name}" /><!--显示字段-->
  18. <TextView
  19. android:layout_width="match_parent"
  20. android:layout_height="wrap_content"
  21. android:gravity="center"
  22. android:textSize="18sp"
  23. android:text="@{field.password}" /><!--显示字段-->
  24. </LinearLayout>
  25. </layout>

调用

  1. public class FieldTestActivity extends Activity {
  2. UserField userField = new UserField();
  3. @Override
  4. protected void onCreate(@Nullable Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
  7. binding.setField(userField);
  8. userField.name.set("Alen");
  9. userField.password.set("121212");
  10. }
  11. }

移动架构-MVVM框架的更多相关文章

  1. iOS应用千万级架构:MVVM框架

    业务模块内的MVC和MVVM架构 目前,唯品会中MVC和MVVM架构并存,后期会偏重于MVVM架构的使用. MVC架构 Model:程序中要操纵的实际对象的抽象,为Controller提供经过抽象的业 ...

  2. js架构设计模式——前端MVVM框架设计及实现(二)

    前端MVVM框架设计及实现(二) 在前端MVVM框架设计及实现(一)中有一个博友提出一个看法: “html中使用mvvm徒增开发成本” 我想这位朋友要表达的意思应该是HTML定义了大量的语法标记,HT ...

  3. js架构设计模式——前端MVVM框架设计及实现(一)

    前端MVVM框架设计及实现(一) 最近抽出点时间想弄个dom模块化的模板引擎,不过现在这种都是MVVM自带的,索性就想自己造轮子写一个简单的MVVM框架了 借鉴的自然还是从正美的avalon开始了,我 ...

  4. “老坛泡新菜”:SOD MVVM框架,让WinForms焕发新春

    火热的MVVM框架 最近几年最热门的技术之一就是前端技术了,各种前端框架,前端标准和前端设计风格层出不穷,而在众多前端框架中具有MVC,MVVM功能的框架成为耀眼新星,比如GitHub关注度很高的Vu ...

  5. 迷你MVVM框架 avalonjs 入门教程

    新官网 请不要无视这里,这里都是链接,可以点的 OniUI组件库 学习教程 视频教程: 地址1 地址2 关于AvalonJs 开始的例子 扫描 视图模型 数据模型 绑定 作用域绑定(ms-contro ...

  6. 迷你MVVM框架 avalonjs1.5 入门教程

    avalon经过几年以后,已成为国内一个举足轻重的框架.它提供了多种不同的版本,满足不同人群的需要.比如avalon.js支持IE6等老旧浏览器,让许多靠政府项目或对兼容性要求够高的公司也能享受MVV ...

  7. JavaScript富应用MVC MVVM框架

    对框架的挑选 Ember.js.Backbone.js.Knockout.js.Spine.js.Batman.js , Angular.js 1. 轻量级的应用选择哪一个会比较好?2. 那一个比较简 ...

  8. vue.js学习笔记(一):什么是mvvm框架,vue.js的核心思想

    一:MVVM框架 MVVM框架的应用场景:  1.针对具有复杂交互逻辑的前端应用 2.提供基础的架构抽象 3.提供ajax数据持久化,保证前端用户体验 二:vue.js的核心思想 (一):数据驱动 ( ...

  9. MVVM框架avalon在兼容旧式IE

    迷你MVVM框架avalon在兼容旧式IE做的努力 当前标签: avalon 共3页: 1 2 3 下一页  迷你MVVM框架avalon在兼容旧式IE做的努力 司徒正美 2014-03-13 11: ...

随机推荐

  1. NO.24两两交换链表中的节点

    NO.24两两交换链表中的节点 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例:给定 1->2->3-&g ...

  2. 内存原理与PHP的执行过程

    一.内存结构 栈区:保存的是变量名(术语:引用),对于cpu来说,读写速度很快 堆区:存储“复杂”的数据,数组.对象.字符串(字符串比较特殊)等 数据段:又分为数据段全局区(用于存储简单的数据,如数字 ...

  3. Mysql远程无法连接

    #登陆mysql $ mysql -uroot -p mysql> use mysql; mysql> update user set host = '%' where user = 'r ...

  4. [bzoj 1861][zjoi2006] 书架

    传送门 Description 1. Top S--表示把编号为S的书放在最上面. 2. Bottom S--表示把编号为S的书放在最下面. 3. Insert S T--T∈{-1,0,1},若编号 ...

  5. Java 集合介绍,常用集合类

    Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念: (1)Collection.一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素,而Set不能有重 ...

  6. Java+Bigdata学习路线

    Java+Bigdata学习路线 2019-05-28 07:04:33 @Auther:MrZhangxd STAGE 第一阶段:JAVA基础 |-第一阶段:JAVA基础 | |-可掌握的核心能力 ...

  7. hive tez调优(3)

    根据.方案最右侧一栏是一个8G VM的分配方案,方案预留1-2G的内存给操作系统,分配4G给Yarn/MapReduce,当然也包括了HIVE,剩余的2-3G是在需要使用HBase时预留给HBase的 ...

  8. 什么是vue的全家桶

    vue.js有著名的全家桶系列,包含了vue-router,vuex, vue-resource,再加上构建工具vue-cli,就是一个完整的vue项目的核心构成.

  9. JAVA基础知识|Serializable

    一.序列化和反序列化 序列化:把对象转换为字节序列的过程称为对象的序列化. 反序列化:把字节序列恢复为对象的过程称为对象的反序列化. 将内存中对象的信息保存下来,可以有很多种方式实现这一功能.其中ja ...

  10. 简单动态字符串-redis设计与实现

    简单动态字符串 Sds (Simple Dynamic String,简单动态字符串)是 Redis 底层所使用的字符串表示, 几乎所有的 Redis 模块中都用了 sds. 本章将对 sds 的实现 ...