mvp
2017-01-19 16:59
309 查看
MVP是个好东西,可是最近项目一直用的是mvc模式,先马克下之前鼓捣过的mvp框架,过年回家再用它重构下代码。
建议写死依赖的版本号,而不要使用“+”,避免版本升级带了一些奇葩的问题。
在app.build依赖
再加上
然后在整个项目的build文件上面加入:
OK,到这里我们的环境搭建就完成了
首先我们看下项目的目录结构:
base文件
BaseContract(基本的文件类,我们可以在里面写上view层,model层,Presenter层的interface)
BaseModel(基本的model层,所有耗时操作应该写在model层中)
BaseView层(写入跟用户交互的方法集合类,比如showTosast,showDialog)
看下我们的基本baseActivity.java:
相对应的BaseFragment.java 代码如下:
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
LoginContract.java
LoginModel.java
LoginPresenter.java
LoginActivity.java
先上依赖库
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的学习
- 2016年7月微软MVP申请开始了!
- MVP on Android
- Android MVC、MVP和MVVP的概念、运用及区别
- 面试之路(3)-详解MVC,MVP,MVVM
- 教你认清MVC,MVP和MVVM
- Android App的设计架构:MVC,MVP,MVVM与架构经验谈
- MVC,MVP 和 MVVM
- Demo 基于 Retrofit_RxJava, 并且采用了 MVP 模式
- 解读Android官方MVP项目单元测试
- RxJava RxAndroid Retrofit RxBus Dragger2 OkHttp MVP MVVM DataBinding
- (Android架构)T-MVP:泛型深度解耦下的MVP大瘦身
- MVC,MVP,MVVM以及VIPER架构
- 设计模式MVP案例----省市查询
- T-MVP:泛型深度解耦下的MVP大瘦身
- MVP架构-Android官方MVP项目和响应式MVP-RxJava项目架构分析对比解读
- 从Dagger2基础到Google官方架构MVP+Dagger2架构详解
- MVP模式与MVC模式 概念
- android项目重构之mvp
- 从MVC到MVP,记一次代码重构