ArkUI Image:图片不再失控,页面质感直接拉满
ArkUI Image:图片不再失控,页面质感直接拉满
这篇我们聊 Image。图片这东西看着简单,真写页面时经常出事:不是路径错了,就是比例歪了,要么封面一铺满就把重点裁没了。
所以这个案例不做花活,只把四种来源放到一页里:media、rawfile、SVG、网络图。最后再看一眼 Contain、Cover、Fill 到底差在哪。
用本地资源是为了稳定,用网络图是为了贴近真实页面。没有谁更高级,按场景选就行。
参考文档:
- 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.svgentry/src/main/resources/base/media/startIcon.pngentry/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.png 在 media,读取方式还是 $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。封面、横幅想要铺满,再考虑 Cover。Fill 要谨慎,它很容易把图拉得不像原来的样子。
看案例时盯这几处
打开案例页,重点看这些:
- 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')
}
}
- 点赞
- 收藏
- 关注作者
评论(0)