MVX Android设计架构浅析-MVVM
来源:互联网 发布:矩阵秩为1和迹的关系 编辑:程序博客网 时间:2024/06/05 15:22
简介
读过MVX Android设计架构浅析的童鞋应该还记得2005年微软工程师John Gossman在自己的博客上首次公布了MVVM模式。时隔10年之久才在Android活跃起来,究其原因是之前Android并不支持Data-binding,所以在了解MVVM之前很有必要对Data-binding有个充分的认识。当然这里不是重点,所以不再深究。
那么MVVM和前篇博客中介绍的MVX Android设计架构浅析-MVP有啥区别呢?下面用两张简单的交互图解释一下。
如果上面两张图看的不太明白,可以复习一下MVX Android设计架构浅析-MVP其中的代码部分介绍的很清楚。
简单的说一下上面MVVM的交互图
可以看到对view中数据的所有绑定和更新操作都是通过Data Binding框架实现的。通过ObservableField类,View在model发生变化时会作出反应,在XML文件中对属性的引用使得框架在用户操作View时可以将变化推送给对应的ViewModel。我们也可以通过代码订阅属性的变化,这样可以实现例如当CheckBox被点击后,TextView被禁用这样的功能。像这样使用标准Java类来表示View的视觉状态的一个很大优势是明显的:你可以很容易对这种视觉行为进行单元测试。
- 现在MVVM风头正劲,不过还没有十分红火,原因是Data-binding还没有完美实现,Android仅支持单向数据绑定也就是从Module到View,而另外一个方向从View到Module需要手动去实现。
简单交互图
我们将上面的图示经过精简一下MVVM的交互就简单表示如下
代码
Talk is cheap,show me the code.(废话少说,直接上代码)
MVVM – VIEW – XML
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="data" type="com.nilzor.presenterexample.MainModel"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivityFragment"> <TextView android:text="@{data.numberOfUsersLoggedIn}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:id="@+id/loggedInUserCount"/> <TextView android:text="# logged in users:" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="false" android:layout_toLeftOf="@+id/loggedInUserCount"/> <RadioGroup android:layout_marginTop="40dp" android:id="@+id/existingOrNewUser" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:orientation="horizontal"> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Returning user" android:checked="@{data.isExistingUserChecked}" android:id="@+id/returningUserRb"/> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="New user" android:id="@+id/newUserRb" /> </RadioGroup> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/username_block" android:layout_below="@+id/existingOrNewUser"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Username:" android:id="@+id/textView" android:minWidth="100dp"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/username" android:minWidth="200dp"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentStart="false" android:id="@+id/password_block" android:layout_below="@+id/username_block"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Password:" android:minWidth="100dp"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textPassword" android:ems="10" android:id="@+id/password"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/password_block" android:id="@+id/email_block" android:visibility="@{data.emailBlockVisibility}"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Email:" android:minWidth="100dp"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textEmailAddress" android:ems="10" android:id="@+id/email"/> </LinearLayout> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{data.loginOrCreateButtonText}" android:id="@+id/loginOrCreateButton" android:layout_below="@+id/email_block" android:layout_centerHorizontal="true"/> </RelativeLayout></layout>
MVVM – VIEW – JAVA
package com.nilzor.presenterexample;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.CompoundButton;import android.widget.Toast;import com.nilzor.presenterexample.databinding.FragmentMainBinding;public class MainActivityFragment extends Fragment { private FragmentMainBinding mBinding; private MainModel mViewModel; public MainActivityFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_main, container, false); mBinding = FragmentMainBinding.bind(view); mViewModel = new MainModel(this, getResources()); mBinding.setData(mViewModel); attachButtonListener(); return view; } private void attachButtonListener() { mBinding.loginOrCreateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mViewModel.logInClicked(); } }); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { ensureModelDataIsLodaded(); } private void ensureModelDataIsLodaded() { if (!mViewModel.isLoaded()) { mViewModel.loadAsync(); } } public void showShortToast(String text) { Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show(); }}
MVVM – VIEWMODEL
package com.nilzor.presenterexample;import android.content.res.Resources;import android.databinding.ObservableField;import android.os.AsyncTask;import android.view.View;import java.util.Random;public class MainModel { public ObservableField numberOfUsersLoggedIn = new ObservableField(); public ObservableField isExistingUserChecked = new ObservableField(); public ObservableField emailBlockVisibility = new ObservableField(); public ObservableField loginOrCreateButtonText = new ObservableField(); private boolean mIsLoaded; private MainActivityFragment mView; private Resources mResources; public MainModel(MainActivityFragment view, Resources resources) { mView = view; mResources = resources; // You might want to abstract this for testability setInitialState(); updateDependentViews(); hookUpDependencies(); } public boolean isLoaded() { return mIsLoaded; } private void setInitialState() { numberOfUsersLoggedIn.set("..."); isExistingUserChecked.set(true); } private void hookUpDependencies() { isExistingUserChecked.addOnPropertyChangedCallback(new android.databinding.Observable.OnPropertyChangedCallback() { @Override public void onPropertyChanged(android.databinding.Observable sender, int propertyId) { updateDependentViews(); } }); } public void updateDependentViews() { if (isExistingUserChecked.get()) { emailBlockVisibility.set(View.GONE); loginOrCreateButtonText.set(mResources.getString(R.string.log_in)); } else { emailBlockVisibility.set(View.VISIBLE); loginOrCreateButtonText.set(mResources.getString(R.string.create_user)); } } public void loadAsync() { new AsyncTask() { @Override protected Void doInBackground(Void... params) { // Simulating some asynchronous task fetching data from a remote server try {Thread.sleep(2000);} catch (Exception ex) {}; numberOfUsersLoggedIn.set("" + new Random().nextInt(1000)); mIsLoaded = true; return null; } }.execute((Void) null); } public void logInClicked() { // Illustrating the need for calling back to the view though testable interfaces. if (isExistingUserChecked.get()) { mView.showShortToast("Invalid username or password"); } else { mView.showShortToast("Please enter a valid email address"); } }}
附:
MVX Android设计架构浅析
MVX Android设计架构浅析-MVC
MVX Android设计架构浅析-MVP
MVX Android设计架构浅析-MVVM
- MVX Android设计架构浅析-MVVM
- MVX Android设计架构浅析
- MVX Android设计架构浅析-MVP
- MVX Android设计架构浅析-MVC
- Android架构设计模式总结(MVX)
- Android App 的设计架构:MVC、MVP、MVVM与架构
- Android App的设计架构 MVC,MVP,MVVM与架构
- WPF架构设计MVVM
- ios 架构设计 MVVM
- ios MVVM架构设计
- android MVX杂谈
- 架构:Android的MVVM
- Android-架构-MVVM
- Android App的设计架构:MVC,MVP,MVVM
- Android App的设计架构:MVC,MVP,MVVM经验谈
- 浅谈Android架构设计模式中MVC、MVP、MVVM
- Android架构设计---关于MVVM模式的探讨
- Android App 的设计架构:MVC、MVP、MVVM 的分析
- 【BZOJ1565】[NOI2009]植物大战僵尸【最大权闭合图】【拓扑排序】
- Android 在使用Bitmap的时候为啥要手动调用recycle方法呢?
- SpringMVC——最小化配置
- fir.im Weekly - Stanford 的 Swift 课程来了
- Restfu架构设计l概述
- MVX Android设计架构浅析-MVVM
- c++判断文件夹是否存在,若不存在,调用linux命令创建它
- HDU 1028 Ignatius and the Princess III(整数拆分问题)
- 一个HTTP Servlet处理POST方式的例子
- MVX Android设计架构浅析-MVP
- 古代常见文学常识汇总
- 扁平系统的力量
- MVX Android设计架构浅析-MVC
- paoding-rose 框架BaseDAO