一、啥场景需要 resizable

图片要适配不同尺寸容器时,直接拉伸容易导致关键区域变形。比如圆角、边框、图案细节这些地方一旦拉伸,视觉效果就崩了。

典型例子:聊天消息气泡。气泡背景要随内容长度和高度动态调整,但四周圆角和小三角指示符不能变形。

Image 组件的 resizable 属性能精准指定图片的可拉伸区域和固定区域,确保不同尺寸容器里都能保持好效果。

本文用两个场景讲 resizable

  • 聊天消息气泡
  • 可拉伸占位图(中间 Logo 不变形)

二、实现原理:固定区域和可拉伸区域

resizable 核心原理:用特定规则划分图片固定区域和可拉伸区域。拉伸时只拉伸可拉伸区域,固定区域保持原始尺寸形态不变。

resizable 参数类型是 ResizableOptions,有两种方案:

  • slice:九宫格方式
  • lattice:网格矩阵方式

slice 方式:九宫格

通过 slice 参数指定原图片上下左右四个方向偏移值(px),把图片划分为九宫格布局:

  • 四个角区域为固定区域
  • 其余为可拉伸区域

拉伸时各区域效果:

  • 四个角保持固定宽高
  • 中间区域可上下左右拉伸
  • 顶部和底部可拉伸区域保持高度不变
  • 左右两侧可拉伸区域保持宽度不变

slice 除了 resizable,还能在 backgroundImageResizable 里使用。

注意:坐标参数传数字时默认单位是 vp,最终转成 px。要让固定区域在不同设备保持一致效果,要传 px 单位数据:

Image($r('app.media.bg_right_message'))
  .resizable({
    slice: {
      left: '40px',
      top: '80px',
      right: '70px',
      bottom: '40px'
    }
  })

lattice 方式:网格矩阵

通过 lattice 参数设置横向和纵向坐标点数组(px),把图片划分为规则矩形网格。行列数为数组长度 + 1。

关键规则:偶数行与偶数列交叉处的格子为固定区域(蓝色部分),其余为可拉伸区域。拉伸时固定区域保持原尺寸,其他区域根据需要拉伸。

比如上图用 x 轴坐标点数组 [1, 50, 648] 和 y 轴坐标点数组 [1, 50, 253],划分成 3 行 3 列网格。蓝色区域是偶数行偶数列交叉的固定区域。

行列计数从 0 开始

  • 0 行 0 列、0 行 2 列、2 行 0 列、2 行 2 列 → 均为偶数行列交叉区域,固定不可拉伸

示例代码:

// x轴坐标点数组(图片分割)
private xDivs: Array<number> = [1, 60, 243];
// y轴坐标点数组(图片分割)
private yDivs: Array<number> = [1, 50, 253];
// 创建网格对象
private drawingLatticeFirst: DrawingLattice =
  drawing.Lattice.createImageLattice(this.xDivs, this.yDivs, this.xDivs.length, this.yDivs.length);

// 应用
Image($r('app.media.placeholder_img'))
  .height(this.imgHeight)
  .width('100%')
  .resizable({
    lattice: this.drawingLatticeFirst
  })

slice 和 lattice 对比

对比维度 slice lattice
核心逻辑 上下左右四个偏移量定义四个角为固定区域 横向纵向坐标点划分网格矩阵,偶数行列交叉区域为固定
适用场景 四个角不拉伸,如聊天气泡、输入框背景、优惠券 中间区域不拉伸,如带 Logo 占位图、复杂边框弹窗

三、slice 实现聊天消息气泡

场景咋回事

聊天消息气泡是社交应用常见场景。消息内容长度高度不同时,气泡要保持四周圆角和小三角指示符形状不变。

实现步骤

第一步:划分网格确定偏移值

通过 UX 提供的坐标点或 PhotoShop 等工具,找到原图固定区域上下左右准确偏移值。

第二步:实现消息气泡布局

slice 支持 backgroundImageResizable 属性。给消息内容 Text 组件设置 backgroundImage,作为内容背景图片。内容宽高不同时背景图片随之伸缩。把偏移值赋给 backgroundImageResizableslice 参数:

Text(this.message)
  .fontSize($r('sys.float.Body_L'))
  .fontColor($r('sys.color.font_primary'))
  // 设置背景图片
  .backgroundImage($r('app.media.bg_left_message'))
  // 设置最大宽度和最小宽高,避免内容过多或过少时异常
  .constraintSize({
    maxWidth: 'calc(100% - 90vp)',
    minHeight: 40,
    minWidth: 50
  })
  // 设置内容内边距
  .padding({
    left: 20,
    top: 10,
    right: 10,
    bottom: 10
  })
  // 设置背景图片尺寸
  .backgroundImageSize({
    height: '100%',
    width: '100%'
  })
  // 设置固定区域偏移值(九宫格 slice)
  .backgroundImageResizable({
    slice: {
      left: '70px',
      top: '80px',
      right: '40px',
      bottom: '40px'
    }
  })

关键点:

  • backgroundImage:设置背景图片资源
  • backgroundImageSize:设置背景图片尺寸为 100%
  • backgroundImageResizable:设置 slice 偏移值,px 单位

四、lattice 实现可拉伸占位图

场景咋回事

可拉伸占位图需要边缘区域可拉伸,中间 Logo 区域保持不变。

实现步骤

第一步:划分网格确定坐标点数组

中间 Logo 区域是固定部分。根据 lattice 原理,划分时要把中间区域放在偶数行偶数列交叉点。

Logo 区域坐标数组可由 UX 提供,或用 PhotoShop 手动定位。示例中 x 轴坐标数组为 [150, 648],y 轴为 [150, 733]。

直接用这坐标点,图片被划分为 3 行 3 列网格。Logo 位于第 1 行第 1 列(非偶数行列),达不到预期效果:

解决办法:在 x 轴和 y 轴各增加一个坐标点,使 Logo 位于第 2 行第 2 列(偶数行列)。

在原坐标前添加一个较小值如 1,新 x 轴坐标变成 [1, 150, 648],y 轴变成 [1, 150, 733]:

第二步:实现可拉伸占位图布局

根据获得的坐标点数组,用 createImageLattice() 创建网格对象,设给 lattice 参数:

@Component
struct PlaceholderImgView {
  // 默认图片高度
  @State imgHeight: Length = '35%';
  // 是否放大
  @State isEnlarge: boolean = false;
  // x轴坐标点数组
  private xDivs: Array<number> = [1, 60, 243];
  // y轴坐标点数组
  private yDivs: Array<number> = [1, 50, 253];
  // 创建网格对象
  private drawingLatticeFirst: DrawingLattice =
    drawing.Lattice.createImageLattice(this.xDivs, this.yDivs, this.xDivs.length, this.yDivs.length);

  build() {
    NavDestination() {
      Stack() {
        Image($r('app.media.placeholder_img'))
          .height(this.imgHeight)
          .width('100%')
          .resizable({
            lattice: this.drawingLatticeFirst
          })
          .onClick(() => {
            if (this.isEnlarge) {
              this.imgHeight = '35%';
            } else {
              this.imgHeight = '100%';
            }
            this.isEnlarge = !this.isEnlarge;
          })
      }
    }
  }
}

点击图片切换高度,验证拉伸效果。

五、踩坑总结

坑一:不同设备拉伸区域显示不一致

用 slice 设置不拉伸区域后,手机显示正常,平板上固定区域变形。

原因:传数字参数默认单位是 vp。不同设备对 vp 的 px 换算比例有差异,导致固定区域效果不一致。

解决:用 px 单位:

.resizable({
  slice: {
    left: '40px',
    top: '80px',
    right: '70px',
    bottom: '40px'
  }
})

坑二:resizable 未生效,图片仍变形

设置了 slice 或 lattice,不可拉伸区域依然变形。

原因

  1. 坐标值定位不准确,无法精准锁定目标区域
  2. 参数配置异常,比如坐标值超出图片宽高范围,或无效参数值

解决

  1. 坐标点定位不准 → 参考原理章节重新校准划分
  2. 参数异常 → 保证坐标点在图片区域内
    • lattice x 轴坐标:大于等于 0,小于图片宽度
    • lattice y 轴坐标:大于等于 0,小于图片高度
    • slice 参数范围:参考 ResizableOptions 说明

六、使用建议

  • 四个角不拉伸 → 用 slice(简单,九宫格)
  • 中间区域不拉伸 → 用 lattice(灵活,网格矩阵)
  • 多设备一致性 → 用 px 单位
  • 坐标点定位 → 用 PhotoShop 或 UX 提供数值

记住核心原理:固定区域不变,可拉伸区域拉伸。slice 是九宫格,lattice 是网格矩阵。两者适用场景不同,别混用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐