注册

Android 时钟翻页效果

背景


今天逛掘金,发现了一个有意思的web view效果,想着Android能不能实现一下捏。


image.png
原文链接:juejin.cn/post/724435…


具体实现分析请看上文原文链接,那我们开始吧!


容器


val space = 10f //上下半间隔
val bgBorderR = 10f //背景圆角
//上半部分
val upperHalfBottom = height.toFloat() / 2 - space / 2
canvas.drawRoundRect(
0f,
0f,
width.toFloat(),
upperHalfBottom,
bgBorderR,
bgBorderR,
bgPaint
)
//下半部分
val lowerHalfTop = height.toFloat() / 2 + space / 2
canvas.drawRoundRect(
0f,
lowerHalfTop,
width.toFloat(),
height.toFloat(),
bgBorderR,
bgBorderR,
bgPaint
)

image.png


绘制数字


我们首先居中绘制数字4


val number4 = "4"
textPaint.getTextBounds(number4, 0, number4.length, textBounds)
//居中显示
val x = (width - textBounds.width()) / 2f - textBounds.left
val y = (height + textBounds.height()) / 2f - textBounds.bottom
canvas.drawText(number4, x, y, textPaint)

image.png


接下来我们将数字切分为上下两部分,分别绘制。


val number4 = "4"
textPaint.getTextBounds(number4, 0, number4.length, textBounds)
val x = (width - textBounds.width()) / 2f - textBounds.left
val y = (height + textBounds.height()) / 2f - textBounds.bottom
// 上半部分裁剪
canvas.save()
canvas.clipRect(
0f,
0f,
width.toFloat(),
upperHalfBottom
)
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
// 下半部分裁剪
canvas.save()
canvas.clipRect(
0f,
lowerHalfTop,
width.toFloat(),
height.toFloat()
)
canvas.drawText(number4, x, y, textPaint)
canvas.restore()

image.png


翻转卡片


如何实现让其旋转呢?
而且还得是3d的效果了。我们选择Camera来实现。
我们先让数字'4'旋转起来。


准备工作,通过属性动画来改变旋转的角度。


private var degree = 0f //翻转角度
private val camera = Camera()
private var flipping = false //是否处于翻转状态
...
//动画
val animator = ValueAnimator.ofFloat(0f, 360f)
animator.addUpdateListener { animation ->
val animatedValue = animation.animatedValue as Float
setDegree(animatedValue)
}
animator.doOnStart {
flipping = true
}
animator.doOnEnd {
flipping = false
}
animator.duration = 1000
animator.interpolator = LinearInterpolator()
animator.start()
...

private fun setDegree(degree: Float) {
this.degree = degree
invalidate()
}

让数字'4'旋转起来:


  override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 居中绘制数字4
val number4 = "4"
textPaint.getTextBounds(number4, 0, number4.length, textBounds)
val x = (width - textBounds.width()) / 2f - textBounds.left
val y = (height + textBounds.height()) / 2f - textBounds.bottom

if (!flipping) {
canvas.drawText(number4, x, y, textPaint)
} else {
camera.save()
canvas.translate(width / 2f, height / 2f)
camera.rotateX(-degree)
camera.applyToCanvas(canvas)
canvas.translate(-width / 2f, -height / 2f)
camera.restore()
canvas.drawText(number4, x, y, textPaint)
}
}

file.gif

我们再来看一边效果图:
我们希望将卡片旋转180度,并且0度-90度由上半部分完成,90度-180度由下半部分完成。


我们调整一下代码,先处理一下上半部分:


...
val animator = ValueAnimator.ofFloat(0f, 180f)
...
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val space = 10f //上下半间隔
//上半部分
val upperHalfBottom = height.toFloat() / 2 - space / 2
...
// 居中绘制数字4
val number4 = "4"
textPaint.getTextBounds(number4, 0, number4.length, textBounds)
val x = (width - textBounds.width()) / 2f - textBounds.left
val y = (height + textBounds.height()) / 2f - textBounds.bottom

if (!flipping) {
//上半部分裁剪
canvas.save()
canvas.clipRect(
0f,
0f,
width.toFloat(),
upperHalfBottom
)
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
} else {
if (degree < 90) {
//上半部分裁剪
canvas.save()
canvas.clipRect(
0f,
0f,
width.toFloat(),
upperHalfBottom
)
camera.save()
canvas.translate(width / 2f, height / 2f)
camera.rotateX(-degree)
camera.applyToCanvas(canvas)
canvas.translate(-width / 2f, -height / 2f)
camera.restore()
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
}
}
}

效果如下:


upper.gif

接下来我们再来看一下下半部分:


override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val space = 10f //上下半间隔
//下半部分
val lowerHalfTop = height.toFloat() / 2 + space / 2

// 居中绘制数字4
val number4 = "4"
textPaint.getTextBounds(number4, 0, number4.length, textBounds)
val x = (width - textBounds.width()) / 2f - textBounds.left
val y = (height + textBounds.height()) / 2f - textBounds.bottom

if (!flipping) {
// 下半部分裁剪
canvas.save()
canvas.clipRect(
0f,
lowerHalfTop,
width.toFloat(),
height.toFloat()
)
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
} else {
if (degree > 90) {
canvas.save()
canvas.clipRect(
0f,
lowerHalfTop,
width.toFloat(),
height.toFloat()
)
camera.save()
canvas.translate(width / 2f, height / 2f)
val bottomDegree = 180 - degree
camera.rotateX(bottomDegree)
camera.applyToCanvas(canvas)
canvas.translate(-width / 2f, -height / 2f)
camera.restore()
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
}
}
}

lower.gif

那我们将上下部分结合起来,效果如下:


all.gif

数字变化


好!我们完成了翻转部分,现在需要在翻转的过程中将数字改变:

我们还是举例说明:数字由'4'变为'5'的情况。我们思考个问题,什么时候需要改变数字?

上半部分在翻转开始的时候,上半部分底部显示的数字就应该由'4'变为'5',但是旋转的部分还是应该为'4',
下半部分开始旋转的时候底部显示的数字还是应该为'4',而旋转的部分该为'5'。


canvas.save()
canvas.clipRect(
0f,
0f,
width.toFloat(),
upperHalfBottom
)
canvas.drawText(number5, x, y, textPaint)
canvas.restore()
// 下半部分裁剪
canvas.save()
canvas.clipRect(
0f,
lowerHalfTop,
width.toFloat(),
height.toFloat()
)
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
//=====⬆️=====上述的代码显示的上下底部显示的内容,即上半部分地步显示5,下半部分显示4
if (degree < 90) {
//上半部分裁剪
canvas.save()
canvas.clipRect(
0f,
0f,
width.toFloat(),
upperHalfBottom
)
camera.save()
canvas.translate(width / 2f, height / 2f)
camera.rotateX(-degree)
camera.applyToCanvas(canvas)
canvas.translate(-width / 2f, -height / 2f)
camera.restore()
canvas.drawText(number4, x, y, textPaint)
canvas.restore()
//=====⬆️=====上述的代码表示上半部分旋转显示的内容,即数字4
} else {
canvas.save()
canvas.clipRect(
0f,
lowerHalfTop,
width.toFloat(),
height.toFloat()
)
camera.save()
canvas.translate(width / 2f, height / 2f)
val bottomDegree = 180 - degree
camera.rotateX(bottomDegree)
camera.applyToCanvas(canvas)
canvas.translate(-width / 2f, -height / 2f)
camera.restore()
canvas.drawText(number5, x, y, textPaint)
canvas.restore()
//=====⬆️=====上述的代码表示下半部分旋转显示的内容,即数字5
}

效果图如下:大伙可以在去理一下上面数字的变化的逻辑。


a.gif

最后我们加上背景再看一下效果:


a.gif

小结


上述代码仅仅提供个思路,仅为测试code,正式代码可不能这么写哦 >..<


作者:蹦蹦蹦
来源:juejin.cn/post/7271518821809438781

0 个评论

要回复文章请先登录注册