Android架构设计模式(三)

前言

在Android日常开发中或者在面试过程中总会涉及到“设计模式”这个词。听起来很厉害。实际上在开发中很常见又很难用准确的言语表达出来。随口说出的设计模式有:单例模式,中介者模式,观察者模式等等这些都属于java设计模式,这将会以单独的系列篇在以后的文章中总结。此设计模式系列仅含有应用架构设计模式,这里我就MVC,MVP,MVVM这3个最常见的架构设计模式来总结。

MVVM设计模式

上两篇中总结了MVC、MVP架构,本篇来学习MVVM设计模式,MVVM是Model-View-ViewModel的简写。MVVM主要由以下3个部分组成。

MVVM定义

  1. Model:数据实体模型
  2. View:对应于Activity、xml代表的视图,负责View的绘制以及与用户交互,这里不涉及业务逻辑,不处理数据,UI和数据必须分开
  3. ViewModel:负责完成View与Model间的交互,负责主要的业务逻辑,这里不会持有任何控件的引用,避免造成内存泄露。

MVVM与MVP的区别

MVVM与MVP非常相似,唯一区别就是View和Model进行双向绑定,两者之间有一方发生变化则会反应到另一方面上。而MVP与MVVM的主要区别是,MVP中的View更新需要通过Presenter,而MVVM则不需要,因为View与Model进行了双向绑定,数据的修改会直接反应到View角色上,而View的修改也会导致数据的变更。此时,ViewModel角色需呀做的只是业务逻辑的处理,以及修改View或者Model的状态。MVVM模式优点像ListView与Adapter、数据集的关系,这个Adapter就是ViewModel角色,它与View进行绑定,由于数据集进行绑定,当数据集合发生变化时,调用Adapter的notifyDataSetChanged之后View就直接更新,他们之间没有直接的耦合,使得ListView变得更为灵活。

MVVM的实现实例

Android中常使用databinding来实现MVVM框架。场景:显示一个用户信息的列表到Activity上,在单项中如果用户的年龄小于23,年龄背景就显示为蓝色,否则年龄背景就显示为红色。点击列表中的单项来增加对应用户的年龄。效果图如下。

首先要在build.gradle中添加此段代码,这样我们就可以使用databinding了。

User.java

public class User extends BaseObservable {
    private String name;
    private int age;
    private String sex;
    private int icon;

    public User() {
    }

    public User(String name, int age, String sex, int icon) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.icon = icon;
    }

    @BindingAdapter({"icon"})
    public static void setIcon(ImageView iv, int icon) {
        iv.setBackgroundResource(icon);
    }

    public void onItemClick(View view) {
        setAge(age+1);//点击一次年龄加1
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

RootAdapter.java

public class RootAdapter<T> extends BaseAdapter {
    private LayoutInflater inflater;
    private int layoutId;
    private int variableId;
    private List<T> list;

    public RootAdapter(Context context, int layoutId, List<T> list, int resId) {
        this.layoutId = layoutId;
        this.list = list;
        this.variableId = resId;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewDataBinding dataBinding;
        if (convertView == null) {
            dataBinding = DataBindingUtil.inflate(inflater, layoutId, parent, false);
        }else{
            dataBinding = DataBindingUtil.getBinding(convertView);
        }
        dataBinding.setVariable(variableId, list.get(position));
        return dataBinding.getRoot();
    }

}

在RootAdapter这个类中就可以看出MVVM的强大之处,这个adapter在没有多余逻辑下基本可以抽成模板来使用,不涉及数据显示相关代码。
MainActivity.java

public class MainActivity extends AppCompatActivity {
    private List<User> users;
    private RootAdapter<User> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView lv = findViewById(R.id.lv);
        users = new ArrayList<>();
        adapter = new RootAdapter<>(MainActivity.this, R.layout.item_user, users, BR.user);
        lv.setAdapter(adapter);

        initData();
    }

    private void initData() {
        users.add(new User("王也",22,"男",R.mipmap.wy));
        users.add(new User("冯宝宝",103,"女",R.mipmap.fbb));
        users.add(new User("诸葛青",22,"男",R.mipmap.zgq));
        users.add(new User("张灵玉",24,"男",R.mipmap.zly));
        users.add(new User("夏禾",24,"女",R.mipmap.xh));
        users.add(new User("吕良",17,"男",R.mipmap.ll));
        adapter.notifyDataSetChanged();
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

item_user.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">

    <data>

        <variable
            name="user"
            type="com.fpt.mvvm.User"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:onClick="@{user.onItemClick}"
        android:clickable="true">

        <ImageView
            android:id="@+id/iv"
            android:layout_width="60dp"
            android:layout_height="60dp"
            app:icon="@{user.icon}"
            android:layout_marginLeft="10dp"/>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp">

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user.name}"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user.sex}"
                android:layout_toRightOf="@+id/tv_name"
                android:layout_marginLeft="10dp"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@{user.age &lt; 23 ? 0xFF0000FF:0xFFFF0000}"
                android:text="@{String.valueOf(user.age)}"
                android:layout_alignParentBottom="true"/>
        </RelativeLayout>

    </LinearLayout>
</layout>

这个item_user.xml中涉及databinding中的重点,有图片显示以及文本显示,点击事件绑定,基本运算和字符串拼接都在此处以及User.java中有体现。具体不在此处细谈。

MVVM总结

MVVM可以算是MVP的升级版,其中的VM是ViewModel的缩写,ViewModel可以理解成是View的数据模型和Presenter的合体,ViewModel和View之间的交互通过Data Binding完成,而Data Binding可以实现双向的交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力。


关注我的技术公众号,我会不定期推送关于安卓的文章,内容包含Android日常开发遇到中的问题、常用框架的介绍以及需要熟记的概念等等。
微信扫一扫下方的二维码即可关注:

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页