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

作者:timer 时间:2023-08-26 12:45:39 

即刻点赞展示

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

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

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

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

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

现在只需要根据这张图,写出对应的动画即可。 分为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号区域就行。绘制差异化文字时,需要加上不变的文字的宽度就行。

效果展示

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

源码

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)
       }
   }
}

来源:https://juejin.cn/post/7179181214530551867

标签:android,点赞,数字切换
0
投稿

猜你喜欢

  • Android App在线程中创建handler的方法讲解

    2021-06-24 17:00:33
  • 基于C#实现微信支付宝扫码支付功能

    2023-09-24 04:53:49
  • java 中String.equals和==的比较

    2023-03-09 08:45:16
  • java中List接口与实现类介绍

    2022-11-17 02:54:40
  • IntelliJ IDEA Run时报“无效的源发行版:16“错误问题及解决方法

    2022-06-04 18:08:35
  • C#程序员统计自己的代码行数

    2021-10-18 05:37:37
  • C#多线程之线程控制详解

    2022-02-04 17:51:44
  • C#下载歌词文件的同步和异步方法

    2023-04-11 22:46:49
  • Java反射机制详解_动力节点Java学院整理

    2022-08-19 18:10:13
  • C#实现将Email地址转成图片显示的方法

    2022-09-26 09:35:36
  • 生产消费者模式实现方式和线程安全问题代码示例

    2023-11-26 19:44:17
  • Android开发中的几种网络请求方式详解

    2021-06-01 17:45:21
  • slf4j jcl jul log4j1 log4j2 logback各组件系统日志切换

    2023-08-08 13:00:41
  • JavaWeb框架MVC设计思想详解

    2022-09-09 06:43:50
  • C#异步编程详解

    2023-02-13 13:10:39
  • Springboot创建项目的图文教程(idea版本)

    2022-09-30 13:30:17
  • Java进程cpu占用过高问题解决

    2021-08-09 00:16:59
  • Java实现的对称加密算法AES定义与用法详解

    2021-10-16 22:16:05
  • C# 从枚举值获取对应的文本描述详解

    2021-06-14 00:47:58
  • Android实现悬浮对话框代码

    2022-11-23 16:12:45
  • asp之家 软件编程 m.aspxhome.com