Android自定義View模仿即刻點贊數(shù)字切換效果實例
即刻點贊展示
點贊的數(shù)字增加和減少并不是整個替換,而是差異化替換。再加上動畫效果就看的很舒服。
自己如何實現(xiàn)這種數(shù)字切換呢?
下面用一張圖來展示我的思路:
現(xiàn)在只需要根據(jù)這張圖,寫出對應(yīng)的動畫即可。 分為2種場景:
- 數(shù)字+1:
- 差異化的數(shù)字從3號區(qū)域由漸變動畫(透明度 0- 255) + 偏移動畫 (3號區(qū)域繪制文字的基線,2號區(qū)域繪制文字的基線),將數(shù)字移動到2號位置處
- 差異化的數(shù)字從2號區(qū)域由漸變動畫(透明度 255- 0) + 偏移動畫(2號區(qū)域繪制文字的基線,1號區(qū)域繪制文字的基線),將數(shù)字移動到1號位置處
- 數(shù)字-1
- 差異化的數(shù)字從1號區(qū)域由漸變動畫(透明度 0- 255) + 偏移動畫 (1號區(qū)域繪制文字的基線,2號區(qū)域繪制文字的基線),將數(shù)字移動到2號位置處
- 差異化的數(shù)字從2號區(qū)域由漸變動畫(透明度 255- 0) + 偏移動畫(2號區(qū)域繪制文字的基線,3號區(qū)域繪制文字的基線),將數(shù)字移動到3號位置處
公共部分就是: 不變的文字不需要做任何處理,繪制在2號區(qū)域就行。繪制差異化文字時,需要加上不變的文字的寬度就行。
效果展示
源碼
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模仿即刻點贊數(shù)字切換效果的文章就介紹到這了,更多相關(guān)Android模仿即刻點贊數(shù)字切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android中SparseArray性能優(yōu)化的使用方法
這篇文章主要為大家詳細介紹了Android中SparseArray性能優(yōu)化的使用方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-04-04Android—基于微信開放平臺v3SDK開發(fā)(微信支付填坑)
這篇文章主要介紹了Android—基于微信開放平臺v3SDK開發(fā)(微信支付填坑),具有一定的參考價值,有需要的可以了解一下。2016-11-11自定義一個theme在不同的sdk環(huán)境下繼承不同的值
可能很多在高版本下編繹apk的同學(xué),可能都曾有和我一樣的困惑,就是如何讓低版本的用戶也能有高版本的體驗?zāi)?/div> 2013-01-01android使用ItemDecoration給RecyclerView 添加水印
本篇文章主要介紹了android使用ItemDecoration給RecyclerView 添加水印,介紹了自定義Drawable來完成水印圖片和使用ItemDecoration來布局水印,有興趣的可以了解一下。2017-02-02最好用的Android省市區(qū)三級聯(lián)動選擇效果
這篇文章主要為大家詳細介紹了最好用的Android省市區(qū)三級聯(lián)動選擇效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-02-02Android 搜索結(jié)果匹配關(guān)鍵字且高亮顯示功能
這篇文章主要介紹了Android 搜索結(jié)果匹配關(guān)鍵字且高亮顯示功能,需要的朋友可以參考下2017-05-05最新評論