HarmonyOS 6商城开发学习:聊天气泡背景自适应拉伸——Image.resizable与borderImage点九图平替实战
在HarmonyOS 6购物比价或电商客服类应用中,聊天气泡(Bubble)/商品价格标签/优惠券角标 常需要用一张带圆角和尖角的 PNG 作为背景,文字长短不一希望背景自动撑开且图案(如边框、气泡嘴)不变形。很多同学直接给 Text设 backgroundImage发现文字一长背景就被强制拉伸失真,或点九图(.9.png)不生效。
官方明确说明:HarmonyOS 不支持 Android 式 .9.png资源,但可通过 Image组件的 resizable({slice})属性或 borderImage实现同等效果。本文将完整讲解两种平替方案。
一、问题现象:Text设背景图文字长后变形
1. 错误尝试
// ❌ 问题:背景图被整体等比拉伸,气泡嘴/边框图案扭曲
Text('满199减50 限今日使用')
.backgroundImage($r('app.media.bubble_left'))
.backgroundImageSize(ImageSize.FILL)
.padding(10)
当文字变长 → Column/Row撑宽 → backgroundImageSize(FILL)把整张 PNG 横向拉伸 → 左侧气泡尖角变扁、边框变粗,视觉失真。
2. 根因揭秘
普通 backgroundImage对图片做整体缩放,无法区分"可拉伸区"和"固定区"。Android 的点九图思路是:图片指定上下左右各 N px 为拉伸区,其余区域保持原样不被缩放——HarmonyOS 用以下两种等价方式实现。
二、方案一(推荐):容器 + Image.resizable 做背景层
用 Column/Row包 Text,给容器设 .background(builder)其中放一个 Image并配 resizable({slice:{top,bottom,left,right}})。
slice 参数含义(单位:vp 或 px,与图片原始尺寸对应)
slice: { top: T, bottom: B, left: L, right: R }
-
top/bottom:距图片上/下边缘多少像素内的横向条带可被纵向拉伸 -
left/right:距图片左/右边缘多少像素内的纵向条带可被横向拉伸 -
四个角区域(左上/右上/左下/右下)永不拉伸,保持原比例 → 气泡嘴、圆角不变形
例:一张 60×40 气泡图,上下各留 10vp 拉伸区、左右各留 15vp 拉伸区 →
slice:{top:10,bottom:10,left:15,right:15}
完整代码
// utils/BubbleBg.ets
import { ImageFit } from '@kit.ArkUI';
/**
* 聊天气泡背景 Builder
* @param res 点九风格 PNG资源
* @param sliceTop 上边缘不拉伸区高度(vp)
* @param sliceBottom 下边缘不拉伸区高度(vp)
* @param sliceLeft 左边缘不拉伸区宽度(vp)
* @param sliceRight 右边缘不拉伸区宽度(vp)
*/
export function bubbleBackground(
res: Resource,
sliceTop: number = 8,
sliceBottom: number = 8,
sliceLeft: number = 16,
sliceRight: number = 16
) {
return () => {
Image(res)
.objectFit(ImageFit.Fill)
.resizable({
slice: {
top: sliceTop,
bottom: sliceBottom,
left: sliceLeft,
right: sliceRight
},
resizableEdges: { top: true, bottom: true, left: true, right: true }
})
.width('100%')
.height('100%')
};
}
// pages/ChatBubblePage.ets
import { bubbleBackground } from '../utils/BubbleBg';
@Entry
@Component
struct ChatBubblePage {
build() {
Column({ space: 16 }) {
// 左气泡(对方)
Row() {
Column() {
Text('你好,这款耳机支持主动降噪吗?')
.fontColor('#333')
}
.padding({ horizontal: 12, vertical: 8 })
.background(
bubbleBackground(
$r('app.media.bubble_left'), // 左侧带尖角气泡
8, 8, 16, 16
)
)
}
// 右气泡(自己)
Row() {
Column() {
Text('支持的哦~满199减50券可以用')
.fontColor('#FFF')
}
.padding({ horizontal: 12, vertical: 8 })
.background(
bubbleBackground(
$r('app.media.bubble_right'), // 右侧尖角气泡
8, 8, 16, 16
)
)
}
.alignSelf(HorizontalAlign.End)
}
.padding(24)
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
要点:
-
Image必须设.width('100%').height('100%')铺满容器 -
objectFit(ImageFit.Fill)确保 resizable 生效(不保持原图宽高比) -
容器
Column/Row不设固定宽 → 随TextlayoutWeight或内容自适应
三、方案二:Text 直接设 borderImage(无额外容器)
若不想包一层容器,可直接对 Text用 borderImage+ slice,效果等价:
Text('满5000减4000 活动仅今日')
.fontColor('#0A59F7')
.textAlign(TextAlign.Center)
.padding({ horizontal: 12, vertical: 8 })
.borderImage({
source: $r('app.media.bubble_left'),
slice: { top: 8, bottom: 8, left: 16, right: 16 },
width: { top: 8, bottom: 8, left: 16, right: 16 }, // 边框宽度=slice值
repeat: RepeatMode.Stretch,
fill: true
})
fill: true使图片内容填充满 border 区域(类似 background),repeat: Stretch拉伸指定区。
适用场景:单个 Text/Button独立做气泡标签时省一层布局;复杂嵌套内容(多行+图标)仍推荐方案一(Column.background(builder))更灵活。
四、避坑指南
|
问题 |
原因 |
修复 |
|---|---|---|
|
背景图仍整体拉伸 |
|
用画图工具量 PNG 四边固定区像素 → vp 换算后填入 slice |
|
气泡尖角变形 |
slice 把尖角区域也纳入了拉伸区 |
确保尖角落在四角"不拉伸区"内(slice 不覆盖尖角所在边区域) |
|
borderImage 显示不全 |
未设 |
|
|
容器背景盖住内容 |
Image 盖在内容上层(z序问题) |
用 |
|
想用 .9.png 文件 |
HarmonyOS 不支持 |
转普通 PNG + |
五、总结:聊天气泡自适应背景 SOP
-
准备普通 PNG:在 PS/Figma 留上下左右各 N px 纯色/渐变拉伸区,四角图案放中心不拉伸区
-
测 slice 值:距上/下/左/右边缘多少 vp 是可拉伸带 → 填入
resizable({slice:{top,bottom,left,right}}) -
容器包 Text:
Column.background(bubbleBackground($r(...), t,b,l,r))+Image设width/height 100%+objectFit(Fill) -
简单标签可用
borderImage平替(单组件无嵌套)
核心法则:HarmonyOS 6 中"点九图平替 = Image.resizable({slice})或 borderImage({slice,fill:true})",不支持 .9.png但效果完全等价且可控。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任。
更多推荐



所有评论(0)