Android自定义View模仿即刻点赞数字切换效果实例

2022-12-25 06:18:40
目录
即刻点赞展示自己如何实现这种数字切换呢?效果展示源码总结

即刻点赞展示

点赞的数字增加和减少并不是整个替换,而是差异化替换。再加上动画效果就看的很舒服。

自己如何实现这种数字切换呢?

下面用一张图来展示我的思路:

现在只需要根据这张图,写出对应的动画即可。 分为2种场景:

    数字+1:
      差异化的数字从3号区域由渐变动画(透明度 0- 255) + 偏移动画 (3号区域绘制文字的基线,2号区域绘制文字的基线),将数字移动到2号位置处差异化的数字从2号区域由渐变动画(透明度 255- 0) + 偏移动画(2号区域绘制文字的基线,1号区域绘制文字的基线),将数字移动到1号位置处数字-1
        差异化的数字从1号区域由渐变动画(透明度 0- 255) + 偏移动画 (1号区域绘制文字的基线,2号区域绘制文字的基线),将数字移动到2号位置处差异化的数字从2号区域由渐变动画(透明度 255- 0) + 偏移动画(2号区域绘制文字的基线,3号区域绘制文字的基线),将数字移动到3号位置处

        公共部分就是: 不变的文字不需要做任何处理,绘制在2号区域就行。绘制差异化文字时,需要加上不变的文字的宽度就行。

        效果展示

        源码

        class LikeView @JvmOverloads constructor(
            context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
        ) : View(context, attrs, defStyleAttr) {
        
            private val paint = Paint().also {
                it.isAntiAlias = true
                it.textSize = 200f
            }
        
            private val textRect0 = Rect(300, 100, 800, 300)
            private val textRect1 = Rect(300, 300, 800, 500)
            private val textRect2 = Rect(300, 500, 800, 700)
        
            private var nextNumberAlpha: Int = 0
                set(value) {
                    field = value
                    invalidate()
                }
        
            private var currentNumberAlpha: Int = 255
                set(value) {
                    field = value
                    invalidate()
                }
        
            private var offsetPercent = 0f
                set(value) {
                    field = value
                    invalidate()
                }
        
            private val fontMetrics: FontMetrics = paint.fontMetrics
            private var currentNumber = 99
            private var nextNumber = 0
            private var motionLess = currentNumber.toString()
            private var currentMotion = ""
            private var nextMotion = ""
        
            private val animator: ObjectAnimator by lazy {
                val nextNumberAlphaAnimator = PropertyValuesHolder.ofInt("nextNumberAlpha", 0, 255)
                val offsetPercentAnimator = PropertyValuesHolder.ofFloat("offsetPercent", 0f, 1f)
                val currentNumberAlphaAnimator = PropertyValuesHolder.ofInt("currentNumberAlpha", 255, 0)
                val animator = ObjectAnimator.ofPropertyValuesHolder(
                    this,
                    nextNumberAlphaAnimator,
                    offsetPercentAnimator,
                    currentNumberAlphaAnimator
                )
                animator.duration = 200
                animator.interpolator = DecelerateInterpolator()
                animator.addListener(
                    onEnd = {
                        currentNumber = nextNumber
                    }
                )
                animator
            }
        
            override fun onDraw(canvas: Canvas) {
                paint.alpha = 255
        
                paint.color = Color.LTGRAY
                canvas.drawRect(textRect0, paint)
        
                paint.color = Color.RED
                canvas.drawRect(textRect1, paint)
        
                paint.color = Color.GREEN
                canvas.drawRect(textRect2, paint)
        
                paint.color = Color.BLACK
                if (motionLess.isNotEmpty()) {
                    drawText(canvas, motionLess, textRect1, 0f)
                }
        
                if (nextMotion.isEmpty() || currentMotion.isEmpty()) {
                    return
                }
        
                val textHorizontalOffset =
                    if (motionLess.isNotEmpty()) paint.measureText(motionLess) else 0f
                if (nextNumber > currentNumber) {
                    paint.alpha = currentNumberAlpha
                    drawText(canvas, currentMotion, textRect1, textHorizontalOffset, -offsetPercent)
                    paint.alpha = nextNumberAlpha
                    drawText(canvas, nextMotion, textRect2, textHorizontalOffset, -offsetPercent)
                } else {
                    paint.alpha = nextNumberAlpha
                    drawText(canvas, nextMotion, textRect0, textHorizontalOffset, offsetPercent)
                    paint.alpha = currentNumberAlpha
                    drawText(canvas, currentMotion, textRect1, textHorizontalOffset, offsetPercent)
                }
            }
        
            private fun drawText(
                canvas: Canvas,
                text: String,
                rect: Rect,
                textHorizontalOffset: Float = 0f,
                offsetPercent: Float = 0f
            ) {
                canvas.drawText(
                    text,
                    rect.left.toFloat() + textHorizontalOffset,
                    rect.top + (rect.bottom - rect.top) / 2f - (fontMetrics.bottom + fontMetrics.top) / 2f + offsetPercent * 200,
                    paint
                )
            }
        
        
            override fun onDetachedFromWindow() {
                super.onDetachedFromWindow()
                animator.end()
            }
        
            fun plus() {
                if (currentNumber == Int.MAX_VALUE) {
                    return
                }
                nextNumber = currentNumber + 1
        
                processText(findEqualsStringIndex())
        
                if (animator.isRunning) {
                    return
                }
                animator.start()
            }
        
            fun minus() {
                if (currentNumber == 0) {
                    return
                }
                nextNumber = currentNumber - 1
                processText(findEqualsStringIndex())
                if (animator.isRunning) {
                    return
                }
                animator.start()
            }
        
            private fun findEqualsStringIndex(): Int {
                var equalIndex = -1
                val nextNumberStr = nextNumber.toString()
                val currentNumberStr = currentNumber.toString()
        
                val endIndex = min(currentNumberStr.length, nextNumberStr.length) - 1
        
                for (index in 0..endIndex) {
                    if (nextNumberStr[index] != currentNumberStr[index]) {
                        break
                    }
                    equalIndex = index
                }
                return equalIndex
            }
        
            private fun processText(index: Int) {
                val currentNumberStr = currentNumber.toString()
                val nextNumberStr = nextNumber.toString()
                if (index == -1) {
                    motionLess = ""
                    currentMotion = currentNumberStr
                    nextMotion = nextNumberStr
                } else {
                    motionLess = currentNumberStr.substring(0, index + 1)
                    currentMotion = currentNumberStr.substring(index + 1)
                    nextMotion = nextNumberStr.substring(index + 1)
                }
            }
        }

        总结

        到此这篇关于Android自定义View模仿即刻点赞数字切换效果的文章就介绍到这了,更多相关Android模仿即刻点赞数字切换内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!