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

前言:

最近在項目中引入了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、先上一張效果圖

GIF.gif

2、實踐代碼:
請查看項目中的以下幾個類
CommonProdListActivity
ProdListCategoryActivity
ProdListColumnActivity
ProdListCouponActivity
ProdListProDetReduceActivity
ProdListShopCarReduceActivity

小結:

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,065評論 25 708
  • 自Android 5.0之后,谷歌公司推出了RecylerView控件,RecylerView,我想看到一個新名詞...
    苦可樂閱讀 2,344評論 0 5
  • L小姐總是告誡自己要辯證理性的看待問題,不能太唯心。心理測試顯示她是一個理性的人,很多時候她懷疑這權威的測試結果不...
    BigDragon閱讀 264評論 0 0
  • 有些回憶注定會不平凡,要用一生去珍藏,那些往事都是我們一生中最珍貴,最美好的事,而那些事情,就是那些見證我們成長...
    璃沫丶Triete閱讀 719評論 0 1
  • 早上給XXX家開單子,她家賣的是BL。 XXX家喊我開單子。跟我說:“338斤乘以1.5”。我瞥了一眼稱說:“你幫...
    橋上風景閱讀 101評論 1 1