Android 改造SwipeRefreshLayout 支持上

2017-03-26  本文已影响59人  坚持编程_lyz

原文链接 : http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650822399&idx=1&sn=9cc9c74d063cc00c6820bdfefc6bf518&chksm=80b78261b7c00b7799577b10a42f598249b02d3bc1d22f281be0391807d103477ccaad9dfc82&mpshare=1&scene=23&srcid=0323qkEeDyK3MOkfXs3LaNYh%23rd

请支持原文作者 : 鸿洋

看到这个文章我才想起来,今天推荐一个项目,是我的一位好朋友之前在创业公司的一个项目,后来项目失败了,经允许后将源码分享出来了,该项目也对SwipeRefreshLayout进行了扩展,支持了上拉,如果有实际需求,不妨也试下这个(项目质量非常高)。

https://github.com/pinguo-sunjianfei/Android-Application-ZJB

ps:该项目服务端已经停了,所以只能看代码了,运行不起来了~~

本文作者

本文由Get_Zoom投稿。
Get_Zoom的博客地址:
http://blog.csdn.net/Get_Zoom

1

NestedScrolling机制

之前笔者在设计的时候,想在ViewPager的页面上实现仿微信的左滑删除,但是怎么都实现不了,因为其中跟ViewPager的滑动冲突了,当时才疏学浅(现在也是),进了很多坑,比如滑动的拦截、滑动事件在Down之后会跳过判断等,在没有系统学习过这方面知识的情况下以大败告终。

所以,谷歌人性化地推出了这个机制,滑动之前和父控件商量一下,一切多么融洽,和之前盲人摸象的方式相比人性化多了。

(1)滑动流程

子view获取到点击事件后,询问父亲是否需要配合滑动,然后每一次滑动之前都会询问父亲,并记录下父亲消耗的滑动距离,在上面完成后才进行自己自身的滑动。

(2)接口

NestedScrollingChild

NestedScrollingParent

(3)帮助类

NestedScrollingChildHelper NestedScrollingParentHelper

故名思义,上面的帮助类帮助我们处理了上面父子接口的方法。

它们帮助我们实现了逻辑上的方法,在一些情况下我们只希望处理子接口或者父接口,为了对接可以在另一个接口使用帮助类(比如下面的实例,改造SwipeRefreshLayout,我们更希望作为父亲处理子view事件而滑动自身,对于上层组件(父)不是很关心,就可以使用NestedScrollingParentHelper来方便编程。

借用网上的一张图,可以看到两个接口之间的对应关系:

图片来源:https://segmentfault.com/a/119000000287365

2

实例演示:改造SwipeRefreshLayout

(1)目的
SwipeRefreshLayout就是一个实现了NestedScrolling机制的控件,可以方便的实现下拉刷新。

现在我们想加上上拉刷新功能,可以反着做,给下方加一个可拉动的控件(小圆圈),然后处理它的滑动事件。为了能兼顾上层,我们再外面还加了常规的CoordinatorLayout和AppBarLayout作为测试。

成果:


(2)准备工作
改造之前当然要先把人家之前的成果准备好。



首先当然是把我们的SwipeRefreshLayout移过来,可以换一个名字避免以后的冲突。

然后布局中要用到两个控件,一个是CircleImageView,也就是显示的小圆圈,附带阴影功能;一个是MaterialProgressDrawable,用于在CircleImageView显示进程(颜色滑动)。

在移动的时候SwipeRefreshLayout会报错,报错时把东西移动过来就行了。
然后阅读源码,我们暂时只处理NestedScrolling机制,所以一般的移动流程比如onTouchEvent可以先放着,以后为了和其他不带NestedScrolling控件兼容的时候再改进。

(3)开始改造工作
**1.参数测量 **
一个主要的问题就是,我们下面的圆圈(加载圈,以下简称圆圈)要放在哪?

原生的直接放在中间然后上去一个圆圈身位的地方,所以下面圆圈水平位置一样,竖直的话就放在屏幕下方。

综合测试出这样的距离比较好:

所以相应的位置放置我们就这样:

2.作为父控件配合滑动


a.是否配合滑动

这里我们只增加了一个上拉刷新标志位

这里也是,只是增加了mTotalUnconsumedBottom ,这是我们上拉刷新的未消费路程。

b.滑动之前

我们可以看到源码中的onNestedPreScroll有这么一段处理:

在滑动前先判断,我们未消费滑动路程是否还有,有则判断方向,如果是滑动的反方向,也就是我们再下拉刷新一半的时候又往回拉,这时做出处理,选择消费当前滑动路程。

所以我们可以写出下方的拖动预处理:

这里有个坑要提醒下,一开始笔者自作聪明,觉得consumed参数应该传绝对值,导致后来往回滑的时候子控件跑得飞快(可以想想为什么),所以这里消费了负的路程就传回负的路程,可以看看源码中NestedScrollingChildHelper的实现。

c.正式滑动

这个反而比较容易,只需要加上判断当前子控件还能不能往上滑。

滑动处理:

大概解释一下,我们的滑动时分段的,在mTotalDragDistance滑动之前是线性的,在这之后会做一个加速的处理,最多延伸一个mSpinnerOffsetEnd
在实际的view位置改变中,我们使用的是
setTargetOffsetTopAndBottomForBottom (targetY-mCurrentTargetOffsetBottom, true);

这个方法,里面是采用
ViewCompat.offsetTopAndBottom

来改变view的位置的。

d.结束滑动

如果手指离开的时候,拖动距离不为零,那么我们要做判断,做出相应的处理

可以看到主要有两种处理,以mTotalDragDistance为界限,超过这个滑动距离我们就显示刷新,没有的话就动画回到原点。

**3.作为子控件配合滑动 **
我们知道,AppBarLayout和CoordinatorLayout会配合滑动,子view往上滑的时候会隐藏,如果不做处理,在下端圆圈滑动到一半的时候往回滑会把AppBar又拖出来,消费滑动事件,所以我们选择拦截,在下部圆圈滑动的时候优先处理滑动:

到这里,我们的控件就基本完成了,当然谷歌出版的控件,动画效果是不能少的,它的美观也体现在这里,由于基本是能模仿的,所以这里不再多讲。这个控件的改造主要麻烦在它的动画衔接以及滑动处理机制(加速等),剩下的都很好理解,建议大家动手试一试。

项目地址:
https://github.com/SGZoom/DailyWidget/tree/master/widgetpro

文章跟代码可能包含很多不足之处,欢迎大家帮忙指出错误与不足,谢谢~

参考文章:
https://segmentfault.com/a/1190000002873657

http://blog.csdn.net/al4fun/article/details/53888990

上一篇下一篇

猜你喜欢

热点阅读