浅谈:
M:Model,就是entity实体
V:View,就是activity,提供修改UI界面方法给P调用
P:Presenter,将activity界面获取到的数据拿过来处理,处理完了调用activity提供的方法更新界面UI
我们开发的时候,跟界面显示有关的才在Activity中做,否则就在Presenter中做,Presenter做网络请求,把拿到的Model传给Activity做数据展示。
MVP模式下,我们想要的效果是这样:在Activity中直接通过getP()得到这个Activity对应的Presenter对象,然后调用Presenter里面的方法。然后在Presenter中可以通过getV()直接得到这个Activity。
这边没有封装Model层。
举个登录的栗子:
LoginActivity中提供登录成功和登录失败的不同状态显示,然后调用getP().login(number,pwd);去登陆。
这个login(String,String)登录方法是在Presenter里面定义的,他在登录里面请求后台,得到响应,然后调用getV().loginSuccess(User user)或者getV().loginFail(String errorMsg)来在Activity中显示成功或失败的效果。
搭建:
我尽量简化来理解。先来看一下主要创建的基类和base类,基类放都要共同实现的方法,base放每个activity都要用到的方法:
其中还有BaseFragment类似BaseActivity的封装法,先不举例,避免混淆,具体看demo。
先来写mvp的基类:
1、首先IModel可以提取实体类共同的方法来写,比如error_code、msg之类的,这个看服务器给你什么数据。我这里没有,所以这个Imodel是空的。
2、然后IPresenter:
public interface IPresenter<V extends IView> {
/**
* 关联P与V(绑定,VIEW销毁适合解绑)
*/
void attachView(V view);
/**
* 取消关联P与V(防止内存泄漏)
*/
void detachView();
/**
* 获取view
*/
V getV();
/**
* 是否已经绑定了view
*/
boolean isAttachedV();
}
3、IView:
public interface IView<P extends IPresenter> {
/**
* @return 布局id
*/
int getLayoutId() ;
void initView();
void initData();
void initListener();
P newP();
void error();
}
然后是实现基类的base类:
1、BaseModel,同样,啥都没写。
2、BasePresenter:
import com.xaehu.myapplication.mvp.IPresenter;
import java.lang.ref.WeakReference;
public class BasePresenter<V extends BaseActivity> implements IPresenter<V> {
private WeakReference<V> v;
@Override
public void attachView(V view) {
v = new WeakReference<>(view);
}
@Override
public void detachView() {
if (v.get() != null) {
v.clear();
}
v = null;
}
@Override
public V getV() {
if(v == null || v.get() == null){
return null;
}
return v.get();
}
@Override
public boolean isAttachedV() {
return v != null && v.get() != null;
}
}
3、BaseActivity:
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.Toast;
import com.xaehu.myapplication.mvp.IView;
import java.util.Objects;
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements IView<P> {
private P p;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initView();
initData();
initListener();
initSetting();
}
protected void initSetting(){
//左侧添加一个默认的返回图标
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(hasBackBtn());
//设置返回键可用
getSupportActionBar().setHomeButtonEnabled(hasBackBtn());
}
/**
* @return 是否有返回按钮
*/
protected boolean hasBackBtn(){
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//Toolbar的事件---返回
if(item.getItemId() == android.R.id.home){
if(hasBackBtn()){
finish();
}
}
return super.onOptionsItemSelected(item);
}
public void showToast(String msg){
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public void showLongToast(String msg){
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
/**
* 获取Presenter
*/
public P getP(){
if(p == null){
p = newP();
}
if(p != null){
if(!p.isAttachedV()){
p.attachView(this);
}
}
return p;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(getP()!=null){
getP().detachView();
}
p = null;
}
}
这样,就搭建完成了,然后用法举例:
比如登录的activity:LoginActivity:
public class LoginActivity extends BaseActivity<LoginPresenter> implements View.OnClickListener {
private EditText etName;
private EditText etPwd;
private Button btnLogin;
@Override
public int getLayoutId() {
return R.layout.acticity_login;
}
@Override
public void initView() {
etName = findViewById(R.id.et_name);
etPwd = findViewById(R.id.et_pwd);
btnLogin = findViewById(R.id.btn_login);
}
@Override
public void initData() {
}
@Override
public void initListener() {
btnLogin.setOnClickListener(this);
}
@Override
public LoginPresenter newP() {
return new LoginPresenter();
}
@Override
public void error() {
}
@Override
public void onClick(View v) {
getP().login(etName.getText().toString(),etPwd.getText().toString());
}
@Override
protected boolean hasBackBtn() {
return true;
}
//登录成功做界面跳转,参数应该是实体,这里是为了测试用
public void loginSuccess(String name,String pwd){
Intent intent = new Intent(this,MainActivity.class);
intent.putExtra("name",name);
intent.putExtra("pwd",pwd);
startActivity(intent);
}
public void loginFail(){
showToast("用户名或密码错误");
}
}
对应的P:
public class LoginPresenter extends BasePresenter<LoginActivity> {
public void login(String name,String pwd){
if(name.isEmpty() || pwd.isEmpty()){
getV().showToast("用户名和密码都不能为空");
}else{
TODO: 请求
TODO: 模拟请求
if(!name.equals("1")){
getV().loginSuccess(name,pwd);
}else{
getV().loginFail();
}
}
}
}
最后附上demo地址:GitHub