• Android MVC,MVP,MVVM模式入门——重构登陆注册功能


    一  MVC模式:

    M:model,业务逻辑

    V:view,对应布局文件

    C:Controllor,对应Activity

    项目框架:

    代码部分:

      layout文件(适用于MVC和MVP两个Demo):

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <EditText
            android:id="@+id/edit_username"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:hint="username"/>
    
        <EditText
            android:id="@+id/edit_password"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:inputType="textPassword"
            android:hint="password"/>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/login"
                android:layout_marginLeft="10dp"
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:text="Login"/>
    
            <Button
                android:id="@+id/clear"
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:layout_marginLeft="100dp"
                android:text="Clear"/>
    
        </LinearLayout>
    
        <ProgressBar
            android:id="@+id/login_progressbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="invisible"
            />
    </LinearLayout>

      User类,定义成员对象:

    package com.example.liang.userloginformvc.modle;
    
    /**
     * Created by liang on 2016/8/22.
     */
    public class User {
        private String username;
        private String password;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }

      OnLoginListener,j监听登陆状态:

    package com.example.liang.userloginformvc.modle;
    
    /**
     * Created by liang on 2016/8/22.
     */
    public interface OnLoginListener {
        void loginSuccess(User user);
        void loginFailed();
    }

      UserBiz,登陆逻辑:

    package com.example.liang.userloginformvc.modle;
    
    /**
     * Created by liang on 2016/8/22.
     */
    public class UserBiz {
    
        public void login(final String username,final String password,final OnLoginListener loginListener){
            new Thread(){
                @Override
                public void run() {
                    try{
                        Thread.sleep(2000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    //模拟登陆
                    if("lqy".equals(username)&&"123".equals(password)){
                        User user=new User();
                        user.setUsername(username);
                        user.setPassword(password);
                        loginListener.loginSuccess(user);
                    }else{
                        loginListener.loginFailed();
                    }
                }
            }.start();
    
        }
    }

    MVC模式总结:

      首先从响应用户点击事件,到MainActivity,获取View中的数据再交给Model层处理,通过返回的数据,MainActivity再改变视图。

    从MVC到MVP:由于View和Model之间的依赖还是太强,希望他们可以绝对独立的存在,慢慢的就演化出了MVP

    二  MVP模式

    M:业务逻辑与实体模型

    V:负责View的绘制以及用户交互,对应Activity

    P:负责完成View与Modle之间的交换

    项目框架:

    代码部分:

      user:

    package com.example.liang.userloginformvp.bean;
    
    /**
     * Created by liang on 2016/8/22.
     */
    public class User{
        private String username;
        private String password;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }

      IUserBiz:

    package com.example.liang.userloginformvp.biz;
    
    
    
    /**
     * Created by liang on 2016/8/22.
     */
    public interface IUserBiz{
        public void login(String username,String password,OnLoginListener onLoginListener);
    }

      OnLoginListener:

    package com.example.liang.userloginformvp.biz;
    
    
    
    /**
     * Created by liang on 2016/8/22.
     */
    public interface IUserBiz{
        public void login(String username,String password,OnLoginListener onLoginListener);
    }

      UserBiz:

    package com.example.liang.userloginformvp.biz;
    
    import com.example.liang.userloginformvp.bean.User;
    
    /**
     * Created by liang on 2016/8/22.
     */
    public class UserBiz implements IUserBiz {
        //在这里重写IUserBiz中的方法
        @Override
        public void login(final String username, final String password, final OnLoginListener loginListener) {
            //注意,这里并没有new OnLoginListener的对象,所以也没有重写方法
            //要留在UserLoginPresenter中重写,并通过登陆的状态调用相应的视图
            new Thread(){
                @Override
                public void run() {
                    try{
                        Thread.sleep(2000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    //模拟登陆成功
                    if("lqy".equals(username)&&"123".equals(password)){
                        User user=new User();
                        user.setUsername(username);
                        user.setPassword(password);
                        loginListener.loginSuccess(user);
                    }else{
                        loginListener.loginFailed();
                    }
                }
            }.start();
        }
    }

      UserLoginPresenter:

    public class UserLoginPresenter {
        private IUserBiz userBiz;
        private IUserLoginView userLoginView;
        private Handler mHandler = new Handler();
    
        public UserLoginPresenter(IUserLoginView userLoginView) {
            this.userLoginView = userLoginView;
    
            //给userBiz一个子类的空间,此时方法已经被重写,登陆逻辑已被判断
            this.userBiz = new UserBiz();
        }
    
        public void login() {
            userLoginView.showLoading();
            userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
                @Override
                public void loginSuccess(final User user) {
                    //需要在UI线程执行
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            userLoginView.toMainActivity(user);
                            userLoginView.hideLoading();
                        }
                    });
    
                }
    
                @Override
                public void loginFailed() {
                    //需要在UI线程执行
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            userLoginView.showFailedError();
                            userLoginView.hideLoading();
                        }
                    });
    
                }
            });
        }
    
        public void clear() {
            userLoginView.clearUserName();
            userLoginView.clearPassword();
        }
    
    
    }

      IUserLoginPresenter:

    package com.example.liang.userloginformvp.view;
    
    import com.example.liang.userloginformvp.bean.User;
    
    /**
     * Created by liang on 2016/8/22.
     */
    public interface IUserLoginView {
        String getUserName();
    
        String getPassword();
    
        void clearUserName();
    
        void clearPassword();
    
        void showLoading();
    
        void hideLoading();
    
        void toMainActivity(User user);
    
        void showFailedError();
    }

      MainActivity:

    package com.example.liang.userloginformvp;
    
    import android.support.v7.app.ActionBarActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ProgressBar;
    import android.widget.Toast;
    
    import com.example.liang.userloginformvp.bean.User;
    import com.example.liang.userloginformvp.presenter.UserLoginPresenter;
    import com.example.liang.userloginformvp.view.IUserLoginView;
    
    public class MainActivity extends ActionBarActivity implements IUserLoginView{
    
        private EditText usernaem_edit,password_edit;
        private Button login_btn,clear_btn;
        private ProgressBar loading;
    
        private UserLoginPresenter userLoginPresenter=new UserLoginPresenter(this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initViews();
        }
    
        private void initViews(){
            usernaem_edit=(EditText)findViewById(R.id.edit_username);
            password_edit=(EditText)findViewById(R.id.edit_password);
    
            login_btn=(Button)findViewById(R.id.login);
            clear_btn=(Button)findViewById(R.id.clear);
    
            loading=(ProgressBar)findViewById(R.id.login_progressbar);
            loading.setVisibility(View.INVISIBLE);
            login_btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    userLoginPresenter.login();
                }
            });
    
            clear_btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    userLoginPresenter.clear();
                }
            });
    
        }
    
        @Override
        public String getUserName() {
            return usernaem_edit.getText().toString();
        }
    
        @Override
        public String getPassword() {
            return password_edit.getText().toString();
        }
    
        @Override
        public void clearUserName() {
            usernaem_edit.setText("");
        }
    
        @Override
        public void clearPassword() {
            password_edit.setText("");
        }
    
        @Override
        public void showLoading() {
            loading.setVisibility(View.VISIBLE);
        }
    
        @Override
        public void hideLoading() {
            loading.setVisibility(View.GONE);
        }
    
        @Override
        public void toMainActivity(User user) {
            Toast.makeText(this,user.getUsername()+" login success , to MainActivity",Toast.LENGTH_SHORT)
                    .show();
        }
    
        @Override
        public void showFailedError() {
            Toast.makeText(this, "login failed", Toast.LENGTH_SHORT).show();
        }
    }

    MVP总结:Activity作为View加载视图并响应点击事件(直接想登陆),点击事件交由Presenter处理,Presenter将登陆逻辑交给Model处理,并根据model处理结果告诉View改返回什么试图。

    ------------------------------------分隔线

    从MVP到MVVM中:至于MVVM基本上和MVP一模一样,感觉只是名字替换了一下。他的关键技术就是今天的主题(Data Binding)。View的变化可以自动的反应在ViewModel,ViewModel的数据变化也会自动反应到View上。这样开发者就不用处理接收事件和View更新的工作,框架已经帮你做好了。

    三  MVVM模式

    M:model,业务逻辑

    V:加载视图,和layout通信,进行数据绑定与更新,对应Activity(由于代码是在太少,有时自带启动自己的方法)

    VM:判断条件,逻辑实现

    项目框架:

    首先配置环境

    加一句:

      LoginViewModel

    package com.example.liang.userloginformvvm.viewmodel;
    
    import android.content.Context;
    import android.databinding.ObservableField;
    import android.databinding.ObservableInt;
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.view.View;
    
    import com.example.liang.userloginformvvm.modle.User;
    import com.example.liang.userloginformvvm.view_activity.MainActivity;
    
    /**
     * Created by liang on 2016/8/23.
     */
    public class LoginViewModel implements ViewModel {
        private Context context;
        //用于数据刷新的便捷类型:ObservableField,ObservableInt····
        public ObservableField<String> loginMessage;
        public ObservableInt loginMessageVisibility;
        private String editTextUsernameValue = "";
        private String editTextPasswordValue = "";
    
        public LoginViewModel(Context context) {
            this.context = context;
            this.loginMessage = new ObservableField<>("");
            this.loginMessageVisibility = new ObservableInt(View.INVISIBLE);
        }
    
        //登陆,实际上这个方法是在layout文件中调用的
    
        public void loginAuthentication(View view) {
            if ((editTextUsernameValue.equals("lqy")) && (editTextPasswordValue.equals("123"))) {
                loginMessage.set("");
                loginMessageVisibility.set(View.INVISIBLE);
                User user = new User(editTextUsernameValue, editTextPasswordValue);
                context.startActivity(MainActivity.newIntent(context, user));
            } else if ((editTextUsernameValue.equals("")) || (editTextPasswordValue.equals(""))) {
                loginMessage.set("Username or Password can't be empty!");
                loginMessageVisibility.set(View.VISIBLE);
            } else {
                loginMessage.set("Username = lqy 
     Password = 123");
                loginMessageVisibility.set(View.VISIBLE);
            }
        }
    
        //观察Text变化的TextWatcher
        public TextWatcher getUsernameUpdate() {
            return new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
                }
    
                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    editTextUsernameValue = charSequence.toString();
                }
    
                @Override
                public void afterTextChanged(Editable editable) {
    
                }
            };
        }
    
        public TextWatcher getPasswordUpdate() {
            return new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
                }
    
                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    editTextPasswordValue = charSequence.toString();
                }
    
                @Override
                public void afterTextChanged(Editable editable) {
    
                }
            };
        }
    
    
        @Override
        public void destroy() {
    
        }
    }

      MainViewModel:

    package com.example.liang.userloginformvvm.viewmodel;
    
    import android.content.Context;
    
    import com.example.liang.userloginformvvm.modle.User;
    
    /**
     * Created by liang on 2016/8/23.
     */
    public class MainViewModel {
        private Context context;
        private User user;
    
        public MainViewModel(Context context, User user) {
            this.context = context;
            this.user = user;
        }
    
        public String getUsername(){
            return user.username;
        }
        public String getPassword(){
            return user.password;
        }
    }

      ViewModel:

    package com.example.liang.userloginformvvm.viewmodel;
    
    /**
     * Created by liang on 2016/8/23.
     */
    public interface ViewModel {
        void destroy();
    }

      User:

    package com.example.liang.userloginformvvm.modle;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    /**
     * Created by liang on 2016/8/23.
     */
    //实现Parcelable接口,可以让类在网络或进程中传递
    public class User implements Parcelable{
        public String username;
        public String password;
    
        public User(String username,String password){
            this.username=username;
            this.password=password;
        }
    
        public User(Parcel in) {
            username = in.readString();
            password = in.readString();
        }
    
        public static final Creator<User> CREATOR=new Creator<User>() {
            @Override
            public User createFromParcel(Parcel parcel) {
                return new User(parcel);
            }
    
            @Override
            public User[] newArray(int i) {
                return new User[i];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel parcel, int i) {
            parcel.writeString(this.username);
            parcel.writeString(this.password);
        }
    }

      

      LoginActivity

    package com.example.liang.userloginformvvm.view_activity;
    
    import android.databinding.DataBindingUtil;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    
    
    import com.example.liang.userloginformvvm.R;
    import com.example.liang.userloginformvvm.databinding.ActivityLoginBinding;
    import com.example.liang.userloginformvvm.viewmodel.LoginViewModel;
    
    /**
     * Created by liang on 2016/8/23.
     */
    public class LoginActivity extends AppCompatActivity{
        //当给布局指定格式之后会产生相应的Bing类,例如activity_login.xml会产生ActivityLoginBinding类
        ActivityLoginBinding loginBinding;
        LoginViewModel viewModel;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //指定布局,没有XXX.findViewById了
            loginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);
    
            viewModel=new LoginViewModel(this);
            loginBinding.setViewmodel(viewModel);
        }
    }

      MainActivity

    package com.example.liang.userloginformvvm.view_activity;
    
    import android.content.Context;
    import android.content.Intent;
    import android.databinding.DataBindingUtil;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    
    import com.example.liang.userloginformvvm.R;
    import com.example.liang.userloginformvvm.databinding.ActivityMainBinding;
    import com.example.liang.userloginformvvm.modle.User;
    import com.example.liang.userloginformvvm.viewmodel.MainViewModel;
    
    /**
     * Created by liang on 2016/8/23.
     */
    public class MainActivity extends AppCompatActivity{
        ActivityMainBinding activityMainBinding;
        MainViewModel mainViewModel;
    
        //传递数据的标志
        private static final String EXTRA_USER = "extra_user";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            activityMainBinding= DataBindingUtil.setContentView(this, R.layout.activity_main);
    
    
            User user=getIntent().getParcelableExtra(EXTRA_USER);
            
            mainViewModel=new MainViewModel(this,user);
            activityMainBinding.setMainviewmodel(mainViewModel);
        }
    
        //viewmodel唤醒这个Activity的方法
        public static Intent newIntent(Context context, User user){
            Intent intent = new Intent(context, MainActivity.class);
            intent.putExtra(EXTRA_USER,user);
            return intent;
        }
    }

      下面就是非常重要的layout文件了:

    activity_mian.xml

    <?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>
            <variable
                name="viewmodel"
                type="com.example.liang.userloginformvvm.viewmodel.LoginViewModel">
    
            </variable>
        </data>
        <!--先要指定绑定的类-->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".view_activity.LoginActivity">
    
    
            <TextView
                android:id="@+id/loginmessage"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{viewmodel.loginMessage}"
                android:textAlignment="center"
                android:textColor="@color/colorAccent"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:layout_marginBottom="20dp"/>
    
            <!--监听text的变化-->
            <EditText
                android:id="@+id/login_username"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="textPersonName"
                android:ems="10"
                android:layout_centerHorizontal="true"
                android:layout_below="@+id/loginmessage"
                android:hint="UserName"
                android:layout_marginBottom="10dp"
                app:addTextChangedListener="@{viewmodel.usernameUpdate}"/>
    
            <EditText
                android:id="@+id/login_password"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="textPassword"
                android:hint="Password"
                android:ems="10"
                android:layout_below="@id/login_username"
                android:layout_centerHorizontal="true"
                android:layout_marginBottom="10dp"
                app:addTextChangedListener="@{viewmodel.passwordUpdate}" />
    
            <Button
                android:layout_width="210dp"
                android:layout_height="wrap_content"
                android:text="LOGIN"
                android:layout_below="@id/login_password"
                android:layout_centerHorizontal="true"
                android:onClick="@{viewmodel.loginAuthentication}"/>
    
        </RelativeLayout>
    </layout>

    activity_login.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
        <data>
            <variable
                name="mainviewmodel"
                type="com.example.liang.userloginformvvm.viewmodel.MainViewModel"></variable>
        </data>
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".view_activity.MainActivity">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:orientation="horizontal">
                <TextView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:text="Welcome"
                    android:layout_weight="1"/>
    
                <TextView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="3"
                    android:text="@{mainviewmodel.username}"/>
            </LinearLayout>
    
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:orientation="horizontal">
    
                <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:text="Password is"
                android:layout_weight="1"/>
    
                <TextView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="3"
                    android:text="@{mainviewmodel.password}"/>
            </LinearLayout>
        </LinearLayout>
    
    </layout>

    MVVM总结:通过View加载布局,并通过布局调用ViewModel中的方法,VeiwModel可以通过调用后的结果决定启动什么视图,同时ViewModel保持和model的通信。

    2016.8.24首次截稿 

    有什么错误麻烦大家指出来,在线更新。 

    参考资料:

    鸿洋老师的博客: http://blog.csdn.net/lmj623565791/article/details/46596109

    胡笛老师的微信推文

    还有github上的资源

    以及http://my.oschina.net/u/1175007/blog/613889

  • 相关阅读:
    C# 使用正则表达式按指定长度分割字符串
    C# vs2017创建Com组件,并注册
    js将4个字节型字符串转为Float
    js 替换字符串中的双引号
    js截取两个字符串之间的子字符串
    C# Winform设计运行时,界面模糊
    Scala字节数组转换为数字
    Scala环境搭建及Intellij IDEA安装
    C# 循环时,操作另外一个进程直到操作完成,循环继续执行
    如何搭建node
  • 原文地址:https://www.cnblogs.com/Liang-Blog/p/5801414.html
Copyright © 2020-2023  润新知