Android 上拉加载的简单实现

在android产品中,上拉加载 在某种程度上与下拉刷新一样,可以看做用户使用产品最常见的交互之一。当列表数据量很大时,我们通常不会一次性请求和展示所有的数据,而是启用分页功能。那市面上常见的app的上拉加载功能都有哪些特点呢?如何选取适合自己项目的上拉加载样式并实现呢?
接下来我们会简单分析并实现两种目前最常见的上拉加载样式,并分别给出集成到项目中的建议。

Android 上拉加载的简单实现


上拉加载交互的分类

根据交互的不同,我给这两种上拉取了个名字,分别叫做二次上拉型自动加载型
二次上拉型,顾名思义,是一种和下拉刷新交互类似的上拉,FooterView可以看做和下拉刷新时的HeaderView,当用户滚动列表到底部时,不会主动触发上拉。
只有当用户的手指再次执行上拉动作时,FooterView才随着手指向上的移动而展示出来。当手指上拉的距离到达设定的临界值时,触发上拉加载。直接上图:

上拉样式1

仔细观察动图我们可以发现,当用户滑动列表到底部时,首先看到了水波纹,然后用户再次上拉,加载更多的底部才会展示出来。

自动加载型,则比较像在列表的底部增加了单独的item,如ListView的addFooterView的方法,滑动到底部自动展示出FooterView,没有手指继续拉动触发上拉的交互。看图:

上拉样式2

这里插播一则小感悟,产品和设计很多时候其实并不知道他们要实现的功能和样式到底是什么样的,产品说要做分页,设计说那你就看着别人怎么做的,滑到底部有上拉就可以了。苦逼的还是码农,如果像我一样不幸一开始采用二次上拉型,最后沟通很很久才发现其实设计想要的是自动加载型,那真是欲哭无泪啊。再次证明,好的安卓开发应该对市面上的app常见的交互及实现心里应该有个谱,这样讨论样式和交互的时候才不会被误导,做无用功。

开源库调研

言归正传,让我们来分析下两种上拉加载都有哪些现有的轮子呢?不幸的是,轮子有,但说实话都不尽如人意.

  • 第一种上拉加载的开源库实现并不多,通常都是第三方下拉刷新附带的功能,或者官方SwipeRefreshLayouy的拓展。这里推荐我自己的一个开源项目 wangyeming/SuperSwipeRefreshLayout
    用法类似官方的SwipeRefreshLayout, 对内部子元素没有侵入性,缺点是暂不支持任意布局。
    我曾经调研过很多开源的支持上拉刷新的控件,不过都不尽如人意。一来这种上拉的交互体验不是很好,用户需要二次上拉才会触发加载,导致相应的开源库非常少。其次是本身实现起来需要考虑的点比较多,尤其是想要支持任意布局的话,还是有难度的。从项目角度,我们当然希望引入的第三方库尽可能小,易于修改,自定义。
    一些开源库如bingoogolapple/BGARefreshLayout-Android等对项目来说实在是太重了。再比如类似于XListView这种,局限性太强,只能使用固定的view,不是个好的解决方案。依我个人浅见,前面推荐的SuperSwipeRefreshLayout至少还是比较适合引入到项目中,只有SuperSwipeRefreshLayout.java一个文件, 无侵入性,缺点前面也说了,暂时不支持任意布局。

其实如果进项目中看,会发现其实也是fork了一个官方SwipeRefreshLayouy的拓展的开源库,在实际使用中,真是发现了无数个坑,不光上拉没做好,下拉也被改的各种bug,README中有我做了哪些修改和增强,其实整个的设计还是遵循原来的方案,对于后续的想法,个人感觉上拉的实现还是过于粗糙,对child支持也不够,希望有兴趣和能力的小伙伴和我一起完善这个库,谢谢啦~

  • 相对来说,第二种上拉的开源组件就很多了,思路都大同小异,自定义的ListView或RecyclerView或adpater,比如jianghejie/XRecyclerView, HomHomLin/Android-PullToRefreshRecyclerView 等等。 从项目角度,我们在项目中一般都有自己的下拉刷新控件,我们希望引入的库不光体积小,侵入少,最好只集成最基础的上拉,把下拉的职责交还给下拉刷新控件。这里有一个库很不错,CymChad/BaseRecyclerViewAdapterHelper, 从使用角度,任意的RecyclerView,只要adapter继承自库中的基类adapter即可,而下拉功能依旧可以使用之前项目中的下拉刷新组件。

既然要用,我们就好好分析下这个库的功能和实现,我们发现这个库集成的功能还是比较多的,包括各种动画效果,侧滑,真正的上拉加载其实只是其中的一小部分功能。与其平白无故多了十几个文件,我们不如分析下这个库的实现原理,自己动手来简单实现一个上拉加载的组件。

简单实现

先问目的:前面说的很清楚了,简单易用的上拉加载的轮子。

实现方式:自定义基类adapter,如果把adapter原数据源简单抽象为一种itemType,那我们的工作就是再增加一种itemType,对应着上拉加载的FooterView。因为RecyclerView没有ListView那样addHedaer()和setEmptyView()的方法,所以我们这里再增加两种itemType:

1
2
3
4
private static final int TYPE_HEADER = 0;
private static final int TYPE_BASE = 1;
private static final int TYPE_EMPTY = 2;
private static final int TYPE_LOADMORE = 3;

毫无疑问,我们设计的adapter应该可以自定义上拉加载的样式,这里我将上拉View的样式简单的分为三个场景:加载中,加载失败,没有更多。根据设计原则,依赖抽象而不是依赖具体实现,这里设计了一个抽象的LoadMoreView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class LoadMoreView extends FrameLayout {

public LoadMoreView(Context context) {
super(context);
}

public abstract void showLoading();

public abstract void showRetry();

public abstract void showEnd();

public abstract boolean isLoadMoreEnable();

public abstract void setOnRetryClickListener(OnRetryClickListener listener);
}

这里设计的几个接口也比较粗糙,通过钩子方法,大家可以根据需求自己定义上拉View的样式。

接下来的事就简单多了,对外暴露的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void showLoadMoreLoading() {
vLoadMore.showLoading();
}

public void showLoadMoreRetry() {
vLoadMore.showRetry();
}

public void showLoadMoreEnd() {
vLoadMore.showEnd();
}

public void setHeaderView(View vHeader) {
this.vHeader = vHeader;
}

public void setEmptyView(View vEmpty) {
this.vEmpty = vEmpty;
}

只要处理好getItemCount(), getItemViewType(), onBindViewHolder(), onCreateViewHolder()这几个基本的方法,我们的轮子就初步完工啦。

具体代码大家可以参考: wangyeming/LoadMoreRecyclerViewAdapter, 里面有完成的代码和实现。

谢谢大家~