0.
今天是母親節,謹以此文獻和此程式給我的母親,節日快樂。
話不多說,先看效果
附上git地址 Github
1.
想好再動筆,整個卷軸有左右卷軸柄,紙和文字組成,預設卷軸關閉,點選是張開,張開後點選就合上,開啟時速度由快到慢。功能分析到這,思路也就很明確了,那就動筆吧。
2.
private var dis = 0 //每個卷軸到中心的距離
private var textColor = Color.BLACK //文字顏色
private var reelColor = Color.RED //卷軸柄的顏色
private var paperColor = Color.WHITE //紙的顏色
private var textSize = 20f //文字顏色
private var text = "" //卷軸上的文字
private var reelWidth = 40f //卷軸柄寬度
private var duration = 3000 //卷軸開啟所需時間
private lateinit var disAnimator: ValueAnimator //操控捲軸開啟進度
private var isExpand = false //卷軸是否處於開啟狀態
private var reelTopBarHeight = 20f //卷軸柄上端小木塊的高度
private var lineOffset = 10f //紙上分割線的距離
複製程式碼
這裡要說到的是dis變數用來控制每個卷軸柄到中心的距離,也是控制紙寬度的引數,而disAnimator是用來控制dis的改變,同時為了控制卷軸開啟速度和總時間的。
private fun initAnimator()
{
disAnimator = ValueAnimator.ofInt(0, duration / 1000)
disAnimator.duration = duration.toLong()
disAnimator.interpolator = AccelerateDecelerateInterpolator()
disAnimator.addUpdateListener {
dis = (it.animatedFraction * (width / 2 - reelWidth)).toInt()
postInvalidate()
}
}
複製程式碼
disAnimator設定了的值的變化範圍是從0到總時間/1000, 由dis = (it.animatedFraction * (width / 2 - reelWidth)).toInt()可知 0<=dis<=width/2-reelWidth
3.
實現文字隨著紙張的展開逐漸顯露出來的效果,這是這個控制元件的重點。首先看drawText方法
private fun drawText(canvas: Canvas)
{
textSize = Math.min(textSize, (height - reelTopBarHeight * 2 - lineOffset * 2))
paint.isFakeBoldText = true
paint.textSize=textSize
val centerX = width.toFloat() / 2
val centerY = height.toFloat() / 2
val rect = Rect()
paint.getTextBounds(text, 0, text.length, rect)
canvas.drawText(text, centerX - rect.width() / 2, centerY + rect.height() / 2, paint)
}
複製程式碼
很簡單,就是講文字繪製在中心位置。
再看darwPaper方法
private fun drawPaper(canvas: Canvas)
{
val centerX = width.toFloat() / 2
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvasTmp = Canvas(bitmap)
paint.color = paperColor
canvasTmp.drawRect(centerX - dis, reelTopBarHeight, centerX + dis, height - reelTopBarHeight, paint)
paint.color = Color.BLACK
paint.strokeWidth = 2f
canvas.drawBitmap(bitmap, 0f, 0f, paint)
}
複製程式碼
繪製寬度隨dis改變的紙。
要想實現文字只在已有寬度的紙上顯示,那麼我們就需要接住xfermode了,關於xfermode我就不仔細說了,不懂的請自行百度,這裡我們採用DST_ATOP來實現這個功能,大家可能發現為什麼drawPaper的時候生成了一個bitmap,和一個tempCanvas,最終繪製的是一個bitmap,那是因為為了控制它的透明區域不要太小,要讓它足夠覆蓋到要和它結合繪製的文字內容,否則得到的結果很可能不是你想要的 看onDraw方法
override fun onDraw(canvas: Canvas?)
{
super.onDraw(canvas)
canvas?.let {
val count = it.saveLayer(null, null, Canvas.ALL_SAVE_FLAG) //設定離屏緩衝,不設定的話會有問題
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP) //設定xfermode
drawText(it) //繪製文字
drawPaper(it) //繪製紙
paint.xfermode = null
drawPaerLines(it) //繪製紙上的線
it.restoreToCount(count) //恢復
drawReels(it) //繪製卷軸柄
}
}
複製程式碼
4.
根據狀態來判斷點選時卷軸是開啟還是收縮回去
override fun onTouchEvent(event: MotionEvent?): Boolean
{
when (event?.action)
{
MotionEvent.ACTION_DOWN ->
{
if (!disAnimator.isRunning)
{
if (!isExpand)
{
val centerX = width / 2.toFloat()
if (event.x >= centerX - reelWidth && event.x <= centerX + reelWidth)
{
startAnimator()
isExpand = true
}
}
else
{
disAnimator.reverse()
isExpand = false
}
}
return true
}
}
return false
}
複製程式碼
5.
因為使用了Anmator,為了避免記憶體洩漏,注意新增以下程式碼
override fun onDetachedFromWindow()
{
super.onDetachedFromWindow()
disAnimator.cancel()
}
複製程式碼
公眾號:滑板上的老砒霜,定期分享技術原創文章。