仿QQ的recyclerview效果实现

最近在Google官方的github库,看到了一个有意思的recyclerView效果。android

像这样:git

我的感受彷佛和QQ的效果差很少,只不过QQ用的是Fling动画,而这里用的是Spring动画。有意思的是,彷佛关于实现该效果所使用的EdgeEffectFactory这个类网上博客介绍很少,正好看其API比较简单,因而打算写篇博客来介绍这个动画的实现效果。github

EdgeEffectFactory的API介绍

EdgeEffectFactory是存在于Recyclerview的内部的一个静态类,便可以经过RecyclerView.EdgeEffectFactory获得,官方解释这个类的做用是让你自定义RecyclerView的过分边缘滚动的效果,看上面的gif能够知道,确实是实现了一个自定义的过分边缘滚动效果。ide

咱们须要建立一个EdgeEffectFactory并重写其createEdgeEffect方法,在createEdgeEffect中须要返回一个EdgeEffect对象函数

就像这样:动画

EdgeEffect的API介绍

上面说到了咱们要给createEdgeEffect中返回一个EdgeEffect对象,那么这个EdgeEffect类是什么东东?spa

Android官网说这个类用做:“当用户滚动到2D空间中的内容边界以外时,此类将执行可滚动窗口小部件边缘使用的图形效果。 ”3d

大致能够理解为过分边缘滚动效果的具体实现code

咱们须要重写这个类中的onPull(deltaDistance: Float)onPull(deltaDistance: Float, displacement: Float)onRelease()onAbsorb方法。这四个方法(实际上是3个)的调用时机很是好理解。cdn

onPull介绍:

onPull调用时机就是在用户朝远离边缘的方向拉动的时候,咱们须要在这个方法里面去更新recyclerView的每个可见item的translationY

onRelease介绍:

onRelease调用时机就是在用户放开的手指的那一刻,咱们须要在这里让recyclerView的每个可见item的translationY值变成0,为了实现一个translationY之间的过渡,咱们可使用属性动画,使用SpringAnimation或者FlingAnimation。示例使用的是SpringAnimation(弹簧动画)。

onAbsorb介绍:

onAbsorb的调用时机就是recyclerView在脱离用户手指滑动期间,到了recyclerview的边缘而且此时速度不为0。咱们须要在这里使用一个SpringAnimation或着FlingAnimation来作一个相似“缓冲”的效果

实现代码:

val edgeEffectFactory = object : RecyclerView.EdgeEffectFactory() {
        override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
            return object : EdgeEffect(view.context) {
                override fun onPull(deltaDistance: Float) {
                    super.onPull(deltaDistance)
                    handlePull(deltaDistance)
                }
				
                override fun onPull(deltaDistance: Float, displacement: Float) {
                    super.onPull(deltaDistance, displacement)
                    handlePull(deltaDistance)
                }
				//更新recyclerView的每个可见item的`translationY`值
                private fun handlePull(deltaDistance: Float) {
                    val sign = if (direction == DIRECTION_BOTTOM) -1 else 1
                    val translationYDelta = sign * view.height *  deltaDistance * OVERSCROLL_TRANSLATION_MAGNITUDE
                    //一个内联函数,更新每个recyclerview的可见item的translationY值
                    view.forEachVisibleHolder { holder: CheeseHolder ->
                        holder.translationY.cancel()
                        holder.itemView.translationY += translationYDelta

                    }
                }
				//在这里让recyclerView的每个可见item的translationY值变成0,使用到了SpringAnimation
                override fun onRelease() {
                    super.onRelease()
                    view.forEachVisibleHolder { holder: CheeseHolder ->
                        holder.translationY.start()
                    }
                }
				//用SpringAnimation来作一个recyclerview的到达边缘的惯性缓冲效果
                override fun onAbsorb(velocity: Int) {
                    super.onAbsorb(velocity)
                    val sign = if (direction == DIRECTION_BOTTOM) -1 else 1
                    val translationVelocity = sign * velocity * FLING_TRANSLATION_MAGNITUDE
                    view.forEachVisibleHolder { holder: CheeseHolder ->
                        holder.translationY.setStartVelocity(translationVelocity).start()
                    }
                }
            }
        }
    }
复制代码

其中的CheeseHolder是一个Recyclerview的ViewHolder,咱们在里面存有一个SpringAnimation

class CheeseHolder(view: View) : RecyclerView.ViewHolder(view) {
        val translationY: SpringAnimation = SpringAnimation(itemView, SpringAnimation.TRANSLATION_Y)
                .setSpring(
                        SpringForce()
                                .setFinalPosition(0f)
                                .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
                                .setStiffness(SpringForce.STIFFNESS_LOW)
                )
		...
    }
复制代码

最后

只要经过

recyclerview.edgeEffectFactory = adapter.edgeEffectFactory
复制代码

至此,就实现了开篇的gif效果。

这篇文章实际上是参考Google官方的Motion库的代码。

本身也写一个实现:github

若是有什么好的建议欢迎评论指正。

相关文章
相关标签/搜索