说到mvp 不得不说的就是mvc 了 来先看一个经典的图
C和P的区别
1、先看C
C 就是 Controller,控制器。负责从 View 读取数据,控制用户输入,并向 Model 发送数据。简单来说,就是起到一个沟通的作用,能很大程度上的解决 Model 和 View 的耦合问题。
换句话说就是,它是一个 Model 与 View 之间的桥梁,让 Model 和 View 之间不再紧紧关联。
比如 View 接收到了用户输入数据,先交给 Controller ,Controller 再转交给 Model ,反之亦然。
但是如果controller 有问题 view 跟model 也能直接沟通
2、观察一下P
p即 Presenter 主持人。。。好 我们叫主持吧。。 作用跟跟 C 类似,仍然是负责 View 和 Model 之间的沟通。但是它彻底让 View 和 Model 不能直接沟通。如果想要沟通,就必须通过这个主持者来主持它们两个应该干啥。
比如 View 接收到了用户输入数据,不能直接给 Model ,要交给 Presenter ,Presenter 再转交给 Model ,反之亦然。
如果view接收到了用户操作必须要先交给Presenter再通过Presenter去转交给model 反过来也是 当model 改变了 也必须通过Presenter来反过来通知view去更新 不能像mvc一样model 改变view直接就能刷新了
3、简单的区别
C 和 P 都是为了解放 Model 和 View 之间的联系,只不过 C 是很大程度上解决,但 P 是彻底让它们两断了联系。
也就是c让model 、View 松散耦合,p让Model、View 解耦
来看下详细的区别
1、mvc
从下图中我们可以看到
*用户 Event(事件)会导致 Controller 改变 Model 或 View 或同时改变两者。
*只要 Controller 改变了 Model 的数据或属性,所有依赖的 View 都会自动更新。
*类似的,只要 Controller 改变了 View ,View 会从潜在的 Model 中获取数据进行更新。
2、mvp
Presenter 中同时持有 View 以及 Model 的 Interface 引用,而 View 持有 Presenter 的实例。
当某个 View 需要展示某些数据时,首先会调用 Presenter 的某个接口,然后 Presenter 会调用 Model 请求数据。
当 Model 数据加载成功后会调用 Presenter 的回调方法通知 Presenter 数据加载完毕,最后 Presenter 再调用 View 层接口展示加载后数据。
3、主要区别
在 MVC 中:
View 可以与 Model 直接交互;
Controller 可以被多个 View 共享;
Controller 可以决定显示哪个 View。
在 MVP 中:
View 不直接与 Model 交互;
Presenter 与 View 通过接口来交互,更有利于添加单元测试;
通常 View 与 Presenter 是一对一的,但复杂的 View 可能绑定多个 Presenter 来处理;
Presenter 也可以直接进行 View 上的渲染。
好先看下经典的目录结构吧
好像是看不出什么来 这就是一个简单的登陆demo 万年不变
我们可以来分析一下
通过上面的介绍我们可以知道,Presenter 是用来Model 和View 之间交互的 所以必须要持有他们各自的对象
model 还要有一个业务类 这在mvc中也是一样的 索性我们就抽一个接口出来
Presenter 与view 又是通过接口来交互的所以我们就需要给view 定义一个接口
所以我们就要看到底需要什么东西
Model:负责检索操作数据,也就是常见的业务类对bean的操作
ModelInterface: 处理bean的业务逻辑接口 由bean来实现
view:暂时就看出activity 用来记录响应用户操作的
viewInterface : view 要实现用户操作的结果接口(比如登陆 界面上肯定有登陆注册按键 就要有 登陆成功失败的接口以及注册成功失败的接口)Presenter 通过 它 与view交互
Presenter 是个类 是用来连接view 与model的桥梁 持有二者的接口对象
好吧 看起来非常多 但是逻辑确实是清晰了一点 谁是干什么的 谁控制谁响应 这种是清晰了一点。。我体会还不深 勿喷
ok 现在我们可以抽取代码了
1、bean
/**
* Created by MnyZhao on 2017/11/3.
*/
public class UserBean {
/*名字*/
private String name;
/*密码*/
private String passWord;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
}
2、业务操作接口 业务操作类 针对bean
UserModelInterface
/**
* Created by MnyZhao on 2017/11/3.
* @description 业务操作接口 控制user的动作接口
* @remark
*/
public interface IUserModel {
/*注册接口*/
boolean onRegister(String name,String passWord);
/*登陆接口*/
boolean onLogin(String name,String passWord);
}
实现类 UserModel
/**
* Created by MnyZhao on 2017/11/3.
* model类 实现IUserModel中的动作接口
*/
public class UserModel implements IUserModel {
private Map<String, String> userMap = new HashMap<>();
/**
* 注册账号放入集合
*
* @param name 用户名
* @param passWord 密码
* @return 返回是否成功(true or false)
*/
@Override
public boolean onRegister(String name, String passWord) {
if (!userMap.containsKey(name)) {
userMap.put(name, passWord);
return true;
}
return false;
}
/**
* 登陆接口
*
* @param name 用户名
* @param passWord 密码
* @return 是否登陆成功(true or false)
*/
@Override
public boolean onLogin(String name, String passWord) {
return passWord.equals(userMap.get(name));
}
}
3、现在我们要看看view了
ViewInterface 针对界面用户操作的相应接口
/**
* Created by MnyZhao on 2017/11/3.
*/
public interface IUserVIew {
//检查输入的合法性
boolean checkInputInfo();
//注册成功
void onRegisterSucceed();
//注册失败
void onRegisterFaild();
//登录成功
void onLoginSucceed();
//登录失败
void onLoginFaild();
}
4、model view 都有了 我们就要Presenter了
Presenter类 用来沟通view 与model
所以要持有二者的接口对象
/**
* Created by MnyZhao on 2017/11/3.
*/
public class UserPresenter {
private IUserModel mUserModel;
private IUserVIew mIuserView;
public UserPresenter(IUserVIew mIuserView) {
this.mUserModel = new UserModel();
this.mIuserView = mIuserView;
}
public void registerUser(String name, String pwd) {
//根据Model中的结果调用不同的方法进行UI展示
if (mUserModel.onRegister(name, pwd)) {
mIuserView.onRegisterSucceed();
} else {
mIuserView.onRegisterFaild();
}
}
public void loginUser(String name, String pwd) {
//根据Model中的结果调用不同的方法进行UI展示
if (mUserModel.onLogin(name, pwd)) {
mIuserView.onLoginSucceed();
} else {
mIuserView.onLoginFaild();
}
5、接下来就要在activity(view)中实现了 需要实现view的接口(IUserVIew)并持有 Presenter的实例才能与view 进行交互
public class MainActivity extends AppCompatActivity implements IUserVIew {
private TextInputLayout mTilName;
private TextInputLayout mTilPassword;
private TextInputEditText mTieName;
private TextInputEditText mTiePassWord;
private Button mBtnRegister, mBtnLogin;
private UserPresenter userPeresenter;
private String name, pwd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initUserPersenter();
}
private void initView() {
mTilName = (TextInputLayout) findViewById(R.id.til_name);
mTilPassword = (TextInputLayout) findViewById(R.id.til_password);
mTieName = (TextInputEditText) findViewById(R.id.tie_name);
mTiePassWord = (TextInputEditText) findViewById(R.id.tie_password);
mBtnRegister = (Button) findViewById(R.id.btn_register);
mBtnLogin = (Button) findViewById(R.id.btn_login);
mBtnRegister.setOnClickListener(viewListener);
mBtnLogin.setOnClickListener(viewListener);
}
private View.OnClickListener viewListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
if (checkInputInfo()) {
userPeresenter.loginUser(name, pwd);
}
break;
case R.id.btn_register:
if (checkInputInfo()) {
userPeresenter.registerUser(name, pwd);
}
break;
}
}
};
private void initUserPersenter() {
this.userPeresenter = new UserPresenter(this);
}
@Override
public boolean checkInputInfo() {
name = mTieName.getText().toString().trim();
pwd = mTiePassWord.getText().toString().trim();
if (("").equals(name)) {
mTilName.setError("Name is not null");
return false;
}
if (("").equals(pwd)) {
mTilPassword.setError("PassWord is not null");
return false;
}
return true;
}
@Override
public void onRegisterSucceed() {
Toast.makeText(this, "Register Successful", Toast.LENGTH_SHORT).show();
}
@Override
public void onRegisterFaild() {
Toast.makeText(this, "Register Error", Toast.LENGTH_SHORT).show();
}
@Override
public void onLoginSucceed() {
Toast.makeText(this, "Login Successful", Toast.LENGTH_SHORT).show();
}
@Override
public void onLoginFaild() {
Toast.makeText(this, "Login Error", Toast.LENGTH_SHORT).show();
}
}
到此 代码详细无比 只要复制粘贴就能完美运行。。哦还少一些资源文件。。等我补上
drawable
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 填充的颜色 -->
<solid android:color="#63B8FF" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="10px" />
<!-- padding:Button里面的文字与Button边界的间隔 -->
<padding
android:left="20px"
android:top="20px"
android:right="20px"
android:bottom="20px"
/>
</shape>
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 填充的颜色 -->
<solid android:color="#FFFFFF" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="10px" />
<!-- padding:Button里面的文字与Button边界的间隔 -->
<padding
android:left="20px"
android:top="20px"
android:right="20px"
android:bottom="20px"
/>
</shape>
xml
<?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"
android:padding="10dp"
tools:context="com.mnyzhao.mymvpdemo.MainActivity">
<android.support.design.widget.TextInputLayout
android:id="@+id/til_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColorHint="@color/colorAccent"
app:errorTextAppearance="@color/colorPrimaryDark">
<android.support.design.widget.TextInputEditText
android:id="@+id/tie_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="User"
android:textColor="@color/colorAccent"
android:textColorHint="@color/colorAccent" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/til_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textColorHint="@color/colorAccent">
<android.support.design.widget.TextInputEditText
android:id="@+id/tie_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="PassWord"
android:textColor="@color/colorAccent" />
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<Button
android:id="@+id/btn_register"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_weight="1"
android:background="@drawable/whiteshape"
android:text="Register"
android:textAllCaps="false"
android:textColor="#63B8FF"
android:textSize="18sp" />
<Button
android:layout_width="30dp"
android:layout_height="wrap_content"
android:visibility="invisible" />
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_weight="1"
android:background="@drawable/blueshape"
android:text="Login"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
如果你还不能运行 那 可能你缺少Material Design的包
compile 'com.android.support:design:25.+'
ok 如果还不行的话 那对不起 可能我不适合写代码………….
还是放上资源链接吧 不知道为什么 没有0积分的选项。。。
Demo