Android自定義View模仿即刻點(diǎn)贊數(shù)字切換效果實(shí)例
即刻點(diǎn)贊展示

點(diǎn)贊的數(shù)字增加和減少并不是整個(gè)替換,而是差異化替換。再加上動(dòng)畫效果就看的很舒服。
自己如何實(shí)現(xiàn)這種數(shù)字切換呢?
下面用一張圖來(lái)展示我的思路:

現(xiàn)在只需要根據(jù)這張圖,寫出對(duì)應(yīng)的動(dòng)畫即可。 分為2種場(chǎng)景:
- 數(shù)字+1:
- 差異化的數(shù)字從3號(hào)區(qū)域由漸變動(dòng)畫(透明度 0- 255) + 偏移動(dòng)畫 (3號(hào)區(qū)域繪制文字的基線,2號(hào)區(qū)域繪制文字的基線),將數(shù)字移動(dòng)到2號(hào)位置處
- 差異化的數(shù)字從2號(hào)區(qū)域由漸變動(dòng)畫(透明度 255- 0) + 偏移動(dòng)畫(2號(hào)區(qū)域繪制文字的基線,1號(hào)區(qū)域繪制文字的基線),將數(shù)字移動(dòng)到1號(hào)位置處
- 數(shù)字-1
- 差異化的數(shù)字從1號(hào)區(qū)域由漸變動(dòng)畫(透明度 0- 255) + 偏移動(dòng)畫 (1號(hào)區(qū)域繪制文字的基線,2號(hào)區(qū)域繪制文字的基線),將數(shù)字移動(dòng)到2號(hào)位置處
- 差異化的數(shù)字從2號(hào)區(qū)域由漸變動(dòng)畫(透明度 255- 0) + 偏移動(dòng)畫(2號(hào)區(qū)域繪制文字的基線,3號(hào)區(qū)域繪制文字的基線),將數(shù)字移動(dòng)到3號(hào)位置處
公共部分就是: 不變的文字不需要做任何處理,繪制在2號(hào)區(qū)域就行。繪制差異化文字時(shí),需要加上不變的文字的寬度就行。
效果展示

源碼
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)
}
}
}總結(jié)
到此這篇關(guān)于Android自定義View模仿即刻點(diǎn)贊數(shù)字切換效果的文章就介紹到這了,更多相關(guān)Android模仿即刻點(diǎn)贊數(shù)字切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android中SparseArray性能優(yōu)化的使用方法
這篇文章主要為大家詳細(xì)介紹了Android中SparseArray性能優(yōu)化的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04
Android 混合動(dòng)畫詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 混合動(dòng)畫詳解及實(shí)現(xiàn)代碼的相關(guān)資料,簡(jiǎn)單的一種動(dòng)畫(如旋轉(zhuǎn)、縮放、漸變、位移等)有時(shí)候并不能滿足我們項(xiàng)目的要求,這時(shí)候就需要運(yùn)用到混合動(dòng)畫,需要的朋友可以參考下2016-11-11
Android—基于微信開(kāi)放平臺(tái)v3SDK開(kāi)發(fā)(微信支付填坑)
這篇文章主要介紹了Android—基于微信開(kāi)放平臺(tái)v3SDK開(kāi)發(fā)(微信支付填坑),具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11
自定義一個(gè)theme在不同的sdk環(huán)境下繼承不同的值
可能很多在高版本下編繹apk的同學(xué),可能都曾有和我一樣的困惑,就是如何讓低版本的用戶也能有高版本的體驗(yàn)?zāi)?/div> 2013-01-01
android使用ItemDecoration給RecyclerView 添加水印
本篇文章主要介紹了android使用ItemDecoration給RecyclerView 添加水印,介紹了自定義Drawable來(lái)完成水印圖片和使用ItemDecoration來(lái)布局水印,有興趣的可以了解一下。2017-02-02
最好用的Android省市區(qū)三級(jí)聯(lián)動(dòng)選擇效果
這篇文章主要為大家詳細(xì)介紹了最好用的Android省市區(qū)三級(jí)聯(lián)動(dòng)選擇效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Android 搜索結(jié)果匹配關(guān)鍵字且高亮顯示功能
這篇文章主要介紹了Android 搜索結(jié)果匹配關(guān)鍵字且高亮顯示功能,需要的朋友可以參考下2017-05-05最新評(píng)論

