Google官方架構MVP解析與實戰

wbqdavid 8年前發布 | 84K 次閱讀 Android開發 移動開發

1 前言

當然對于MVP的解說也是使用也是層出不窮,我也網絡上也能看到各種版本的解說,之前博客也有文章的更新,里面有MVP的詳細說明和項目代碼--->Android中的MVP模式,帶實例

本篇文章將參考 google官方android MVP架構項目的實現,來實現自己的項目。或許看了這篇文章之后,你再去梳理一下google官方架構項目,會讓你收獲更多。官方的實例肯定具有更好的權威性

推薦關注安卓各種架構相關文章合集github地址:AndroidArchitectureCollection

2 google官方MVP架構解析

1 項目目錄

打開github,展開項目目錄,會發現項目結構的組織方式是按照功能進行分模塊的,當然根據個人情況,也可以按照ui,model,view,presenter這種情況進行劃分組織目錄。

google官方架構MVP解析與實戰

google官方MVP架構目錄視圖


2 具體實現流程

我們將關注度放到具體的一個taskdetail模塊當中來解析實現MVP的流程。

google官方架構MVP解析與實戰

taskdetail模塊


2.1 TaskDetailContract
可以看到這里是通過一個協議類XXXContract來對View和Presenter的接口進行繼承。這樣做的好處也就是,我們可以將基礎的View層的操作放在BaseView里面,對基礎的Presenter層的操作放在BasePresenter里面。減少后續代碼的重復。一個協議類也將View和Presenter管理起來,方便操作。

google官方架構MVP解析與實戰


2.2 BaseView
那么來看看BaseView,主要是有一個setPresenter的操作,MVP中Presenter和View層是需要交互的,這里通過setPresenter操作,我們也就可以獲得Presenter層的實例進行交互了。所以在我們自己的代碼中,我們也可以將加載的loading,以及加載錯誤頁面,加載失敗頁面等操作放在BaseView里面,這是每個View都會有的:

google官方架構MVP解析與實戰

2.3 BasePresenter
BasePresenter中只有一個start方法,表示“開始”,我們可以在這里進行數據加載初始化等。

google官方架構MVP解析與實戰


2.3 TaskDetailActivity
可以看到這里這里一個初始化了fragment的activity,主要操作當讓是new了一個XXXPresenter。activity在項目中是一個全局的控制者,負責創建view以及presenter實例,并將二者聯系起來,

google官方架構MVP解析與實戰

2.4 TaskDetailFragment
Fragment是MVP中View的實現類,它不與Model 層進行交互,只和presenter的實例進行交互。

google官方架構MVP解析與實戰


2.5 TaskDetailPresenter
Presenter的真正實現類,在這里進行model層和view層的交互。

google官方架構MVP解析與實戰

通過上面的分析,在來梳理一下整個步驟:
1 官方MVP實例,通過協議類XXXContract來對View和Presenter的接口進行內部繼承。是對BaseView和BasePresenter的進一步封裝,所以我們實現的View和Presenter也只需要繼承XXXContract中的對應內部接口就行。

2 activity的作用主要是創建View(這里是相應的fragment),以及創建presenter,并把view傳遞給presenter

3 在presenter的實現類的構造函數中,通過view的setPresenter,讓view獲得了presenter實例。這樣view中就可以對Presenter中的方法進行操作了。

4 在presenter的實現類中,可以對Model數據進行操作。實例中,數據的獲取、存儲、數據狀態變化都是model層的任務,presenter會根據需要調用該層的數據處理邏輯并在需要時將回調傳入。這樣model、presenter、view都只處理各自的任務,此種實現確實是單一職責最好的詮釋。

3 實戰應用:

說了這么多,通過一個主頁面的搭建,來完整的使用MVP吧。
3.1 BaseView
我在這里沒有添加setPresenter方法,而是將loading,以及加載錯誤,網絡加載錯誤等頁面都放在了這里面。

public interface BaseView {

// void setPresenter(P presenter);

void showLoading(String msg);

void hideLoading();

void showError(String msg, View.OnClickListener onClickListener);

void showEmpty(String msg, View.OnClickListener onClickListener);

void showEmpty(String msg, View.OnClickListener onClickListener, int imageId);

void showNetError(View.OnClickListener onClickListener);

}</code></pre>

3.2 BasePresenter
只有一個start方法。將在view界面初始化后調用(onResume方法中)

public interface BasePresenter {   
 void start();
}

3.3 DrawerMainContract
可以看到相比官方的實例我在其中添加了一個onGetDrawerListListener,主要是用于獲取model層數據之后,進行數據的監聽。

public interface DrawerMainContract {

interface Presenter extends BasePresenter{

//獲取數據 void getDrawerList(); //頭像點擊 void onDrawerIconClicked(); //獲取fragment void getSelectFragment(int position); }

interface View extends BaseView{

//獲取數據后,更新界面 void onDrawerListGet(ArrayList<DrawerItemsData.DrawerItem> list); //設置頭像 void setDrawerIcon(int resId); //fragment返回后 void onSelectFragmentGet(Fragment fragment);

}

//數據監聽 interface onGetDrawerListListener { void onSuccess(); void onError(); }

}</code></pre>

3.4 DrawerMainActivity
在開發中Fragment,Activity以及自定義view都可以作為MVP中View的實現。這里也在創建presenter實例的時候傳入了當前view。并調用了 mPresenter.getDrawerList()進行獲取數據,mPresenter.getSelectFragment(0);設置當前為第一個fragment。

/**

  • Created by Anthony on 2016/5/3.
  • Class Note:
  • 1 use{@link DrawerLayout} to
  • acts as a top-level container for window content that allows for
  • interactive "drawer" views to be pulled out from the edge of the window.
  • 2 View in MVP
  • see {@link DrawerMainContract}------Manager role of MVP
  • {@link DrawerMainPresenter}---------Presenter
  • &{@link DrawerMainActivity}---------View
  • &{@link DrawerItemsData}------------Model */ public class DrawerMainActivity extends AbsBaseActivity implements DrawerMainContract.View, View.OnClickListener { private Toolbar actionBarToolbar; public static DrawerLayout drawerLayout; private ListView mDrawerMenu; private CircleImageView mUserImg; private NavDrawerListAdapter mAdapter; private DrawerMainPresenter mPresenter;

    @Override protected int getContentViewID() {

     return R.layout.activity_drawer;
    

    }

    @Override protected void onResume() {

     super.onResume();
     mPresenter.start();
    

    }

    @Override protected void initViewsAndEvents() {

     super.initViewsAndEvents();//一定要調用super,進行父類中的一些初始化操作
     initDrawerLayout();
     setupToolBar();
    
     //hide toolBar
     ActionBar actionBar = getSupportActionBar();
     if (actionBar != null) {
         actionBar.hide();
     }
    
     mUserImg = (CircleImageView) findViewById(R.id.user_img);
     mUserImg.setOnClickListener(this);
     mDrawerMenu = (ListView) findViewById(R.id.left_menu);

// mPresenter = new DrawerMainPresenter(this, mContext); //create and initialize presenter, mPresenter= new DrawerMainPresenter(this,mContext);

    //use presenter to load data
    mPresenter.getDrawerList();
    //default select first fragment
    mPresenter.getSelectFragment(0);
}




protected void initDrawerLayout() {
    drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawerLayout == null) {
        // current activity does not have a drawer.
        return;
    }
    if (getDrawerLayoutId() != 0) {
        FrameLayout leftLayout = (FrameLayout) findViewById(R.id.left_drawer_layout);
        View nav_drawer_layout = getLayoutInflater().inflate(getDrawerLayoutId(), null);
        leftLayout.addView(nav_drawer_layout);
    }

}


protected void setupToolBar() {
    if (actionBarToolbar == null) {
        actionBarToolbar = (Toolbar) findViewById(R.id.toolbar);
        if (actionBarToolbar != null) {
            setSupportActionBar(actionBarToolbar);
        }
    }
    final ActionBar ab = getSupportActionBar();
    if (ab == null)
        return;
    ab.setHomeAsUpIndicator(R.drawable.ic_menu);
    ab.setDisplayHomeAsUpEnabled(true);
}


public static void openDrawer() {
    if (drawerLayout == null)
        return;
    drawerLayout.openDrawer(GravityCompat.START);
}

public static void closeDrawer() {
    if (drawerLayout == null)
        return;
    drawerLayout.closeDrawer(GravityCompat.START);
}

/**
 * close drawer if drawer is opening
 */
@Override
public void onBackPressed() {
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}


protected int getDrawerLayoutId() {
    return R.layout.nav_drawer_layout;
}

@Override
public void onClick(View v) {
    if (v.getId() == R.id.user_img) {
        mPresenter.onDrawerIconClicked();
    }
}

@Override
public void onDrawerListGet(final ArrayList<DrawerItemsData.DrawerItem> mDrawerItems) {
    mAdapter = new NavDrawerListAdapter(this,
            mDrawerItems);
    mDrawerMenu.setAdapter(mAdapter);
    mDrawerMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            if (!BaseUtil.isEmpty(mDrawerItems, i)) {
                DrawerItemsData.DrawerItem drawerItem = mDrawerItems.get(i);
                if (drawerItem != null) {
                    mPresenter.getSelectFragment(i);
                }
            }
        }
    });
}

@Override
public void setDrawerIcon(int resId) {
    mUserImg.setImageResource(resId);
}

@Override
public void onSelectFragmentGet(Fragment fragment) {
    closeDrawer();
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.content, fragment).commit();

}

@Override
protected void onNetworkConnected(NetUtils.NetType type) {
    ToastUtils.getInstance().showToast("this type is"+type);
}

@Override
protected void onNetworkDisConnected() {
    ToastUtils.getInstance().showToast("no network disconnected");
}

@Override
protected View getLoadingTargetView() {
    return null;
}


// @Override // public void setPresenter(DrawerMainContract.Presenter presenter) { // mPresenter= (DrawerMainPresenter) presenter; // }

@Override
public void showLoading(String msg) {
    toggleShowLoading(true, msg);
}

@Override
public void hideLoading() {
    toggleShowLoading(false, "");
}

@Override
public void showError(String msg, View.OnClickListener onClickListener) {

}

@Override
public void showEmpty(String msg, View.OnClickListener onClickListener) {
    toggleShowEmpty(true, msg, onClickListener);
}

@Override
public void showEmpty(String msg, View.OnClickListener onClickListener, int imageId) {
    toggleShowEmpty(true, msg, onClickListener, imageId);
}

@Override
public void showNetError(View.OnClickListener onClickListener) {
    ToastUtils.getInstance().showToast("oops ,no network now!");
}

}</code></pre>

這里也實現了DrawerMainContract.View以及BaseView中的所有方法。

3.5 DrawerMainPresenter
對DrawerMainContract.Presenter和DrawerMainContract.
onGetDrawerListListener的實現。
可以看到DrawerItemsData就是model層的對象。主要封裝的是本地的字符串和圖片數據。

/**

  • Created by Anthony on 2016/5/3.
  • Class Note: Presenter in MVP
  • see {@link DrawerMainContract}--------Manager role of MVP
  • &{@link DrawerMainPresenter}------Presenter
  • &{@link DrawerMainActivity}-------------View
  • &{@link DrawerItemsData}-----------Model */ public class DrawerMainPresenter implements DrawerMainContract.Presenter, DrawerMainContract.onGetDrawerListListener {

    private DrawerMainContract.View mView; private Context mContext; private DrawerItemsData mData;

public DrawerMainPresenter(DrawerMainContract.View mView, Context mContext) {
    this.mContext = mContext;

    this.mView = mView;

// mView.setPresenter(this);//bind presenter for View

    mData = new DrawerItemsData(mContext, this);//bind data listener for Model

}
@Override
public void onDrawerIconClicked() {
    //已經登錄,跳到個人詳情頁
    ToastUtils.getInstance().showToast("icon clicked");
    //沒有登錄 ,則跳到登錄頁面。。。
}

@Override
public void getDrawerList() {
    mData.initItemsData();
}

@Override
public void getSelectFragment(int position) {
    if (position == 3)
        mView.onSelectFragmentGet(new NewsChannelFragment());
    else mView.onSelectFragmentGet(new TestFragment());
}

@Override
public void onSuccess() {
    mView.onDrawerListGet(mData.mDrawerItems);
    mView.setDrawerIcon(R.drawable.icon_head);
}

@Override
public void onError() {
    // show error view
    mView.showNetError(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        }
    });
}
@Override
public void start() {
}

}</code></pre>

3.6 DrawerItemsData
這就是我主頁面的model層的數據,當然后面可能會對這些數據進行修改,或者添加上網絡訪問。但是你會發現再也不用像以前那樣要去修改整個activity了,只需要的是修改這個類。這也是MVP的作用,單一職責。

/**

  • Created by Anthony on 2016/5/3.
  • Class Note:
  • drawer list item data. / public class DrawerItemsData { private Context mContext; public ArrayList<DrawerItem> mDrawerItems; private String[] mMenuTitles; private TypedArray mMenuIconsTypeArray; private TypedArray mMenuIconTintTypeArray; private DrawerMainContract.onGetDrawerListListener mListener;
public DrawerItemsData(Context context, DrawerMainContract.onGetDrawerListListener listener) {
    this.mContext = context;
    this.mListener =listener;

    mDrawerItems=new ArrayList<>();
}

public void initItemsData() {
    mMenuTitles = mContext.getResources().getStringArray(R.array.nav_drawer_items);
    // nav drawer icons from resources
    mMenuIconsTypeArray = mContext.getResources()
            .obtainTypedArray(R.array.nav_drawer_icons);
    mMenuIconTintTypeArray = mContext.getResources()
            .obtainTypedArray(R.array.nav_drawer_tint);

    for (int i = 0; i < mMenuTitles.length; i++) {
        mDrawerItems.add(new DrawerItem(mMenuTitles[i], mMenuIconsTypeArray
                .getResourceId(i, -1), mMenuIconTintTypeArray.getResourceId(i, -1)));
    }
    mMenuIconsTypeArray.recycle();

    if(mDrawerItems.size()==mMenuTitles.length){
        mListener.onSuccess();
    }else{
        mListener.onError();
    }
}

public class DrawerItem {

    private String title;
    private int icon;
    //圖片顏色
    private int tint;
    private String count = "0";
    // boolean to set visiblity of the counter
    private boolean isCounterVisible = false;

    public DrawerItem(){}

    public DrawerItem(String title, int icon, int tint){
        this.title = title;
        this.icon = icon;
        this.tint = tint;
    }

    public DrawerItem(String title, int icon, boolean isCounterVisible, String count){
        this.title = title;
        this.icon = icon;
        this.isCounterVisible = isCounterVisible;
        this.count = count;
    }

    public String getTitle(){
        return this.title;
    }

    public int getIcon(){
        return this.icon;
    }

    public String getCount(){
        return this.count;
    }

    public boolean getCounterVisibility(){
        return this.isCounterVisible;
    }

    public void setTitle(String title){
        this.title = title;
    }

    public void setIcon(int icon){
        this.icon = icon;
    }

    public void setCount(String count){
        this.count = count;
    }

    public int getTint() {
        return tint;
    }

    public void setTint(int tint) {
        this.tint = tint;
    }

    public void setCounterVisibility(boolean isCounterVisible){
        this.isCounterVisible = isCounterVisible;
    }
}

}</code></pre>

具體請參看本篇文章項目github地址:MVPCommon

google官方架構MVP解析與實戰

項目效果

4 參考資料:

Android官方MVP架構示例項目解析
AndroidArchitectureCollection

 

本篇文章項目github地址:MVPCommon

文/CameloeAnthony(簡書)
 

 本文由用戶 wbqdavid 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!