Android MVVM-编程思想2(入门实战MVVM,DataBinding,ViewModel,LiveData)

前言

通过一个小案例,帮助大家了解MVVM。最终实现一个MVVM通用框架。代码:github。(如有错误之处,请在评论区指出,谢谢。如果感觉写的不错,请点赞,关注,谢谢。)

上一个小节,只是理论介绍了MVVM,DataBinding,ViewModel,LiveData,这一小节,讲解一下他们的使用方式。如果已经对这些基础知识很了解,可以直接看下一节。

目录:

Android MVVM-编程思想1(入门介绍MVVM,DataBinding,ViewModel,LiveData)
Android MVVM-编程思想2(入门实战MVVM,DataBinding,ViewModel,LiveData)
Android MVVM-编程思想3(封装基类BaseMvvmActivity,BaseMvvmFragment)

DataBinding 使用

首先开启app模块的gradle配置文件android 节点下开启

 dataBinding {
        enabled true
    }
(1)最简单的使用JDK定义好的类型
xml直接使用
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="title"
            type="String" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:gravity="center"
            android:text="@={title}"
            android:textColor="#ffffff"
            android:textSize="18sp" />
    </LinearLayout>
</layout>

UserActivity 中使用,DataBindingUtil.setContentView(this, R.layout.activity_user),AndroidStudio 构建之后会自动根据XML中的配置生成绑定关系。并返回管理类ActivityUserBinding

  ActivityUserBinding binding = DataBindingUtil.setContentView(this, 	R.layout.activity_user);
  binding.setTitle("DataBinding绑定演示");
(2)如何使用自定义的类?

定义一个类User
普通的类,跟JDK内部的类一样,只是残疾版的绑定,什么意思? Model中的数据可以显示到View上。Model 的数据变动不会实时变化到View上。如何实现数据驱动,如何实现双向绑定?,继续看,后面会讲

User类
public class User {
    private String name;

    private String sex;
    private int age;

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

    public String getName() {
        return name;
    }

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

    @Bindable
    public String getSex() {
        return sex;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
XML中使用
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="title"
            type="String" />

        <variable
            name="user"
            type="chongchong.wei.mvvm_aac.base.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:gravity="center"
            android:text="@={title}"
            android:textColor="#ffffff"
            android:textSize="18sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:gravity="center"
            android:text='@{"名称:"+user.name}'
            android:textSize="18sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:gravity="center"
            android:text="@{user.name}"
            android:textSize="18sp" />

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={user.name}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:gravity="center"
            android:text='@{""+user.age}'
            android:textSize="18sp" />

        <Button
            android:id="@+id/mButton"
            android:layout_width="100dp"
            android:layout_height="40dp"
            android:text="模拟请求网络" />
    </LinearLayout>
</layout>

UserActivity类
public class UserActivity extends AppCompatActivity {
    ActivityUserBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
        binding.setTitle("DataBinding绑定演示");
        binding.setUser(new User("小明", "男", 16));

        binding.mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                requestHttp();
            }
        });
    }

    /**
     * 模拟请求网络刷新数据
     */
    private void requestHttp() {
        binding.getUser().setName("改变Name");//User没有继承BaseObservable ,View不会变化
    }
}

(3)实现User数据变化,View自动更改,加上注解@Bindable,set方法手动更新notifyPropertyChanged

实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservable、ObservableField、ObservableCollection

BaseObservable 提供了 notifyChange() 和 notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BR 的 flag,该 BR 的生成通过注释 @Bindable 生成,可以通过 BR notify 特定属性关联的视图
下面学习一下第一种方式:

User类
public class User extends BaseObservable {
    @Bindable
    private String name;
    @Bindable
    private String sex;
    @Bindable
    private int age;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(chongchong.wei.mvvm_aac.BR.name);
    }


    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
        notifyPropertyChanged(chongchong.wei.mvvm_aac.BR.sex);
    }

    public int getAge() {
        return age;
    }

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

双向数据绑定如何实现?

双向绑定的意思即为当数据改变时同时使视图刷新,而视图改变时也可以同时改变数据
需求:界面上有两个控件,EditText 用于获取用户输入,TextView 用于把用户输入展示出来,绑定变量的方式比单向绑定多了一个等号:@={user.name}

 <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={user.name}" />

这里只是简单介绍DataBinding 的作用和使用方式。还有很多其他场景的使用方式,大家去官网自己查看吧。本系列的目的是搭建MVVM框架,所以就不在这上面讲解过多了。

ViewModel使用

ViewModel 其实就是MVVM中的VM 层 ,一般和LiveData 一起使用,根据官方介绍,VeiwModel是具有生命周期感知的。验证一下
UserVM

public class UserVM extends ViewModel {
    @Override
    protected void onCleared() {
        super.onCleared();
        Log.i("weicc_mvvm_", "onCleared");
    }
}
UserActivity中使用
public class UserActivity extends AppCompatActivity {
    ActivityUserBinding binding;
    UserVM userVM;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
        binding.setTitle("DataBinding绑定演示");
        userVM = ViewModelProviders.of(this).get(UserVM.class);
        binding.setUserVM(userVM);
        Log.i("weicc_mvvm_", "onCreate_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i("weicc_mvvm_", "onStart_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("weicc_mvvm_", "onResume_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i("weicc_mvvm_", "onPause_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("weicc_mvvm_", "onStop_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("weicc_mvvm_", "onDestroy_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.i("weicc_mvvm_", "onConfigurationChanged_hashcode:" + ViewModelProviders.of(this).get(UserVM.class).hashCode());
    }
}

启动运行,然后旋转屏幕–》点击back销毁。可以看出旋转屏幕不会重建ViewModel。
打印:

2019-12-20 15:59:43.502 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onCreate_hashcode:86643892
2019-12-20 15:59:43.503 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onStart_hashcode:86643892
2019-12-20 15:59:43.506 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onResume_hashcode:86643892
2019-12-20 15:59:49.704 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onConfigurationChanged_hashcode:86643892
2019-12-20 15:59:49.707 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onPause_hashcode:86643892
2019-12-20 15:59:49.718 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onStop_hashcode:86643892
2019-12-20 15:59:49.719 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onDestroy_hashcode:86643892
2019-12-20 15:59:49.764 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onCreate_hashcode:86643892
2019-12-20 15:59:49.766 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onStart_hashcode:86643892
2019-12-20 15:59:49.771 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onResume_hashcode:86643892
2019-12-20 15:59:53.086 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onPause_hashcode:86643892
2019-12-20 15:59:53.299 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onStop_hashcode:86643892
2019-12-20 15:59:53.302 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onCleared
2019-12-20 15:59:53.302 11594-11594/chongchong.wei.mvvm_aac I/weicc_mvvm_: onDestroy_hashcode:231874846
LiveData 使用
UserActivity代码
public class UserActivity extends AppCompatActivity {
    ActivityUserBinding binding;

    MutableLiveData<User> mutableLiveData;
    User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("weicc_mvvm_", "onCreate");
        setContentView(R.layout.activity_user);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
        binding.setTitle("DataBinding绑定演示");
        user = new User();
        user.setName("小明");
        user.setAge(12);
        user.setSex("男");
        mutableLiveData = new MutableLiveData();
        mutableLiveData.setValue(user);
        mutableLiveData.observe(this, new Observer<User>() {
            @Override
            public void onChanged(User user) {
                binding.setUser(user);//user没有继承BaseObservable 需要手动刷新数据到界面
                Log.i("weicc_mvvm_", "监听user改变:" + user.toString());
            }
        });
        binding.mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                requestHttp();
            }
        });
    }

    /**
     * 模拟请求网络刷新数据
     */
    private void requestHttp() {
        Log.i("weicc_mvvm_", "点击button的时候改变name");
        user.setName("改变name");
        mutableLiveData.postValue(user);

    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("weicc_mvvm_", "onStop的时候改变name");
        user.setName("改变name");
        mutableLiveData.postValue(user);
    }
}

日志分析:LiveData onStop的数据改变不会推送,所以并不会监听到,正如LiveData官方说明的一样,是具有生命周期感知的观察者。
优势:1,当Activity不在屏幕上时(不可见),LiveData不会出发没必要的界面更新;2,当Activity被销毁时,LiveData将自动清空与Observer的连接

2019-12-20 16:46:42.312 22352-22352/chongchong.wei.mvvm_aac I/weicc_mvvm_: onCreate
2019-12-20 16:46:42.407 22352-22352/chongchong.wei.mvvm_aac I/weicc_mvvm_: 监听user改变:User{name='小明', sex='男', age=12}
2019-12-20 16:46:59.912 22352-22352/chongchong.wei.mvvm_aac I/weicc_mvvm_: 点击button的时候改变name
2019-12-20 16:46:59.919 22352-22352/chongchong.wei.mvvm_aac I/weicc_mvvm_: 监听user改变:User{name='改变name', sex='男', age=12}
2019-12-20 16:47:02.736 22352-22352/chongchong.wei.mvvm_aac I/weicc_mvvm_: onStop的时候改变name
如何把ViewModel,LiveData,DataBinding 的优势结合起来?

聪明的观察者应该已经发现,由于要使用ViewModel,LiveData。所以不能使用DataBinding双向绑定的优势。而LiveData每次都要去写监听,使用起来好像很麻烦。google怎么会忍受,所以google给出了LifecycleOwner组件。
核心代码

 binding.setLifecycleOwner(this);//这样就不用每次都写监听
 binding.setUserVM(userVM);
//        userVM.mUser.observe(this, new Observer<User>() {
//            @Override
//            public void onChanged(User user) {
//                binding.setUser(user);//user没有继承BaseObservable 需要手动刷新数据到界面
//                Log.i("weicc_mvvm_", "监听user改变:" + userVM.toString());
//            }
//        });
User类
public class User {
    private String name;
    private String sex;
    private int age;

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

    public String getName() {
        return name;
    }

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


    public String getSex() {
        return sex;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

UserVM类
public class UserVM extends ViewModel {
    public final MutableLiveData<User> mUser = new MutableLiveData<>();

    @Override
    protected void onCleared() {
        super.onCleared();
        Log.i("weicc_mvvm_", "onCleared");
    }

    public void getUser() {
        //todo http 获取数据
        User user = new User("小明", "男", 12);
        mUser.setValue(user);
    }
}
UserActivity类
public class UserActivity extends AppCompatActivity {
    private ActivityUserBinding binding;
    private UserVM userVM;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
        binding.setTitle("DataBinding绑定演示");
        userVM = ViewModelProviders.of(this).get(UserVM.class); //结合ViewModel
        binding.setLifecycleOwner(this);
        binding.setUserVM(userVM);
        binding.mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                userVM.getUser();
            }
        });
    }
}

XML 文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="title"
            type="String" />

        <variable
            name="userVM"
            type="chongchong.wei.mvvm_aac.user.UserVM" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:gravity="center"
            android:text="@={title}"
            android:textColor="#ffffff"
            android:textSize="18sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:gravity="center"
            android:text='@{"名称:"+userVM.mUser.name}'
            android:textSize="18sp" />


        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={userVM.mUser.name}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:gravity="center"
            android:text='@{""+userVM.mUser.age}'
            android:textSize="18sp" />

        <Button
            android:id="@+id/mButton"
            android:layout_width="100dp"
            android:layout_height="40dp"
            android:text="模拟请求网络" />
    </LinearLayout>
</layout>

总结一下,先通过单个的使用介绍,到最后把DataBinding,ViewModel,LiveData整合起来。基本把MVVM的入门版本完成了。

为什么有些人不推崇DataBinding?

因为如果在XML中过多使用DataBinding的表达式,会降低View的复用性,增加View与Model的耦合度,有些违背,高内聚低耦合的原则,所以大家要根据业务场景,灵活使用。这也是一些人放弃使用DataBinding的原因之一吧。个人觉得手动更新也不错。如有其它不同见解,请在评论区支出谢谢!

下一小节,开始真正的封装之旅。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值