前言:
最近在項目中引入了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
小結:
該版本為初級版,代碼比較簡單,未逐一分析。歡迎大家指正、批評。謝謝!