WearOS表盘Complication无法渲染显示问题求助
问题
按照WearOS文档创建表盘,可正常绘制表盘内容,但尝试渲染Complication时无法显示。已排查以下点:
- 通过
createComplicationSlotsManager创建ComplicationSlots,使用无权限安全数据源(排除权限问题) - 修改Complication激活样式(怀疑颜色导致在黑背景不可见)无效
- render函数中Complication处于启用状态(日志能打印)
- 官方文档示例过时,现有工具无法适配当前API
完整实现代码:
package com.example.mywatchface import android.graphics.* import android.util.Log import android.view.SurfaceHolder import androidx.core.content.ContextCompat import androidx.wear.watchface.* import androidx.wear.watchface.complications.ComplicationSlotBounds import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy import androidx.wear.watchface.complications.SystemDataSources import androidx.wear.watchface.complications.data.ComplicationType import androidx.wear.watchface.complications.rendering.CanvasComplicationDrawable import androidx.wear.watchface.complications.rendering.ComplicationDrawable import androidx.wear.watchface.style.CurrentUserStyleRepository import androidx.wear.watchface.style.UserStyleSchema import java.time.ZonedDateTime /** * Updates rate in milliseconds for interactive mode. We update once a second to advance the * second hand. */ private const val INTERACTIVE_UPDATE_RATE_MS = 1000 /** * Handler message id for updating the time periodically in interactive mode. */ private const val MSG_UPDATE_TIME = 0 private const val HOUR_STROKE_WIDTH = 5f private const val MINUTE_STROKE_WIDTH = 3f private const val SECOND_TICK_STROKE_WIDTH = 2f private const val CENTER_GAP_AND_CIRCLE_RADIUS = 4f private const val SHADOW_RADIUS = 6f class MyWatchFace : WatchFaceService() { inner class MySharedAssets : Renderer.SharedAssets { override fun onDestroy() { } } /** * The watch face itself, which includes the renderer for drawing. */ override suspend fun createWatchFace( surfaceHolder: SurfaceHolder, watchState: WatchState, complicationSlotsManager: ComplicationSlotsManager, currentUserStyleRepository: CurrentUserStyleRepository ): WatchFace { return WatchFace( WatchFaceType.DIGITAL, object : Renderer.CanvasRenderer2<MySharedAssets>( surfaceHolder, currentUserStyleRepository, watchState, CanvasType.HARDWARE, interactiveDrawModeUpdateDelayMillis = 16, clearWithBackgroundTintBeforeRenderingHighlightLayer = true ) { override suspend fun createSharedAssets(): MySharedAssets { return MySharedAssets() } override fun renderHighlightLayer( canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime, sharedAssets: MySharedAssets ) { } override fun render( canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime, sharedAssets: MySharedAssets ) { for ((_, complication) in complicationSlotsManager.complicationSlots) { if (complication.enabled) { Log.d("Hey", "hey") complication.render(canvas, zonedDateTime, renderParameters) } } var paint = Paint() paint.color = ContextCompat.getColor(baseContext, R.color.watch_face_hands) paint.textSize = 100f paint.isAntiAlias = true paint.style = Paint.Style.STROKE paint.strokeWidth = 2f val hours = zonedDateTime.hour val minutes = zonedDateTime.minute val clockHeight = (paint.fontMetrics.descent + paint.fontMetrics.ascent) val time = String.format("%02d:%02d", hours, minutes) drawText(canvas, time, paint, 0f, 0f) paint = Paint() paint.color = ContextCompat.getColor(baseContext, R.color.watch_date) paint.textSize = 20f paint.isAntiAlias = true paint.typeface = Typeface.SANS_SERIF val dayOfWeek = zonedDateTime.dayOfWeek.name.substring(0, 3) val month = zonedDateTime.month.name val dayOfMonth = zonedDateTime.dayOfMonth val dateHeight = paint.fontMetrics.descent + paint.fontMetrics.ascent; val date = String.format("%s, %s %d", dayOfWeek, month, dayOfMonth) drawText(canvas, date, paint, 0f, clockHeight / 2 + dateHeight - 5f) } } ) } private fun drawText( canvas: Canvas, text: String, paint: Paint, offsetX: Float, offsetY: Float ) { val timeWidth = paint.measureText(text) val timeHeight = (paint.fontMetrics.descent + paint.fontMetrics.ascent) / 2 val startX = (canvas.width.toFloat() - timeWidth) / 2 + offsetX val startY = canvas.height.toFloat() / 2 - timeHeight + offsetY canvas.drawText(text, startX, startY, paint) } /** * The specification of settings the watch face supports. * This is similar to a database schema. */ override fun createUserStyleSchema(): UserStyleSchema { return super.createUserStyleSchema() } /** * The complication slot configuration for the watchface. */ override fun createComplicationSlotsManager(currentUserStyleRepository: CurrentUserStyleRepository): ComplicationSlotsManager { val canvasComplicationFactory = CanvasComplicationFactory { watchState, listener -> val complicationDrawable = ComplicationDrawable(this) complicationDrawable.activeStyle.backgroundColor = getColor(R.color.watch_date) complicationDrawable.activeStyle.textColor = getColor(R.color.watch_date) complicationDrawable.activeStyle.iconColor = getColor(R.color.watch_date) complicationDrawable.activeStyle.textSize = 20 CanvasComplicationDrawable(complicationDrawable, watchState, listener) } return ComplicationSlotsManager( listOf( ComplicationSlot.createRoundRectComplicationSlotBuilder( 0, canvasComplicationFactory, listOf( ComplicationType.RANGED_VALUE, ComplicationType.LONG_TEXT, ComplicationType.SHORT_TEXT, ComplicationType.MONOCHROMATIC_IMAGE, ComplicationType.SMALL_IMAGE ), DefaultComplicationDataSourcePolicy( SystemDataSources.DATA_SOURCE_WORLD_CLOCK, ComplicationType.SHORT_TEXT ), ComplicationSlotBounds(RectF(0.1f, 0.5625f, 0.4f, 0.8125f)) ).build() ), currentUserStyleRepository ) } }
同时希望了解ComplicationSlotBounds的正确设置逻辑,目前使用示例值,推测是0-1的屏幕占比范围。
解决思路
1. 调整绘制顺序,避免Complication被覆盖
当前代码先绘制Complication,再绘制时间和日期,导致Complication被后续元素遮挡。修改render函数,先绘制表盘内容,最后绘制Complication:
override fun render( canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime, sharedAssets: MySharedAssets ) { // 1. 先绘制时间、日期等表盘内容 var paint = Paint() paint.color = ContextCompat.getColor(baseContext, R.color.watch_face_hands) paint.textSize = 100f paint.isAntiAlias = true paint.style = Paint.Style.STROKE paint.strokeWidth = 2f val hours = zonedDateTime.hour val minutes = zonedDateTime.minute val clockHeight = (paint.fontMetrics.descent + paint.fontMetrics.ascent) val time = String.format("%02d:%02d", hours, minutes) drawText(canvas, time, paint, 0f, 0f) paint = Paint() paint.color = ContextCompat.getColor(baseContext, R.color.watch_date) paint.textSize = 20f paint.isAntiAlias = true paint.typeface = Typeface.SANS_SERIF val dayOfWeek = zonedDateTime.dayOfWeek.name.substring(0, 3) val month = zonedDateTime.month.name val dayOfMonth = zonedDateTime.dayOfMonth val dateHeight = paint.fontMetrics.descent + paint.fontMetrics.ascent; val date = String.format("%s, %s %d", dayOfWeek, month, dayOfMonth) drawText(canvas, date, paint, 0f, clockHeight / 2 + dateHeight - 5f) // 2. 最后绘制Complication,确保不会被遮挡 for ((_, complication) in complicationSlotsManager.complicationSlots) { if (complication.enabled) { Log.d("Hey", "hey") complication.render(canvas, zonedDateTime, renderParameters) } } }
2. 修复ComplicationDrawable样式问题
当前代码中textColor和backgroundColor使用同一颜色,导致文字与背景融合无法看见。修改为区分颜色,并修正textSize的类型(需为浮点型):
val canvasComplicationFactory = CanvasComplicationFactory { watchState, listener -> val complicationDrawable = ComplicationDrawable(this) // 区分背景与文字/图标颜色,根据表盘背景色调整 complicationDrawable.activeStyle.backgroundColor = ContextCompat.getColor(this, R.color.complication_background) complicationDrawable.activeStyle.textColor = ContextCompat.getColor(this, R.color.complication_text) complicationDrawable.activeStyle.iconColor = ContextCompat.getColor(this, R.color.complication_text) complicationDrawable.activeStyle.textSize = 20f // 必须为浮点型,避免样式异常 CanvasComplicationDrawable(complicationDrawable, watchState, listener) }
3. 验证ComplicationSlotBounds设置逻辑
你的推测正确,ComplicationSlotBounds采用相对坐标系统,取值范围为0到1,对应屏幕宽高的比例:
RectF(left, top, right, bottom)四个参数依次代表:- left:左边界占屏幕宽度的比例
- top:上边界占屏幕高度的比例
- right:右边界占屏幕宽度的比例
- bottom:下边界占屏幕高度的比例
- 示例
RectF(0.1f, 0.5625f, 0.4f, 0.8125f)表示:左边缘在屏幕宽度10%、上边缘在屏幕高度56.25%、右边缘在屏幕宽度40%、下边缘在屏幕高度81.25%的区域 - 测试时可调整为更显眼的位置(比如左上角
RectF(0.05f, 0.1f, 0.45f, 0.35f)),确认Complication是否显示
4. 其他排查点
- 更换测试数据源:比如使用
SystemDataSources.DATA_SOURCE_STEPS(步数数据源),确认是否是原数据源返回数据异常导致不显示 - 检查
renderParameters:确保传递的渲染参数包含正确的显示状态(交互式模式下可优先忽略低功耗模式相关检查)
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

