Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

AndroidIdeaStudio/Android-Ptr-Comparison

Open more actions menu
 
 

Repository files navigation

#安卓下拉刷新开源库对比 目前仅比对github上star数>1500的下拉刷新开源库,在比较完成之后可能会加入其它有代表性的库.

##Repo

Repo Owner Star
(2015.12.5)
version Snap shot
Android-PullToRefresh
(作者已停止维护)
chrisbanes 6014 latest chrisbanes
android-Ultra-Pull-To-Refresh liaohuqiu 3413 1.0.11 liaohuqiu
android-pulltorefresh
(作者已停止维护)
johannilsson 2414 latest johannilsson
Phoenix Yalantis 1897 1.2.3 yalantis
FlyRefresh race604 1843 2.0.0 flyrefresh
SwipeRefreshLayout Android
Support v4
(19.1.0 ↑)
None latest swipe_refresh

##拓展性

|Repo|自定义顶部视图|支持的内容布局| |:--:|:--:|:--:|:---:|:--:|:--:| |Android-PullToRefresh|不支持,只能改代码。
由于仅支持其中实现的LoadingLayout作为顶视图,改代码实现自定义工作量较大。|任意视图,内置:GridView
ListView,HorizontalScrollView
ScrollView ,WebView| |android-Ultra-Pull-To-Refresh|任意视图。
通过继承PtrUIHandler并调用
PtrFrameLayout.addPtrUIHandler()得到最大支持。|任意视图| |android-pulltorefresh|不支持,只能改代码。
代码仅一个ListView,耦合度太高,改动工作量较大。|无法扩展,自身为ListView| |Phoenix|不支持,此控件特点就是顶部视图及动画。|任意视图,只显示最后一个嵌套的子视图。| |FlyRefresh|不支持,此控件特点就是顶部视图及动画。|任意视图| |SwipeRefreshLayout|不支持,固定为Material风格|任意视图|

##易用性

|Repo|可在gradle配置|上拉加载|自动加载|滑动阻尼配置| |:--:|:--:|:------:|:---:|:--:|:--:|:--:| |Android-PullToRefresh|×|√|×|移动比固定1/2| |android-Ultra-Pull-To-Refresh|√|×|√|√| |android-pulltorefresh|×|×|×|移动比固定1/1.7| |Phoenix|√|×|×|移动比固定1/2| |FlyRefresh|√|×|×|×| |SwipeRefreshLayout|√|×|×|移动比固定1/2|

##性能分析

通过捕捉如下图中的操作持续1秒钟的systrace进行性能分析:

trace_operation

注:由于开源库Header大多无法直接放自定义顶部视图,头部视图复杂程度不同,数据对比结果会有所偏差。

###1. Chris Banes's Ptr

滑动实现方式:触摸造成的下拉均是View.scrollTo()实现的;在松手之后,View.post(Runnable)触发Runnable执行回滚动画,在滑回原处之前不断post自己,并配合Interpolator执行scrollTo()进行滚动。

trace snapshot:

trace_chrisbanes

分析

作为Github上星星数最多的Android下拉刷新控件,从性能上看(渲染时间构成)几乎没有什么明显的缺点。可惜的是作者已经不再维护,顶部视图的扩展性比较差,并且gradle中也无法使用。在本次demo这类层级比较简单的环境中,几乎都达到了60fps,可以与后面的trace对比。

###2. liaohuqiu's Ptr

滑动实现方式:触摸造成的下拉均是View.offsetTopAndBottom()实现的;在松手之后,触发Scroller.startScroll()计算回滚,使用View.post(Runnable)不停地监视Scroller的计算结果,从而实现视图变化(此处依然是View.offsetTopAndBottom()完成视图移动)。

trace snapshot:

trace_liaohuqiu

分析

这套开源库可以说是自定义功能最强的组件了,你可以实现PtrUIHandler并将其add到PtrFrameLayout完美地与下拉刷新事件适配。美中不足的就是在下拉状态变化的时候会有一阵measure时间。我查看了一下代码,发现是PtrClassicFrameLayout的顶部视图出了问题:

liaohuqiu_header

看!都是wrap_content,那么当里面的内容变化的时候,是会触发View.requestLayout()的。不要小看这一个子视图的小操作,一个requestLayout()大概是这么一个流程:View.requestLayout()->ViewParent.requestLayout()->...->ViewRootImpl.requestLayout()->ViewRootImpl.doTraversal()=>MEASURE(ViewGroup)=>MEASURE(ChildView of ViewGroup)

在层级复杂的时候(大部分互联网产品由于复杂的产品需求嵌套都会比较多),它会层层向上调用,将measure时间放大至一个可观的层级。下拉刷新界面的卡顿由此而来。

我修改了一下,将其全部变为固定高度、宽度,之后的trace如下:

trace_liaohuqiu_new

measure时间神奇的没掉了吧:)

###3. johannilsson's Ptr

滑动实现方式:初始时setSelection(1)隐藏顶部视图(使用这个下拉刷新控件注意将滚动栏隐藏,否则会露馅)。在拉下来超过header view的measure高度之前,均是ListView自有的滚动;在下拉超过header measure高度之后,对header使用View.setPadding()让header继续下移。

trace snapshot:

trace_johan

分析

通过顶视图调用View.setPadding()来实现的滑动,在下拉距离超过header高度后,会造成不断的requestLayout()!这就解释了为什么图中UI线程的蓝色块时间(measure时间)很明显。当你在视图层级比较复杂的app中使用它时,下拉动作所造成的开销会非常明显,卡顿是必然结果。

###4. Yalantis's Ptr

滑动实现方式:通过View.topAndBottomOffset()移动视图,在松手之后启动一个Animation执行回滚动画,内容视图的移动也使用View.offsetTopAndBottom()实现。为了保证子内容视图的底部padding在移动之后与布局文件中的padding属性一致,它额外调用了View.setPadding()实时设置padding。

顶部动效实现方式:Drawabledraw()中,为Canvas中设置“太阳”偏移量及背景缩放。

trace snapshot:

trace_yalantis

分析

此开源库动画效果非常柔和,且顶部视图全部是通过draw去更新,不会造成第三个开源库那样的大开销问题。可惜的是比较难以去自定义顶部视图,不好在线上产品中使用,不过这个开源库是一个好的练手与学习的对象。由于顶部动效实现开销不大,它的性能同样非常好。

它在松手后回滚时调用的View.setPadding()可能会造成measure开销比较大,于是我特地测了一下松手回滚的trace,一看确实measure时间非常可观:

trace_yalantis_scroll_back

确实它如果要保证展示子视图的padding与布局文件中一致,是必须这么做的(调用View.setPadding()),因为通过View.offsetTopAndBottom()向下移动子视图会影响子视图设置好的底部padding。但是很有意思,它向下移动的时候没有这么设置,拉下来的时候底部padding就没了。回滚动画的时候才设了padding,就显得没那么必要了。我在demo中也进行了实践,确实是这样的:

yalantis_padding

实际上,由于这个库是一个嵌套视图,可以尝试在父视图的onLayout()中对父、子视图的padding属性一起进行处理。不要每次拉动都手动设置padding,这样会造成大量measure开销。

我粗略的做了一点点改动,将它的setPadding()注释掉了,性能就有了很大提高。不过这样一来代码逻辑就有点问题,还需要再做改动,已经跟作者提出issue。

改动后松手回滚trace,已经没有了measure时间:

yalantis_back_trace_new

###5. race604's Ptr

滑动实现方式:View.topAndBottomOffset()

顶部动效实现方式:

  • 飞机滑动 ObjectAnimator.
  • 山体移动、树木弯曲 通过移动距离计算山体偏移、树木轮廓,得出Path后进行draw.

trace snapshot:

trace_flyrefresh

分析:每次拖动都会重新计算背景"山体"与"树木"的Path,造成了draw时间过长。效果不错,也是一个好的学习对象,相比Yalantis的下拉刷新性能上就差一些了,它的draw中的计算量太多。使用起来疑似有bug:拖动到顶部,无法再往上拖动,并且会出现拖动异常。

###6. SwipeRefreshLayout

滑动实现方式:内容固定,仅有顶部动效。

顶部动效实现方式:

  • 上下移动 View.bringToFront() + View.offsetTopAndBottom().
  • 动效 通过移动偏移量计算弧形曲线的角度、三角形的位置,使用drawArc, drawTriangle将他们画到Canvas上。

trace snapshot:

trace_swipe

分析:官方的下拉刷新组件,动画十分美观简洁,API构造清晰明了。但是为什么每次的移动都会有一段明显的measure时间呢?我研究了一下代码,发现罪魁祸首是View.bringToFront(),它在每一次滑动的时候都会对顶部动效视图调用这个函数。仔细追朔这个函数源码,它会走到下面这段代码中:

ViewGroup.java

    public void bringChildToFront(View child) {
        final int index = indexOfChild(child);
        if (index >= 0) {
            removeFromArray(index);
            addInArray(child, mChildrenCount);
            child.mParent = this;
            requestLayout();
            invalidate();
        }
    }

看,它是会触发View.requestLayout()的!这个函数会造成的后果我们在之前已经解释了,它会造成大量的UI线程开销。实际上我认为这个函数是没有调用的必要的,SwipeRefreshLayout明明在重写onLayout()的时候,header会被layout到child之上,没有必要再bringToFront()

于是我copy了一份代码,将这一行注了(对应代码ptr-source-lib/src/main/java/com/android/support/SwipeRefreshLayout.java),再次编译,measure时间确实没掉了,对功能毫无影响,性能却有了很大优化:

trace_swipe

这样一来就不会每一次拉动,都会触发measure。若有同学知道这个bringToFront()在其中有其他我未探测到的功效,请issue指点:)

##总结

Repo 性能 拓展性 综合建议
Android-PullToRefresh ★★★★★ ★★★ 由于作者不再维护,无法在gradle中配置,顶部视图难以拓展,不建议放入工程中使用
android-Ultra-Pull-To-Refresh ★★★★★ ★★★★★ 如之前分析,PtrClassicFrameLayout性能有缺陷;建议使用PtrFrameLayout,性能较好。这套库自定义能力很强,建议使用。
android-pulltorefresh 实现方式上有缺陷,拓展性也很差。优点就是代码非常简单,只能作为反面例子。
Phoenix ★★★★ ★★ 效果非常好,性能不错,可惜比较难拓展顶部视图,为了适配布局padding造成了性能损失,有优化空间。可以作为学习与练手的对象。
FlyRefresh ★★★★ ★★ 效果很新颖,可惜的是顶部视图计算动效上开销太大,优化空间较少,可以作为学习与练手的对象。
SwipeRefreshLayout ★★★ ★★ 官方出品,更新有保障,但是如上分析,其实性能上还是有点缺陷的,拓展性比较差,不建议放入工程中使用。

##附录-知识点参考

  1. 为你的应用加速 - 安卓优化指南
  2. 使用Systrace分析UI性能
  3. Systrace-文档

About

Performance comparison of android "pull to refresh" repos in github.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • HTML 98.9%
  • Java 1.1%
Morty Proxy This is a proxified and sanitized view of the page, visit original site.