android MVVM应用

 

  1. 什么是MVVM

Model–View–ViewModel软件架构模式。
MVVM将图形用户界面的开发与业务逻辑或后端逻辑的开发分离。

 

M(Model)层:模型,定义数据结构。(如Activity、Fragment)

C(Controller)层:实现业务逻辑。在MVVM模式中一般把C层算在M层中。

ViewModel层:ViewModel层的职责是用来处理中转逻辑,将真正获取数据的操作交给Model去执行,然后将获取到的数据更改到自身的LiveData中去。LiveData中的数据改变后,会去通知监听它改变的、当前状态是可见的View们去更改UI。

V(View)层:将ViewModel通过特定的GUI展示出来,并在GUI控件上绑定视图交互事件。UI响应式更新。

注:View层不做任何业务逻辑、不涉及操作数据、不处理数据,UI和数据严格的分开

 

MVVM利用数据绑定、依赖属性、命令、路由事件等新特性,打造了一个更加灵活高效的架构。MVVM是一种架构模式,DataBinding是一个实现数据和UI绑定的框架,是构建MVVM模式的一个工具,两者不能混为一谈。

数据驱动

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

低耦合度

MVVM模式中,数据是独立于UI的。

数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。UI想怎么处理数据都由UI自己决定,ViewModel不涉及任何和UI相关的事,也不持有UI控件的引用。即便是控件改变了(比如:TextView换成EditText),ViewModel也几乎不需要更改任何代码,以此解耦了View层和ViewModel。

更新UI

在MVVM中,数据发生变化后,我们在工作线程直接修改ViewModel的数据即可,不需要考虑切到主线程更新UI,框架已经帮助我们做了。

 

 

2、MVVM基类(以Activity为例)

 

(1)页面常用接口类ViewBehavior

public interface ViewBehavior {

/** 是否显示Loading视图*/
void showLoadingUI(String msg);
/**消失Loading视图*/
void dismissLoadingUI();
/**弹出Toast提示*/
void showToast(String msg);
/**页面跳转*/
void openActivity(Intent intent);
void openActivityForResult(Intent intent, int requestCode);
/**设置回调*/
 void vmSetResult(int resultCode, Intent data) ;
/**返回键点击*/
void backPressed();
/**关闭页面*/
void finishPage();

}

 

(2)ViewModel感知生命周期接口

 

public interface ViewModelLifecycle extends LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    void onAny(LifecycleOwner owner, Lifecycle.Event event);
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreate();
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart();
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume();
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause();
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop();
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy();
}

 

 

 

(3)基于MVVM模式的Activity的基类

public abstract class BaseBVMActivity<B extends ViewDataBinding, VM extends BaseViewModel> extends BaseBindingActivity<B> implements ViewBehavior {

protected VM mViewModel;

@Override
protected void init(Bundle savedInstanceState) {
    initialize(savedInstanceState);
    injectViewModel();
    viewModelInitialized(savedInstanceState);
    initInternalObserver();
}
/**此方法可直接使用viewModel*/
protected void viewModelInitialized(Bundle savedInstanceState) {
}
protected void injectViewModel() {
    VM vm = createViewModel();
//通过ViewModelProvider.Factory创建ViewModel,这样实例化ViewModel的时候, 就可以构造函数传参
mViewModel = (VM) new ViewModelProvider(this, BaseViewModel.createViewModelFactory(vm)).get(vm.getClass());
mViewModel.mContext = getApplication();
getLifecycle().addObserver(mViewModel);
mBinding.setVariable(getVariableId(), mViewModel);
}
protected void initInternalObserver() {
    mViewModel.loadingEvent.observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            showLoadingUI(s);
        }
    });
ViewBehavior接口事件处理

}

}
@Override
protected void onDestroy() {
    super.onDestroy();
    getLifecycle().removeObserver(mViewModel);
}
/**初始化操作*/
protected abstract void initialize(Bundle savedInstanceState);
**创建ViewModel实例*/
protected abstract VM createViewModel();
/**初始化ViewModelid*/
public abstract int getVariableId();

}

 

 

(4)ViewModel基类

 

/**
ViewModel只做和业务逻辑和业务数据相关的事,不做任何和UI相关的事情,ViewModel 层不会持有任何控件的引用,更不会在ViewModel中通过UI控件的引用去做更新UI的事情。
 */
public class BaseViewModel extends ViewModel implements ViewModelLifecycle,
ViewBehavior {

// loading视图显示Event
    protected MutableLiveData<String> loadingEvent = new MutableLiveData<>();

// 弹出Toast提示Event
    protected MutableLiveData<String> showToastEvent = new MutableLiveData<>();

// 生命周期感知
    protected LifecycleOwner mLifecycleOwner;
    protected Application mContext;

    @Override
    public void showLoadingUI(String msg) {
        loadingEvent.postValue(msg);
    }
    @Override
    public void showToast(String msg) {
        showToastEvent.postValue(msg);
    }

    @Override
    public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
        this.mLifecycleOwner = owner;
    }

    @Override
    public void onCreate() {
    }
    onResume、onPause、onStart、onStop...
    @Override
    public void onDestroy() {
    }

    public static ViewModelProvider.Factory createViewModelFactory(BaseViewModel baseViewModel) {
        return new ViewModelFactory(baseViewModel);
    }


    private static class ViewModelFactory implements ViewModelProvider.Factory {
        protected BaseViewModel viewModel;
        public ViewModelFactory(BaseViewModel viewModel) {
            this.viewModel = viewModel;
        }
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            return (T) viewModel;
        }
    }
}

 

 

(5)BindingAdapter

3、M—V—VM之间交互

 

 

· Model与ViewModel之间的双向关系
Model持有ViewModel对象,使用ViewModel里的公有方法及成员变量。
ViewModel可以获取到Model生命周期,MutableLiveData数据监控,从而影响到Model

 

Activity创建对应ViewModel对象,并在基类里将其与xml布局进行关联。

上层创建

@Override
protected AnchorPersonViewModel createViewModel() {
    return new AnchorPersonViewModel();
}

基类关联

protected void injectViewModel() {
    VM vm = createViewModel();
    //通过ViewModelProvider.Factory创建ViewModel,这样实例化ViewModel的时候,就可以构造函数传参
    mViewModel = (VM) new ViewModelProvider(this, BaseViewModel.createViewModelFactory(vm)).get(vm.getClass());
    mViewModel.mContext = getApplication();
    getLifecycle().addObserver(mViewModel);
    mBinding.setVariable(getVariableId(), mViewModel);
}

 

· ViewModel与View之间的双向关系
ViewModel中的数据改变,可以同时改变View上的显示内容。
View上的内容改变(比如输入框中的内容),也可以同时改变ViewModel中对应的数据。即双向绑定。

双向绑定标识符:@={xxx}

示例:

<EditText
    android:layout_width="match_parent"
    android:layout_height="50dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/tv_view"
    android:id="@+id/et_view"
    android:text="@={viewModel.text}"
    android:textSize="22sp"
    />

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/et_view"
    android:id="@+id/tv_view2"
    android:text="@{viewModel.text}"
    android:textSize="22sp"
    />

此时EditText进行了双向绑定,TextView单向绑定。在EditText中编辑文本TextView会同步更改。

4、MutableLiveData简介

  1. API说明

postValue()的特性如下:

1.此方法可以在其他线程中调用

2.如果在主线程执行发布的任务之前多次调用此方法,则仅将分配最后一个值。

3.如果同时调用 .postValue(“a”)和.setValue(“b”),一定是值b被值a覆盖。

setValue():此方法只能在主线程里调用

getValue():返回当前值。

observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)

设置此LiveData数据当前activity或者Fragment的观察者,会给此activity或者Fragment在前台时回调数据。

removeObserver(@NonNull final Observer<? super T> observer) 移除指定的观察者

  1. 使用

MutableLiveData在ViewModel中进行定义,数据填充。在Activity中进行数据监听。在xml中通过获取到的MutableLiveData数据对View进行改变。这里MutableLiveData里存储的可以是网络接口、数据库等任意渠道获取的数据

 

 

 

public final MutableLiveData<Boolean> viewShow = new MutableLiveData<>(true);

public View.OnClickListener viewShowClick = v -> viewShow.setValue(!viewShow.getValue());

 

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="绑定了MutableLiveData组件数据"
    android:id="@+id/tv_view"
    android:visibility="@{viewModel.viewShow?View.VISIBLE:View.GONE}"
    android:textSize="22sp"
    android:textColor="#382176"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/tv_show_view"
    />

 

@Override
protected void initInternalObserver() {
    super.initInternalObserver();
    //MutableLiveData组件数据监听
    mainViewModel.viewShow.observe(this, new Observer<Boolean>() {
        @Override
        public void onChanged(Boolean aBoolean) {
            Log.i(TAG, "onChanged: " + aBoolean);
            showToast("view show change:" + aBoolean);
        }
    });
}

 

 

 

5、使用

(1)dataBinding库引入:

android {

    ......

buildFeatures {
      dataBinding = true
    }

}

  1. xml中引用ViewModel

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="viewModel"
            type="com.sf.mvvm_demo.viewmodel.MainViewModel" />
        <import type="android.view.View" />
    </data>
    <TextView/>......
</layout>

  1. 绑定点击事件,并通过MutableLiveData组件改变View

public final MutableLiveData<Boolean> viewShow = new MutableLiveData<>(true);
public View.OnClickListener viewShowClick = v -> viewShow.setValue(!viewShow.getValue());

 

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="绑定了viewShowClick点击事件"
    android:onClick="@{viewModel.viewShowClick}"
    android:id="@+id/tv_show_view"
    android:textSize="22sp"
    android:textColor="@color/black"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="绑定了MutableLiveData组件数据"
    android:id="@+id/tv_view"
    android:visibility="@{viewModel.viewShow?View.VISIBLE:View.GONE}"
    android:textSize="22sp"
    android:textColor="#382176"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/tv_show_view"
    />

  1. RecyclerView示例

RecyclerView需要绑定adapter(适配器)、itemBinding(布局)、items(数据)

<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="400dp"
    app:layout_constraintTop_toBottomOf="@id/tv_view2"
    app:layout_constraintStart_toStartOf="parent"
    android:id="@+id/rv_mvvm_demo"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    app:adapter="@{viewModel.mvvmDemoAdapter}"
    app:itemBinding="@{viewModel.itemBinding}"
    app:items="@{viewModel.listMutableLiveData}"
    />

绑定adapter:

通过app:adapter属性绑定adapter。这里使用的是BindingRecyclerViewAdapter。adapter不负责填充数据。

在onBindBinding方法里完成列表条目的业务逻辑,很多简单的列表可以不需要adapter也可以完成数据展示。

public class MvvmDemoAdapter extends BindingRecyclerViewAdapter<MvvmDemoBean> {

    @Override
    public void onBindBinding(@NonNull ViewDataBinding binding, int variableId, int layoutRes, int position, MvvmDemoBean item) {
        super.onBindBinding(binding, variableId, layoutRes, position, item);
        View rootView = binding.getRoot();
        rootView.findViewById(R.id.tv_name).setBackgroundColor(Color.RED);
    }

}

 

app:adapter="@{viewModel.mvvmDemoAdapter}"

绑定列表数据

通过app:items属性绑定ViewModel中的集合数据,此时RecyclerView的数据展示随着listMutableLiveData 变化而变化,只需要保证listMutableLiveData 数据正常即可。

public final MutableLiveData<List<MvvmDemoBean>> listMutableLiveData = new MutableLiveData<>();

@Override
public void onCreate() {
    super.onCreate();
        listMutableLiveData.setValue(getListData());
}

public View.OnClickListener addListDataClick = v -> addListData(listMutableLiveData);

public void addListData(MutableLiveData<List<MvvmDemoBean>> list) {
    List<MvvmDemoBean> mvvmDemoBeanList = new ArrayList<>(list.getValue());
    for (int i = 0; i < 2; i++) {
        MvvmDemoBean mvvmDemoBean = new MvvmDemoBean();
        mvvmDemoBean.setName("李四" + i);
        mvvmDemoBean.setCountry("伊拉克" + i);
        mvvmDemoBeanList.add(mvvmDemoBean);
    }
    list.setValue(mvvmDemoBeanList);
}

 

 

 

app:items="@{viewModel.listMutableLiveData}"

绑定xml布局

通过app:itemBinding属性绑定列表条目xml布局。bindExtra是Object类型,可以通过bindExtra添加点击事件、图形变换(如圆角)等任意事件。

public final ItemBinding<Object> itemBinding = ItemBinding.of(BR.item, R.layout.mvvm_item)
//            .bindExtra(BR.transform, ImageTransform.ROUNDED_CORNERS_4DP)
            //注意顺序,需要先定义点击事件,然后绑定
            .bindExtra(BR.onItemClick,onItemClick)
            .bindExtra(BR.onItemNameClick,onItemNameClick)
            .bindExtra(BR.onItemCountryClick,onItemCountryClick);

 

app:itemBinding="@{viewModel.itemBinding}"

开发中遇到列表中需要不同布局的情况时:

public final OnItemBind<MvvmDemoBean> itemBindingType = new OnItemBind<MvvmDemoBean>() {
    @Override
    public void onItemBind(@NonNull ItemBinding itemBinding, int position, MvvmDemoBean item) {
        switch (item.getType()) {
            case 0:
                itemBinding.set(BR.item, R.layout.mvvm_item);
                break;
            case 1:
                itemBinding.set(BR.item, R.layout.mvvm_item2);
                break;
        }
        itemBinding
                .bindExtra(BR.onItemClick,onItemClick)
                .bindExtra(BR.onItemNameClick,onItemNameClick)
                .bindExtra(BR.onItemCountryClick,onItemCountryClick);
    }
};

 

 

  1. MVVM下的Activity→Fragment、Fragment→Fragment数据通讯

在MVVM框架下,Fragment是可以获取到Activity的ViewModel的,

传统数据通讯Intent、单例、静态变量:操作繁琐、数据大小有限制、容易内存泄漏

ViewModel数据通讯:

 横竖屏切换数据不会销毁;

    直接通过ViewModelProvider获取共同的数据对象,无需主动进行数据传递;

数据生命周期由ViewModel内部掌控,无需手动管理销毁;

 

@Override
protected void initInternalObserver() {
    super.initInternalObserver();
    MainViewModel mainViewModel = new     ViewModelProvider(getActivity()).get(MainViewModel.class);
    mainViewModel.shareData.observe(this, new Observer<Boolean>() {
        @Override
        public void onChanged(Boolean aBoolean) {
            mBinding.tvData.setText("Fragment1监听到数据变化" + aBoolean);
        }
    });
}

 

MainActivityViewModel中的数据对象

public final MutableLiveData<Boolean> shareData = new MutableLiveData<>(false);
public View.OnClickListener shareDataClick = v -> shareData.setValue(!shareData.getValue());

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值