您的位置:首页 > 移动开发 > Android开发

安卓下拉刷新开源库对比

2016-03-31 15:39 447 查看

安卓下拉刷新开源库对比

2016-03-31 15:39
450人阅读 评论(0)
收藏
举报

本文章已收录于:


分类:
android(215)




作者同类文章X

目录(?)[+]
Repo
拓展性
易用性
性能分析
1 Chris Baness Ptr
2 liaohuqius Ptr
3 johannilssons Ptr
4 Yalantiss Ptr
5 race604s Ptr
6 SwipeRefreshLayout

总结
附录-知识点参考

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


Repo

RepoOwnerStar

(2015.12.5)
versionSnap shot
Android-PullToRefresh

(作者已停止维护)
chrisbanes6014latest

android-Ultra-Pull-To-Refreshliaohuqiu34131.0.11

android-pulltorefresh

(作者已停止维护)
johannilsson2414latest

PhoenixYalantis18971.2.3

FlyRefreshrace60418432.0.0

SwipeRefreshLayoutAndroid 

Support v4 

(19.1.0 ↑)
Nonelatest


拓展性

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进行性能分析:





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


1. Chris Banes's Ptr

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

trace snapshot:





分析:

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


2. liaohuqiu's Ptr

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

trace snapshot:





分析:

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





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

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

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





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


3. johannilsson's Ptr

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

trace snapshot:





分析:

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


4. Yalantis's Ptr

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

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

trace snapshot:





分析:

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

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





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





实际上,由于这个库是一个嵌套视图,可以尝试在父视图的
onLayout
中进行处理由于位置变化带来的padding影响,这么处理,只是在
layout
阶段处理,不会造成
measure
的大量开销。

我粗略的做了一点点改动,只在父视图中处理padding而不是在子视图里面做,就可以在上拉、下拉的时候保持padding不变,并且性能有了很大提高。不过好像逻辑就有点问题,还需要再做改动,已经跟作者提出issue。

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





改动后的样子,上下拉动时padding不会变动。不过每次回滚的时候顶部会多往里面滚一点,还需作者针对issue完善:





5. race604's Ptr

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


顶部动效实现方式:
飞机滑动 
ObjectAnimator
.
山体移动、树木弯曲 通过移动距离计算山体偏移、树木轮廓,得出
Path
后进行draw.

trace snapshot:





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


6. SwipeRefreshLayout

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

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

trace snapshot:





分析:官方的下拉刷新组件,动画十分美观简洁,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时间确实没掉了,对功能毫无影响,性能却有了很大优化:





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


总结

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


附录-知识点参考

为你的应用加速
- 安卓优化指南
使用Systrace分析UI性能
Systrace-文档
来自:https://github.com/desmond1121/Android-Ptr-Comparison
 



顶 0 踩 0
 
 
上一篇一个Android学习的地方

下一篇阿里巴巴开源代码

我的同类文章

android(215)

http://blog.csdn.net
自定义一个类似listView和recylceView的需要setAdapter的控件2017-03-14阅读21

一个反编译看代码的途径和android6.0权限控制实例2017-01-06阅读115

Android6.0动态权限申请步骤以及需要注意的一些坑2016-12-17阅读733

java算法之归并排序2016-12-07阅读56

java算法之插入排序2016-12-06阅读50

java算法之链表2016-12-06阅读57

•【Android学习笔记】RecycleView
绑定了Adapter的item明明设置了match_parent却不起作用2017-03-09阅读20

Retrofit简单使用手册2016-12-24阅读87

AsyncTask的cancel方法并没有停止任务2016-12-15阅读228

java算法之选择排序2016-12-06阅读52

java算法之冒泡排序2016-12-06阅读40

更多文章
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 安卓