MVP淺析

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’

源碼下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章