You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何监听Wear OS表盘激活/退居后台事件实现切换动画?

实现三星Watch6原生表盘的平滑切换动画

一、监听表盘激活事件(从选择界面切换到实际表盘)

在自定义的WatchFaceService.Engine子类中,重写onVisibilityChanged方法。当用户从表盘选择界面切换到该表盘时,visibility会变为View.VISIBLE,此时可触发指针从预览固定位置过渡到当前实际时间的动画:

override fun onVisibilityChanged(visibility: Int) {
    super.onVisibilityChanged(visibility)
    if (visibility == View.VISIBLE) {
        // 触发从预览位置到实际时间的平滑过渡动画
        startTransitionToActualTime()
    }
}

二、监听表盘即将进入预览状态(切换出表盘到选择界面)

重写onPreviewModeChanged方法,当系统进入表盘预览模式(用户打开表盘选择界面)时,isPreview会变为true,此时触发指针从实际时间过渡到预览固定位置的动画:

override fun onPreviewModeChanged(isPreview: Boolean) {
    super.onPreviewModeChanged(isPreview)
    if (isPreview) {
        // 触发从实际时间到预览固定位置的快速过渡动画
        startTransitionToPreviewPosition()
    }
}

三、实现指针过渡动画

使用ValueAnimator计算指针的角度变化,实现平滑过渡:

private fun startTransitionToActualTime() {
    // 预览图固定位置角度(需与表盘选择界面的预览图匹配)
    val previewHourAngle = 90f
    val previewMinuteAngle = 0f
    // 计算当前实际时间对应的指针角度
    val actualHourAngle = calculateHourAngle(System.currentTimeMillis())
    val actualMinuteAngle = calculateMinuteAngle(System.currentTimeMillis())

    // 时针过渡动画
    ValueAnimator.ofFloat(previewHourAngle, actualHourAngle).apply {
        duration = 500
        addUpdateListener { animation ->
            hourHandAngle = animation.animatedValue as Float
            invalidate()
        }
        start()
    }
    // 分针过渡动画
    ValueAnimator.ofFloat(previewMinuteAngle, actualMinuteAngle).apply {
        duration = 500
        addUpdateListener { animation ->
            minuteHandAngle = animation.animatedValue as Float
            invalidate()
        }
        start()
    }
}

private fun startTransitionToPreviewPosition() {
    val previewHourAngle = 90f
    val previewMinuteAngle = 0f

    ValueAnimator.ofFloat(hourHandAngle, previewHourAngle).apply {
        duration = 300
        addUpdateListener { animation ->
            hourHandAngle = animation.animatedValue as Float
            invalidate()
        }
        start()
    }
    ValueAnimator.ofFloat(minuteHandAngle, previewMinuteAngle).apply {
        duration = 300
        addUpdateListener { animation ->
            minuteHandAngle = animation.animatedValue as Float
            invalidate()
        }
        start()
    }
}

// 计算时针角度
private fun calculateHourAngle(timestamp: Long): Float {
    val calendar = Calendar.getInstance().apply { timeInMillis = timestamp }
    val hours = calendar.get(Calendar.HOUR_OF_DAY) % 12
    val minutes = calendar.get(Calendar.MINUTE)
    return (hours * 30 + minutes * 0.5).toFloat()
}

// 计算分针角度
private fun calculateMinuteAngle(timestamp: Long): Float {
    val calendar = Calendar.getInstance().apply { timeInMillis = timestamp }
    val minutes = calendar.get(Calendar.MINUTE)
    return minutes * 6f
}

注意事项

  • 预览固定位置的角度必须与表盘选择界面的预览图完全匹配,否则动画衔接会出现断层。
  • 在onDraw方法中,需根据实时更新的hourHandAngle、minuteHandAngle变量绘制指针,确保动画过程中界面实时刷新。
  • 若需适配熄屏/亮屏场景,可结合onEnterAmbient和onExitAmbient方法调整动画逻辑。

内容的提问来源于stack exchange,提问作者AZX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 07:05:10