HarmonyOS 「校园二手交易商城」App应用实战 47 直播头部:LiveHeader 与 LiveHeaderPC
·
47 直播头部:LiveHeader 与 LiveHeaderPC
系列五:直播与画中画 · 第 7 篇
对应工程:
features/multishoppinglive/src/main/ets/components/LiveHeader.ets与LiveHeaderPC.ets
引言
直播页顶部信息栏是"主播是谁、多少人在看、怎么退出"的门面。项目提供了两个头部组件:LiveHeader(默认产品用)与 LiveHeaderPC(PC 产品用)。它们长得像、职责不同:手机头部右侧有关闭按钮,点击触发画中画;PC 头部用返回箭头 + 无关闭按钮,因为 PC 的窗口由系统装饰管理。两个组件共享同一个 LiveHeaderState,把"店铺名 + 观看人数"的数据逻辑抽到公共状态类里,是"一个状态、两个视图"的复用样板。
状态层:LiveHeaderState
头部数据极简,两个文本 + 一次资源读取:
@ObservedV2
export class LiveHeaderState {
public storeName: string = '';
public viewersText: string = '';
constructor(context: Context) {
try {
this.storeName = context.resourceManager.getStringSync($r('app.string.LIVE_HEADER_STORE_NAME').id);
this.viewersText = context.resourceManager.getStringSync($r('app.string.LIVE_HEADER_VIEWERS').id);
} catch (error) {
Logger.info('getStringSync called.');
this.storeName = '';
this.viewersText = '';
}
}
}@ObservedV2 标记让字段可被 V2 组件观察;构造时用 getStringSync 同步读字符串资源,读取失败降级为空串而不是抛错。LIVE_HEADER_VIEWERS 是"观看人数"文案(演示工程为静态文案,真实项目应改为 number 类型 + 定时刷新)。
两个头部组件都用 @Local 持有状态:
@Local private headerState: LiveHeaderState = new LiveHeaderState(getHostContextSafe(this.getUIContext()));LiveHeader:手机/默认形态
LiveHeader 的布局是"左侧主播信息卡 + 右侧关闭按钮",两端对齐:
Row() {
Row() {
Image($r('app.media.live_vmall')) // 主播头像
.width(36)
.aspectRatio(1)
.margin({ right: 4 })
Column() {
Text(this.headerState.storeName).fontSize(12)
.fontColor($r('sys.color.font_on_primary'))
Text(this.headerState.viewersText).fontSize(10)
.fontColor($r('sys.color.font_on_primary'))
}
.alignItems(HorizontalAlign.Start)
.margin({ right: 16 })
}
.height(36)
.backgroundColor($r('app.color.live_header_scrim')) // 半透明遮罩
.border({ radius: 18 })
Image($r('app.media.icon_close_3')) // 关闭按钮
.width(24)
.height(24)
.onClick(() => {
this.onClose();
})
}
.width('100%')
.height(36)
.padding({
left: new WidthBreakpointType(16, 24, 32, 32).getValue(this.widthBp),
right: new WidthBreakpointType(16, 24, 32, 32).getValue(this.widthBp)
})
.justifyContent(FlexAlign.SpaceBetween)
.background($r('app.color.live_stage_background'))设计要点:
- 主播卡是"胶囊":
height(36) + borderRadius(18)恰好圆角为高度一半,背景live_header_scrim是半透明遮罩色,主播信息"浮"在画面上又不刺眼。 - 关闭按钮触发
onClose:onClose由页面通过@Event注入。默认产品版LiveBroadCastPage把onClose接到onLiveClose()——注意第 1 篇讲过,default 版的onLiveClose走的是"先 startPip 再 pop"(画中画退出),所以手机用户点关闭看到的不是页面消失,而是视频飞进小窗。 - 左右内边距随断点:
WidthBreakpointType(16, 24, 32, 32)与直播页整体间距体系一致。
LiveHeaderPC:PC 差异化顶栏
LiveHeaderPC 与手机版有三处关键差异:
Row() {
SymbolGlyph($r('sys.symbol.chevron_left')) // 返回箭头替代关闭按钮
.fontSize(24)
.onClick(() => {
this.onClose();
})
.margin({ right: 12 })
.fontColor([$r('sys.color.font_on_primary')])
Row() {
Image($r('app.media.live_vmall'))
.width(36).aspectRatio(1).margin({ right: 4 })
Column() {
Text(this.headerState.storeName).fontSize(12)
Text(this.headerState.viewersText).fontSize(10)
}
.alignItems(HorizontalAlign.Start)
.margin({ right: 16 })
}
.height(36)
.backgroundColor($r('app.color.live_header_scrim'))
.border({ radius: 18 })
}
.width('100%')
.height(36)
.padding({ left: ..., right: ... })
.margin({ top: 12 })
.justifyContent(FlexAlign.Start)- 返回箭头而非关闭:
SymbolGlyph($r('sys.symbol.chevron_left'))(系统符号图标)替代icon_close_3.png。PC 的交互语义是"返回上一页"而非"关闭直播"。 - 无关闭按钮:PC 窗口右上角的关闭/最小化由系统窗口装饰(Decor)接管,应用内不再重复放关闭键——这正是第 1 篇
LiveBroadCastPagePC 版onLiveClose直接pop()、不走画中画的另一层原因(PC 用户习惯用窗口按钮退出)。 - 靠左对齐 + 顶部留白:
justifyContent(FlexAlign.Start)+.margin({ top: 12 }),顶栏悬浮在直播画面左上角,视觉上更像"信息标签"而非"工具栏"。
窗口按钮(装饰)处理
PC 端(2in1 设备)窗口默认带系统装饰:标题栏 + 最小化/最大化/关闭按钮。本项目页面通过 .hideTitleBar(true) 隐藏标题栏、但保留窗口级装饰,于是:
- 应用内的关闭/返回只负责页面栈(
mainPageMap.pop()/ 画中画)。 - 窗口关闭由系统装饰按钮完成,触发 UIAbility 生命周期,页面
aboutToDisappear会兜底执行stopPip()与avPlayerUtil.release(),资源不会泄漏。
LiveBroadCastPage 的侧栏模式(MD/LG/XL)在 PC 产品用 LiveHeaderPC,默认产品即使侧栏模式也用 LiveHeader:if (this.isSideMode()) {
Row() {
Column() {
LiveHeaderPC({ widthBp: ..., onClose: () => { this.onLiveClose(); } }) // pc 版
LiveBroadCast({ ... })
}
}
}注意事项与总结
- 交互语义因端而异:手机"关闭=画中画",PC"返回=退页面",同一
onClose事件在不同产品里绑定了不同实现,事件只传意图、不传实现。 - 系统符号图标优先:PC 版用
SymbolGlyph($r('sys.symbol.chevron_left'))而非位图,随系统字体/主题自适应,PC 上是更"原生"的选择。 - 窗口装饰与应用内按钮分工:
.hideTitleBar(true)只藏标题栏;窗口级关闭由系统装饰负责,应用内按钮只管导航栈;aboutToDisappear兜底释放资源。 - 状态复用:两个头部共享
LiveHeaderState,数据逻辑只写一遍;差异全部收敛在视图层。
更多推荐



所有评论(0)