基于原生能力实现图文混排
场景描述
应用中基于原生能力实现图文混排效果有多个方案可实现,推荐使用Flex作父容器实现的方案(方案一),此方案优点在于节点数量少,结构简单。
方案一:基于Flex作父容器实现图文混排
建议基于Flex容器作为父容器实现图文混排,优点在于减少节点数量。
内部头像使用Image组件,中间部分使用Text文本组件,右边使用Text文本组件。
核心代码
Flex({ direction: FlexDirection.Row }) {
Image($r('app.media.heard'))
.width(30)
.borderRadius(15)
Text() {
Span('文本')
.fontSize(15)
ImageSpan($r('app.media.member'))
.width('40px')
.height('40px')
.objectFit(ImageFit.Fill)
.verticalAlign(ImageSpanAlignment.BASELINE)
.onClick(() => {
console.log('测试输出')
})
Span('\n')
Span('昨天 12:00')
.fontSize(12)
.fontColor('#ffcac8c8')
}
.margin({
left: 10
})
Text('+关注')
.fontSize('28px')
.fontColor('#ff8200')
.border({
width: 1,
color: '#ff8200',
radius: 10,
style: BorderStyle.Solid
})
.padding({
left: 5,
right: 5,
top: 2,
bottom: 2
})
.position({ x: 280, y: 2 })
}
方案二:基于RelativeContainer相对布局实现图文混排效果
使用此方案RelativeContainer子组件要用alignRules布局组件位置,布局时需指定容器id。参与相对布局的容器内组件必须设置id,不设置id的组件组件不显示,容器id固定为__container__。
如以下核心代码中子组件都设置id,父容器未设置id时,父容器默认id固定为__container__。
核心代码
// 相对布局
RelativeContainer() {
Image($r('app.media.heard'))
.width(30)
.borderRadius(15)
.id('Image')
// 以容器作为锚点布局
.alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
left: { anchor: '__container__', align: HorizontalAlign.Start }
})
Text() {
Span('文本')
.fontSize(15)
ImageSpan($r('app.media.member'))
.width('40px')
.height('40px')
.objectFit(ImageFit.Fill)
.verticalAlign(ImageSpanAlignment.BASELINE)
.onClick(() => {
console.log('测试输出')
})
Span('\n')
Span('昨天 12:00')
.fontSize(12)
.fontColor('#ffcac8c8')
}
.id('Text1')
// 以容器内子组件作为锚点进行布局
.alignRules({
top: { anchor: 'Image', align: VerticalAlign.Top },
left: { anchor: 'Image', align: HorizontalAlign.End }
})
.margin({
left: 10
})
Text('+关注')
.fontSize('28px')
.fontColor('#ff8200')
.border({
width: 1,
color: '#ff8200',
radius: 10,
style: BorderStyle.Solid
})
.id('Text2')
// 以容器内子组件作为锚点进行布局
.alignRules({
top: { anchor: 'Text1', align: VerticalAlign.Top },
left: { anchor: 'Text1', align: HorizontalAlign.End },
})
.margin({
left: 150
})
.padding({
left: 5,
right: 5,
top: 2,
bottom: 2
})
}
方案三:基于线性布局实现图文混排效果
基于线性布局实现图文混排时,节点数量会比较多。
核心代码
Row() {
Image($r('app.media.img1'))
.width(30)
.borderRadius(15)
Column() {
Row() {
Text('文本')
.fontSize(15)
Image($r('app.media.vvip_1'))
.width(30)
.borderRadius(15)
.margin({ left: 10 })
.position({ x: 40, y: 2 })
}
Text('昨天 12:00')
.fontSize(12)
.fontColor('#ffcac8c8')
}
.margin({ left: 10, top: 2 })
// 设置Column容器内子组件水平方向上布局
.justifyContent(FlexAlign.Start)
// 设置Column容器内子组件垂直方向上布局
.alignItems(HorizontalAlign.Start)
Text('+关注')
.fontSize('28px')
.fontColor('#ff8200')
.border({
width: 1,
color: '#ff8200',
radius: 10,
style: BorderStyle.Solid
})
.padding({
left: 5,
right: 5,
top: 2,
bottom: 2
})
.position({ x: 280, y: 2 })
}
.width('100%')
.margin({ left: 10, top: 50 })
方案四:基于StyledString(属性字符串)实现图文混排效果
使用StyledString实现图文混排效果,首先得让Text组件与StyledString绑定,绑定后即可使用StyledString设置文本样式以及对文本进行增、删、改、查等操作。
StyledString是一个方便灵活应用文本样式的对象,Text组件可通过TextController中的setStyleString方法与属性字符串绑定。绑定之后即可通过StyledString对文本进行增、删、改、查等一系列操作,并且可以用StyledString设置文本样式。
注意:
1. 组件样式和属性字符串样式冲突时,属性字符串优先级高,冲突样式以属性字符串设置样式为准。
2. Text子组件样式与属性字符串样式冲突,以属性字符串为准。
3. 属性字符串对象不支持@State修饰。
核心代码
import { image } from '@kit.ImageKit'
import { LengthMetrics, LengthMetricsUnit } from '@ohos.arkui.node';
@Component
export struct FourTh {
// PixelMap图片
imagePixelMap: image.PixelMap | undefined = undefined;
// 属性字符串对象
mutableStr: MutableStyledString = new MutableStyledString('');
controller: TextController = new TextController();
// 基于属性字符串设置文本样式
fontStyle2: StyledStringValue = new TextStyle({
fontColor: '#ffcac8c8',
fontSize: LengthMetrics.vp(12)
})
async aboutToAppear() {
// 获取图片资源并同步解析成PixelMap
this.imagePixelMap = await this.getPixmapFromMedia($r('app.media.member'));
}
// 同步将资源解析成PixelMap
private async getPixmapFromMedia(resource: Resource) {
let unit8Array = await getContext(this)?.resourceManager?.getMediaContent({
bundleName: resource.bundleName,
moduleName: resource.moduleName,
id: resource.id
})
let imageSource = image.createImageSource(unit8Array.buffer.slice(0, unit8Array.buffer.byteLength));
let createPixelMap: image.PixelMap = await imageSource.createPixelMap({
desiredPixelFormat: image.PixelMapFormat.RGBA_8888
});
await imageSource.release();
return createPixelMap;
}
build() {
NavDestination() {
Row() {
Image($r('app.media.heard'))
.width(30)
.borderRadius(15)
.margin({
right: 10,
top: 5
})
// Text组件绑定controller
Text(undefined, { controller: this.controller })
.fontSize(15)
Text('+关注')
.fontSize('28px')
.fontColor('#ff8200')
.border({
width: 1,
color: '#ff8200',
radius: 10,
style: BorderStyle.Solid
})
.padding({
left: 5,
right: 5,
top: 2,
bottom: 2
})
.position({ x: 280, y: 2 })
}
.onAppear(() => {
setTimeout(() => {
if (this.imagePixelMap !== undefined) {
// ImageAttachment: New图形对象并设置样式
this.mutableStr = new MutableStyledString(new ImageAttachment({
value: this.imagePixelMap,
size: { width: '40px', height: '40px' },
// layoutStyle: { borderRadius: LengthMetrics.vp(10) },
verticalAlign: ImageSpanAlignment.BASELINE,
objectFit: ImageFit.Fill
}))
}
// insertString:在指定字符之前插入文本,此处即在图片之前添加文本
this.mutableStr.insertString(0, '文本');
// 在图片之后添加文本
let str = new StyledString('\n昨天 12:00', [{
start: 0,
length: 10,
styledKey: StyledStringKey.FONT,
styledValue: this.fontStyle2
}])
// appendStyledString: 在末尾位置追加新的属性字符串
this.mutableStr.appendStyledString(str);
// 通过TextController中的setStyledString方法让属性字符串与Text组件绑定
this.controller.setStyledString(this.mutableStr);
}, 100)
})
.height(200)
.margin({ left: 10, top: 50 })
.width('100%')
.alignItems(VerticalAlign.Top)
}.title('方案4')
}
}