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,正式代码可不能这么写哦 >..<

相关推荐
2501_9151063243 分钟前
iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
超低空1 小时前
Android MediaSession深度解析:车载音乐播放器完整案例
android·架构·客户端
QmDeve1 小时前
Android 集成与使用模糊开关按钮视图 (BlurSwitchButtonView)
android·github
00后程序员张1 小时前
iOS 混淆实操指南多工具组合实现 IPA 混淆、加固与发布治理 IPA 加固
android·ios·小程序·https·uni-app·iphone·webview
xiaoshiquan12062 小时前
as强制过滤指定依赖版本库,解决该依赖不同版本冲突
android
2501_929157683 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户095 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户095 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户095 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º8 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot