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

来源:互联网 发布:js选项卡自动切换 编辑:程序博客网 时间:2024/06/07 14:31
一  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 \n 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

 

原创粉丝点击