Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> google官方架構MVP解析與實戰-(從零開始搭建android框架系列(3))

google官方架構MVP解析與實戰-(從零開始搭建android框架系列(3))

編輯:關於Android編程

1 前言

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

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

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

2 google官方MVP架構解析

1 項目目錄

打開github,展開項目目錄,會發現項目結構的組織方式是按照功能進行分模塊的,當然根據個人情況,也可以按照ui,model,view,presenter這種情況進行劃分組織目錄。
google官方MVP架構目錄視圖
2 具體實現流程

我們將關注度放到具體的一個taskdetail模塊當中來解析實現MVP的流程。
taskdetail模塊
2.1 TaskDetailContract<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjxiciAvPg0Kv8nS1L+0tb3V4sDvysfNqLn90ru49tCt0unA4FhYWENvbnRyYWN0wLS21FZpZXe6zVByZXNlbnRlcrXEvdO/2r340NC8zLPQoaPV4tH51/a1xLrDtKbSsr7NysejrM7Sw8e/ydLUvau7+bShtcRWaWV3suO1xLLZ1/e3xdTaQmFzZVZpZXfA78PmLLbUu/m0obXEUHJlc2VudGVysuO1xLLZ1/e3xdTaQmFzZVByZXNlbnRlcsDvw+aho7z1ydm689D4tPrC67XE1ti4tKGj0ru49tCt0unA4NKyvatWaWV3us1QcmVzZW50ZXK53MDtxvDAtKOst72x47LZ1/ehozxiciAvPg0KPGltZyBhbHQ9"" src="/uploadfile/2016/0513/20160513095918652.jpg" title="\" />
2.2 BaseView
那麼來看看BaseView,主要是有一個setPresenter的操作,MVP中Presenter和View層是需要交互的,這裡通過setPresenter操作,我們也就可以獲得Presenter層的實例進行交互了。所以在我們自己的代碼中,我們也可以將加載的loading,以及加載錯誤頁面,加載失敗頁面等操作放在BaseView裡面,這是每個View都會有的:

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

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

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

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

通過上面的分析,在來梳理一下整個步驟:
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);

}

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 list);
//設置頭像
        void setDrawerIcon(int resId);
//fragment返回後
        void onSelectFragmentGet(Fragment fragment);

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


}

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 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!");
    }
}

這裡也實現了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() {
    }
}

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 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;
        }
    }
}

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

4 參考資料:

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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved