ArkUI Shape:不用切图,也能画出页面的骨架

举报
蓝瘦的蜕变 发表于 2026/10/06 15:56:12 2026/10/06
【摘要】 今天我们先不碰复杂绘图,只把 Shape 当成一块轻量画板来用。页面里那些小背景、小标识、小装饰线,如果每次都切图,维护起来会很累;用 Shape 直接画,很多时候更省事。 先看这个页面画了什么 这个 Demo 分成四块: 第一块把 Rect、Circle、Path 放进同一个 Shape 里,先建立“容器 + 子图形

ArkUI Shape:不用切图,也能画出页面的骨架

今天我们先不碰复杂绘图,只把 Shape 当成一块轻量画板来用。页面里那些小背景、小标识、小装饰线,如果每次都切图,维护起来会很累;用 Shape 直接画,很多时候更省事。

先看这个页面画了什么

这个 Demo 分成四块:

  • 第一块把 Rect、Circle、Path 放进同一个 Shape 里,先建立“容器 + 子图形”的感觉。
  • 第二块对比 fill 和 stroke,一个管内部颜色,一个管边线。
  • 第三块看 strokeWidth,同一条曲线换个线宽,存在感会完全不同。
  • 第四块看 antiAlias,斜线和曲线一般建议打开抗锯齿。

我们写这类页面时,不需要急着上 Canvas。Shape 更像轻量矢量区域,适合把简单视觉元素留在 ArkUI 里维护。

Shape 容器

核心代码长这样:

Shape() {
  Rect({ width: 150, height: 72, radius: 18 })
    .fill('#DBEAFE')
    .stroke('#2563EB')
    .strokeWidth(3)

  Circle({ width: 54, height: 54 })
    .fill('#F97316')

  Path()
    .commands('M34 122 L82 72 L130 122 Z')
    .fill('#0F766E')
}
.height(150)
.viewPort({ x: 0, y: 0, width: 220, height: 150 })

viewPort 可以理解成这块小画板的坐标范围。我们把图形放进去之后,点位、尺寸和偏移会更好控制。

什么时候用它

如果只是画几何图形、标签底、卡片里的装饰线,Shape 很合适。它比切图灵活,也比 Canvas 少一点心智负担。等你要做自由绘制、动态图表、复杂路径运算时,再去看 Canvas 会更顺。

你平时做页面时,有没有为了一个很小的装饰元素专门切过图?这种场景大概率就可以先试试 Shape。

参考文档: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-shape-overview

完整示例代码

文件位置:entry/src/main/ets/pages/arkUI/drawing/ArkUIShapeOverviewDemo.ets

@Entry
@Component
struct ArkUIShapeOverviewDemo {
  @Builder
  sectionTitle(title: string, desc: string) {
    Column({ space: 4 }) {
      Text(title)
        .width('100%')
        .fontSize(16)
        .fontWeight(700)
        .fontColor('#0F172A')

      Text(desc)
        .width('100%')
        .fontSize(13)
        .fontColor('#64748B')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  sampleLabel(text: string) {
    Text(text)
      .fontSize(12)
      .fontColor('#475569')
      .textAlign(TextAlign.Center)
      .width('100%')
  }

  build() {
    Scroll() {
      Column({ space: 18 }) {
        Text('ArkUI Shape')
          .width('100%')
          .fontSize(24)
          .fontWeight(700)
          .fontColor('#0F172A')

        Text('不用切图,也能画出页面的骨架')
          .width('100%')
          .fontSize(22)
          .fontWeight(700)
          .fontColor('#111827')

        Text('这一页先看 Shape 容器。它适合画轻量矢量图形:背景块、装饰线、简单标识、卡片里的小插画,都可以先不用切图。')
          .width('100%')
          .fontSize(14)
          .fontColor('#64748B')
          .lineHeight(22)

        Column({ space: 12 }) {
          this.sectionTitle('Shape 像一个小画板', '我们可以把 Rect、Circle、Path 放进 Shape 里,用统一的尺寸和坐标感组织它们。')

          Shape() {
            Rect({ width: 150, height: 72, radius: 18 })
              .fill('#DBEAFE')
              .stroke('#2563EB')
              .strokeWidth(3)
              .offset({ x: 16, y: 18 })

            Circle({ width: 54, height: 54 })
              .fill('#F97316')
              .stroke('#FED7AA')
              .strokeWidth(4)
              .offset({ x: 126, y: 28 })

            Path()
              .commands('M34 122 L82 72 L130 122 Z')
              .fill('#0F766E')
              .stroke('#134E4A')
              .strokeWidth(2)
          }
          .width('100%')
          .height(150)
          .viewPort({ x: 0, y: 0, width: 220, height: 150 })
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#EFF6FF')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('fill 和 stroke 先分清', 'fill 管内部颜色,stroke 管边线。很多轻量图形只靠这两个属性,就能从占位块变成真正的视觉元素。')

          Row({ space: 12 }) {
            Column({ space: 8 }) {
              Shape() {
                Rect({ width: 96, height: 72, radius: 16 })
                  .fill('#DCFCE7')
                  .stroke('#16A34A')
                  .strokeWidth(4)
              }
              .width(110)
              .height(86)

              this.sampleLabel('有填充也有描边')
            }
            .layoutWeight(1)

            Column({ space: 8 }) {
              Shape() {
                Circle({ width: 72, height: 72 })
                  .fill('#FEF3C7')
                  .stroke('#D97706')
                  .strokeWidth(6)
              }
              .width(110)
              .height(86)

              this.sampleLabel('圆形也一样')
            }
            .layoutWeight(1)
          }
          .width('100%')
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F0FDF4')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('strokeWidth 决定边线存在感', '边线太细像没画,太粗又会抢内容。Demo 里故意放了两个宽度,方便你一眼看出差别。')

          Row({ space: 12 }) {
            Column({ space: 8 }) {
              Shape() {
                Path()
                  .commands('M18 68 C44 18 92 18 118 68')
                  .stroke('#7C3AED')
                  .strokeWidth(2)
              }
              .width(136)
              .height(82)
              .viewPort({ x: 0, y: 0, width: 136, height: 82 })

              this.sampleLabel('strokeWidth(2)')
            }
            .layoutWeight(1)

            Column({ space: 8 }) {
              Shape() {
                Path()
                  .commands('M18 68 C44 18 92 18 118 68')
                  .stroke('#7C3AED')
                  .strokeWidth(8)
              }
              .width(136)
              .height(82)
              .viewPort({ x: 0, y: 0, width: 136, height: 82 })

              this.sampleLabel('strokeWidth(8)')
            }
            .layoutWeight(1)
          }
          .width('100%')
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F5F3FF')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('antiAlias 让斜线边缘更顺', '一般来说我们会保持抗锯齿开启。它不是万能美化,但对斜线、曲线、圆形边缘很有帮助。')

          Row({ space: 12 }) {
            Column({ space: 8 }) {
              Shape() {
                Path()
                  .commands('M16 72 L118 16')
                  .stroke('#C2410C')
                  .strokeWidth(9)
              }
              .width(136)
              .height(88)
              .antiAlias(false)

              this.sampleLabel('antiAlias(false)')
            }
            .layoutWeight(1)

            Column({ space: 8 }) {
              Shape() {
                Path()
                  .commands('M16 72 L118 16')
                  .stroke('#C2410C')
                  .strokeWidth(9)
              }
              .width(136)
              .height(88)
              .antiAlias(true)

              this.sampleLabel('antiAlias(true)')
            }
            .layoutWeight(1)
          }
          .width('100%')
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#FFF7ED')
        .borderRadius(8)
      }
      .width('100%')
      .padding(20)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。