前言:
最近在项目中引入了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、先上一张效果图
2、实践代码:
请查看项目中的以下几个类
CommonProdListActivity
ProdListCategoryActivity
ProdListColumnActivity
ProdListCouponActivity
ProdListProDetReduceActivity
ProdListShopCarReduceActivity
小结:
该版本为初级版,代码比较简单,未逐一分析。欢迎大家指正、批评。谢谢!