Android 一種點贊動畫的實現

語言: CN / TW / HK

最近有個需求,需要仿照公司的H5實現一個遊戲助手,其中一個點讚的按鈕有動畫效果,如下圖:

device-2022-12-03-17 -original-original.gif

分析一下這個動畫,點選按鈕後,拇指首先有個縮放的效果,然後有5個拇指朝不同的方向移動,其中部分有放大的效果。

點選後的縮放效果

本文通過ScaleAnimation 實現縮放效果,程式碼如下:

private fun playThumbUpScaleAnimator() { // x、y軸方向都從1倍放大到2倍,以控制元件的中心為原點進行縮放 ScaleAnimation(1f, 2f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f).run { // 先取消控制元件當前的動畫效果(重複點選時) view.clearAnimation() // 設定動畫的持續時間 duration = 300 // 開始播放動畫 view.startAnimation(this) } }

拇指的散開效果

有5個拇指分別往不同的方向移動,本文通過動態新增View,並對View設定動畫來實現。可以看到在移動的同時還有縮放的效果,所以需要同時播放幾個動畫。

本文通過ValueAnimatorAnimatorSet來實現該效果,程式碼如圖:

``` // 此陣列控制動畫的效果 // 第一個引數控制X軸移動距離 // 第二個引數控制Y軸移動距離 // 第三個引數控制縮放的倍數(基於原大小) val animatorConfig: ArrayList> = arrayListOf( arrayListOf(-160f, 150f, 1f), arrayListOf(80f, 130f, 1.1f), arrayListOf(-120f, -170f, 1.3f), arrayListOf(80f, -130f, 1f), arrayListOf(-20f, -80f, 0.8f))

private fun playDiffusionAnimator() { for (index in 0 until 5) { binding.root.run { if (this is ViewGroup) { // 建立控制元件 val ivThumbUp = AppCompatImageView(context) ivThumbUp.setImageResource(R.drawable.icon_thumb_up) // 設定與原控制元件一樣的大小 ivThumbUp.layoutParams = FrameLayout.LayoutParams(DensityUtil.dp2Px(25), DensityUtil.dp2Px(25)) // 先設定為全透明 ivThumbUp.alpha = 0f addView(ivThumbUp) // 設定與原控制元件一樣的位置 ivThumbUp.x = binding.ivThumbUp.x ivThumbUp.y = binding.ivThumbUp.y AnimatorSet().apply { // 設定動畫集開始播放前的延遲 startDelay = 330L + index * 50L // 設定動畫監聽 addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator) { // 開始播放時把控制元件設定為不透明 ivThumbUp.alpha = 1f }

                    override fun onAnimationEnd(animation: Animator) {
                        // 播放結束後再次設定為透明,並從根佈局中移除
                        ivThumbUp.alpha = 0f
                        ivThumbUp.clearAnimation()
                        ivThumbUp.post { removeView(ivThumbUp) }
                    }

                    override fun onAnimationCancel(animation: Animator) {}

                    override fun onAnimationRepeat(animation: Animator) {}
                })
                // 設定三個動畫同時播放
                playTogether(
                    // 縮放動畫
                    ValueAnimator.ofFloat(1f, animatorConfig[index][2]).apply {
                        duration = 700
                        // 設定插值器,速度一開始快,快結束時減慢
                        interpolator = DecelerateInterpolator()
                        addUpdateListener { values ->
                             (values.animatedValue as Float).let { value ->
                                ivThumbUp.scaleX = value
                                ivThumbUp.scaleY = value
                            }
                        }
                    },
                    // X軸的移動動畫
                    ValueAnimator.ofFloat(ivThumbUp.x, ivThumbUp.x + animatorConfig[index][0]).apply {
                        duration = 700
                        interpolator = DecelerateInterpolator()
                        addUpdateListener { values ->
                            ivThumbUp.x = values.animatedValue as Float
                        }
                    },
                    // Y軸的移動動畫
                    ValueAnimator.ofFloat(ivThumbUp.y, ivThumbUp.y + animatorConfig[index][1]).apply {
                        duration = 700
                        interpolator = DecelerateInterpolator()
                        addUpdateListener { values ->
                            ivThumbUp.y = values.animatedValue as Float
                        }
                    })
            }.start()
        }
    }
}

} ```

示例

整合之後做了個示例Demo,完整程式碼如下:

``` class AnimatorSetExampleActivity : BaseGestureDetectorActivity() {

private lateinit var binding: LayoutAnimatorsetExampleActivityBinding

private val animatorConfig: ArrayList<java.util.ArrayList<Float>> = arrayListOf(
    arrayListOf(-160f, 150f, 1f),
    arrayListOf(80f, 130f, 1.1f),
    arrayListOf(-120f, -170f, 1.3f),
    arrayListOf(80f, -130f, 1f),
    arrayListOf(-20f, -80f, 0.8f))


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.layout_animatorset_example_activity)
    binding.ivThumbUp.setOnClickListener {
        playThumbUpScaleAnimator()
        playDiffusionAnimator()
    }
}

private fun playThumbUpScaleAnimator() {
    // x,y軸方向都從1倍放大到2倍,以控制元件的中心為原點進行縮放
    ScaleAnimation(1f, 2f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f).run {
        // 先取消控制元件當前的動畫效果(重複點選時)
        binding.ivThumbUp.clearAnimation()
        // 設定動畫的持續時間
        duration = 300
        // 開始播放動畫
        binding.ivThumbUp.startAnimation(this)
    }
}

private fun playDiffusionAnimator() {
    for (index in 0 until 5) {
        binding.root.run {
            if (this is ViewGroup) {
                // 建立控制元件
                val ivThumbUp = AppCompatImageView(context)
                ivThumbUp.setImageResource(R.drawable.icon_thumb_up)
                // 設定與原控制元件一樣的大小
                ivThumbUp.layoutParams = FrameLayout.LayoutParams(DensityUtil.dp2Px(25), DensityUtil.dp2Px(25))
                // 先設定為全透明
                ivThumbUp.alpha = 0f
                addView(ivThumbUp)
                // 設定與原控制元件一樣的位置
                ivThumbUp.x = binding.ivThumbUp.x
                ivThumbUp.y = binding.ivThumbUp.y
                AnimatorSet().apply {
                    // 設定動畫集開始播放前的延遲
                    startDelay = 330L + index * 50L
                    // 設定動畫監聽
                    addListener(object : Animator.AnimatorListener {
                        override fun onAnimationStart(animation: Animator) {
                            // 開始播放時把控制元件設定為不透明
                            ivThumbUp.alpha = 1f
                        }

                        override fun onAnimationEnd(animation: Animator) {
                            // 播放結束後再次設定為透明,並從根佈局中移除
                            ivThumbUp.alpha = 0f
                            ivThumbUp.clearAnimation()
                            ivThumbUp.post { removeView(ivThumbUp) }
                        }

                        override fun onAnimationCancel(animation: Animator) {}

                        override fun onAnimationRepeat(animation: Animator) {}
                    })
                    // 設定三個動畫同時播放
                    playTogether(
                        // 縮放動畫
                        ValueAnimator.ofFloat(1f, animatorConfig[index][2]).apply {
                            duration = 700
                            // 設定插值器,速度一開始快,快結束時減緩
                            interpolator = DecelerateInterpolator()
                            addUpdateListener { values ->
                                (values.animatedValue as Float).let { value ->
                                    ivThumbUp.scaleX = value
                                    ivThumbUp.scaleY = value
                                }
                            }
                        },
                        // Y軸的移動動畫
                        ValueAnimator.ofFloat(ivThumbUp.x, ivThumbUp.x + animatorConfig[index][0]).apply {
                            duration = 700
                            interpolator = DecelerateInterpolator()
                            addUpdateListener { values ->
                                ivThumbUp.x = values.animatedValue as Float
                            }
                        },
                        // X軸的移動動畫
                        ValueAnimator.ofFloat(ivThumbUp.y, ivThumbUp.y + animatorConfig[index][1]).apply {
                            duration = 700
                            interpolator = DecelerateInterpolator()
                            addUpdateListener { values ->
                                ivThumbUp.y = values.animatedValue as Float
                            }
                        })
                }.start()
            }
        }
    }
}

} ```

效果如圖:

device-2022-12-03-18 -original-original.gif

個人感覺還原度還是可以的哈哈。