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 在后。配合 ListstackFromEnd(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,与直播舞台的缩放同步。

叠加层布局:与视频区的关系

评论区不是独立页面,而是 LiveBroadCastStack 的第二层,叠在视频之上:

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 商品列表如何在横条与侧栏两种形态间切换。
Logo

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

更多推荐