Android 全新MVVM框架搭建
闲话不多少,还是老套路,处理一个登陆的业务。详细的介绍ofollow,noindex">MVVM 我就不多说了,网上一大把,毕竟吹水还是我的弱项。
主要实现的功能有两个输入框,一个登陆按钮,两个TextView显示登陆结果。
秀一把我的LoginActivity
@BindLayout(R.layout.activity_login) public class LoginActivity extends BaseActivity<ILoginViewModel, LoginActivityBriefnessor> implements ILoginView { @Override protected ILoginViewModel createViewModel(LoginActivityBriefnessor briefnessor) { return new LoginViewModel(this, briefnessor); } }
是不是超级简洁,点击事件呢?怎么不见了,再看一看ViewModel
public class LoginViewModel extends BaseViewModel<ILoginView, ILoginModel, LoginActivityBriefnessor> implements ILoginViewModel { public LoginViewModel(ILoginView view, LoginActivityBriefnessor briefnessor) { super(view, briefnessor); } @Override protected ILoginModel createModel() { return new LoginModel(this); } @Override public void onLoginClick(String account, String pswd) { if (account.length() < 3) { Toast.makeText(context(), "账号不正确", Toast.LENGTH_SHORT).show(); return; } if (pswd.length() < 3) { Toast.makeText(context(), "密码不正确", Toast.LENGTH_SHORT).show(); return; } model.login(account, pswd); } @Override public void callbackLogin(LoginResult result) { briefnessor.setResult(result); } }
ViewModel制作了登陆的数据验证,以及登陆回调的实现,model层完成模拟了登陆。
整个登陆的业务流程Activity并没有参与,在这其中就不得不提一个关键的中间件Briefness ,它连接了View层与Model层,帮我们实现了数据绑定,以及事件传递。
我们先看一看布局是如何实现的
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:imports="com.hacknife.demo.bean.LoginResult,result"a app:viewModel="com.hacknife.demo.mvvm.viewmodel.ILoginViewModel" tools:ignore="MissingPrefix"> <EditText android:id="@+id/et_account" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="40dp" android:layout_marginVertical="10dp" android:hint="账号" /> <EditText android:id="@+id/et_pswd" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="40dp" android:layout_marginVertical="10dp" android:hint="密码" /> <Button android:id="@+id/btn_login" android:layout_width="match_parent" android:layout_height="40dp" android:layout_marginHorizontal="40dp" android:layout_marginVertical="10dp" android:text="登陆" app:transfer="onLoginClick($et_account$,$et_pswd$)" /> <LinearLayout style="@style/text_parent" android:layout_marginHorizontal="40dp"> <TextView style="@style/text_childer" android:text="返回码" /> <TextView android:id="@+id/tv_code" android:layout_width="wrap_content" android:layout_height="wrap_content" app:bind="$result.code$" /> </LinearLayout> <LinearLayout style="@style/text_parent" android:layout_marginHorizontal="40dp"> <TextView style="@style/text_childer" android:text="结果:" /> <TextView android:id="@+id/tv_msg" android:layout_width="wrap_content" android:layout_height="wrap_content" app:bind="$result.msg$" /> </LinearLayout> </LinearLayout>
布局中,根布局绑定了ViewModel,以及用于展示登陆结果的数据源。登陆按钮传递单击登陆事件,剩下的两个textView绑定相应的字段。
同时在对应的Activity上绑定相应的布局文件,Briefness 就能暂时他强大的功能了,它会自动生成[类名+Briefnessor]的类。
public class LoginActivityBriefnessor implements Briefnessor<LoginActivity> { public EditText et_account; public EditText et_pswd; public Button btn_login; public TextView tv_code; public TextView tv_msg; public LoginResult result; public ILoginViewModel viewModel; @Override public void bind(final LoginActivity host, Object source) { if (!Utils.contentViewExist(host)) { host.setContentView(R.layout.activity_login); } et_account = (EditText) host.findViewById(R.id.et_account); et_pswd = (EditText) host.findViewById(R.id.et_pswd); btn_login = (Button) host.findViewById(R.id.btn_login); tv_code = (TextView) host.findViewById(R.id.tv_code); tv_msg = (TextView) host.findViewById(R.id.tv_msg); btn_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewModel.onLoginClick(et_account.getText().toString().trim() , et_pswd.getText().toString().trim()); } }); } @Override public void clear() { this.result = null; this.viewModel = null; } @Override public void clearAll() { this.result = null; this.viewModel = null; this.et_account = null; this.et_pswd = null; this.btn_login = null; this.tv_code = null; this.tv_msg = null; } @Override public void bindViewModel(Object viewModel) { this.viewModel = (ILoginViewModel) viewModel; } public void setResult(LoginResult result) { if (result == null) return; this.result = result; BriefnessInjector.injector(tv_code,result.getCode()); BriefnessInjector.injector(tv_msg,result.getMsg()); } }
该类实现了数据绑定,以及向ViewModel发送消息附带输入框中的值。
Briefness的具体用法,请参考https://github.com/hacknife/briefness
下面说一说BaseActivity
public abstract class BaseActivity<T extends IBaseViewModel,B extends Briefnessor> extends AppCompatActivity implements IBaseView { protected T viewModel; protected B briefnessor; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); briefnessor = (B )Briefness.bind(this); viewModel = createViewModel(briefnessor); briefnessor.bindViewModel(viewModel); initView(); } protected abstract T createViewModel(B briefnessor); @Override protected void onDestroy() { super.onDestroy(); if (viewModel != null) viewModel.dettachView(); } protected void initView() { } @Override public Context applicationContext() { return getApplication(); } @Override public Activity context() { return this; } }
BaseActivity中初始化了Briefness并创建ViewModel绑定到Briefness,并实现了IBaseView
public interface IBaseView { Context applicationContext(); Activity context(); }
BaseViewModel继承AbsViewmodel并实现IBaseViewModel,持有Briefnessor,View,Model。
public abstract class BaseViewModel<V extends IBaseView, M extends IBaseModel, B extends Briefnessor> extends AbsViewModel<V> implements IBaseViewModel { protected V view; protected M model; protected B briefnessor; public BaseViewModel(V view, B briefnessor) { this.attachView(view); this.attachBriefnessor(briefnessor); this.view = getView(); this.briefnessor = (B) getBriefnessor(); model = createModel(); } protected abstract M createModel(); @Override public Context applicationContext() { return view.applicationContext(); } @Override public Activity context() { return view.context(); } }
AbsViewModel 的作用主要是释放相应的连接关系。
public abstract class AbsViewModel<T> implements IBaseViewModel{ protected WeakReference<T> mViewRef; protected WeakReference<Briefnessor> mBriefnessorRef; protected void attachView(T view) { mViewRef = new WeakReference<T>(view); } protected void attachBriefnessor(Briefnessor briefnessor) { mBriefnessorRef = new WeakReference<Briefnessor>(briefnessor); } protected T getView() { return mViewRef.get(); } protected Briefnessor getBriefnessor() { return mBriefnessorRef.get(); } public boolean isViewAttached() { return mViewRef != null && mViewRef.get() != null & mBriefnessorRef != null & mBriefnessorRef.get() != null; } public void dettachView() { if (mViewRef != null) { mViewRef.clear(); mViewRef = null; } if (mBriefnessorRef != null) { mBriefnessorRef.clear(); mBriefnessorRef = null; } } }
BaseModel 持有ViewModel,并实现了IBaseView。
public abstract class BaseModel<VM extends IBaseViewModel> implements IBaseModel { protected VM viewModel; public BaseModel(VM viewModel) { this.viewModel = viewModel; } @Override public Context applicationContext() { return viewModel.applicationContext(); } @Override public Activity context() { return viewModel.context(); } }
写的不好,请多多见谅,毕竟小学都没毕业:joy:
如果还有什么不明白的,代码直通车https://github.com/hacknife/Briefness/tree/master/example/src/main/java/com/hacknife/demo/mvvm