Android 全新MVVM框架搭建

作者: Android高级开发 | 来源:发表于2019-02-21 16:50 被阅读9次

    闲话不多少,还是老套路,处理一个登陆的业务。详细的介绍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 &amp;&amp; mViewRef.get() != null &amp; mBriefnessorRef != null &amp; 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();
        }
    }
    

    写的不好,请多多见谅。
    如果还有什么不明白的,代码直通车https://github.com/hacknife/Briefness/tree/master/example/src/main/java/com/hacknife/demo/mvvm

    • image
    image

    +qq群:853967238。获取以上高清技术思维图,以及相关技术的免费视频学习资料

    相关文章

      网友评论

        本文标题:Android 全新MVVM框架搭建

        本文链接:https://www.haomeiwen.com/subject/kdkdyqtx.html