如何使用Databinding实现MVVM架构
如果想要使用Databinding来实现MVVM的架构,这篇文章将会教会你实现的具体流程:
一、首先需要在app.build里面的android层级下添加dataBinding配置
android {dataBinding {enabled = true}
}
二、在xml中添加databinding的代码,与viewModel进行绑定关联
<?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"xmlns:tools="http://schemas.android.com/tools"><data><variablename="anchorBindingViewModel"type="com.cdtye.emergencyDirection.viewmodel.MaoDuanEnrollmentViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"android:orientation="vertical"tools:context=".view.activity.BdLabelBindingActivity"><ScrollViewandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:fillViewport="true"android:scrollbars="none"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:background="@color/white"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:paddingTop="10dp"android:paddingBottom="10dp"android:paddingLeft="10dp"android:background="@color/colorBackground"android:textColor="@color/success_stroke_color"android:textSize="13sp"android:text="基本信息"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="锚段编号"android:textColor="@color/black"android:textSize="@dimen/sp_14" /><EditTextandroid:id="@+id/et_anchor_no"android:layout_width="0dp"android:layout_weight="1"android:layout_height="@dimen/ll_height1"android:background="@null"android:gravity="right"android:hint="@string/please_set"android:inputType="text"android:textColor="@color/text_color2"android:textColorHint="@color/text_color3"android:textSize="@dimen/text_small_20" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="invisible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="锚段长度"android:textColor="@color/black"android:textSize="@dimen/sp_14" /><EditTextandroid:id="@+id/et_anchor_length"android:layout_width="0dp"android:layout_weight="1"android:layout_height="@dimen/ll_height1"android:background="@null"android:gravity="right"android:hint="@string/please_set"android:inputType="numberDecimal"android:textColor="@color/text_color2"android:textColorHint="@color/text_color3"android:textSize="@dimen/text_small_20" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="invisible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="30dp"android:layout_marginLeft="20dp"android:layout_marginRight="20dp"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="4dp"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="@color/black"android:textSize="@dimen/sp_14"android:text="线路"/><TextViewandroid:id="@+id/tv_line"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"android:gravity="center|right"android:hint="请选择"android:text="@{anchorBindingViewModel.lineName}"android:textColorHint="@color/text_color3"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="4dp"android:layout_gravity="center"android:visibility="visible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="部门"android:textColor="@color/black"android:textSize="@dimen/sp_14" /><TextViewandroid:id="@+id/tv_bm"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_weight="1"android:textColorHint="@color/text_color3"android:gravity="right"android:hint="请选择"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="visible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="@color/black"android:textSize="@dimen/sp_14"android:text="开始区间站场"/><TextViewandroid:id="@+id/tv_start_district_station"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"android:gravity="center|right"android:hint="请选择"android:text="@{anchorBindingViewModel.startStationBean.siteName}"android:textColorHint="@color/text_color3"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="visible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="@color/black"android:textSize="@dimen/sp_14"android:text="结束区间站场"/><TextViewandroid:id="@+id/tv_end_district_station"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"android:gravity="center|right"android:hint="请选择"android:text="@{anchorBindingViewModel.endStationBean.siteName}"android:textColorHint="@color/text_color3"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="visible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="@color/black"android:textSize="@dimen/sp_14"android:text="行别"/><TextViewandroid:id="@+id/tv_xingb"android:layout_width="0dp"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"android:gravity="center|right"android:text="@{anchorBindingViewModel.directionName}"android:hint="请选择"android:textColorHint="@color/text_color3"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="开始公里标"android:textColor="@color/black"android:textSize="@dimen/sp_14" /><EditTextandroid:id="@+id/tv_start_km_mark"android:layout_width="0dp"android:background="@color/transparent"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_weight="1"android:hint="K0000+000"android:textColorHint="@color/gray1"android:gravity="right"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="invisible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="结束公里标"android:textColor="@color/black"android:textSize="@dimen/sp_14" /><EditTextandroid:id="@+id/tv_end_km_mark"android:layout_width="0dp"android:layout_height="wrap_content"android:background="@color/transparent"android:layout_gravity="center"android:layout_weight="1"android:hint="K0000+000"android:textColorHint="@color/gray1"android:gravity="right"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="invisible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="支柱坐标"android:textColor="@color/black"android:textSize="@dimen/sp_14" /><TextViewandroid:id="@+id/tv_gps"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_weight="1"android:text="请进行定位"android:gravity="right"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="visible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:background="@color/line"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ll_height1"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:layout_marginTop="@dimen/width_2"android:layout_marginBottom="@dimen/width_1"android:visibility="gone"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="@color/black"android:textSize="@dimen/sp_14"android:text="设备编码"/><TextViewandroid:id="@+id/tv_devnum"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_weight="1"android:gravity="right"android:textColor="@color/text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/width_2"android:layout_gravity="center"android:visibility="invisible"android:src="@mipmap/down"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="@dimen/width_line"android:layout_marginLeft="@dimen/width_4"android:layout_marginRight="@dimen/width_4"android:visibility="gone"android:background="@color/line"/></LinearLayout></ScrollView><LinearLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:layout_margin="20dp"android:orientation="horizontal"><Buttonandroid:id="@+id/btn_save_and_continue"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:visibility="gone"android:background="@drawable/blue_stroke_button_background"android:gravity="center"android:text="保存继续" /><Buttonandroid:id="@+id/btn_save"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:background="@drawable/blue_stroke_button_background"android:gravity="center"android:text="保存" /><Buttonandroid:id="@+id/btn_close"android:layout_width="0dp"android:layout_height="match_parent"android:layout_marginLeft="10dp"android:layout_weight="1"android:gravity="center"android:background="@drawable/blue_button_background"android:textColor="@color/white"android:textSize="15sp"android:text="关闭" /></LinearLayout></LinearLayout>
</layout>
接下来就需要搭建ViewModel
public class MaoDuanEnrollmentViewModel extends DeviceEnrollmentViewModel {public static final String TAG = MaoDuanEnrollmentViewModel.class.getName();public LiveDataEvent<String> lineName = new LiveDataEvent<>();/*** 站点*/public LiveDataEvent<List<AnchorSiteBean>> queryStartSeclineSiteListEvent = new LiveDataEvent<>();public LiveDataEvent<List<AnchorSiteBean>> queryEndSeclineSiteListEvent = new LiveDataEvent<>();public LiveDataEvent<AnchorSiteBean> startStationBean = new LiveDataEvent<>();public LiveDataEvent<AnchorSiteBean> endStationBean = new LiveDataEvent<>();public LiveDataEvent<List<DirectionBean>> queryDirectionListEvent = new LiveDataEvent<>();/*** 保存成功*/public LiveDataEvent<JCWAnchorBean> saveSuccessEvent = new LiveDataEvent<>();/*** 行别名称*/public LiveDataEvent<String> directionName = new LiveDataEvent<>();//这里是一些网络请求的方法public void getJcwDirectionList(){}public void saveAnchorData(JCWAnchorBean jcwAnchorBean) {addSubscribe(useCase.uploadJcwAnchorInfo(jcwAnchorBean).subscribe(list -> {LogUtils.d(TAG, "saveSuccess");//保存成功,将保存的数据给到saveSuccessEventsaveSuccessEvent.call(jcwAnchorBean);//伪代码,模拟获取到的数据实时更新UIviewModel.directionName.setValue(direction.getDirectionName());}, throwable -> {LogUtils.e(TAG, throwable);}));}
}
addSubscribe是使用Rxjava进行集中的请求处理
protected void addSubscribe(Disposable disposable) {if (compositeDisposable == null) {compositeDisposable = new CompositeDisposable();}compositeDisposable.add(disposable);}
LiveDataEvent是一个封装好的MutableLiveData,可以监听任意类型数据
public class LiveDataEvent<T> extends MutableLiveData<T> {private static final String TAG = LiveDataEvent.class.getName();private long lastEventSendTime = 0;private AtomicBoolean atomicBoolean = new AtomicBoolean(false);@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {if (hasActiveObservers()) {Log.e(TAG, "只能有一个事件 notified");}super.observe(owner, t -> {if (atomicBoolean.compareAndSet(true, false)) {observer.onChanged(t);}});}@MainThreadprivate void setDataValue(@Nullable T t) {atomicBoolean.set(true);super.setValue(t);}@MainThreadpublic void call(T t) {setDataValue(t);}/*** 触发事件** @param t 泛型,需要发送的数据* @param limitEventSend 是否限制事件的发出,比如规定1秒内不能发出两个事件,防止多次点击按钮*/@MainThreadpublic void call(T t, boolean limitEventSend) {if (limitEventSend) {//必须超过规定的时间才能发出事件if (!isFastClick()) {setDataValue(t);}} else {setDataValue(t);}}/*** 事件需要规定的事件才能发出,比如防止点击过快** @return true-小于规定时间*/public boolean isFastClick() {boolean flag = true;long currentClickTime = System.currentTimeMillis();if ((currentClickTime - lastEventSendTime) >= Config.INTERVAL_CLICK_TIME) {flag = false;}lastEventSendTime = currentClickTime;return flag;}
}
三、在baseActivity中要做的事情:
public abstract class BaseActivity<T extends ViewDataBinding, E extends BaseViewModel> extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);int layoutRes = getLayout();if (layoutRes > 0) {//如果不使用MVVM设计模式,则实现IBase接口的useMvvm()方法,并且返回falseif (useMvvm()) {binding = DataBindingUtil.setContentView(this, layoutRes);initViewModel();} else {setContentView(layoutRes);}}}protected void initViewModel() {int variableId = getVariableId();viewModel = bindViewModel();if (viewModel == null) {return;}//订阅事件observerEvent();viewModel.attachView(this, this);//xml 关联ViewModelbinding.setVariable(variableId, viewModel);//支持LiveData绑定xml,数据动态刷新binding.setLifecycleOwner(this);//让ViewModel拥有View的生命周期感应getLifecycle().addObserver(viewModel);}protected abstract int getLayout();/*** 获取ViewModel id*/@Overridepublic int getVariableId() {return -1;}/*** Binding ViewModel instance to ViewDataBinding*/@Overridepublic E bindViewModel() {return null;}@Overridepublic void observerEvent() {viewModel.backEvent.observe(this, s -> goBack());}@Overridepublic void goBack() {finish();}
}
四、具体xml对应的activity
public class MaoDuanEnrollmentActivity extends DeviceEnrollmentActivity<ActivityMaoduanEnrollmentBindingBinding, MaoDuanEnrollmentViewModel>{private static final String TAG = MaoDuanEnrollmentActivity.class.getName();private JCWAnchorBean jcwKeyDevBeanAfterSuccess;@Overridepublic int getVariableId() {return BR.anchorBindingViewModel;}@Overrideprotected int getLayout() {return R.layout.activity_maoduan_enrollment_binding;}@Overridepublic MaoDuanEnrollmentViewModel bindViewModel() {return new MaoDuanEnrollmentViewModel();}@Overridepublic void observerEvent() {super.observerEvent();viewModel.saveSuccessEvent.observe(this, jcwAnchorBean -> {jcwKeyDevBeanAfterSuccess = jcwAnchorBean;if (binding.btnSaveAndContinue.getVisibility() != View.VISIBLE) {binding.btnSaveAndContinue.setVisibility(View.VISIBLE);binding.btnSave.setVisibility(View.GONE);}});}
}
后续事件的监听只需要放在obserEvent方法中即可
优点与缺点:
MVVM相比于MVP的优势:相对于MVP更一步的解耦了,用户只需要进行UI的修改而无需关心数据的来源,ViewModel中主要获取数据并给到Activity,Activity根据Data进行相应的业务操作,减轻了Activity的负重。
缺点:ViewModel中比较臃肿,数据请求,数据解析都集中在这里边,且每一个需要获取数据展示的视图都需要一个LiveDataEvent进行监听,若xml中需要实时更新数据的UI较多,ViewModel的责任比较大。