MVP(面向接口编程)
1.为了解耦,MVC中V-C耦合
- Model:Bean+业务逻辑
- View:Activity、Fragment
-
Presenter:起到桥梁作用,相当于经纪人的作用
-
登录模块:
- 先创建数据模型(bean),如登录模块的User(bean)
- 然后创建业务逻辑的接口,包含登录的业务逻辑,包含所需的参数和回调接口(接口回调判断登录逻辑的结果处理),
- 然后创建具体的Model层实现类,实现该接口,处理具体的业务逻辑
-
package com.itheima.mvpdemo.model;
/**
* 登录用户
*/
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;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
Model业务逻辑层: -
-
/**
* Model层:登录业务逻辑
*/
public interface IUserLoginModel {
/**
* 登录业务逻辑
* @param username 用户名
* @param password 密码
* @param listener 回调接口
*/
void login(String username, String password, OnUserLoginListener listener);
}
Model登录回调接口层 -
-
package com.itheima.mvpdemo.model;
/**
* 登录回调接口
*/
public interface OnUserLoginListener {
/**
* 登录成功回调
* @param user
*/
void loginSuccess(User user);
/**
* 登录失败回调
*/
void loginFailed();
}
Model业务逻辑具体实现层
package com.itheima.mvpdemo.model;
import android.os.SystemClock;
/**
* Model层具体实现
*/
public class UserLoginModel implements IUserLoginModel {
@Override
public void login(final String username, final String password, final OnUserLoginListener listener) {
//模拟子线程
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(1500);
//登录:判断用户名和密码
if("heima27".equals(username) && "123456".equals(password)){//登录成功
User user = new User(username, password);
listener.loginSuccess(user);
}else{//登录失败
listener.loginFailed();
}
}
}).start();
}
}
- (1)有什么操作,该操作需要什么?:1.获取用户名、 2.获取密码;
- (2)这些操作结果是什么?对应的反馈是什么?:1.登录成功跳转到主页、 2.登录失败提示用户;
- (3)有什么用户交互:1.显示滚动框、 2.隐藏滚动框。
package com.itheima.mvpdemo.view;
import com.itheima.mvpdemo.model.User;
/**
* View层接口:Activity/Fragment实现该接口
* View层如何写代码?看页面哪些是View,通过下面几个方面分析
*
* 1.有哪些操作?获取用户名、获取密码
* 2.用哪些用户交互(为了提高用户体验):显示滚动条、隐藏滚动条
* 3.上面的操作和用户结合带来上面结果(有什么结果):成功跳转主页、失败提示用户
*/
public interface IUserLoginView {
/**获取用户名*/
String getUsername();
/**获取密码*/
String getPassword();
/**显示滚动条*/
void showProgressbar();
/**隐藏滚动条*/
void hideProgressbar();
/**成功跳转主页*/
void jumpToMain(User user);
/**失败提示用户*/
void showLoginError();
}
Presenter层:Activity/Fragment初始化Presenter层对象,调用Presenter中M层和View层通信的方法,Presenter层需要考虑两个问题:
-
1.既然P层是M和V的桥梁,那么P层类中肯定有M和V层对象;
- 2.P层是桥梁,那么肯定要提供方法,让M层和V层在桥梁上“对话”。
-
其实也是主要看该功能有什么操作
package com.itheima.mvpdemo.presenter;
import android.os.Handler;
import com.itheima.mvpdemo.model.IUserLoginModel;
import com.itheima.mvpdemo.model.OnUserLoginListener;
import com.itheima.mvpdemo.model.User;
import com.itheima.mvpdemo.model.UserLoginModel;
import com.itheima.mvpdemo.view.IUserLoginView;
/**
* presenter:起到桥梁作用
* 1.既然p层起到桥梁的作用:p层必须持有view层和model层对象,联系view和model
* 2.既然p层起到桥梁作用,p层必须提供桥梁方法
*/
public class UserLoginPresenter implements OnUserLoginListener {
private IUserLoginModel userLoginModel;//model层
private IUserLoginView userLoginView;//view层
/**
* 问题:两个参数的构造方法对吗?
* 1.p层是View层调用的,如果是两个参数构造方法,说明view层持有model层对象,view-model耦合,不符合MVP架构
*
p层构造方法只能传入View层对象,Model层对象是通过创建Model层的具体实现类来获取的* @param userLoginView
*/
/*public UserLoginPresenter(IUserLoginModel model, IUserLoginView userLoginView) {
this.model = model;
this.userLoginView = userLoginView;
}*/
public UserLoginPresenter(IUserLoginView userLoginView) {
this.userLoginModel = new UserLoginModel();
this.userLoginView = userLoginView;
}
/**
* presenter层桥梁方法:model层和view层在该方法通信
*/
public void login(){
//1.显示滚动条
userLoginView.showProgressbar();
//2.调用model层登录业务:下面一行代码集中体现了p层起到桥梁的作用
userLoginModel.login(userLoginView.getUsername(), userLoginView.getPassword(), this);
}
@Override
public void loginSuccess(final User user) {
//放到UI线程
mHandler.post(new Runnable() {
@Override
public void run() {
//1.隐藏滚动条
userLoginView.hideProgressbar();
//2.跳转主页
userLoginView.jumpToMain(user);
}
});
}
@Override
public void loginFailed() {
mHandler.post(new Runnable() {
@Override
public void run() {
//1.隐藏滚动条
userLoginView.hideProgressbar();
//2.提示用户登录失败
userLoginView.showLoginError();
}
});
}
private Handler mHandler = new Handler();
}
public class LoginActivityMVP extends Activity implements OnClickListener, IUserLoginView {
private EditText etUsername;
private EditText etPassword;
private ProgressBar progressBar;
private Context context;
private Button btnLogin;
private UserLoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity_mvp);
context = this;
initViews();
btnLogin.setOnClickListener(this);
//创建presenter对象
presenter = new UserLoginPresenter(this);
}
private void initViews() {
etUsername = (EditText) findViewById(R.id.et_username);
etPassword = (EditText) findViewById(R.id.et_password);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
btnLogin = (Button) findViewById(R.id.btnLogin);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLogin:
presenter.login();
break;
}
}
@Override
public String getUsername() {
return etUsername.getText().toString().trim();
}
@Override
public String getPassword() {
return etPassword.getText().toString().trim();
}
@Override
public void showProgressbar() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgressbar() {
progressBar.setVisibility(View.GONE);
}
@Override
public void jumpToMain(User user) {
//模拟跳转
Toast.makeText(LoginActivityMVP.this, "登录成功,跳转主页", Toast.LENGTH_SHORT).show();
}
@Override
public void showLoginError() {
Toast.makeText(LoginActivityMVP.this, "用户名或者密码错误,重新登录!", Toast.LENGTH_SHORT).show();
}
}
gradle文件
说明:请求网络使用async-http
package com.example.chuxiong.mvpdemo.bean;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: MainModelBean
* @创建者: chuxiong
* @创建时间: 2017/3/21 14:41
* @描述: TODO
*/
public class MainModelBean {
private static final String TAG = "MainModelBean";
private String city;
private String wd;
private String ws;
private String time;
public String getCity() {
return city+"\n";
}
public void setCity(String city) {
this.city = city;
}
public String getWd() {
return wd+"\n";
}
public void setWd(String wd) {
this.wd = wd;
}
public String getTime() {
return time+"\n";
}
public void setTime(String time) {
this.time = time;
}
public String getWs() {
return ws+"\n";
}
public void setWs(String ws) {
this.ws = ws;
}
}
package com.example.chuxiong.mvpdemo.model;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: IMainLoading
* @创建者: chuxiong
* @创建时间: 2017/3/21 15:50
* @描述: TODO
*/
public interface IMainLoading {
void load(IMainLoadingListener listener);
}
package com.example.chuxiong.mvpdemo.model;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: IMainLoadingListener
* @创建者: chuxiong
* @创建时间: 2017/3/21 15:51
* @描述: TODO
*/
public interface IMainLoadingListener {
void loadDataSuccess(MainModelBean mainModelBean);
void loadDataFailure();
}
package com.example.chuxiong.mvpdemo.model;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.JsonHttpResponseHandler;
import org.json.JSONException;
import org.json.JSONObject;
import cz.msebera.android.httpclient.Header;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: MainLoadModel
* @创建者: chuxiong
* @创建时间: 2017/3/21 15:53
* @描述: TODO
*/
public class MainLoadModel
implements IMainLoading
{
@Override
public void load(final IMainLoadingListener listener) {
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
asyncHttpClient.get("http://www.weather.com.cn/adat/sk/101010100.html",
new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode,
Header[] headers,
JSONObject response)
{
super.onSuccess(statusCode, headers, response);
try {
MainModelBean mainModelBean = new MainModelBean();
JSONObject weatherinfo = response.getJSONObject(
"weatherinfo");
mainModelBean.setCity(weatherinfo.optString("city"));
mainModelBean.setWd(weatherinfo.optString("WD"));
mainModelBean.setWs(weatherinfo.optString("WS"));
mainModelBean.setTime(weatherinfo.optString("time"));
//加载成功
listener.loadDataSuccess(mainModelBean);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(int statusCode,
Header[] headers,
Throwable throwable,
JSONObject errorResponse)
{
super.onFailure(statusCode, headers, throwable, errorResponse);
//加载失败
listener.loadDataFailure();
}
});
}
}
package com.example.chuxiong.mvpdemo.presenter;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.presenter
* @文件名: IPresenter
* @创建者: chuxiong
* @创建时间: 2017/3/21 16:04
* @描述: TODO
*/
public interface IPresenter<V> {
void attachView(V view);
void detachView();
}
-
1.既然P层是M和V的桥梁,那么P层类中肯定有M和V层对象;
- 2.P层是桥梁,那么肯定要提供方法,让M层和V层在桥梁上“对话”。
package com.example.chuxiong.mvpdemo.presenter;
import android.os.Handler;
import com.example.chuxiong.mvpdemo.model.IMainLoading;
import com.example.chuxiong.mvpdemo.model.IMainLoadingListener;
import com.example.chuxiong.mvpdemo.model.MainLoadModel;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
import com.example.chuxiong.mvpdemo.view.IMainView;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.presenter
* @文件名: MainPresenter
* @创建者: chuxiong
* @创建时间: 2017/3/21 16:02
* @描述: TODO
*/
public class MainPresenter
implements IPresenter<IMainView>, IMainLoadingListener
{
private static final String TAG = "MainPresenter";
private IMainView mIMainView;//view层
private IMainLoading mIMainLoading;//model层
private Handler mHandler = new Handler();
public MainPresenter(IMainView IMainView) {
attachView(IMainView);
this.mIMainLoading = new MainLoadModel();
}
@Override
public void attachView(IMainView view) {
this.mIMainView = view;
}
@Override
public void detachView() {
this.mIMainView = null;
}
/**
* presenter层桥梁方法:model层和view层在该方法通信
*/
public void loadData() {
mIMainView.showProgress();
//制造延迟效果
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mIMainLoading.load(MainPresenter.this);
}
},3000);
}
@Override
public void loadDataSuccess(MainModelBean mainModelBean) {
mIMainView.showData(mainModelBean);
mIMainView.hideProgress();
}
@Override
public void loadDataFailure() {
mIMainView.hideProgress();
mIMainView.showLoginError();
}
}
- (1)有什么操作:1.加载数据;
- (2)这些操作结果是什么:1.加载成功,显示信息、 2.加载失败提示用户;
- (3)有什么用户交互:1.显示滚动框、 2.隐藏滚动框。
package com.example.chuxiong.mvpdemo.view;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
/*
* @项目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.view
* @文件名: IMainView
* @创建者: chuxiong
* @创建时间: 2017/3/21 14:39
* @描述: TODO
*/
public interface IMainView {
void showData(MainModelBean mainModelBean);
void showProgress();
void hideProgress();
/**失败提示用户*/
void showLoginError();
}
package com.example.chuxiong.mvpdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
import com.example.chuxiong.mvpdemo.presenter.MainPresenter;
import com.example.chuxiong.mvpdemo.view.IMainView;
public class MainActivity extends AppCompatActivity implements IMainView, View.OnClickListener {
private Button mBtnLoad;
private TextView mTv;
private ProgressBar mProgressBar;
private MainPresenter mMainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
mBtnLoad = (Button) findViewById(R.id.load_btn);
mTv = (TextView) findViewById(R.id.tv);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mBtnLoad.setOnClickListener(this);
mMainPresenter = new MainPresenter(this);
}
@Override
public void showData(MainModelBean mainModelBean) {
String showData = getResources().getString(R.string.city) + mainModelBean.getCity()
+ getResources().getString(R.string.wd) + mainModelBean.getWd()
+ getResources().getString(R.string.ws) + mainModelBean.getWs()
+ getResources().getString(R.string.time) + mainModelBean.getTime();
mTv.setText(showData);
}
@Override
public void showProgress() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
mProgressBar.setVisibility(View.GONE);
}
@Override
public void showLoginError() {
Toast.makeText(this,"加载失败",Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View v) {
/* new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mMainPresenter.loadData();
}
}, 5000);*/
mMainPresenter.loadData();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMainPresenter.detachView();
}
}