一直想记录下最近学习MVP模式的一些心得,又怕写的太渣,没办法,先写写然后及时更新。 我会粘贴一些代码,对于MVP模式的理解会在代码中详细注释
例一:闪屏界面判断是否已经登录
/**
* MVP 思想 : MVP中Activity/fragment属于View ,因此要有一个View的接口,然后Activity/fragment实现View接口
* P 中创建业务层 Presenter中定义功能,实现类PresenterImpl中操作功能/逻辑
* 在Activity/fragment中 声明初始化 p,只能new SplashPresenter 的实现类,不能new SplashPresenter接口,
* P 中一般要回调Activity/fragment 中的方法,所以要传入“this”,(PresenterImpl 是 Presenter的实现类)
* 在 p 中生成构造函数,xxView(因为是面向接口,生成的是xxView构造函数,而不是xxActivity构造函数)
* Activity/fragment 中的回调方法一般都添加到View中去,而真正调用的是在PresenterImpl中,在Activity/fragment实现逻辑操作
*
* View: 对于View层也是视图层,在View层中只负责对数据的展示,提供友好的界面与用户进行交互。
* 在Android开发中通常将Activity或者Fragment作为View层。View与UI相关的
*
* Model: 对于Model层也是数据层。它区别于MVC架构中的Model,在这里不仅仅只是数据模型。
* 在MVP架构中Model它负责对数据的存取操作,例如对数据库的读写,网络的数据的请求等。
*
* Presenter:对于Presenter层他是连接View层与Model层的桥梁并对业务逻辑进行处理。
* 在MVP架构中Model与View无法直接进行交互。所以在Presenter层它会从Model层获得所需要的数据,
* 进行一些适当的处理后交由View层进行显示。这样通过Presenter将View与Model进行隔离,
* 使得View和Model之间不存在耦合,同时也将业务逻辑从View中抽离。
*/
//View层
public class SplashActivity extends BaseActivity implements SplashView {
private static final long DURATION = 2000;
private SplashPresenter mSplashPresenter;
@InjectView(R.id.iv_splash)
ImageView mIvSplash;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
ButterKnife.inject(this);
//TODO
/**
* 在Activity/fragment中 声明初始化 p ,只能new SplashPresenter 的实现类,不能new SplashPresenter接口
*/
mSplashPresenter = new SplashPresenterImpl(this);
/**
* 1. 判断是否已经登录了 P层
* 2. 如果登录了,直接进入MainActivity view层
* 3. 否则闪屏2秒后(添加渐变动画),进入LoginActivity view层
*/
mSplashPresenter.checkLogined();
}
//Activity/fragment 中的回调方法一般都添加到View中去,而真正调用的是在PresenterImpl中,在Activity/fragment实现逻辑操作
@Override
public void onCheckedLogin(boolean isLogined) {
if (isLogined) {
//跳转Activity类抽取 MainActivity.class :要调转的类 ,true :跳转之后finish掉
startActivity(MainActivity.class, true);
} else {
// 否则闪屏2秒后(添加渐变动画),进入LoginActivity
ObjectAnimator alpha = ObjectAnimator.ofFloat(mIvSplash, "alpha", 0, 1).setDuration(DURATION);
alpha.start();
//动画执行完再跳转,直接回调监听,不需要handler
alpha.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
startActivity(LoginActivity.class, true);
}
});
}
}
}
/**
* 作者: itheima
* 时间:2016-10-15 10:50
* 网址:http://www.itheima.com
* 生成构造函数,xxView(因为是面向接口,生成的是xxView构造函数,而不是xxActivity构造函数)
*/
public interface SplashView {
void onCheckedLogin(boolean isLogined);
}
P层:
/**
* 作者: itheima
* 时间:2016-10-15 10:50
* 网址:http://www.itheima.com
* P 中创建业务层 Presenter中定义功能,实现类PresenterImpl中操作功能/逻辑
*/
//创建业务层 Presenter中定义功能
public interface SplashPresenter {
void checkLogined();
}
/**
* 作者: itheima
* 时间:2016-10-15 10:51
* 网址:http://www.itheima.com
* P 中创建业务层 Presenter中定义功能,实现类PresenterImpl中操作功能/逻辑
*/
public class SplashPresenterImpl implements SplashPresenter {
//在 p 中生成构造函数,xxView(因为是面向接口,生成的是xxView构造函数,而不是xxActivity构造函数)
private SplashView mSplashView;
public SplashPresenterImpl(SplashView splashView) {
mSplashView = splashView;
}
//实现类PresenterImpl中操作功能/逻辑
@Override
public void checkLogined() {
if (EMClient.getInstance().isLoggedInBefore() && EMClient.getInstance().isConnected()) {
//已经登录过了
mSplashView.onCheckedLogin(true);
} else {
//还未登录
mSplashView.onCheckedLogin(false);
}
}
}
例二:用户登录是否成功,保存账户和密码
View层:
public class LoginActivity extends BaseActivity implements TextView.OnEditorActionListener ,LoginView{
private static final int REQUEST_SDCARD = 1;
@InjectView(R.id.et_username)
EditText mEtUsername;
@InjectView(R.id.til_username)
TextInputLayout mTilUsername;
@InjectView(R.id.et_pwd)
EditText mEtPwd;
@InjectView(R.id.til_pwd)
TextInputLayout mTilPwd;
@InjectView(R.id.btn_login)
Button mBtnLogin;
@InjectView(R.id.tv_newuser)
TextView mTvNewuser;
private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
// window.setNavigationBarColor(Color.TRANSPARENT);
}
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
/**
* 数据的回显
*/
mEtUsername.setText(getUserName());
mEtPwd.setText(getPwd());
mEtPwd.setOnEditorActionListener(this);
mLoginPresenter = new LoginPresenterImpl(this);
}
/**
* 当再次startActivity的时候,接收新的Intent对象
* 调用的前提是该启动模式是singleTask,或者singleTop但是他得在最上面才有效
* @param intent
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mEtUsername.setText(getUserName());
mEtPwd.setText(getPwd());
}
@OnClick({R.id.btn_login, R.id.tv_newuser})
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_login:
login();
break;
case R.id.tv_newuser:
startActivity(RegistActivity.class,false);
break;
}
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (v.getId()==R.id.et_pwd){
if (actionId== EditorInfo.IME_ACTION_DONE){
login();
}
}
return false;
}
private void login() {
String username = mEtUsername.getText().toString().trim();
String pwd = mEtPwd.getText().toString().trim();
if (!StringUtils.checkUsername(username)){
mTilUsername.setErrorEnabled(true);
mTilUsername.setError("用户名不合法");
mEtUsername.requestFocus(View.FOCUS_RIGHT);
return;
}else {
mTilUsername.setErrorEnabled(false);
}
if (!StringUtils.checkPwd(pwd)){
mTilPwd.setErrorEnabled(true);
mTilPwd.setError("密码不合法");
mEtPwd.requestFocus(View.FOCUS_RIGHT);
return;
}else{
mTilPwd.setErrorEnabled(false);
}
/**
* 1. 动态申请权限
*/
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PermissionChecker.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},REQUEST_SDCARD);
return;
}
showDialog("正在玩命登录中...");
mLoginPresenter.login(username,pwd);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode==REQUEST_SDCARD){
if (grantResults[0]==PermissionChecker.PERMISSION_GRANTED){
//被授权了
login();
}else{
showToast("没有给予该应用权限,不让你用了");
}
}
}
@Override
public void onLogin(String username, String pwd, boolean success, String msg) {
hideDialog();
if (success){
/**
* 1.保存用户
* 2. 跳转到主界面
*/
saveUser(username, pwd);
startActivity(MainActivity.class,true);
}else {
/**
* 1.Toast
*/
showToast("登录失败了:"+msg);
}
}
}
/**
* 作者: itheima
* 时间:2016-10-15 17:13
* 网址:http://www.itheima.com
*/
public interface LoginView {
void onLogin(String username,String pwd,boolean success,String msg);
}
P层:P 中创建业务层 Presenter中定义功能,实现类PresenterImpl中操作功能/逻辑
/**
* 作者: itheima
* 时间:2016-10-15 17:13
* 网址:http://www.itheima.com
*/
public interface LoginPresenter {
void login(String username,String pwd);
}
/**
* 作者: itheima
* 时间:2016-10-15 17:14
* 网址:http://www.itheima.com
*/
public class LoginPresenterImpl implements LoginPresenter {
private LoginView mLoginView;
public LoginPresenterImpl(LoginView loginView) {
mLoginView = loginView;
}
@Override
public void login(final String username, final String pwd) {
//环信目前(3.5.x)的所有回调方法都是在子线程中回调的
EMClient.getInstance().login(username, pwd, new CallBackListener() {
@Override
public void onMainSuccess() {
mLoginView.onLogin(username,pwd,true,null);
}
@Override
public void onMainError(int i, String s) {
mLoginView.onLogin(username,pwd,false,s);
}
});
}
}
例三:注册是否成功,成功保存账户密码进入登录页面,失败吐司
View层:
public class RegistActivity extends BaseActivity implements TextView.OnEditorActionListener, RegistView {
@InjectView(R.id.et_username)
EditText mEtUsername;
@InjectView(R.id.til_username)
TextInputLayout mTilUsername;
@InjectView(R.id.et_pwd)
EditText mEtPwd;
@InjectView(R.id.til_pwd)
TextInputLayout mTilPwd;
@InjectView(R.id.btn_regist)
Button mBtnRegist;
private RegistPresenter mRegistPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
// window.setNavigationBarColor(Color.TRANSPARENT);
}
setContentView(R.layout.activity_regist);
ButterKnife.inject(this);
mRegistPresenter = new RegistPresenterImpl(this);
//给UI绑定事件
mEtPwd.setOnEditorActionListener(this);
}
@OnClick(R.id.btn_regist)
public void onClick() {
regist();
}
private void regist() {
String username = mEtUsername.getText().toString().trim();
String pwd = mEtPwd.getText().toString().trim();
if (!StringUtils.checkUsername(username)){
mTilUsername.setErrorEnabled(true);
mTilUsername.setError("用户名不合法");
mEtUsername.requestFocus(View.FOCUS_RIGHT);
return;
}else {
mTilUsername.setErrorEnabled(false);
}
if (!StringUtils.checkPwd(pwd)){
mTilPwd.setErrorEnabled(true);
mTilPwd.setError("密码不合法");
mEtPwd.requestFocus(View.FOCUS_RIGHT);
return;
}else{
mTilPwd.setErrorEnabled(false);
}
showDialog("正在注册...");
mRegistPresenter.regist(username,pwd);
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (v.getId() == R.id.et_pwd) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
regist();
return true;
}
}
return false;
}
@Override
public void onRegist(String username, String pwd, boolean isSuccess, String msg) {
hideDialog();
if (isSuccess){
/**
* 将注册成功的数据保存到本地
* 跳转到登录界面
*/
saveUser(username, pwd);
startActivity(LoginActivity.class,true);
}else {
/**
* 弹吐司,告诉用户失败了
*/
showToast("注册失败:"+msg);
}
}
}
/**
* 作者: itheima
* 时间:2016-10-15 11:49
* 网址:http://www.itheima.com
*/
public interface RegistView {
void onRegist(String username,String pwd,boolean isSuccess,String msg);
}
P层:
/**
* 作者: itheima
* 时间:2016-10-15 11:47
* 网址:http://www.itheima.com
*/
public interface RegistPresenter {
void regist(String username,String pwd);
}
/**
* 作者: itheima
* 时间:2016-10-15 11:48
* 网址:http://www.itheima.com
*/
public class RegistPresenterImpl implements RegistPresenter {
private RegistView mRegistView;
public RegistPresenterImpl(RegistView registView) {
mRegistView = registView;
}
@Override
public void regist(final String username, final String pwd) {
/**
* 1. 先注册Bmob云数据库
* 2. 如果Bmob成功了再去注册环信平台
* 3. 如果Bmob成功了,环信失败了,则再去把Bmob上的数据给删除掉
*/
User user = new User();
user.setPassword(pwd);
user.setUsername(username);
user.signUp(new SaveListener() {
//Bmob中的回调方法都是在主线程中被调用的
@Override
public void done(final User user, BmobException e) {
if (e==null){
//new Thread。。。new线程是一个很耗时的操作,不如牺牲空间换时间
//提前把线程创建好,不用每次都要new线程
//成功了再去注册环信平台
ThreadUtils.runOnSubThread(new Runnable() {
@Override
public void run() {
try {
EMClient.getInstance().createAccount(username, pwd);
//环信注册成功 主线程中回调
ThreadUtils.runOnMainThread(new Runnable() {
@Override
public void run() {
mRegistView.onRegist(username,pwd,true,null);
}
});
} catch (final HyphenateException e1) {
e1.printStackTrace();
//将Bmob上注册的user给删除掉
user.delete();
//环信注册失败了
ThreadUtils.runOnMainThread(new Runnable() {
@Override
public void run() {
mRegistView.onRegist(username,pwd,false,e1.toString());
}
});
}
}
});
}else {
//失败了,将结果告诉Activity
mRegistView.onRegist(username,pwd,false,e.getMessage());
}
}
});
}
}
另外我也参考了一些资料:http://chuansong.me/n/632293551521
http://chuansong.me/n/2271649851831
希望对你有所帮助