简单点,基于Ultra-Pull-To-Refresh的下拉刷新上拉加载再封装

开始前总是要有废话的

很早之前,上拉加载下拉刷新这种交互方式一经推出,就火炸了。如果你在两三年前就接触过android开发,你一定听说过PullToRefreshListView这个开源框架,使用起来很简单,首先感谢伟大的作者开源这么优秀的作品,但是对于新手来讲,这个框架有些过于庞大了,类和方法实在太多,定制功能太复杂,并且不得不说,再使用过程中,这个框架的局限性很大,说到底他只是个ListView,当你和其他可滑动控件一起套用时,就会出现各种的问题,并且这个框架的作者已经好几年没有维护过他了。
我在git上尝试查找能替代他的家伙,万幸这个家伙被我成功发现了。虽然他的岁数也已经很大了,但到现在我仍然在使用并且也一直在维护他的代码。android-Ultra-Pull-To-Refresh,同样感谢liaohuqiu贡献这么优秀的作品。我习惯叫这个框架为Ptr,至于为什么到现在我还要推荐这个框架,请听我慢慢道来:

  • 首先他不是个ListView也不是个GridView,他是个ViewGroup,这意味着什么?他意味着你的整个ViewGroup都是可以下拉的,你的ViewGroup里可以装任何内容,TextView,Button,ListView,RecycleView都可以,他不在局限我只是个ListView,简单说,下拉这个动作,不绑定任何控件,他是独立的。这个优点我给满分。
  • 充分抽象解耦合,我们可以定义属于我们自己的刷新样式,只要实现统一接口,定制你的样式没有那么难,典型的面向对象思想。
  • 类相对较少。你说这也是个优点?我说这是个大大的优势,类少代码易读,核心类集中,修改与定制会很方便。

那么他没有缺点吗?当然有!

  • 或许这也不算是个缺点,在作者公布的开源项目中,并不支持上拉加载更多功能,至于为什么,作者在他的issues里已经回复过了,大概意思是:下拉刷新和加载更多,不是同一个层级的功能。加载更多不应该由 UltraPTR 去实现,而应该由 Content自己去实现。没关系作者的另外一个开源库,有实现这一功能,在这篇文章中,我们把他整合在一起!
  • 作者源码中的实现使用的是ListView,这里我们会换成RecycleView,毕竟要与时俱进嘛!
  • 同样在滑动嵌套中,会有冲突的问题。没关系,代码中已经解决了一些。

看完这篇文章你会得到什么?

我不会对作者的源码进行解析,毕竟网上的轮子已经很多了,重复造轮子是可耻的。也不会把修改源码的过程进行讲解,为啥?因为很早之前就改好了,到现在已经忘了具体的过程。。。哈!Sorry,这篇文章我会尽力展示框架的结构和使用方式,帮助你更好理解Ptr的优势,思路是最重要的,代码并不重要!

开始干活啦

gif中我展示了上拉加载,下拉刷新,标准,全部功能,空View五种情况。这里只是为了展示,其实我们在使用过程中,基本不会用到各种情况之间的互相切换。项目中目前只定义了两种样式的头部,第一种就是类似gif中MaterialDesign的样式,第二种就是传统样式类似PullToRefreshListView中的,这里没有展示。

一、使用指南

1.布局使用

 <com.leinyo.superptrwithrv.widget.ptr.PullToRefreshView
        android:id="@+id/pull_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:ptr_refresh_mode="none"
        app:ptr_scrollable="true"
        />

此处不废话,讲解属性含义:

<declare-styleable name="PullToRefreshView">
        <attr name="ptr_refresh_mode" format="enum">
            <enum name="none" value="0"/>
            <enum name="pull_from_start" value="1"/>
            <enum name="pull_from_end" value="2"/>
            <enum name="both" value="3"/>
        </attr>
        <attr name="ptr_check_login" format="boolean"/>
        <attr name="ptr_header_mode" format="enum">
            <enum name="material" value="0"/>
            <enum name="normal" value="1"/>
        </attr>
        <attr name="ptr_padding_left" format="integer"/>
        <attr name="ptr_padding_right" format="integer"/>
        <attr name="ptr_scrollable" format="boolean"/>
    </declare-styleable>
  • ptr_refresh_mode 刷新方式
    none 标准的RecycleView
    pull_from_start 支持下拉刷新
    pull_from_end 支持上拉加载
    both 上拉加载下拉刷新
  • ptr_check_login 是否检查登录状态 只有登录(这部分代码自己实现)才会触发下拉刷新功能
  • ptr_header_mode 下拉刷新头样式
    material gif中演示的样式
    normal 标准模式
  • ptr_padding_left ptr_padding_right 设置RecycleView的padding
  • ptr_scrollable 是否显示滚动条

2.刷新动作对应回调

下拉刷新对应回调接口:

public interface OnPullRefreshListener {
        void onPullRefresh();
    }

onPullRefresh()方法会在刷新动画达到临界值以后回调。
取消下拉动画方法:

 public void onPullRefreshComplete() {
        mPtrFrameLayout.refreshComplete();
    }

上拉加载对应回调接口:

public interface OnLoadMoreListener {
        void onLoadMoreRefresh();
    }

取消上拉动画方法:

    public void onLoadMoreComplete(boolean hasMore) {
        mLoadMoreRecyclerViewContainer.loadMoreFinish(hasMore);
    }

boolean值 代表是否还可以继续上拉 false 不会再回调onLoadMoreRefresh()!!!

both对应回调接口:

 public interface OnRefreshListener {
        void onPullRefresh();

        void onLoadMoreRefresh();
    }

取消全部动画方法:

  public void onLoadComplete(boolean hasMore) {
        mLoadMoreRecyclerViewContainer.loadMoreFinish(hasMore);
        if (mCurrentRefreshMode == REFRESH_FROM_START) {
            if (isRefreshing()) {
                mPtrFrameLayout.refreshComplete();
            }
        }
    }

3.添加EmptyView

如果我们的返回的数据是空的,需要显示一个空页面。我们不需要控制两个View的show与gone,ListView能做的我们同样能到。
mPullView.addEmptyView(mEmpty);
注意:addEmptyView方法不能在显示之前设置,否则会先显示空View。为啥,后面再说。

4.添加头HeadView

同样ListView能做的我们也可以。
mPullView.addHeaderView(mHeadView);

二、架构层次

这一部分一定是要上图的,说多少都是无意的。

这部分你最好对Ptr有了解

  • PtrFrameLayout
    最外层ViewGroup,只负责下拉刷新,首先接触到触摸事件,符合下拉逻辑,则显示头布局,否则向下分发事件
  • LoadMoreRecyclerViewContainer
    第二层ViewGroup,只负责上拉加载,监听上拉事件,符合逻辑,通知RecyclerView绘制Foot布局。
  • RecyclerView
    最后一层,我只负责显示布局,包括Head,EmptyView,Foot和正常的数据布局。

怎么样?一张图是不是已经足够清楚了?看到现在你是否已经感叹作者的设计能力了?再看看上面说到作者在issues里已经回复的:下拉刷新和加载更多,不是同一个层级的功能。加载更多不应该由 UltraPTR 去实现,而应该由 Content自己去实现。
每一层级,只对应自己的业务逻辑,并不关心其他人在干什么,这就叫单一职责,解耦合。

本来觉得这里应该是讲得最多的,但是写到这里,发现实在没啥可说的了,如果你真的看过Ptr的源码,相信到这里你也已经没有疑惑了,而且有种神清气爽的感觉,其实就是这么简单,代码设计很重要,这也是为啥有的代码让人看得很爽,有的代码让人看了想吐

三、显示布局

我们上面说过真正负责显示布局的是RecyclerView了,怎么显示?当然是交给Adapter就可以了,多布局显示使用RecyclerView.Adapter的getItemViewType(int position)。需要记住的是:
我们抽出基类BaseRefreshAdapter,里面对多布局显示有一些基础的操作,需要子类继承我们抽出基类BaseRefreshAdapter,并实现onCreateHolder(ViewGroup parent, int viewType)(他等同于onCreateViewHolder(ViewGroup parent, int viewType))和onBindHolder(VH holder, int position)(等同于onBindViewHolder(RecyclerView.ViewHolder holder, int position)),在子类中你只需要关心你自己的Item布局就可以了,其他交给BaseRefreshAdapter中的逻辑就可以了。
这里面有泛型定义,看看源码很好理解。
这就不难理解为啥上面说过addEmptyView方法不能在显示布局之前设置了,因为如果你初始化就执行addEmptyView方法,那么当adapter初始化时,就会执行onCreateViewHolder()一系列方法,这样马上就会显示EmptyView了,等你的真正需要的数据返回才会刷新数据布局。

最后也还是要有些废话的

本人并未侧重讲解怎么封装,怎么修改源码适配RecyclerView,也并未拆源码讲解Ptr原理,也希望大家能理解,时间有点紧,而且轮子很多,不想再造了。
我希望如果你看到最后,这篇文章会对你有所帮助,学习人家的设计模式的同时,你也得到了一个支持多种功能,基于RecyclerView的再封装Ptr框架(感觉好绕口啊),至于为何叫SuperPtr,纯碎是为了好玩。。。
如果您在使用过程中,有疑问和改进意见欢迎联系我。
SupterPtr 对您的支持我深表感谢,喜欢请您star哦~~

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

推荐阅读更多精彩内容