鸿蒙开发(三)canvas实现虚线控件
在鸿蒙 ArkUI 开发中,我们经常会遇到需要自定义控件的场景,比如:
- 虚线分割线
- 时间轴连接线
- 卡片虚线边框
- 打印样式的分割区域
系统组件并没有直接提供虚线控件,这时候最通用、最灵活的方式就是 —— 使用 Canvas 自绘。
今天这篇文章,我就结合真实项目经验,详细分享:
- 如何在 ArkUI 中用 Canvas 绘制虚线
- 在列表场景中的关键坑点
- 如何封装一个高复用的虚线组件
看完你不仅能写出来,还能避免 90% 的常见问题。
一、为什么要用 Canvas 画虚线?
很多同学第一反应是:
能不能用 BorderStyle.Dashed?
答案是:不推荐。
原因有三个:
❌ 1. 样式不可控
系统虚线:
- 长度不可调
- 间距不可控
- 无法做动画
- 无法画斜线
而 Canvas 可以精确控制每一个像素。
❌ 2. 无法适配复杂布局
例如:
- 时间轴虚线
- 不规则路径
- 动态宽高变化
这些场景系统虚线完全做不了。
✅ 3. Canvas 是通用解决方案
只要掌握 Canvas:
- 横线
- 竖线
- 斜线
- 曲线虚线
- 动画虚线
全部都能实现。
二、最容易踩坑的点(必须重点看)
这是实际开发中最容易出问题的地方。
⭐ 核心坑:ForEach 中 context 不能复用
在 ArkUI 中:
CanvasRenderingContext2D
是不能共享的实例!
场景区别
✅ 普通控件中使用
可以直接写:
private context = new CanvasRenderingContext2D(settings)
没问题。
❌ 在 ForEach 中使用(大坑)
如果你写成:
ForEach(list, () => {
ComDash() // 内部共用同一个 context
})
就会出现:
- 虚线不显示
- 只显示一个
- 绘制错乱
- 刷新后消失
⭐ 根本原因
Canvas context 本质是:
绑定到单个渲染节点的绘图上下文
如果多个组件共用:
就会发生 绘制目标冲突。
✅ 正确做法
每个 item 必须拥有:
独立的 CanvasRenderingContext2D 实例
也就是说:
👉 context 必须写在组件内部,而不是外部共享
三、Canvas 绘制虚线核心原理
先理解原理,再看代码。
虚线的实现核心 API
只有一个关键方法:
context.setLineDash([dashLength, gapLength])
参数含义:
|
参数 |
说明 |
|
第一个值 |
实线长度 |
|
第二个值 |
间隔长度 |
例如:
setLineDash([10, 4])
效果就是:
████ ████ ████
绘制流程
Canvas 绘线标准流程:
1️⃣ 设置画笔属性
2️⃣ 设置虚线规则
3️⃣ 开始路径
4️⃣ 移动起点
5️⃣ 绘制终点
6️⃣ stroke 渲染
四、核心绘制代码详解
这是最精简的实现方式:
Canvas(this.context)
.onReady(() => {
this.context.lineWidth = this.context.height
this.context.strokeStyle = this.strokeColor
this.context.setLineDash([this.dashWidth, this.dashGap])
this.context.beginPath()
this.context.moveTo(0, 0)
this.context.lineTo(this.context.width, this.context.height)
this.context.stroke()
this.context.closePath()
})
关键细节解析
⭐ 1. lineWidth 设置技巧
this.context.lineWidth = this.context.height
作用:
让虚线填满整个高度,形成“分割线效果”。
否则只会是一条很细的线。
⭐ 2. 使用 context.width / height
Canvas 实际绘制区域:
由外部布局决定,而不是组件内部写死。
这点非常重要!
否则会出现:
- 画不满
- 缩放异常
五、完整虚线组件封装(推荐实战写法)
下面是一个生产级可复用组件。
直接复制即可使用。
ComDash 虚线组件
import { Percent } from 'libcommon'
@ComponentV2
export struct ComDash {
@Param dashWidth: number = 10
@Param dashGap: number = 4
@Param strokeColor: string = "#C8C7CC"
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Canvas(this.context)
.onReady(() => {
this.context.lineWidth = this.context.height
this.context.strokeStyle = this.strokeColor
this.context.setLineDash([this.dashWidth, this.dashGap])
this.context.beginPath()
this.context.moveTo(0, 0)
this.context.lineTo(this.context.width, this.context.height)
this.context.stroke()
this.context.closePath()
})
.width(Percent.percent_100)
.height(Percent.percent_100)
}
}
六、组件参数说明
|
参数 |
作用 |
|
dashWidth |
虚线段长度 |
|
dashGap |
间隔长度 |
|
strokeColor |
虚线颜色 |
七、使用示例
ComDash({
dashWidth: 8,
dashGap: 6,
strokeColor: "#FF5A5F"
})
.height(1)
.width("100%")
即可得到:
👉 自适应宽度的虚线分割线。
八、进阶技巧
1️⃣ 画横线 / 竖线
只需要修改 lineTo:
横线
lineTo(this.context.width, 0)
竖线
lineTo(0, this.context.height)
2️⃣ 画斜线
当前示例就是:
lineTo(width, height)
3️⃣ 动态虚线动画
通过修改:
context.lineDashOffset
即可实现“流动虚线”。
九、常见问题排查指南
❌ 虚线不显示
检查:
- 是否调用 setLineDash
- context 是否重复使用
❌ 线条太细
需要设置:
lineWidth = height
❌ ForEach 中失效
100% 是 context 复用问题。
十、总结
在 ArkUI 中实现虚线控件,核心其实很简单:
⭐ 三个关键点
1️⃣ 每个 Canvas 必须独立 context
2️⃣ 使用 setLineDash 控制虚线样式
3️⃣ 绘制区域由外部布局控制
最佳实践建议
在项目中推荐:
👉 封装成通用组件统一使用
👉 禁止外部创建 context
👉 避免在 ForEach 外部声明 context
这样可以彻底避免绘制问题。
结语
Canvas 虽然看起来简单,但在 ArkUI 中涉及:
- 渲染生命周期
- 组件实例隔离
- 布局与绘制同步
掌握这些细节后,你不仅能写虚线控件,还能轻松实现:
- 自定义图表
- 复杂动画
- 高性能绘制组件
更多推荐

所有评论(0)