专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
鸿洋  ·  Android | 扩大View点击区域的几种方式 ·  2 天前  
stormzhang  ·  双十一晚会不香了? ·  3 天前  
鸿洋  ·  鸿蒙中是如何实现UI自动刷新的? ·  1 周前  
51好读  ›  专栏  ›  开发者全社区

Android下拉刷新控件SwipeRefreshLayout源码浅析

开发者全社区  · 公众号  · android  · 2016-11-27 13:12

正文

作者:Gordon_H

原文:http://blog.csdn.net/u011443509


SwipeRefreshLayout是Android官方的下拉刷新控件,使用简单,界面美观,不熟悉的朋友可以随便搜索了解一下,我们这里直接进入正题。


首先给张流程图吧,标出了几个主要方法的作用,可以结合着看一下哈。





这种下拉刷新控件的原理不难,基本就是监听手指的运动,获取手指的坐标,通过计算判断出是哪种操作,然后就是回调相应的接口了。


SwipeRefreshLayout是继承自ViewGroup的,根据Android的事件分发机制,触摸事件应该是先传递到ViewGroup,根据onInterceptTouchEvent的返回值决定是否拦截事件的,那么就onInterceptTouchEvent出发:




是否拦截的情况有很多种,这里如果满足五个条件之一就直接返回false,使用时触摸事件发生冲突的话就可以从这里出发分析,这里也不具体展开了。


简单看一下,在ACTION_DOWN中记录下手指坐标,ACTION_MOVE中计算出移动的距离,并且判断是否大于阈值,是的话就将mIsBeingDragged标志位设为true。最后返回的是mIsBeingDragged。


SwipeRefreshLayout一般是嵌套可滚动的View使用的,正常滚动时会满足前面的条件(canChildScrollUp()),这时不进行拦截,只有当滚动到顶部才会进入后面action的判断。


当拦截了手势之后,接下来肯定就进入了自己的onTouchEvent方法:




这里省略了一些代码,前面还有几行跟上面的类似,也是在满足其中一个条件时直接返回;switch中也还有几行处理多指触控的,这些都略过了。


看一下ACTION_MOVE中计算了手指移动的距离,这时的mIsBeingDragged正常情况下应为true,当距离大于零就会执行moveSpinner。


在ACTION_UP中则会执行finishSpinner,到这里就可以猜出,执行刷新的逻辑主要就在这两个方法中。


看这两个方法前,要知道两个重要的成员变量:


一个是mCircleView,是CircleImageView的实例,继承了ImageView,主要绘制进度圈的背景;另一个是mProgress,是MaterialProgressDrawable的实例,继承自Drawable且实现Animatable接口,主要绘制进度圈,SwipeRefreshLayout正是通过调用其方法来绘制动画。


接下来就先看一下moveSpinner:




主要就是根据移动的距离和最大移动计算移动的百分比,中间还经过一些优化体验的效果(比如在刚开始移动时增长比较快,超过刷新的距离后就增长比较慢)。倒数第二行执行了setProgressRotation,传入的是经过一堆计算后的rotation,传入该方法后,mProgress就根据它来绘制进度圈,因此主要的动画就应该在这个方法内。


最后一行执行setTargetOffsetTopAndBottom,就是调整进度圈的位置,我们来看一下:




比较简单,就是调整进度圈的位置并进行记录。


最后来手指抬起后执行的finishSpinner:




逻辑也很简单,如果移动的距离没有达到设定值,则移动到初始位置后会执行startScaleDownAnimation,也就是消失的动画了。


当移动的距离超过设定值时就执行setRefreshing(true,true),在该方法里更新一些成员变量的值后会执行animateOffsetToCorrectPosition,由名字就知道是执行动画将进度圈移动到正确位置的(也就是头部)。





逻辑基本相同,进行一些设置后,最后都会执行mCircleView的startAnimation,只是传入的值以及监听器不同。


如果是要执行刷新的操作,传入的值是头部高度,监听器为:




动画完成后,也就是进度圈移动到头部后,会执行mProgress.start();这里执行的就是在刷新时进度圈转啊转的动画。


接下来注意到如果mListener不为空就会执行onRefresh方法,这个mListener其实就是执行setOnRefreshListener所设置的监听器,因此在这里完成刷新。


到这里整个刷新流程就结束了。


这样就基本把SwipeRefreshLayout的流程过了一遍,但是要实现这样一个控件还是有很多小问题需要考虑的,这里主要是把思路理清,知道如果出现问题该怎样解决。另外从源码也可以看出swipeRefreshLayout的定制性是比较差的,也不知道google是不是故意这样希望以后全都用这种统一样式的下拉刷新。


当然有一些第三方下拉刷新的定制性还是比较好的,使用上也不难。但是有些人(比如我)是比较倾向于使用官方的控件的,不到万不得已都不想用第三方工具。下次会写一篇探讨一下用swipeRefreshLayout实现自定义样式的文章~






关于Java和Android大牛频道

Java和Android大牛频道是一个数万人关注的探讨Java和Android开发的公众号,分享和原创最有价值的干货文章,让你成为这方面的大牛!

我们探讨android和Java开发最前沿的技术:android性能优化 ,插件化,跨平台,动态化,加固和反破解等,也讨论设计模式/软件架构等。由群来自BAT的工程师组成的团队

关注即送红包,回复:“百度” 、“阿里”、“腾讯” 有惊喜!!!关注后可用入微信群。群里都是来自百度阿里腾讯的大牛。

欢迎关注我们,一起讨论技术,扫描和长按下方的二维码可快速关注我们。搜索微信公众号:JANiubility。

公众号:JANiubility