Recently, I saw an interesting recyclerView effect in Google’s official Github library.

Like this:

Personally, it seems to have the same effect as QQ, but QQ uses the Fling animation, while Spring animation is used here. Interestingly, it seems that there are not many blogs about the EdgeEffectFactory class used to implement this effect. Since its API is relatively simple, I decided to write a blog about the implementation effect of this animation.

EdgeEffectFactory API introduction

EdgeEffectFactory is Recyclerview inside of a static class, which can be through Recyclerview. EdgeEffectFactory, official explain the role of this class allows you to customize Recyclerview excessive edge rolling effect, As you can see from the GIF above, it does implement a custom over-edge scrolling effect.

We need to create an EdgeEffectFactory and rewrite its createEdgeEffect method, where we need to return an EdgeEffect object

Something like this:

EdgeEffect API introduction

We are going to return an EdgeEffect object to createEdgeEffect. What is this EdgeEffect class?

The Android website says this class is used as: “This class will perform the graphical effects used at the edges of scrollable widgets when the user scrolls outside the boundaries of content in 2D space.”

It can be understood as the concrete realization of excessive edge scrolling effect

We need to override the onPull(deltaDistance: Float), onPull(deltaDistance: Float, Displacement: Float), onRelease() and onAbsorb methods in this class. The timing of the invocation of these four methods (actually three) is easy to understand.

OnPull is introduced:

OnPull is called when the user is pulling away from the edge. We need to update the translationY value of every visible item of recyclerView in this method

OnRelease is introduced:

The onRelease call time is the moment when the user releases his finger. Here we need to make the translationY value of every visible item of recyclerView become 0. In order to achieve a transition between translationY, we can use property animation. Use SpringAnimation or FlingAnimation. The example uses a SpringAnimation.

OnAbsorb is introduced:

The time when onAbsorb is invoked is when recyclerView reaches the edge of recyclerView when it slides away from the user’s finger and the speed is not zero. We need to use a SpringAnimation or FlingAnimation here to create a “buffering” effect

Implementation code:

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)
                }
				// Update the translationY value of every visible item in recyclerView
                private fun handlePull(deltaDistance: Float) {
                    val sign = if (direction == DIRECTION_BOTTOM) - 1 else 1
                    val translationYDelta = sign * view.height *  deltaDistance * OVERSCROLL_TRANSLATION_MAGNITUDE
                    // An inline function that updates the translationY value of every recyclerView visible item
                    view.forEachVisibleHolder { holder: CheeseHolder ->
                        holder.translationY.cancel()
                        holder.itemView.translationY += translationYDelta

                    }
                }
				// here we make the translationY value of every visible item of recyclerView 0, using SpringAnimation
                override fun onRelease(a) {
                    super.onRelease()
                    view.forEachVisibleHolder { holder: CheeseHolder ->
                        holder.translationY.start()
                    }
                }
				// Use SpringAnimation to create a recyclerView inertia buffer effect to the edge
                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()
                    }
                }
            }
        }
    }
Copy the code

The CheeseHolder is a ViewHolder of Recyclerview, in which we have a 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)
                )
		...
    }
Copy the code

The last

As long as through the

recyclerview.edgeEffectFactory = adapter.edgeEffectFactory
Copy the code

At this point, the opening GIF effect is achieved.

This article is actually a reference to Google’s official Motion library code.

Write your own implementation: Github

If you have any good suggestions, please comment and correct them.