HarmonyOS 鸿蒙Next Canvas 自定义view 浅色和深色模式切换时自定义view消失未刷新成功
HarmonyOS 鸿蒙Next Canvas 自定义view 浅色和深色模式切换时自定义view消失未刷新成功
canvas内部绘制的view无法使用state驱动刷新,在build方法中使用参数来控制刷新canvas。在界面中点击按钮动态修改参数,可以实现canvasUI的刷新,但是浅色和深色模式切换的时候刷新失败了,应该如何实现canvas的刷新呢?
浅色模式 自动切换到深色模式
点击重置数据按钮的深色模式
手动切换手机的暗黑模式,切换之后界面自动变成了界面2 再次点击按钮之后变成界面3 重新进入界面也是界面3 求解:为什么暗黑模式切换的时候canvas不能刷新呢
自定义view 绘制折线图
@Component
export struct BloodSugarLineChart {
@State manage: LineChartManage = new LineChartManage()
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
private image1: ImageBitmap = new ImageBitmap(’/common/images/icon_home_data_select.png’)
private widthText: number = 10 //Y轴文本宽度
private heightText: number = 10 //X轴文本高度
private widthView: number = 200
private heightView: number = 200
@State touchX: number = -10
@StorageProp(‘currentColorMode’) @Watch(“updateCurrentColorMode”) currentMode: number =
ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT
updateCurrentColorMode() {
//将深色模式的状态最新结果传递进manage,方便判断颜色变化
this.manage.updateCurrentMode(this.currentMode)
this.manage.invalidate() //动态修改控制参数,实现UI的重新绘制
}
build() {
RelativeContainer() {
if (this.manage.isUpdateLineChar) {
this.drawCanvas()
} else {
this.drawCanvas()
}
if (this.touchX > 0) {
this.drawTouchLine()
this.drawToast()
}
}
}
/
* 绘制弹框的信息
*/
@Builder
drawToast() {
Text(滑动坐标:${this.touchX}
)
}
/
* 绘制触摸点分割线坐标
*/
@Builder
drawTouchLine() {
Text()
.size({
width: this.manage.lineWidth,
height: this.heightView - this.manage.heightTop - this.manage.heightBottom - this.heightText
})
.margin({ left: this.touchX, top: this.manage.heightTop, bottom: this.manage.heightBottom + this.heightText })
.backgroundColor($r(‘app.color.tv_red’))
}
@Builder
drawCanvas() {
Canvas(this.context)
.padding(4)
.borderRadius(10)
.onReady(() => {
//计算Y轴文本的宽度
this.computeValueWidthTextHeight()
this.context.save()
//竟变化坐标。y轴向上为正
this.context.scale(1, -1)
//平移坐标系到左下角
this.context.translate(0, -this.heightView)
this.drawXValueText()
this.drawYLineView()
this.drawLineTargetBg()
this.drawBloodSugarData()
})
.onTouch((event: TouchEvent) => {
this.onTouchChange(event)
})
}
drawTouch() {
this.context.strokeStyle = this.manage.getColorLine()
this.context.beginPath()
this.context.moveTo(this.touchX, this.heightView - this.manage.heightTop)
this.context.lineTo(this.touchX, this.heightText + this.manage.heightBottom)
this.context.stroke()
}
/
* 触摸事件
*/
onTouchChange(event: TouchEvent) {
switch (event.type) {
case TouchType.Down: //按下
logIMessage(‘移动了’)
this.touchX = event.touches[0].x
break
case TouchType.Move: //移动
this.touchX = event.touches[0].x
break
case TouchType.Up: //抬起
case TouchType.Cancel:
this.resetTouch()
break
}
}
resetTouch() {
this.touchX = -10
logIMessage(‘抬起了’)
}
/
* 计算文本的宽度和高度
*/
computeValueWidthTextHeight() {
this.manage.updateCurrentMode(this.currentMode)
//计算Y轴最大值
if (this.manage.yValueMax > 0 && this.manage.yValueMax % this.manage.sizeValueText != 0) {
let interval = this.manage.yValueMax / this.manage.sizeValueText
this.manage.yValueMax = (parseInt(interval.toString()) + 1) * this.manage.sizeValueText
}
this.widthView = this.context.width
this.heightView = this.context.height
this.context.font = this.manage.fontSize
this.widthText = this.context.measureText(${this.manage.yValueMax}
).width + 4
this.heightText = this.context.measureText(${this.manage.xValueMax}
).height
}
/
* 绘制达标范围的背景色
*/
drawLineTargetBg() {
if (this.manage.isDrawTargetBg) {
let y1 = this.getBloodSugarY(this.manage.valueTargetLow)
let y2 = this.getBloodSugarY(this.manage.valueTargetHigh)
let x1 = this.getBloodSugarX(this.manage.xValueMin)
let x2 = this.getBloodSugarX(this.manage.xValueMax)
this.context.fillStyle = this.manage.getColorLineTargetBg()
let path = new Path2D()
path.moveTo(x1, y1)
path.lineTo(x1, y2)
path.lineTo(x2, y2)
path.lineTo(x2, y1)
path.closePath()
this.context.fill(path)
this.context.fillStyle = this.manage.getColorText()
let high = this.manage.valueTargetHigh.toString()
let low = this.manage.valueTargetHigh.toString()
let widthTextItem = this.context.measureText(high).width
this.context.textBaseline = ‘bottom’
this.drawText(high, x2 - widthTextItem, y2)
this.context.textBaseline = ‘top’
this.drawText(low, x2 - widthTextItem, y1)
}
}
/
* 绘制血糖数据
*/
drawBloodSugarData() {
for (let i = 0; i < this.manage.dataBloodSugar.length; i++) {
this.drawBloodSugarLine(this.manage.dataBloodSugar[i])
}
}
/
* 绘制Y轴分割线
*/
drawYLineView() {
this.context.lineWidth = this.manage.lineWidth
this.context.lineCap = ‘round’
this.context.strokeStyle = this.manage.getColorLine()
this.context.fillStyle = this.manage.getColorText()
this.context.textBaseline = ‘middle’
let heightInterval =
(this.heightView - this.heightText - this.manage.heightBottom - this.manage.heightTop) / this.manage.sizeValueText
let interval = this.manage.yValueMax / this.manage.sizeValueText
for (let i = 0; i <= this.manage.sizeValueText; i++) {
let y = this.manage.heightBottom + this.heightText + (heightInterval * i)
this.context.beginPath()
this.context.moveTo(this.widthText, y)
this.context.lineTo(this.widthView, y)
this.context.stroke()
let value = (this.manage.yValueMin + (interval * i))
this.drawText(value.toFixed(0), 2, y)
}
}
/
* 绘制底部X轴的文本
*/
drawXValueText() {
this.context.fillStyle = this.manage.getColorText()
this.context.textBaseline = ‘bottom’
this.context.font = this.manage.fontSize
let data = this.manage.dataXName
if (data.length == 0) {
return
}
let widthItemText: number = this.context.measureText(data[0]).width
let interval: number = (this.widthView - this.widthText - data.length * widthItemText) / (data.length - 1)
for (let i = 0; i <= data.length; i++) {
let x = this.widthText + (widthItemText + interval) * i
let y = this.manage.heightBottom / 2
this.drawText(data[i], x, y)
}
}
/
* 绘制曲线
*/
drawBloodSugarLine(lineChartEntity: LineChartEntity) {
this.context.lineWidth = 1
this.context.lineCap = ‘round’
this.context.strokeStyle = lineChartEntity.isGradient ? this.getLineGradient() : lineChartEntity.color
let path = new Path2D()
let pathBg = new Path2D()
let xCircle: number = 0
let yCircle: number = 0
let valueCircle: number = 0
let data = lineChartEntity.data
for (let i = 0; i < data.length; i++) {
let entry = data[i]
let x = this.getBloodSugarX(entry.x)
let y = this.getBloodSugarY(entry.y)
if (i == data.length - 1) {
xCircle = x
yCircle = y
valueCircle = data[(data.length - 1)].y
}
if (i == 0) {
// 如果是第一个数据点,将路径的起点移动到该点
path.moveTo(x, y)
pathBg.moveTo(x, this.heightText + this.manage.heightBottom)
pathBg.lineTo(x, y)
} else {
// 对于其他数据点,使用贝塞尔曲线连接前一个点和当前点
let prevEntry = data[i - 1]
let prevX = this.getBloodSugarX(prevEntry.x)
let prevY = this.getBloodSugarY(prevEntry.y)
let cX = (prevX + x) / 2 // 控制点的 x 坐标为前一个点和当前点的中点
let cY = (prevY + y) / 2 // 控制点的 y 坐标为前一个点和当前点的中点
path.quadraticCurveTo(prevX, prevY, cX, cY)
pathBg.quadraticCurveTo(prevX, prevY, cX, cY)
}
}
pathBg.lineTo(xCircle, this.heightText + this.manage.heightBottom)
pathBg.closePath()
this.context.stroke(path)
if (lineChartEntity.isDrawBg) {
this.context.fillStyle = lineChartEntity.color
this.context.globalAlpha = 0.2
this.context.fill(pathBg)
}
}
private getBloodSugarX(value: number) {
let widthLineChart = this.widthView - this.widthText
let x = (value - this.manage.xValueMin) * widthLineChart / (this.manage.xValueMax - this.manage.xValueMin) +
this.widthText
return x
}
private getBloodSugarY(value: number) {
let highLineChart = this.heightView - this.heightText - this.manage.heightTop - this.manage.heightBottom
let y: number = (value - this.manage.yValueMin) * highLineChart / (this.manage.yValueMax - this.manage.yValueMin) +
this.heightText
return y
}
/
* 绘制文本
* @param x 文本左下角的x坐标
* @param y 文本左下角的Y坐标
*/
drawText(message: string, x: number, y: number) {
this.context.font = this.manage.fontSize
this.context.save()
//平移到绘制文字的位置
this.context.translate(x, y)
//将画布进行翻转
this.context.scale(1, -1)
this.context.fillText(message, 0, 0)
this.context.restore() //画布恢复上个状态
}
/
* 绘制图片
* @param x 图片左下角的x坐标
* @param y 图片左下角的Y坐标
*/
drawImage(imageBitmap: ImageBitmap, x: number, y: number) {
this.context.save()
this.context.translate(x, y + imageBitmap.height)
this.context.scale(1, -1)
this.context.drawImage(imageBitmap, 0, 0)
this.context.restore() //画布恢复上个状态
}
/
* 绘制图片
* @param x 图片左上角的x坐标
* @param y 图片左上角的Y坐标
*/
drawPixelMap(pixelMap: image.PixelMap, x: number, y: number) {
this.context.save()
this.context.translate(x, y)
this.context.scale(1, -1)
this.context.drawImage(pixelMap, 0, 0)
this.context.restore() //画布恢复上个状态
}
/
* 绘制渐变
*/
getLineGradient(): CanvasGradient {
let grad = this.context.createLinearGradient(0, this.heightView, 0, 0)
let high: number = (this.heightView - this.getBloodSugarY(this.manage.valueTargetHigh)) / this.heightView
let low: number = (this.heightView - this.getBloodSugarY(this.manage.valueTargetLow)) / this.heightView
grad.addColorStop(0.0, ‘#F79400’)
grad.addColorStop(high, ‘#F79400’)
grad.addColorStop(high, ‘#01BD8B’)
grad.addColorStop(low, ‘#01BD8B’)
grad.addColorStop(low, ‘#FF5555’)
grad.addColorStop(1.0, ‘#FF5555’)
return grad
}
private testBloodSugar() {
if (this.manage.dataBloodSugar.length == 0) {
if (true) {
let data: Array<EntryEntity> = []
for (let i = 3; i < 97; i = i + 3) {
let random = Math.random() * 10 + Math.random() * 10
data.push(new EntryEntity(i, random))
}
this.manage.dataBloodSugar.push(new LineChartEntity(’#16CCA6’, false, true, data))
}
if (true) {
let data: Array<EntryEntity> = []
for (let i = 3; i < 97; i = i + 3) {
let random = Math.random() * 10 + Math.random() * 10
data.push(new EntryEntity(i, random))
}
this.manage.dataBloodSugar.push(new LineChartEntity(’#D7DDE1’, false, false, data))
}
}
}
}
折线图控制器
@Observed
export class LineChartManage {
dataBloodSugar: Array<LineChartEntity> = []
dataXName: Array<string> = [‘00:00’, ‘06:00’, ‘12:00’, ‘18:00’, ‘23:59’]
xValueMin: number = 0
xValueMax: number = 100
yValueMin: number = 0
yValueMax: number = 16
lineWidth: number = 0.6
viewWidth: string | number = ‘100%’
viewHeight: string | number = ‘300’
isUpdateLineChar: boolean = false
isDrawTargetBg: boolean = true //是否绘制达标背景
valueTargetHigh: number = 8.5
valueTargetLow: number = 4.4
sizeValueText: number = 4 //Y轴刻度值个数
heightBottom: number = 2 //底部边距
heightTop: number = 10 //顶部边距
fontSize: string = ‘10vp’ //字体大小
private colorLineTargetBg: string = ‘#70e3fae5’
private colorLineTargetBgNight: string = ‘#B3242424’
private colorLine: string = ‘#f2f2f2’
private colorLineNight: string = ‘#333333’
private colorText: string = ‘#AAAAAA’
private colorTextNight: string = ‘#999999’
/
* app颜色模式
* COLOR_MODE_LIGHT 浅色模式
* COLOR_MODE_DARK 深色模式
*/
private currentMode: number = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT
getColorLine() {
return (this.currentMode == ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) ? this.colorLine : this.colorLineNight
}
getColorText() {
return (this.currentMode == ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) ? this.colorText : this.colorTextNight
}
getColorLineTargetBg() {
return this.colorLineTargetBg
}
updateCurrentMode(currentMode: number) {
this.currentMode = currentMode
}
//动态修改控制参数,实现canvasUI的刷新
invalidate() {
this.isUpdateLineChar = !this.isUpdateLineChar
}
}
@Observed
export class LineChartEntity {
title: string = ‘今日’
color: string = ‘#16CCA6’
isGradient: boolean = true //折线是否渐变(针对不同区域显示不同的颜色)
isEndCircle: boolean = false
isDrawLine: boolean = true
isDrawBg: boolean = false
data: Array<EntryEntity> = []
constructor(color: string = ‘#16CCA6’, isDrawBg: boolean = false, isGradient: boolean = true,
data: Array<EntryEntity> = []) {
this.color = color
this.isDrawBg = isDrawBg
this.isGradient = isGradient
this.data = data
}
}
@Observed
export class EntryEntity {
x: number = 0
y: number = 0
dataObject: string | number | boolean = ‘’
constructor(x: number = 0, y: number = 0, dataObject: string | number | boolean = ‘’) {
this.x = x
this.y = y
this.dataObject = dataObject
}
}
page界面
@Entry
@Component
struct ShapeTestPage {
@State manage: LineChartManage = new LineChartManage()
aboutToAppear(): void {
this.resetBloodSugar()
}
build() {
Column() {
Text(‘自定义view’)
.id(‘ShapeTestPageHelloWorld’)
.fontSize(50)
.fontWeight(FontWeight.Bold)
BloodSugarLineChart({ manage: this.manage })
.margin({ top: 30 })
.height(200)
.padding(4)
.borderRadius(10)
.backgroundColor($r(‘app.color.bg_view_white’))
ButtonSelectView({
name: ‘重置数据’, onclick: () => {
showToast(‘重置血糖数据’)
this.resetBloodSugar()
}
}).margin({ top: 30 })
ButtonSelectView({
name: ‘重置时间’, onclick: () => {
showToast(‘重置时间’)
this.manage.dataXName[0] = ‘01:00’
this.manage.dataXName[1] = ‘02:00’
this.manage.dataXName[2] = ‘03:00’
this.manage.dataXName[3] = ‘04:00’
this.manage.dataXName[4] = ‘05:00’
this.manage.invalidate()
}
}).margin({ top: 30 })
}
.height(‘100%’)
.width(‘100%’)
.padding({ top: 20, left: 14, right: 14 })
.backgroundColor($r(‘app.color.bg_activity_light_green’))
}
/**
* 重置血糖数据
*/
resetBloodSugar() {
this.manage.dataBloodSugar = []
let valueMax = 16
if (this.manage.dataBloodSugar.length == 0) {
if (true) {
let data: Array<EntryEntity> = []
for (let i = 3; i < 97; i = i + 3) {
let random = Math.random() * 10 + Math.random() * 10
data.push(new EntryEntity(i, random))
if (valueMax < random) {
valueMax = random
}
}
this.manage.dataBloodSugar.push(new LineChartEntity(’#16CCA6’, false, true, data))
}
if (true) {
let data: Array<EntryEntity> = []
for (let i = 3; i < 97; i = i + 3) {
let random = Math.random() * 10 + Math.random() * 10
data.push(new EntryEntity(i, random))
if (valueMax < random) {
valueMax = random
}
}
this.manage.dataBloodSugar.push(new LineChartEntity(’#D7DDE1’, false, false, data))
}
}
this.manage.yValueMax = valueMax
this.manage.invalidate()
}
}
找到了一个解决办法 在ability中监听app的焦点事件,当app获取焦点的时候通知UI进行刷新即可
ability方法:
自定义view执行的方法:
使用这种思路 可以实现切换暗黑模式以后 进入界面的瞬间重新绘制UI 如果严谨点的话 可以记录上次暗黑模式的状态 判断当前的状态是否和上次一样 如果不一致就进行修改
针对HarmonyOS 鸿蒙Next Canvas自定义view在浅色和深色模式切换时消失未刷新成功的问题,以下是一些可能的解决方案:
- 监听系统配置变化:确保你的自定义view已经正确监听了系统配置的变化。可以通过在
onConfigurationUpdate
方法中处理配置变化,确保当系统切换到深色或浅色模式时,能够正确刷新自定义view。 - 动态更新颜色值:在自定义view中,确保使用的颜色资源是动态的,能够根据系统主题变化而变化。避免使用硬编码的颜色值,而是使用系统预置资源或自定义资源ID来引用颜色资源。
- 检查Canvas绘制逻辑:确认Canvas的绘制逻辑在深浅色模式切换时没有问题。可能需要重写或调整绘制逻辑,以适应不同颜色模式下的显示效果。
- 验证资源文件配置:检查应用的资源文件配置,确保深色模式限定词目录(如dark)下的color.json文件已经正确配置,并且与浅色模式下的颜色资源相对应。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html 。