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

小结:

该版本为初级版,代码比较简单,未逐一分析。欢迎大家指正、批评。谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,486评论 25 707
  • 自Android 5.0之后,谷歌公司推出了RecylerView控件,RecylerView,我想看到一个新名词...
    苦可乐阅读 2,281评论 0 5
  • L小姐总是告诫自己要辩证理性的看待问题,不能太唯心。心理测试显示她是一个理性的人,很多时候她怀疑这权威的测试结果不...
    BigDragon阅读 240评论 0 0
  • 有些回忆注定会不平凡,要用一生去珍藏,那些往事都是我们一生中最珍贵,最美好的事,而那些事情,就是那些见证我们成长...
    璃沫丶Triete阅读 703评论 0 1
  • 早上给XXX家开单子,她家卖的是BL。 XXX家喊我开单子。跟我说:“338斤乘以1.5”。我瞥了一眼称说:“你帮...
    桥上风景阅读 85评论 1 1