MVP + SmartRefreshLayout +RecylerView 列表基類的封裝(初級版)

weixin_33850890發表於2017-08-15

前言:

最近在專案中引入了SmartRefreshLayout (github地址)智慧重新整理控制元件,支援所有的View(AbsListView,RecyclerView,WebView .... View)和多層巢狀的檢視結構。使用更簡單,強大。在我們專案中,存在多個下拉重新整理的控制元件(XRecylerView、TwinklingRefreshLayout),效果較混亂,不夠統一。程式碼相似度高,重複程式碼較多。為了提高程式碼的複用性、減少重複的程式碼的編寫,將公共的邏輯進行抽取(如設定標題欄、無網路、資料空介面、上拉重新整理、下拉載入、設定adapter、設定LayoutManager、設定ItemDecoration等),故初步封裝了一個基類。

封裝思路:

一、通用列表Activity主要功能分析:

下拉重新整理、上拉載入更多、網路異常處理、空資料介面處理、Activity 標題欄設定、返回頂部按鈕設定、RecylerView LayoutManager設定、RecylerView Adapter設定、RecylerView ItemDecoration設定。

二、程式碼編寫分析:

抽取公有的程式碼,父類進行預設實現,子類可以重寫定製。減少重複程式碼的編碼,讓開發列表介面變得更簡單高效。

程式碼實現:

1、佈局程式碼:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<!--設定activity Title標題-->
<LinearLayout
    android:id="@+id/ll_base_list_refresh_title_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"/>

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/cl_base_list_content_root"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        app:elevation="2dp">

        <!--可以跟隨上下滑動顯示隱藏的頭部局-->
        <!--scroll|snap 滑動到頂部才顯示出來-->
        <!--app:layout_scrollFlags="scroll|snap"-->
        <!--scroll|enterAlways|snap 往上滑就顯示出來-->
        <!--app:layout_scrollFlags="scroll|enterAlways|snap"-->
        <LinearLayout
            android:id="@+id/ll_base_list_scroll_top_root"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/common_white_color"
            android:orientation="vertical"
            app:layout_scrollFlags="scroll|enterAlways|snap"/>

    </android.support.design.widget.AppBarLayout>

    <com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:id="@+id/srl_base_list_refreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:srlEnableHeaderTranslationContent="true"
        app:srlEnableLoadmore="true"
        app:srlEnableRefresh="true">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rcy_base_list_recylerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/common_white_color"
            />
    </com.scwang.smartrefresh.layout.SmartRefreshLayout>

    <!--新增右下角一鍵置頂按鈕-->
    <include layout="@layout/inc_to_top"/>

    <!--網路異常的介面-->
    <include
        android:id="@+id/view_base_list_no_net_root"
        layout="@layout/common_no_net_layout"
        android:visibility="gone"/>

    <!--未有資料載入為空顯示的空介面-->
    <LinearLayout
        android:id="@+id/ll_base_list_empty_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/common_white_color"
        android:orientation="vertical"
        android:visibility="gone"/>

    </android.support.design.widget.CoordinatorLayout>
</LinearLayout>

2、java程式碼:

public abstract class BaseListRefreshActivity<V extends BaseView, P extends BasePresenter<V>, T extends RecyclerView.Adapter> extends AppBaseMVPActivity<V, P> {

private LinearLayout mLlTitleTopRoot, mLlScrollTopRoot, mLlEmptyRoot;
private View mTitleView, mTopFixedView, mTopScrollView, mEmptyView, mNotNetRoot;
private Button mBtnNoNetReload;
private ImageView mImgTop;
private CoordinatorLayout mClContentRoot;
private RecyclerView mRecylerView;
private SmartRefreshLayout mRefreshLayout;
private RecyclerView.ItemDecoration mDefaultItemDecoration;
private RecyclerView.LayoutManager mDefaultLayoutManager;
public Context mContext;
public LayoutInflater mInflater;
private T mAdapter;

public T getAdapter() {
    return mAdapter;
}

public SmartRefreshLayout getSmartRefreshLayout() {
    return mRefreshLayout;
}

public RecyclerView getRecylerView() {
    return mRecylerView;
}

public RecyclerView.ItemDecoration getDefaultItemDecoration() {
    return mDefaultItemDecoration;
}

public RecyclerView.LayoutManager getDefaultLayoutManager() {
    return mDefaultLayoutManager;
}

public View getTitleView() {
    return mTitleView;
}

public View getTopFixedView() {
    return mTopFixedView;
}

public View getTopScrollView() {
    return mTopScrollView;
}

public View getEmptyView() {
    return mTopScrollView;
}

@Override
public int getLayoutResId() {
    return R.layout.activity_base_list_refresht;
}

@Override
public void initData() {
    mContext = this;
    mInflater = LayoutInflater.from(this);
}

@Override
public void initView(Bundle savedInstanceState) {
    findId2View();  // 找到View
    handleTitleTopEmptyView();  // 處理標題欄、空介面View
    handleNotNetReload();  // 處理無網路,重新載入
    handleGoTopClick();  // 處理返回底部按鈕的點選事件
    initSmartRefreshLayout();  // 初始化設定SmartRefreshLayout重新整理控制元件
    initRefreshLayoutSetting(mRefreshLayout);  // 提供該方法,方便子類更改SmartRefreshLayout重新整理控制元件的設定
    setDefaultRecylerView(); // 設定RecylerView
    hideGoTopIcon(); // 隱藏返回頂部按鈕
    showNormalContentView(); // 顯示正常內容的佈局
    loadDataFromServer(true); // 請求伺服器資料
}

/**
 * 提供初始化佈局管理器的方法,子類可重寫,預設實現線性佈局
 *
 * @return LayoutManager
 */
protected RecyclerView.LayoutManager initLayoutManager() {
    return new LinearLayoutManager(mContext);
}

/**
 * 提供初始化 ItemDecoration的方法,子類可重寫,預設實現LinearVerItemDecoration
 *
 * @return RecyclerView.ItemDecoration
 */
protected RecyclerView.ItemDecoration initItemDecoration() {
    return new LinearVerItemDecoration();
}

/**
 * 通過id找到View
 */
private void findId2View() {

    mLlTitleTopRoot = findView(R.id.ll_base_list_refresh_title_root);
    mClContentRoot = findView(R.id.cl_base_list_content_root);
    mLlScrollTopRoot = findView(R.id.ll_base_list_scroll_top_root);
    mRecylerView = findView(R.id.rcy_base_list_recylerView);
    mRefreshLayout = findView(R.id.srl_base_list_refreshLayout);
    mImgTop = findView(R.id.btn_toTop);

    // 設定資料為空時顯示的空佈局
    mLlEmptyRoot = findView(R.id.ll_base_list_empty_root);

    // 無網路佈局
    mNotNetRoot = findView(R.id.view_base_list_no_net_root);
    mBtnNoNetReload = findView(R.id.refresh_again);
}

/**
 * 處理新增標題、頂部View
 */
private void handleTitleTopEmptyView() {
    // 新增 Activity 標題欄
    mTitleView = initTitleView(mInflater, mLlTitleTopRoot);
    if (mTitleView != null) {
        mLlTitleTopRoot.addView(mTitleView);
    }
    // 新增 顯示在標題欄下面的View
    mTopFixedView = initTopFixedView(mInflater, mLlTitleTopRoot);
    if (mTopFixedView != null) {
        mLlTitleTopRoot.addView(mTopFixedView);
    }
    // 新增可以隨手勢上下滑動顯隱的View
    mTopScrollView = initTopScrollView(mInflater, mLlScrollTopRoot);
    if (mTopScrollView != null) {
        mLlScrollTopRoot.addView(mTopScrollView);
    }
    // 新增資料為空時顯示的空介面
    mEmptyView = initEmptyView(mInflater, mLlEmptyRoot);
    if (mEmptyView != null) {
        mLlEmptyRoot.addView(mEmptyView);
    }
}

/**
 * 初始化標題欄,子類進行實現
 *
 * @param inflater
 * @param titleParent
 * @return
 */
protected abstract View initTitleView(LayoutInflater inflater, LinearLayout titleParent);

/**
 * 初始化 空介面,子類進行實現
 *
 * @param inflater
 * @param emptyParent
 * @return
 */
protected abstract View initEmptyView(LayoutInflater inflater, LinearLayout emptyParent);

/**
 * 網路異常 重新載入按鈕點選回撥監聽方法
 */
protected abstract void onNoNetReload();

/**
 * recylerView 滑動事件回撥監聽,通常我們返回頂部的按鈕的顯隱需要用到該方法,子類進行實現
 *
 * @param recyclerView
 * @param dx
 * @param dy
 */

protected abstract void initOnScrolled(RecyclerView recyclerView, int dx, int dy);

/**
 * 初始化Adapter,子類進行實現
 *
 * @return adapter
 */
protected abstract T initAdapter();

/**
 * 從伺服器介面載入資料,子類進行實現
 */
protected abstract void loadDataFromServer(boolean isShowProgress);

/**
 * 下拉正在重新整理載入回撥方法
 *
 * @param refreshlayout
 */
protected abstract void onRefreshing(RefreshLayout refreshlayout);


/**
 * 上拉正在載入更多回撥方法
 *
 * @param refreshlayout
 */
protected abstract void onLoadmoreing(RefreshLayout refreshlayout);


/**
 * 初始化 頂部固定欄目View,需要子類重寫即可
 *
 * @param inflater
 * @param topParent
 * @return View
 */
protected View initTopFixedView(LayoutInflater inflater, LinearLayout topParent) {
    return null;
}


/**
 * 初始化 頂部可隨手勢上下移動顯隱的欄目View,需要子類重寫即可
 *
 * @param inflater
 * @param topScrollParent
 * @return View
 */
protected View initTopScrollView(LayoutInflater inflater, LinearLayout topScrollParent) {
    return null;
}

/**
 * 自定義RefreshLayout設定的方法,子類重新該方法設定即可
 *
 * @param smartRefreshLayout
 */
protected void initRefreshLayoutSetting(SmartRefreshLayout smartRefreshLayout) {

}

/**
 * 初始化 指定重新整理的頭佈局樣式
 * 子類需要定製重寫該方法
 */
protected RefreshHeader initRefreshHeader() {
    return new ClassicsHeader(mContext).setSpinnerStyle(SpinnerStyle.Translate);//指定為經典Header,預設是 貝塞爾雷達Header
}

/**
 * 初始化 指定重新整理的腳佈局樣式
 * 子類需要定製重寫該方法
 */
protected RefreshFooter initRefreshFooter() {
    return new ClassicsFooter(mContext).setSpinnerStyle(SpinnerStyle.Translate); //設定為平移模式
}

/**
 * 初始化 SmartRefreshLayout
 * 1、設定灰色背景
 * 2、設定頭佈局、腳佈局
 * 3、開啟滑動底部自動觸發載入更多功能
 * 4、設定下拉重新整理、上拉載入更多的監聽回撥
 */
private void initSmartRefreshLayout() {
    mRefreshLayout.setBackgroundResource(R.color.common_light_gray_color);
    mRefreshLayout.setRefreshHeader(initRefreshHeader());
    mRefreshLayout.setRefreshFooter(initRefreshFooter());
    mRefreshLayout.setEnableAutoLoadmore(true);
    mRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
        @Override
        public void onRefresh(final RefreshLayout refreshlayout) {
            onRefreshing(refreshlayout);
        }
    });
    mRefreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
        @Override
        public void onLoadmore(final RefreshLayout refreshlayout) {
            onLoadmoreing(refreshlayout);
        }
    });
}

/**
 * 處理網路異常重新載入邏輯
 */
private void handleNotNetReload() {
    mBtnNoNetReload.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // 判斷網路是否可用
            if (NetworkUtils.isAvailable(mContext)) {
                onNoNetReload();
                return;
            }
            showToastError("網路異常");
        }
    });
}

/**
 * 處理回頂部按鈕點選事件
 */
protected void handleGoTopClick() {
    mImgTop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mRecylerView.scrollToPosition(0);
        }
    });
}

/**
 * 設定預設單列顯示的RecylerView
 */
private void setDefaultRecylerView() {
    mDefaultItemDecoration = initItemDecoration();
    mDefaultLayoutManager = initLayoutManager();
    mAdapter = initAdapter();
    mRecylerView.setLayoutManager(mDefaultLayoutManager);
    mRecylerView.addItemDecoration(mDefaultItemDecoration);
    mRecylerView.setAdapter(mAdapter);
    mRecylerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            initOnScrolled(recyclerView, dx, dy);
        }
    });
}

/**
 * 顯示一鍵回頂部圖示
 */
protected void showGoTopIcon() {
    if (mImgTop != null) {
        mImgTop.setVisibility(View.VISIBLE);
    }
}

/**
 * 隱藏一鍵回頂部圖示
 */
protected void hideGoTopIcon() {
    if (mImgTop != null) {
        mImgTop.setVisibility(View.GONE);
    }
}

/**
 * 顯示空佈局
 */
protected void showEmptyView() {
    mLlEmptyRoot.setVisibility(View.VISIBLE);
    mRefreshLayout.setVisibility(View.GONE);
    mNotNetRoot.setVisibility(View.GONE);
}

/**
 * 顯示網路異常佈局
 */
protected void showNotNetView() {
    mLlEmptyRoot.setVisibility(View.GONE);
    mRefreshLayout.setVisibility(View.GONE);
    mNotNetRoot.setVisibility(View.VISIBLE);
}

/**
 * 顯示正常列表內容的View
 */
protected void showNormalContentView() {
    mRefreshLayout.setVisibility(View.VISIBLE);
    mLlEmptyRoot.setVisibility(View.GONE);
    mNotNetRoot.setVisibility(View.GONE);
  }
}

實踐:

1、先上一張效果圖

2923602-ea8659774896dae1.gif
GIF.gif

2、實踐程式碼:
請檢視專案中的以下幾個類
CommonProdListActivity
ProdListCategoryActivity
ProdListColumnActivity
ProdListCouponActivity
ProdListProDetReduceActivity
ProdListShopCarReduceActivity

小結:

該版本為初級版,程式碼比較簡單,未逐一分析。歡迎大家指正、批評。謝謝!

相關文章