在Android日常开发中或者在面试过程中总会涉及到“设计模式”这个词。听起来很厉害。实际上在开发中很常见又很难用准确的言语表达出来。随口说出的设计模式有:单例模式,中介者模式,观察者模式等等这些都属于java设计模式,这将会以单独的系列篇在以后的文章中总结。此设计模式系列仅含有应用架构设计模式,这里我就MVC,MVP,MVVM这3个最常见的架构设计模式来总结。
上两篇中总结了MVC、MVP架构,本篇来学习MVVM设计模式,MVVM是Model-View-ViewModel的简写。MVVM主要由以下3个部分组成。
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变得更为灵活。
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 extends BaseAdapter {
private LayoutInflater inflater;
private int layoutId;
private int variableId;
private List list;
public RootAdapter(Context context, int layoutId, List 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 users;
private RootAdapter 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
<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
<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 < 23 ? 0xFF0000FF:0xFFFF0000}"
android:text="@{String.valueOf(user.age)}"
android:layout_alignParentBottom="true"/>
RelativeLayout>
LinearLayout>
layout>
这个item_user.xml中涉及databinding中的重点,有图片显示以及文本显示,点击事件绑定,基本运算和字符串拼接都在此处以及User.java中有体现。具体不在此处细谈。
MVVM可以算是MVP的升级版,其中的VM是ViewModel的缩写,ViewModel可以理解成是View的数据模型和Presenter的合体,ViewModel和View之间的交互通过Data Binding完成,而Data Binding可以实现双向的交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力。
关注我的技术公众号,我会不定期推送关于安卓的文章,内容包含Android日常开发遇到中的问题、常用框架的介绍以及需要熟记的概念等等。
微信扫一扫下方的二维码即可关注: