学习android的MVVM设计模式

来源:互联网 发布:湖南农大网络教学平台 编辑:程序博客网 时间:2024/05/22 09:05

最近更迷上了国外的技术网站,结合一些国内好的网站Get新技术。

MVVM(Model-View-ViewModel)是MVP的升级篇,厉害了我的哥。

“关键在于View和Model的双向绑定,当View有用户输入后,ViewModel通知Model更新数据,同理Model数据更新之后,ViewModel通知View更新”--摘自郭霖文章原话

我自己写些博客不是很好,只是借鉴别人的文章get新技术,然后记录下来。MVVM的知识来自http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236908&idx=1&sn=9e53f42e18a81795ef0cfe6fe3959ec2&scene=24&srcid=0910cK3vXJpNzY0CO28i1Qhs#wechat_redirect

国外原文地址:

https://developer.android.com/topic/libraries/data-binding/index.html

1、在build.gradle的android{}中配置

dataBinding{    enabled = true}
2、新建一个User类

/** * Created by GuoMeng on 2017/3/14. * Description */public class User {    private String name;    private String firstName;    private String lastName;    private boolean isStudent;    public User(String name, String firstName, String lastName, boolean isStudent) {        this.name = name;        this.firstName = firstName;        this.lastName = lastName;        this.isStudent = isStudent;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getFirstName() {        return firstName;    }    public void setFirstName(String firstName) {        this.firstName = firstName;    }    public String getLastName() {        return lastName;    }    public void setLastName(String lastName) {        this.lastName = lastName;    }    public boolean isStudent() {        return isStudent;    }    public void setStudent(boolean student) {        isStudent = student;    }}
3、在activity_main.xml文件改为

提示:注解很重要

<?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">    <!-- name属性表示变量的名称,type表示这个变量的类型,实例就是我们实体类的位置 -->    <!-- 写法1 -->    <!-- 声明了需要用到的user对象 -->    <data>        <import type="com.gm.mvvmdemo.User"/><!-- type指定路径 -->        <variable name="user" type="User" />    </data>    <!-- 写法1 存在同名UserEntity时,设置别名(注意alias要与type一致) -->      <!--  <data>            <import type="com.gm.mvvmdemo.User" alias="MUser"/>            <import type="com.gm.mvvmdemo.others.User" alias="OUser"/>            <variable name="user" type="MUser"/>            <variable name="oUser" type="OUser"/>        </data>-->    <!-- 写法2 -->    <!--<data>        <variable name="user" type="com.gm.mvvmdemo.User"/>    </data>-->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.firstName}"            android:textSize="20sp"/>        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.lastName}"            android:textSize="25sp"/>    </LinearLayout></layout>
4、默认情况下,在MainActivity中可以调用ActivityMainBinding

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);User user = new User("", "开发者", "程序员", true);binding.setUser(user);
运行app,数据就会显示在控件上。也可不用默认创建的ActivityMainBinding,那就在activity_main.xml的data 标签添加class属性,比如:

<data class="MainBinding">    ...</data>
5、比如在activity_main.xml中的两个控件用的都引用了同名的User类,那么可以设置别名,再引用

<!-- 写法1 存在同名UserEntity时,设置别名(注意alias要与type一致) -->    <data>        <import type="com.gm.mvvmdemo.User" alias="MUser"/>        <import type="com.gm.mvvmdemo.others.User" alias="OUser"/>        <variable name="user" type="MUser"/>        <variable name="oUser" type="OUser"/>    </data>
控件调用都一样使用name属性的命名取引用

6、如何查看ActivityMainBinding?需要反编译app,才可以看到ActivityMainBinding类以及默认对应创建的setUser方法

/** * ActivityMainBinding自动生成的,可以通过反编译查看setUser方法 * 下面的setUser方法: * public void setUser(User paramUser) { *  this.mUser = paramUser; *  try{ *      this.mDirtyFlags = (1L|this.mDirtyFlags); *      super.requestRebind(); *      return; *  } *  finally{} * } * */
7、消除空指针

* 消除空指针* 自动生成的DataBinding代码会检查null,避免出现NullPointException* 例如在表达式中 @{user.firstName}如果user == null 那么会为 user.firstName 设置默认值null* 而不会导致程序崩溃(基本类型将赋予默认值如 int为0,引用类型都会赋值null)*
8、比如设置控件的显示隐藏,需要导入

<import type="android.view.View"/>

<!-- 注意:只要是在Java中需要导入包的类,这边都需要导入,如:Map、ArrayList等,不过 java.lang 包里的类是可以不用导包的。 -->

一个xml中只能存在一个data,否则会报错

9、三元运算

<!-- 三元运算(需用"")--><!-- android:text="@{user.student ? "学生":"工人"}" --><!-- visibility需要导包,因此在data中添加android.view.View。这里还需注意只能存在一个data 否则报错--><TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{user.student ? user.firstName:user.lastName}"    android:textSize="25sp"/><TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{user.student ? user.firstName:user.lastName}"    android:visibility="@{user.student ? View.VISIBLE:View.GONE}"    android:background="#6ddc5a"// 这里使用背景颜色验证下可行性    android:textSize="25sp"/>
10、非

<!-- ?? 除了常用的操作法,另外还提供了一个 null 的合并运算符号 ??,这是一个三目运算符的简便写法 "user.firstName" ?? "user.lastName"  相当于 "user.firstName" != null ? "user.firstName":"user.lastName"android:text="@{user.name ?? user.firstName}"没有效果,不可行android:text="@{user.name !=null ? user.firstName:user.lastName}" --><TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{user.name !=null ? user.firstName:user.lastName}"    android:textSize="25sp"/>
11、

<!-- 所支持的操作符如下: 数学运算符 + - / * % 字符串拼接 + 逻辑运算 && || 二进制运算 & | ^ 一元运算符 + - ! ~ 位运算符 >> >>> << 比较运算符 == > < >= <= instanceof Grouping () 文字 - character, String, numeric, null 类型转换 cast 方法调用 methods call 字段使用 field access 数组使用 [] Arrary access 三元运算符 ? -->
12、这里加载图片的我设置失败,有待研究

13、点击事件

(1)在MainActivity类中声明onClick方法(类似平时实现监听接口)

public void onClick(View view) {    switch (view.getId()) {        case R.id.btn_one:            Log.d("execute", "按钮one的点击事件");            break;        case R.id.btn_two:            Log.d("execute", "按钮two的点击事件");            break;    }}
(2)在activity_main.xml的data中添加

<variable name="mainActivity" type="com.gm.mvvmdemo.MainActivity"/>
(3)绑定监听

<Button    android:id="@+id/btn_one"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="按钮1"    android:onClick="@{mainActivity.onClick}"/><Button    android:id="@+id/btn_two"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="按钮2"    android:onClick="@{mainActivity.onClick}"/>
(4)最后在MainActivity中调用方法

binding.setMainActivity(this);
14、外部类实现监听

(1)先新建一个MyHandler类

public class MyHandler {    public void onClick(View view) {        Log.d("execute", "外部的点击事件");    }}
(2)同样添加

<variable name="handle" type="com.gm.mvvmdemo.MyHandler"/>
(3)然后绑定

<Button    android:id="@+id/btn_three"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="按钮3"    android:onClick="@{handle.onClick}"/>
(4)最后调用

binding.setHandle(new MyHandler());

15、调用类成员变量

// public 或 public static可行,private修饰的不能直接在xml中调用,只能通过共有方法调用public String name = "Hello , Coder";

// 若是上面改为静态共有的可以直接

//android:text="@{mainActivity.name}"

共有方法

public String getTheName() {    String name = this.name;    return name;}

给控件设置内容

<TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{mainActivity.getTheName()}"/>

16、数据改变时更新UI,ActivityMainBinding、User改为全局的,再调用delay方法

private void delay() {    new Handler().postDelayed(new Runnable() {        @Override        public void run() {            user.setFirstName("haha");            binding.setUser(user);        }    },2000);}
17、继承BaseObservable更加方便,项目中创建了两个User类

目前的User类是另一个类,正好回顾下使用别名

(1)继承

public class User extends BaseObservable{
(2)刷新

public void setFirstName(String firstName) {    this.firstName = firstName;    notifyPropertyChanged(BR._all);}
public void setLastName(String lastName) {    this.lastName = lastName;    notifyPropertyChanged(BR._all);}
(3)

<import type="com.gm.mvvmdemo.others.User" alias="OUser"/>
<variable name="oUser" type="OUser"/>
<TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{oUser.firstName}"/>

(4)测试效果,这里我休眠3秒

final com.gm.mvvmdemo.others.User oUser = new com.gm.mvvmdemo.others.User("", "大哥", "小弟", true);binding.setOUser(oUser);new Thread(new Runnable() {    @Override    public void run() {        try {            Thread.sleep(3000);            oUser.setFirstName("太阳");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}).start();

18、更方便

* Data Binding的开发者贴心得为我们准备了一系列的 ObservableField,包括: ObservableBoolean,* ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat,* ObservableDouble 以及 ObservableParcelable 

(1)新建一个User2类

public class User2 {    public final ObservableField<String> firstName = new ObservableField<>();    public final ObservableField<String> lastName = new ObservableField<>();    public final ObservableInt age = new ObservableInt();    public final ObservableBoolean isStudent = new ObservableBoolean();}

(2)同样导包+引用

<import type="com.gm.mvvmdemo.User2"/>
<variable name="user2" type="User2"/>

<TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{user2.firstName+user2.lastName+String.valueOf(user2.age)}"/>
这里我自己尝试是否可以直接拼接在一起

(3)同样实现数据绑定

User2 user2 = new User2();binding.setUser2(user2);user2.firstName.set("郭");user2.lastName.set("孟");user2.age.set(24);user2.isStudent.set(true);
也可与17、(4)设置等待3秒后改变值

19、ObservableArrayMap和ObservableArrayList不能使用,我也不知到为什么

提示:xml文件也只能存在一个父布局

20、RecyclerView的使用

(1)添加RecyclerView控件

<android.support.v7.widget.RecyclerView    android:id="@+id/recycler_view"    android:layout_width="match_parent"    android:layout_height="match_parent"/>
(2)自定义adapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder>{    private List<User2> mData = new ArrayList<>();    public MyAdapter(List<User2> data) {        this.mData = data;    }    @Override    public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return MyHolder.create(                LayoutInflater.from(parent.getContext()), parent);    }    @Override    public void onBindViewHolder(MyHolder holder, int position) {        holder.bindTo(mData.get(position));    }    @Override    public int getItemCount() {        if (mData == null) {            return 0;        }        return  mData.size();    }    public static class MyHolder extends RecyclerView.ViewHolder{        private UserItemBinding mBinding;        static MyHolder create(LayoutInflater inflater, ViewGroup parent) {            UserItemBinding binding = UserItemBinding.inflate(inflater, parent, false);            return new MyHolder(binding);        }        private MyHolder(UserItemBinding binding) {            super(binding.getRoot());            this.mBinding = binding;        }        public void bindTo(User2 user) {            mBinding.setUser2(user);            mBinding.executePendingBindings();        }    }}
(3)setAdapter

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);LinearLayoutManager layoutManager = new LinearLayoutManager(        this, LinearLayoutManager.VERTICAL, false);recyclerView.setLayoutManager(layoutManager);recyclerView.setAdapter(new MyAdapter(data));
(4)user_item.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable name="user2" type="com.gm.mvvmdemo.User2"/>    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:padding="10dp"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user2.firstName}"/>        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="."/>        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user2.lastName}"/>        <View            android:layout_width="0dp"            android:layout_height="0dp"            android:layout_weight="1"/>        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text='@{user2.age+""}'/>    </LinearLayout></layout>
21、前面自己也有测试引用

比如设置textview时可以'@{user.age+""}'或者是"@{String.valueOf(user.age)}"  ,需要自己细心,前面我自己有测试了

0 0