仿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

若是有什麼好的建議歡迎評論指正。

相關文章
相關標籤/搜索