MVP(Model View Presenter)的设计模式是从MVC中演化而来的,主要作用是能够:
划分模块职责,
降低模块耦合
易测试,提高代码复用
Model:数据:负责数据的检索
View: 视图:负责视图的绘制,和用户的交互
presenter: 控制器:负责在Model和View之间交互,负责两者业务的逻辑处理
在MVC中,在Controller中,有View和Model的部分处理逻辑,在View处理时间的过程中,也有Model操作的过程,
该模式看起来模式简单,层级关系不多,而且代码量少,但是大部分逻辑都杂糅到了一起,
在测试和调试的过程中较之MVP而言很不方便
在MVC中,数据,控制器,视图之间互相操作的关系是这样的:
而在MVP中,presenter单线操作数据Model部分的数据,
同样的presenter也是单线的操作视图View模块的东西,但是中间是是通过调用接口的方式,
当View界面和数据有交互的时候,再通过接口调用Presenter,再由Presenter调用Model来进行数据的改变
在MVP中,数据,控制器,视图之间相互操作的关系是这样的:
简单的演示一个登陆操作的界面,用户输入用户名密码,然后模拟操作访问服务器验证用户名密码的操作,此过程显示进度条,登陆成功之后显示成功界面,失败提示失败信息
简单的看一下界面:
activity_user_login.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" 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=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/username"
android:textSize="18sp"
android:layout_alignParentLeft="true"
android:layout_marginTop="20dp"
android:id="@+id/id_tv_username"/>
<EditText
android:layout_width="170dp"
android:layout_height="40dp"
android:id="@+id/id_et_username"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"
android:text="username"
android:layout_alignBottom="@+id/id_tv_username" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/passwprd"
android:textSize="18sp"
android:layout_alignParentLeft="true"
android:layout_marginTop="80dp"
android:id="@+id/id_tv_password"/>
<EditText
android:layout_width="170dp"
android:layout_height="40dp"
android:id="@+id/id_et_password"
android:layout_alignBottom="@id/id_tv_password"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"
android:text="password" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/id_btn_login"
android:text="Login"
android:layout_below="@+id/id_et_password"
android:layout_alignEnd="@+id/id_tv_username"
android:layout_marginTop="35dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/id_btn_clear"
android:text="Clear"
android:layout_alignTop="@+id/id_btn_login"
android:layout_alignEnd="@+id/id_et_password"
android:layout_marginEnd="24dp" />
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/id_progressbar"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
RelativeLayout>
界面效果如下:
程序运行结果预览:
界面写好之后,开始分析该程序的数据和功能:
首先,需要有一个JavaBean对象User,保存username 和password字段
需要String类型的username和password
package com.jishihuitong.mvpdemo.bean;
/**
* Created by hss on 2016/8/2.
*/
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
然后根据视图,肯定要有一个login的方法,在MVP中presenter操作model和view的过程都是使用接口的,所以要有一个IUserModel和UserModelImpl继续完成
IUserModel类
package com.jishihuitong.mvpdemo.model;
/**
* Created by hss on 2016/8/2.
*/
public interface IUserModel {
/**
* 登录数据模块的操作
* @param username 用户名
* @param password 密码
* @param onLoginListener 登录监听
*/
public void login(String username,String password,OnLoginListener onLoginListener);
}
IUserModel实现类
package com.jishihuitong.mvpdemo.model;
import com.jishihuitong.mvpdemo.bean.User;
/**
* Created by hss on 2016/8/2.
*/
public class UserModelImpl implements IUserModel {
@Override
public void login(final String username, final String password, final OnLoginListener onLoginListener) {
new Thread(){
@Override
public void run() {
try {
//模拟从网络加载数据,需要时间
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if("username".equals(username) && "password".equals(password)){
User user = new User();
user.setUsername(username);
user.setPassword(password);
onLoginListener.loginSuccess(user);
}else{
onLoginListener.loginFailed();
}
}
}.start();
}
}
登录成功或者失败的监听
package com.jishihuitong.mvpdemo.model;
import com.jishihuitong.mvpdemo.bean.User;
/**
* Created by hss on 2016/8/2.
*/
public interface OnLoginListener {
/**
* 登录成功
* @param user user对象
*/
public void loginSuccess(User user);
/**
* 登录失败
*/
public void loginFailed();
}
View层,上面说到,MVP设计模式中,调用View层的方法都是通过接口调用的,
所以我们需要显示定义一个View的接口ILoginVeiw,在该接口中需要有什么方法呢?
首先需要
getUsername();
getPassword();
清空输入框内容的方法
clearUsername();
clearPassword();
登录成功和登录失败的方法
toMainActivity(User user);
showFailedError();
显示进度和隐藏进度的方法
showLoading();
hideLoading();
所以综合上述表述,写出来的ILoginView接口应该是这样的:
package com.jishihuitong.mvpdemo.view;
import com.jishihuitong.mvpdemo.bean.User;
/**
* Created by hss on 2016/8/2.
*/
public interface IUserLoginView {
/**
* View层接口获取用户名的方法
* @return 输入的用户名
*/
public String getUserName();
/**
* View层接口获取密码的方法
* @return 输入的密码
*/
public String getPassword();
/**
* View层清除用户名
*/
public void clearUserName();
/**
* View层清除密码的方法
*/
public void clearPassword();
/**
* View层登录成功的方法
* @param user user对象
*/
public void toMainActivity(User user);
/**
* View层显示登录失败的方法
*/
public void showFailedError();
/**
* View层显示进度条的方法
*/
public void showLoading();
/**
* View层隐藏进度条的方法
*/
public void hideLoading();
}
而加载布局的一个类显然就是View层的了,该类要实现ILoginView接口,并且实现里面的方法:
然后登录操作,需要使用present来执行,
UserLoginActivity.java
package com.jishihuitong.mvpdemo.view;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.jishihuitong.mvpdemo.R;
import com.jishihuitong.mvpdemo.bean.User;
import com.jishihuitong.mvpdemo.presenter.UserLoginPresenter;
/**
* 登录界面主activity IView的实现类,
*/
public class UserLoginActivity extends AppCompatActivity implements IUserLoginView {
private EditText id_et_username;
private EditText id_et_password;
private Button id_btn_login;
private Button id_btn_clear;
private UserLoginPresenter mUserLoginPrestener = new UserLoginPresenter(this);
private ProgressBar id_progressbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_login);
initView();
}
/**
* 初始化控件
*/
private void initView() {
id_et_username = (EditText) findViewById(R.id.id_et_username);
id_et_password = (EditText) findViewById(R.id.id_et_password);
id_btn_login = (Button) findViewById(R.id.id_btn_login);
id_btn_clear = (Button) findViewById(R.id.id_btn_clear);
id_progressbar = (ProgressBar) findViewById(R.id.id_progressbar);
id_btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// presenter控制逻辑 登录操作
mUserLoginPrestener.login();
}
});
id_btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUserLoginPrestener.clear();
}
});
}
@Override
public String getUserName() {
return id_et_username.getText().toString();
}
@Override
public String getPassword() {
return id_et_password.getText().toString();
}
@Override
public void clearUserName() {
id_et_username.setText("");
}
@Override
public void clearPassword() {
id_et_password.setText("");
}
@Override
public void toMainActivity(User user) {
Toast.makeText(getApplicationContext(),getUserName()+"登录成功",Toast.LENGTH_SHORT).show();
}
@Override
public void showFailedError() {
Toast.makeText(getApplicationContext(),getUserName()+"登录失败",Toast.LENGTH_SHORT).show();
}
@Override
public void showLoading() {
id_progressbar.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
id_progressbar.setVisibility(View.INVISIBLE);
}
}
UserLoginPresenter.java
package com.jishihuitong.mvpdemo.presenter;
import android.os.Handler;
import com.jishihuitong.mvpdemo.bean.User;
import com.jishihuitong.mvpdemo.model.IUserModel;
import com.jishihuitong.mvpdemo.model.OnLoginListener;
import com.jishihuitong.mvpdemo.model.UserModelImpl;
import com.jishihuitong.mvpdemo.view.IUserLoginView;
/**
* Created by hss on 2016/8/2.
*/
public class UserLoginPresenter {
private IUserModel userModel;
private IUserLoginView userLoginView;
private Handler mHandler = new Handler() {
};
public UserLoginPresenter(IUserLoginView userLoginView) {
this.userModel = new UserModelImpl();
this.userLoginView = userLoginView;
}
/**
* 登录操作
*/
public void login() {
// 设置进度条可见
userLoginView.showLoading();
//数据控制器控制model操作登录
userModel.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.clearPassword();
userLoginView.clearUserName();
}
}
总体来说,虽然说代码的层次增加了,代码量也增加了,但是仔细看看,是不是代码感觉特别明朗,层次感特别清晰呢,该模式主要适用于数据和view交互比较多的模块,
MVP时下很火,所以在此了解学习一下,有不足的地方,欢迎指正。
点击下载代码