HarmonyOS 「校园二手交易商城」App应用实战 45 直播评论:LiveComment 与消息流
45 直播评论:LiveComment 与消息流
系列五:直播与画中画 · 第 5 篇
对应工程:
features/multishoppinglive/src/main/ets/components/LiveComment.ets
引言
直播间没有评论就像球赛没有解说。评论流是直播页最有"烟火气"的悬浮层:它叠加在视频画面之上、自动滚动、新消息从底部涌入。本项目用三个文件把它讲透:LiveComment(组件)、LiveCommentState(@ObservedV2 状态)、LiveCommentModel(数据)。虽然演示工程用静态字符串模拟消息流,但组件结构已经为真实消息推送预留了完整接缝。
数据层:LiveCommentModel 与 LiveCommentState
评论数据来自 LiveCommentModel,从字符串资源读取三条示例评论:
export class LiveCommentModel {
public static getComments(context: Context): string[] {
return [
LiveCommentModel.getStringSafe(context, $r('app.string.LIVE_COMMENTS_LIST3')),
LiveCommentModel.getStringSafe(context, $r('app.string.LIVE_COMMENTS_LIST2')),
LiveCommentModel.getStringSafe(context, $r('app.string.LIVE_COMMENTS_LIST1')),
];
}
}注意数组顺序:LIST3 在前、LIST1 在后。配合 List 的 stackFromEnd(true),数组尾部即列表视觉底部,最新消息(LIST1)显示在最下方——这是消息流"新消息在底部"的约定。
LiveCommentState 用 @ObservedV2 标记,持有评论数组与"官方"角标文案:
@ObservedV2
export class LiveCommentState {
public comments: string[] = [];
public officialLabel: string = '';
constructor(context: Context) {
this.comments = LiveCommentModel.getComments(context);
try {
this.officialLabel = context.resourceManager.getStringSync($r('app.string.LIVE_OFFICIAL_REVIEW').id);
} catch (error) {
Logger.info('getStringSync called.');
}
}
}组件侧用 @Local 持有状态实例:
@Local private state: LiveCommentState = new LiveCommentState(getHostContextSafe(this.getUIContext()));@ObservedV2 + @Local 是 V2 状态管理的标准组合:数据类被观察、组件本地持有。真实项目中,comments 数组换成 @Trace 字段、由消息推送回调 push 新消息,UI 会自动增量刷新。
评论消息样式:官方角标 + 气泡
每条评论是一个 ListItem,内部 Row 由"官方角标 + 评论文本"组成:
ListItem() {
Row() {
Text(this.state.officialLabel)
.fontColor($r('sys.color.font_on_primary'))
.fontSize(12)
.linearGradient({
angle: 0,
direction: GradientDirection.Left,
colors: [['#CF0A2C', 0], ['#FF6354', 1]]
})
.border({ radius: 4 })
.padding(4)
.margin({ right: 6 })
.visibility(index === this.state.comments.length - 1 ? Visibility.Visible : Visibility.None)
.textOverflow({ overflow: TextOverflow.Clip })
Text(item)
.layoutWeight(1)
.fontColor($r('sys.color.font_on_primary'))
.lineHeight(20)
.fontSize(12)
}
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Top)
.margin({ bottom: 8 })
.border({ radius: 8 })
.backgroundColor($r('app.color.live_comment_bubble'))
.padding({ left: 8, top: 6, bottom: 6, right: 6 })
}三个设计点:
- 官方角标只在最后一条显示:
visibility(index === comments.length - 1)——角标跟随最新一条评论,模拟"主播官方回复"的强调效果。 - 角标用
linearGradient渐变:#CF0A2C → #FF6354的红色渐变是直播间的视觉锤,与商品价格色、购物袋按钮色保持同一品牌色系。 - 评论文本
layoutWeight(1):文本占满剩余宽度,超长内容靠Text的默认换行 +textOverflow(Clip)截断,不会撑破气泡。
滚动消息流:List 的 stackFromEnd
消息流的滚动是 List 三件套配置实现的:
List() {
ForEach(this.state.comments, (item: string, index: number) => {
ListItem() { /* 上文的评论行 */ }
.visibility(this.breakpoint.heightBreakpoint === HeightBreakpoint.HEIGHT_MD &&
index < this.state.comments.length - 2 ? Visibility.None : Visibility.Visible)
}, (item: string, index: number) => `${item}-${index}`)
}
.width(new WidthBreakpointType(228, 400, 420, 420).getValue(this.widthBp))
.stackFromEnd(true)
.syncLoad(true)stackFromEnd(true):列表内容从底部堆叠,而不是默认的从顶部。新消息push到数组末尾后自然出现在底部视口内,这正是聊天流的滚动语义。syncLoad(true):同步加载所有子组件。演示数据只有三条,同步加载免去异步加载的空窗闪烁;真实高并发消息流可改回false或配合懒加载。- 高度避让:
HeightBreakpoint.HEIGHT_MD(中等高度,如折叠屏展开态)时只保留最后两条评论(index < length - 2的隐藏),压缩消息区给视频让位,是"高度断点"参与布局的典型例子。 - 宽度随断点:
WidthBreakpointType(228, 400, 420, 420)让评论条在 XS 下 228vp、SM 下 400vp、宽屏下 420vp,与直播舞台的缩放同步。
叠加层布局:与视频区的关系
评论区不是独立页面,而是 LiveBroadCast 里 Stack 的第二层,叠在视频之上:
Column() {
LiveComment({ widthBp: this.widthBp })
LiveShopList({ isSideListLayout: false, ... })
}
.justifyContent(FlexAlign.End)
.alignItems(HorizontalAlign.Start)Column 整体靠底部对齐(FlexAlign.End)、评论靠左(HorizontalAlign.Start),视觉上"评论在左下角、商品条在其下方"。半透明的 live_comment_bubble 气泡背景让评论既清晰可读又不完全遮挡画面——这是所有直播悬浮层的共同设计哲学。
注意事项与总结
- 数组顺序即时间线:
getComments的返回顺序决定了消息流先后,新消息永远push到数组尾部,配合stackFromEnd(true)才正确。 - ForEach 的 key 要稳定:
${item}-${index}保证评论内容变化时节点复用稳定;真实场景建议用服务端消息 id 做 key。 - 高度断点参与布局:
HEIGHT_MD下压缩评论数量,避免小高度窗口被消息占满;高度断点与宽度断点一样值得系统化使用。 - 悬浮层要半透明:气泡背景用
live_comment_bubble这类带透明度的资源色,否则会像一块膏药糊在画面上。
LiveShopList 商品列表如何在横条与侧栏两种形态间切换。
更多推荐


所有评论(0)