您的位置:首页 > 移动开发 > Android开发

mvp

2017-01-19 16:59 309 查看
MVP是个好东西,可是最近项目一直用的是mvc模式,先马克下之前鼓捣过的mvp框架,过年回家再用它重构下代码。


先上依赖库

compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'com.android.support:design:24.2.1'
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.github.chrisbanes.photoview:library:1.2.3'


建议写死依赖的版本号,而不要使用“+”,避免版本升级带了一些奇葩的问题。


依赖retrolambda

在app.build依赖
apply plugin: 'me.tatarka.retrolambda'


再加上
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}


然后在整个项目的build文件上面加入:
classpath 'me.tatarka:gradle-retrolambda:3.2.5'


OK,到这里我们的环境搭建就完成了

首先我们看下项目的目录结构:



base文件

BaseContract(基本的文件类,我们可以在里面写上view层,model层,Presenter层的interface)
/**
* Created by Ly on 2016/11/2.
*/

public class BaseContract  {
}


BaseModel(基本的model层,所有耗时操作应该写在model层中)
/**
* Created by Ly on 2016/11/2.
*/

public interface BaseModel {
}


BaseView层(写入跟用户交互的方法集合类,比如showTosast,showDialog)
/**
* Created by Ly on 2016/11/2.
*/

public interface BaseView {
void TsShow(String msg);
}


看下我们的基本baseActivity.java:
package com.Ly.BaseJustTalk.base;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.MenuItem;
import android.widget.Toast;

import com.Ly.BaseJustTalk.R;

import butterknife.ButterKnife;

/**
* Created by Ly on 2017/1/12.
*/
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {

protected T mPresenter;
private SwipeRefreshLayout mRefreshLayout;
private AppBarLayout mAppBar;
private Toolbar mToolbar;
private ProgressDialog mProgressBar;
protected Context mContext;
private boolean mIsRequestDataRefresh = false;

private static final String EXTRA_KEY = "extra";

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dogetExtra();
mContext = this;
// 允许为空,不是所有的页面都要实现这个模式
if (createPresenter() != null) {
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}
setContentView(provideContentViewId());
ButterKnife.bind(this);

mAppBar = findViewById(R.id.app_bar_layout);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null && mAppBar != null) {
setSupportActionBar(mToolbar); //把Toolbar当做ActionBar给设置

if (canBack()) {
ActionBar actionBar = getSupportActionBar();
if (null != actionBar) {
//设置ActionBar一个返回箭头,主界面没有,次级界面有
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (Build.VERSION.SDK_INT >= 21) {
//Z轴浮动
mAppBar.setElevation(10.6F);
}
}
}
if (isSetRefresh()) {
setupSwipeRefresh();
}
}

public static void doStartActivity(Context context, Bundle bundle, Class<?> clz) {
Intent intent = new Intent(context, clz);
if (bundle != null) {
intent.putExtra(EXTRA_KEY, bundle);
}
context.startActivity(intent);
}

protected abstract void dogetExtra();

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 此时android.R.id.home即为返回箭头
if (item.getItemId() == android.R.id.home) {
onBackPressed();
finish();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
}

/**
* 生成下拉刷新
*/
private void setupSwipeRefresh() {
mRefreshLayout = findViewById(R.id.swipe_refresh);
if (null != mRefreshLayout) {
mRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary);
mRefreshLayout.setProgressViewOffset(true,
0,
(int) TypedValue.applyDimension
(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()
.getDisplayMetrics()));
}
}

/**
* 设置刷新
*
* @param requestDataRefresh
*/
public void setRefresh(boolean requestDataRefresh) {
if (mRefreshLayout == null) {
return;
}
if (!requestDataRefresh) {
mIsRequestDataRefresh = false;
mRefreshLayout.postDelayed(() -> {
if (mRefreshLayout != null) {
mRefreshLayout.setRefreshing(false);
}
}, 1000);
} else {
mRefreshLayout.setRefreshing(true);
}
}

/**
* 数据刷新
*/
public void requestDataRefresh() {
mIsRequestDataRefresh = true;
}

/**
* 判断当前页面是否可以返回,
* 主界面不可以返回  子界面可以放回
*
* @return
*/
public boolean canBack() {
return false;
}

/**
* 判断子Activity是否需要上下拉刷新功能
*
* @return
*/
public Boolean isSetRefresh() {
return false;
}

/**
* 创建P
*
* @return
*/
protected abstract T createPresenter();

/**
* 引入布局文件
*
* @return
*/
protected abstract int provideContentViewId();

protected void ShowTs(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

protected void ShowDialog() {
ShowDialog(null);
}

protected void ShowDialog(String msg) {
if (TextUtils.isEmpty(msg)) {
msg = getString(R.string.loading);
}
mProgressBar = ProgressDialog.show(this, null, msg);
}

protected void DissDialog() {
if (mProgressBar != null && mProgressBar.isShowing()) {
mProgressBar.dismiss();
}
}
}


相对应的BaseFragment.java 代码如下:
package com.Ly.BaseJustTalk.base;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.Ly.BaseJustTalk.R;

import butterknife.ButterKnife;

/**
* Created by Ly on 2011/1/12.
*/

public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
protected Context mContext;
protected T mPresenter;

private boolean mIsRequestDataRefresh = false;
private SwipeRefreshLayout mRefreshLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(createViewLayoutId(), container, false);
ButterKnife.bind(this, rootView);
initView(rootView);
if (isSetRefresh()) {
setupSwipeRefresh(rootView);
}
return rootView;
}

@Override
public void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}

private void setupSwipeRefresh(View view) {
mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
if (mRefreshLayout != null) {
mRefreshLayout.setColorSchemeResources(R.color.refresh_progress_1,
R.color.refresh_progress_2, R.color.refresh_progress_3);
mRefreshLayout.setProgressViewOffset(true, 0, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
mRefreshLayout.setOnRefreshListener(this::requestDataRefresh);
}
}

public void requestDataRefresh() {
mIsRequestDataRefresh = true;
}

public void setRefresh(boolean requestDataRefresh) {
if (mRefreshLayout == null) {
return;
}
if (!requestDataRefresh) {
mIsRequestDataRefresh = false;
mRefreshLayout.postDelayed(() -> {
if (mRefreshLayout != null) {
mRefreshLayout.setRefreshing(false);
}
}, 1000);
} else {
mRefreshLayout.setRefreshing(true);
}
}

protected abstract T createPresenter();

protected abstract int createViewLayoutId();

protected void initView(View rootView) {
}

public Boolean isSetRefresh() {
return true;
}
}


Ok,直接看实例代码来理解,就用登录界面的吧。



登录界面的包目录

其中LoginActiviy为用户能看到的界面;

Contract是mvp的协议层;Model为数据处理层;Presenter可以理解为桥梁,负责沟通连接M层和V层。

在我的理解里的MVP是这个意思:我们可以看到,这个类持有了View和Model两个模块,在方法体里面,我们调用了model的方法去做耗时,在结果方法体里面我们调用了view的方法去修改UI,同时presenter这个模块又被view持有了,view可以在声明周期里面去调用特定的方法,view和presenter相互沟通,view和model完全隔离,presenter调控model,presenter沟通全局。

看下我们的登录界面:

activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5"
android:gravity="center"
android:text="@string/text_welcome"
android:textSize="25sp" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="@dimen/margin_left"
android:layout_marginRight="@dimen/margin_right"
android:layout_weight="3"
android:orientation="vertical">

<android.support.design.widget.TextInputLayout
android:id="@+id/til_username"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:id="@+id/et_username"
style="@style/edit_style"
android:inputType="text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/text_username" />
</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="@dimen/margin6">

<EditText
android:id="@+id/et_password"
style="@style/edit_style"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/text_password" />
</android.support.design.widget.TextInputLayout>

<Button
android:id="@+id/bt_login"
style="@style/button_style"
android:layout_width="match_parent"
android:layout_height="@dimen/button_height"
android:layout_marginTop="@dimen/margin20"
android:text="@string/text_login_reg" />
</LinearLayout>
</LinearLayout>


LoginContract.java
package com.Ly.BaseJustTalk.ui.activity.Login;

import com.Ly.BaseJustTalk.base.BaseModel;
import com.Ly.BaseJustTalk.base.BaseView;

/**
* Created by Ly on 2017/1/16.
*/
public class LoginContract {

interface LoginView extends BaseView {
String getUserName();

String getUserPass();

//            userName错误后进行提示
void setUserNameErr(String errMsg);

//            密码错误后进行提示
void setPassErr(String errMsg);

void doJustalkLogin();

void doLoginFial();
}

interface LoginModel extends BaseModel {

/**
* @param userName
* @param Pass
* @return 0 合法账户密码 给予执行下一步
* -1 用户名为空
* -2 用户密码为空
* -3 用户名不合法
* -4 用户密码不合法
*/
int isRightUserNamePass(String userName, String Pass);

/**
* 进行justalk的登录
*
* @param userName
* @param pass
* @return
*/
boolean doJustalkLogin(String userName, String pass);
}

interface LoginPresenter {
//        验证用户输入的信息
void doVerificationInfo();

//          验证正确后进行登录
void doJustalkLogin();
}
}


LoginModel.java
package com.Ly.BaseJustTalk.ui.activity.Login;

import android.text.TextUtils;
import android.util.Log;

import com.Ly.BaseJustTalk.R;
import com.Ly.BaseJustTalk.application.LyApplication;
import com.justalk.cloud.juslogin.LoginDelegate;

/**
* Created by Administrator on 2017/1/16.
*/
public class LoginModel implements LoginContract.LoginModel {

/**
* 验证账号合法性
*
* @param userName
* @param Pass
* @return 0 合法账户密码 给予执行下一步
* -1 用户名为空
* -2 用户密码为空
* -3 用户名不合法
* -4 用户密码不合法
*/
@Override
public int isRightUserNamePass(String userName, String Pass) {
if (TextUtils.isEmpty(userName)) {
return -1;
} else if (TextUtils.isEmpty(Pass)) {
return -2;
} else if (userName.length() < 4 || userName.length() > 12) {
return -3;
} else if (Pass.length() < 6 || Pass.length() > 12) {
return -4;
} else
return 0;
}

@Override
public boolean doJustalkLogin( String userName, String pass) {
if (LoginDelegate.getInitState() == LoginDelegate.InitStat.MTC_INIT_FAIL) {
return false;
}
String server = LyApplication.getInstance().getString(R.string.JusTalkCloud_network_address);
String network = null;
if (!server.startsWith("sudp:")) {
network = server;
server = "sarc:arc@AccessEntry:99";
Log.e("testtest", server);
}
if (LoginDelegate.login(userName, pass, server, network)) {
return true;
}
return false;
}
}


LoginPresenter.java
package com.Ly.BaseJustTalk.ui.activity.Login;

import com.Ly.BaseJustTalk.base.BasePresenter;

/**
* Created by Ly on 2017/1/16.
*/
public class LoginPresenter extends BasePresenter<LoginContract.LoginView> implements
LoginContract.LoginPresenter {
private LoginContract.LoginView loginView;
private LoginContract.LoginModel loginModel;

public LoginPresenter(LoginContract.LoginView loginView) {
this.loginView = loginView;
loginModel = new LoginModel();
}

@Override
public void doVerificationInfo() {
int resultcode = this.loginModel.isRightUserNamePass(this.loginView.getUserName(), this.loginView.getUserPass());
switch (resultcode) {
case -1:
this.loginView.setUserNameErr("请输入用户名");
break;
case -2:
this.loginView.setPassErr("请输入用户密码");
break;
case -3:
this.loginView.setUserNameErr("请检查用户名格式或长度");
break;
case -4:
this.loginView.setPassErr("请检查密码格式或长度");
break;
case 0:
this.loginView.setUserNameErr(null);
this.loginView.setPassErr(null);
this.loginView.doJustalkLogin();
break;
default:
this.loginView.TsShow("未知错误");
break;
}
}

@Override
public void doJustalkLogin() {
boolean isLoginSuccess = this.loginModel.doJustalkLogin(this.loginView.getUserName(), this.loginView.getUserPass());
if (!isLoginSuccess) {
this.loginView.doLoginFial();
}
}
}


LoginActivity.java
package com.Ly.BaseJustTalk.ui.activity.Login;

import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;

import com.Ly.BaseJustTalk.R;
import com.Ly.BaseJustTalk.base.BaseActivity;
import com.Ly.BaseJustTalk.ui.activity.MainActivity;
import com.Ly.BaseJustTalk.utils.Signer;
import com.justalk.cloud.juslogin.LoginDelegate;
import com.justalk.cloud.lemon.MtcCli;
import com.justalk.cloud.lemon.MtcCliConstants;

import butterknife.Bind;
import butterknife.OnClick;

/**
* Created by Ly on 2017/1/16.
*/
public class LoginActivity extends BaseActivity<LoginContract.LoginView, LoginPresenter>
implements LoginContract.LoginView, LoginDelegate.Callback {
@Bind(R.id.et_username)
EditText etUsername;
@Bind(R.id.til_username)
TextInputLayout tilUsername;
@Bind(R.id.et_password)
EditText etPassword;
@Bind(R.id.til_password)
TextInputLayout tilPassword;
@Bind(R.id.bt_login)
Button btLogin;

private LoginContract.LoginPresenter loginPresenter = new LoginPresenter(this);

@OnClick(R.id.bt_login)
void toLogin() {
loginPresenter.doVerificationInfo();
}

@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(this);
}

@Override
protected int provideContentViewId() {
return R.layout.activity_login;
}

@Override
public void TsShow(String msg) {
ShowTs(msg);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LoginDelegate.setCallback(this);
}

@Override
protected void dogetExtra() {

}

@Override
public String getUserName() {
return etUsername.getText().toString().trim();
}

@Override
public String getUserPass() {
return etPassword.getText().toString().trim();
}

@Override
public void setUserNameErr(String errMsg) {
tilUsername.setError(errMsg);
}

@Override
public void setPassErr(String errMsg) {
tilPassword.setError(errMsg);
}

@Override
public void doJustalkLogin() {
ShowDialog();
if (MtcCli.Mtc_CliGetState() != MtcCliConstants.EN_MTC_CLI_STATE_LOGINED) {
this.loginPresenter.doJustalkLogin();
} else {
MainActivity.doStartActivity(mContext, null, MainActivity.class);
}
}

@Override
public void doLoginFial() {
ShowTs(getString(R.string.tips_login_fail));
}

@Override
public void mtcLoginOk() {
MainActivity.doStartActivity(mContext, null, MainActivity.class);
}

@Override
public void mtcLoginDidFail() {
ShowTs(getString(R.string.tips_login_fail_justalk));
}

@Override
public void mtcLogoutOk() {

}

@Override
public void mtcLogouted() {

}

@Override
public void mtcAuthRequire(String s, String s1) {
Log.e("LHT", "mtcAuthRequire: " + s + "----" + s1);
String key =
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
"tRPBKBjUTEQwaKEjPy8YnX1bUODPB+7hto8KeGJbnCdCcnJdLxPFE7ld1skKIyPi" +
"YkyHj73JqA41ntHML2LNqw5Mhs1pewE4QLCu6icIUNtH8+bL53EhVnfdzwIDAQAB" +
"AoGAA6i6c5xHEGfKzoDHQJgiC5X9ZFAtES5AG3IMJmtF9flQyeoeDzRit+/FwNXi" +
"M1CKohnvLAJTvPs/8TBp5us4rabQ5Hnp+ylr7I2IbYIP2LV6TKkiTq/fBOJBxZiw" +
"qs0tjXxRZnC2IWqoCt/ciE4DXQIYVV3gYMRcKae5KZ3F2LECQQDqL4Sd2xyG0FsW" +
"cKwrlFRQ1cfFrSF/jmm2onkCZgDfq0R5aIGewpbTciLj8rf/Zq0XgAmCa3qQYo6M" +
"7G0OgIXTAkEAxbIC2xJocvPfEFbUd+hEWDFl/3LtQWZSHVLx9SXLXWSRY4/3dyRM" +
"6L78eQ2yWIVF4pxJrIHTbJqhxItlVM/elQJBAJ3jRZ0L8hKufQsHEf0btzD8wQB0" +
"doZCZOF+bumADgy+sp7MJ7/636dVZ1KZ/RWTixWx/DdS8UJRQFygtfI2EoMCQHky" +
"4tFPfb1LiStJMES6nnu6/R8YZB++DQVxPmjeXMjKyN9S+ZGPLZ9axwmnvfjK68c7" +
"rWcWyHlCa35FP0A5l+kCQB5cEu5Au1RkY9XfUodKmFhlCvdY8Ig0JgZ8DC6m+A31" +
"o4xrCoGHiPldKdCo0I7gQ4WMvoVNHCQyNv5qcw9t7uk=";
String code = Signer.signWithKey(key, s, s1, 3600);
LoginDelegate.promptAuthCode(code);
}

@Override
protected void onDestroy() {
super.onDestroy();
LoginDelegate.logout();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android mvp