Android 时钟翻页效果

背景

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

原文链接:juejin.cn/post/724435...

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

容器

kt 复制代码
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
)

绘制数字

我们首先居中绘制数字4

kt 复制代码
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)

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

less 复制代码
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)

翻转卡片

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

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

kt 复制代码
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'旋转起来:

kt 复制代码
  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)
      }
  }

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

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

kt 复制代码
...
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()
        }
    }
}

效果如下:

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

kt 复制代码
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()
        }
    }
}

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

数字变化

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

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

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

kt 复制代码
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
}

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

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

小结

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

相关推荐
OkeyProxy6 小时前
設置Android設備全局代理
android·代理模式·proxy模式·代理服务器·海外ip代理
刘志辉7 小时前
vue传参方法
android·vue.js·flutter
前期后期9 小时前
Android OkHttp源码分析(一):为什么OkHttp的请求速度很快?为什么可以高扩展?为什么可以高并发
android·okhttp
轻口味12 小时前
Android应用性能优化
android
全职计算机毕业设计12 小时前
基于 UniApp 平台的学生闲置物品售卖小程序设计与实现
android·uni-app
dgiij12 小时前
AutoX.js向后端传输二进制数据
android·javascript·websocket·node.js·自动化
SevenUUp13 小时前
Android Manifest权限清单
android
高林雨露13 小时前
Android 检测图片抓拍, 聚焦图片后自动完成拍照,未对准图片的提示请将摄像头对准要拍照的图片
android·拍照抓拍
wilanzai13 小时前
Android View 的绘制流程
android
INSBUG14 小时前
CVE-2024-21096:MySQLDump提权漏洞分析
android·adb