ArkUI Image:图片不再失控,页面质感直接拉满

举报
蓝瘦的蜕变 发表于 2026/08/20 16:35:17 2026/08/20
【摘要】 这篇我们聊 Image。图片这东西看着简单,真写页面时经常出事:不是路径错了,就是比例歪了,要么封面一铺满就把重点裁没了。 所以这个案例不做花活,只把四种来源放到一页里:media、rawfile、SVG、网络图。最后再看一眼 Contain、Cover、Fill 到底差在哪。 用本地资源是为了稳定,用网络图是为了贴近

ArkUI Image:图片不再失控,页面质感直接拉满

这篇我们聊 Image。图片这东西看着简单,真写页面时经常出事:不是路径错了,就是比例歪了,要么封面一铺满就把重点裁没了。

所以这个案例不做花活,只把四种来源放到一页里:mediarawfile、SVG、网络图。最后再看一眼 ContainCoverFill 到底差在哪。

用本地资源是为了稳定,用网络图是为了贴近真实页面。没有谁更高级,按场景选就行。

参考文档:

  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui
  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-ui-development

先准备几张图

案例里用到这几个资源:

  • entry/src/main/resources/base/media/icon_huawei.svg
  • entry/src/main/resources/base/media/startIcon.png
  • entry/src/main/resources/rawfile/icon_dev.png
  • 一个网络图片 URL

网络图别忘了 ohos.permission.INTERNET。这个漏掉时,页面看起来就像代码没写对,其实只是权限没给。

SVG 的优势是能跟着换色

案例里用的是 media 目录下的 icon_huawei.svg,但这个能力不是 media 专属。只要传给 Image 的资源本身是 SVG,就可以配合 fillColor 换成页面需要的颜色。

Image($r('app.media.icon_huawei'))
  .width(72)
  .height(72)
  .objectFit(ImageFit.Contain)
  .draggable(false)
  .alt($r('app.media.startIcon'))

Image($r('app.media.icon_huawei'))
  .width(72)
  .height(72)
  .objectFit(ImageFit.Contain)
  .fillColor('#2563EB')
  .draggable(false)
  .alt($r('app.media.startIcon'))

左边保留 SVG 原色,右边用 fillColor 改成蓝色。做主题色图标时,这种写法比准备多套 PNG 省事。

PNG 也走同一套资源读取

startIcon.pngmedia,读取方式还是 $r('app.media.xxx')

Image($r('app.media.startIcon'))
  .width(96)
  .height(96)
  .objectFit(ImageFit.Contain)
  .draggable(false)
  .alt('startIcon')

图标类资源优先保完整。留点空白可以接受,拉变形就很明显了。

rawfile 记得带文件名后缀

rawfile 适合原样放进去的文件。这里最容易忘的是后缀:

Image($rawfile('icon_dev.png'))
  .width(112)
  .height(112)
  .objectFit(ImageFit.Contain)
  .draggable(false)
  .alt('icon_dev')

文件叫 icon_dev.png,代码里就写 icon_dev.png。别只写 icon_dev

网络图就直接给 URL

网络图片直接传字符串:

const NETWORK_IMAGE_URL: string =
  'https://developer.huawei.com/allianceCmsResource/resource/HUAWEI_Developer_VUE/images/0417/kaifa-04.png';

Image(NETWORK_IMAGE_URL)
  .width('100%')
  .height(170)
  .objectFit(ImageFit.Cover)
  .borderRadius(8)

这里用了 Cover,因为我们想让它铺满横向区域。代价也很直白:图片边缘可能会被裁掉。

objectFit 别凭感觉选

先记住这三个:

  • Contain:图完整,可能留白。
  • Cover:框铺满,可能裁边。
  • Fill:强行拉满,容易变形。

头像、图标这类东西,我一般先试 Contain。封面、横幅想要铺满,再考虑 CoverFill 要谨慎,它很容易把图拉得不像原来的样子。

看案例时盯这几处

打开案例页,重点看这些:

  • SVG 能不能正常显示,fillColor 换色是否生效。
  • startIcon 是否从 media 加载出来。
  • icon_dev.png 是否从 rawfile 加载出来。
  • 网络图是否能铺满横向区域。
  • 最后一组 Contain / Cover / Fill 的区别是否明显。

这篇不跑 hvigor。打开案例页看效果就够了。

把网络图的 ImageFit.Cover 改成 ImageFit.Contain,你会很直观看到裁切和留白的差别。你项目里图片最容易在哪一步翻车?路径、权限、比例,还是封面裁切?可以留言说说。

完整示例代码

文件位置:entry/src/main/ets/pages/arkUI/basic/ArkUIImageDemo.ets

const NETWORK_IMAGE_URL: string =
  'https://developer.huawei.com/allianceCmsResource/resource/HUAWEI_Developer_VUE/images/0417/kaifa-04.png';

@Entry
@Component
struct ArkUIImageDemo {
  @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
  sourceLabel(label: string) {
    Text(label)
      .fontSize(12)
      .fontColor('#475569')
      .padding({ left: 10, right: 10, top: 5, bottom: 5 })
      .backgroundColor('#E2E8F0')
      .borderRadius(6)
  }

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

        Text('图片别把页面撑变形')
          .width('100%')
          .fontSize(22)
          .fontWeight(700)
          .fontColor('#111827')

        Text('今天我们先把几种图片来源跑通,再看同一张图放进固定区域后,Contain、Cover、Fill 会有什么差别。')
          .width('100%')
          .fontSize(14)
          .fontColor('#64748B')
          .lineHeight(22)

        Column({ space: 12 }) {
          this.sectionTitle('SVG 图标换色', '案例里用的是 media 里的 SVG;只要资源本身是 SVG,就可以用 fillColor 换成当前页面需要的颜色。')

          Row({ space: 12 }) {
            Column({ space: 8 }) {
              Image($r('app.media.icon_huawei'))
                .width(72)
                .height(72)
                .objectFit(ImageFit.Contain)
                .draggable(false)
                .alt($r('app.media.startIcon'))

              this.sourceLabel('原色 SVG')
            }
            .layoutWeight(1)
            .height(132)
            .justifyContent(FlexAlign.Center)
            .alignItems(HorizontalAlign.Center)
            .backgroundColor('#F8FAFC')
            .border({ width: 1, color: '#E2E8F0' })
            .borderRadius(8)

            Column({ space: 8 }) {
              Image($r('app.media.icon_huawei'))
                .width(72)
                .height(72)
                .objectFit(ImageFit.Contain)
                .fillColor('#2563EB')
                .draggable(false)
                .alt($r('app.media.startIcon'))

              this.sourceLabel('fillColor')
            }
            .layoutWeight(1)
            .height(132)
            .justifyContent(FlexAlign.Center)
            .alignItems(HorizontalAlign.Center)
            .backgroundColor('#EFF6FF')
            .border({ width: 1, color: '#BFDBFE' })
            .borderRadius(8)
          }
          .width('100%')
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#FFFFFF')
        .border({ width: 1, color: '#E2E8F0' })
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('media 里的 PNG', '启动图标、页面图标这类资源,也可以放到 media 目录后用 $r 读取。')

          Column({ space: 10 }) {
            Image($r('app.media.startIcon'))
              .width(96)
              .height(96)
              .objectFit(ImageFit.Contain)
              .draggable(false)
              .alt('startIcon')

            this.sourceLabel("$r('app.media.startIcon')")
          }
          .width('100%')
          .height(150)
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .backgroundColor('#F0FDFA')
          .borderRadius(8)
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F0FDFA')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('rawfile 里的图片', 'rawfile 适合原样放进去的文件,读取时写完整文件名。')

          Column({ space: 10 }) {
            Image($rawfile('icon_dev.png'))
              .width(112)
              .height(112)
              .objectFit(ImageFit.Contain)
              .draggable(false)
              .alt('icon_dev')

            this.sourceLabel("$rawfile('icon_dev.png')")
          }
          .width('100%')
          .height(166)
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .backgroundColor('#FFF7ED')
          .borderRadius(8)
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#FFFFFF')
        .border({ width: 1, color: '#FED7AA' })
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('网络图片', '网络图片直接传 URL 字符串。项目需要声明 INTERNET 权限,否则真机上可能加载不出来。')

          Image(NETWORK_IMAGE_URL)
            .width('100%')
            .height(170)
            .objectFit(ImageFit.Cover)
            .borderRadius(8)
            .draggable(false)
            .alt('Huawei Developer')

          this.sourceLabel('Image(NETWORK_IMAGE_URL)')
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#EFF6FF')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('objectFit 别乱用', '同一张图放进固定大小的框里,Contain 保完整,Cover 保铺满,Fill 会拉伸。')

          Row({ space: 10 }) {
            Column({ space: 8 }) {
              Image($r('app.media.startIcon'))
                .width('100%')
                .height(92)
                .objectFit(ImageFit.Contain)
                .backgroundColor('#F8FAFC')
                .borderRadius(8)

              Text('Contain')
                .fontSize(12)
                .fontColor('#475569')
            }
            .layoutWeight(1)
            .alignItems(HorizontalAlign.Center)

            Column({ space: 8 }) {
              Image($r('app.media.startIcon'))
                .width('100%')
                .height(92)
                .objectFit(ImageFit.Cover)
                .backgroundColor('#F8FAFC')
                .borderRadius(8)

              Text('Cover')
                .fontSize(12)
                .fontColor('#475569')
            }
            .layoutWeight(1)
            .alignItems(HorizontalAlign.Center)

            Column({ space: 8 }) {
              Image($r('app.media.startIcon'))
                .width('100%')
                .height(92)
                .objectFit(ImageFit.Fill)
                .backgroundColor('#F8FAFC')
                .borderRadius(8)

              Text('Fill')
                .fontSize(12)
                .fontColor('#475569')
            }
            .layoutWeight(1)
            .alignItems(HorizontalAlign.Center)
          }
          .width('100%')
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F8FAFC')
        .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个月内不可修改。