相关文章推荐

https://github.com/huzenan/EasyPullLayout

再老的司机也难免遇到这样的场景,产品跑过来大声对我说:首页要加刷新,下拉刷新非侵入式,上拉加载为侵入式,头部轮播图片最左边向右继续拖拽进入xx页,最右边向左继续拖拽进入xx页!噢,xx页再加一个从中间下拉刷新吧!噢,设计已经出好了刷新的动画和规范,照着做就好了。

(╯‵□′)╯︵┻━┻ 顿时有了掀桌子的小心情,怎么办,写一个统一的刷新的库?太重了而且方法数爆了怎么办?

然而,现在我有了EasyPullLayout,你想加什么随便加就是了,上拉、下拉、左拉、右拉,任何姿势我都能给,整个控件只有一个文件,不到500行代码,支持横向纵向,侵入非侵入,自定义拉拽行为以及刷新内容,ListView、RecyclerView、ViewPager等等什么内容都能包裹进来,再也不用导入这样那样的库来支持各种各样的刷新了。

一共5个demo,其余的都传了效果图到github上,其中变形金刚动画用到了我写的另一个轻量级的控件EasyPath,使用方法很简单,传送门:

https://github.com/huzenan/EasyPath

接着在布局文件中,在需要刷新的地方用EasyPullLayout包裹起来(例如根布局),并为EasyPullLayout下的子View声明layout_type属性,使得子View可以被EasyPullLayout识别,分别可以为content(必选)、edge_top、edge_bottom、edge_left和edge_right:

因为layout_type属性是EasyPullLayout提供的,所以不要忘记加入自定义的命名空间(如上为app)。

上面我们的边缘视图(TransformerView)是自定义的一个视图,我们可以通过EasyPullLayout提供的接口来动态改变它的行为。

EasyPullLayout本身也提供属性可选,用于控制拉拽行为和距离等,详细可以参考github中的“属性”一栏。

EasyPullLayout的设计遵从单一职责原则,只负责处理拉拽相关的操作,其他的均交给外部进行处理,因此其子View可以是任何一种View,这有点类似于RecyclerView只负责使用和回收操作。

EasyPullLayout有3个监听可以设置,分别为:

OnEdgeListener:

可选,用于通知EasyPullLayout当前是否到达边缘,到达边缘后EasyPullLayout会拦截触摸事件,开始拉拽行为,默认会自动监听layout_type为content的子View是否到达边缘。

OnPullListener:

可选,用于EasyPullLatout向外通知当前拉拽的一些参数(例如拉拽进度),我们可以利用这些参数来改变我们的边缘视图的行为,例如调整变形金刚矢量动画当前的执行位置。

OnTriggerListener:

必选,用于EasyPullLayout在触发边缘动作后向外通知,此时EasyPullLayout会一直停留在STATE_TRIGGERING(触发中)的状态,我们做一些耗时操作后,需要调用stop方法让其回到STATE_IDLE(闲置)状态,这样才完成整个过程。

在看了安卓的SwipeRefreshLayout,以及一些开源的刷新库的源码后,有了一些思路,总结起来,控件主要处理的问题有:

  • 1、为EasyPullLayout的子View提供layout_type属性

  • 2、如何摆放子View

  • 3、在什么时机进行拉拽(即处理事件分发)

  • 处理了这3个问题后,剩下的例如拉拽过程,以及触发事件都很好实现了。

    1.提供layout_type属性

    EasyPullLayout需要辨别子View的类型,因此需要子View声明自己的类型,我们在ViewGroup的generateLayoutParams方法中,返回我们自己的LayoutParams:

    这样我们便可以通过子View的LayoutParams对象获取到这个type值。

    2.如何摆放子View

    EasyPullLayout继承自ViewGroup,在完成xml解析后,即onFinishInflate方法中,获取到子View后用一个HashMap来存储,key对应View,value对应View的一些参数,接着再设置默认的OnEdgeListener:

    然后做测量,遍历子View,对每个子View进行测量,然后记录下边缘视图的一些参数,以及根据这些参数初始化EasyPullLayout自身的一些参数:

    然后开始摆放,根据测量时记录的参数,我们将边缘视图分别摆放到四周:

    可以看到最后我们还把子View的当前摆放位置记录下来,因为EasyPullLayout是通过改变View的x和y属性来达到位移效果的, 因此需要参考子View的初始位置。另外这样做的好处是,我们可以不通过onLayout来重置位置,避免回调onLayout。

    3.处理事件分发

    首先要在onInterceptTouchEvent中适当地对事件进行拦截,在ACTION_MOVE事件中回调了OnEdgeListener,这样就把是否进行拦截的判断操作交给了外部进行处理,只要返回正确的类型,则开始对触摸事件进行拦截:

    拦截了触摸事件后,开始进行拉拽操作,在onTouchEvent中,ACTION_MOVE事件对必要的子View进行偏移(设置了对应的fixed选项后,content不会进行偏移,达到侵入式效果),ACTION_CANCEL、ACTION_UP事件则将子View位置还原:

    还原子View位置时,我们通过ValueAnimator,在一段时间内将子View还原,还原后的位置分2种情况,第一种还没超过触发偏移量,则还原回到初始位置,第二种已经超过了触发偏移量,则回到触发偏移量的位置,看图比较直观:

    rollback分为横向和纵向,下面贴出横向的大致的流程:

    因为是Kotlin写的库,所以没有使用kotlin的项目,通过直接gradle直接导入后是看不到源码的,不过不要慌,已经写了一个java的(下面有地址),可以直接复制去用。嗯,随便用,稳稳的。写了好长,逻辑也是已经凌乱了,感谢大伙的围观,谢谢了!

    https://github.com/huzenan/EasyPullLayout/blob/master/EasyPullLayoutJ.java

    Google、滴滴 与 Udacity 联合开发的 Android 课程,有来自硅谷的实战项目,并提供一对一代码审阅和技术辅导,现在部分课程能免费体验,感兴趣的朋友可以扫下面的二维码。 返回搜狐,查看更多

    责任编辑:

    声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
     
    推荐文章