1.MVP
Model-view-presenter (MVP) 是使用者接口設計模式的一種。 隨着UI創建技術的功能日益增強,UI層也履行着越來越多的職責。爲了更好地細分視圖(View)與模型(Model)的功能,讓View專注於處理數據的可視化以及與用戶的交互,同時讓Model只關係數據的處理,基於MVC概念的MVP 模式應運而生。
說明:
M層 對P層傳遞過來的信息(userInfo進行登錄(網絡請求))處理,處理完成之後將處理結果回調給P層
V層 負責響應用戶的交互(獲取數據—->提示操作結果)
P層 傳遞完數據給M層處理之後,實例化回調對象,成功了就通知V層登錄成功,並將相關信息傳給V層 ,失敗了就通知V層顯示錯誤信息,即數據邏輯的處理在P層,也相當於一個控制器,起到紐帶的作用,這樣也就使得M 層和V 層處於一個完全分離的狀態,更好地專注其自身層的業務。
網上的這張圖片說得更直觀
2.MVP的優點編輯
1、模型與視圖完全分離,我們可以修改視圖而不影響模型
2、可以更高效地使用模型,因爲所有的交互都發生在一個地方——Presenter內部
3、我們可以將一個Presenter用於多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因爲視圖的變化總是比模型的變化頻繁。
4、如果我們把邏輯放在Presenter中,那麼我們就可以脫離用戶接口來測試這些邏輯(單元測試)
3.舉個栗子
無圖無真相,我們這是無代碼無真理,下面簡單地嘗試實現一個用mvp 實現的登陸小栗子(ps:網上很多例子也是從登陸開始,估計比較簡單吧)。
1.佈局代碼(用ConstraintLayout)
佈局比較簡單,可以直接忽略
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ECEDF1">
<TextView
android:id="@+id/tv_login_title"
android:layout_width="384dp"
android:layout_height="wrap_content"
android:background="#12B7F5"
android:gravity="center"
android:padding="20dp"
android:text="添加賬號"
android:textColor="#ffffff"
android:textSize="18sp"
tools:ignore="HardcodedText,MissingConstraints"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp" />
<TextView
android:id="@+id/tv_login_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:padding="20dp"
android:text="取消"
android:textColor="#ffffff"
app:layout_constraintHorizontal_bias="0.99"
app:layout_constraintLeft_toLeftOf="@+id/tv_login_title"
app:layout_constraintRight_toRightOf="@+id/tv_login_title"
tools:ignore="HardcodedText,MissingConstraints"
tools:layout_editor_absoluteY="0dp" />
<EditText
android:id="@+id/et_login_name"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginBottom="16dp"
android:layout_marginTop="12dp"
android:background="#ffffff"
android:ems="10"
android:hint="賬號/手機號/郵箱"
android:inputType="textPersonName"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_login_title"
app:layout_constraintVertical_bias="0.13"
tools:ignore="HardcodedText,LabelFor,MissingConstraints,RtlHardcoded" />
<EditText
android:id="@+id/et_login_password"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginBottom="16dp"
android:layout_marginTop="2dp"
android:background="#ffffff"
android:ems="10"
android:hint="密碼"
android:inputType="textPassword"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_login_name"
app:layout_constraintVertical_bias="0.0"
tools:ignore="HardcodedText,LabelFor" />
<Button
android:id="@+id/btn_login_to_login"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginBottom="16dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:background="@drawable/login_login_button_bg"
android:text="登 陸"
android:textColor="#ffffff"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.56"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_login_password"
app:layout_constraintVertical_bias="0.03"
tools:ignore="HardcodedText" />
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="@string/cb_login_agree"
android:textSize="8sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_login_to_login"
app:layout_constraintVertical_bias="0.03"
tools:ignore="MissingConstraints,SmallSp" />
</android.support.constraint.ConstraintLayout>
2.我們先看下包結構
簡單的包結構如下:
各層次包名還算比較清晰,就不多說了。需要說明下,請求網絡驗證用戶信息並沒有做,只是做了模擬。
1.UserInfo.java 普通的bean,只有userName和userPassword字段以及get 和set方法。
2.ILoginModel.java
從名字上看就知道是 M層的接口,實現了登陸功能,代碼如下
public interface ILoginModel {
//登錄
void login(UserInfo userInfo, Result result);
}
3.LoginModel.java
M層接口實現類,負責處理網絡請求返回相應結果
public class LoginModel implements ILoginModel {
@Override
public void login(UserInfo userInfo, Result result) {
//模擬時延
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//模擬網絡請求部分
if("XX".equals(userInfo.getUserName())&&"123".equals(userInfo.getUserPassword())){
result.success();
}else {
result.error();
}
}
4.Reset.java
模擬網絡請求結果返回
public interface Result {
void success();
void error();
}
5.ILoginPresenter.java
P 層接口,聯通V 和M 層的橋樑。
public interface ILoginPresenter {
void login();
}
6.LoginPresenter.java
P層接口實現類,相關邏輯在此處理
public class LoginPresenter implements ILoginPresenter {
private ILoginModel loginModel;
private ILoginView loginView;
public LoginPresenter( ILoginView loginView) {
this.loginModel = new LoginModel();
this.loginView = loginView;
}
@Override
public void login() {
if(TextUtils.isEmpty(loginView.getUserLoginInfo().getUserName())){
loginView.showErrorMsg("用戶名不能爲空");
return;
}
if(TextUtils.isEmpty(loginView.getUserLoginInfo().getUserPassword())){
loginView.showErrorMsg("密碼不能爲空");
return;
}
loginModel.login(loginView.getUserLoginInfo(), new Result() {
@Override
public void success() {
loginView.showInfo("登陸成功!");
}
@Override
public void error() {
loginView.showErrorMsg("錯誤");
}
});
}
}
7.ILoginView.java
V層接口,View需要顯示和用戶響應操作都在這裏。
public interface ILoginView {
void showInfo(String info);//提示用戶
void showErrorMsg(String msg);//發生錯誤就顯示錯誤信息
UserInfo getUserLoginInfo();//獲取用戶登錄信息
}
8.MainActivity.java
V層實現類,也就是我們activity。
public class MainActivity extends AppCompatActivity implements ILoginView{
@Bind(R.id.et_login_name)
EditText etLoginName;
@Bind(R.id.et_login_password)
EditText etLoginPassword;
//一個p對象
private LoginPresenter presenter;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mContext = this;
presenter = new LoginPresenter(this);
}
@OnClick({R.id.tv_login_cancel, R.id.btn_login_to_login, R.id.checkBox})
public void onClick(View view) {
switch (view.getId()) {
case R.id.tv_login_cancel:
finish();
break;
case R.id.btn_login_to_login:
presenter.login();
break;
case R.id.checkBox:
break;
}
}
@Override
public void showInfo(String info) {
Toast.makeText(mContext,"登陸"+info,Toast.LENGTH_SHORT).show();
}
@Override
public void showErrorMsg(String msg) {
Toast.makeText(mContext,"登陸錯誤"+msg,Toast.LENGTH_SHORT).show();
}
@Override
public UserInfo getUserLoginInfo() {
return new UserInfo(etLoginName.getText().toString().trim()
,etLoginPassword.getText().toString().trim());
}
}
一個簡單mvp 模式的小栗子基本到這裏就結束了,裏面用到了ConstraintLayout佈局和ButterKnife,需要在gradle 依賴中引入如下代碼:
compile ‘com.android.support.constraint:constraint-layout:1.0.0-alpha9’
compile ‘com.jakewharton:butterknife:7.0.1’