📚往期笔录记录✏️:
✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
✏️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
✏️ 记录一场鸿蒙开发岗位面试经历~
✏️ 持续更新中……
概述
本文选择社区评论行业应用作为典型案例介绍 “一多” 在实际开发中的应用。社区评论行业应用的核心功能为社区新闻浏览以及热搜榜单查看。根据这些核心功能,案例实现推荐热搜、热搜榜单、卡片详情、图片查看、输入评论等典型页面。文章主要介绍关键布局能力的功能及实现。
架构设计
HarmonyOS的分层架构主要包括三个层次:产品定制层、基础特性层和公共能力层,为开发者构建了一个清晰、高效、可扩展的设计架构。
本示例应用包含热点页、热搜榜单页、卡片详情页、图片查看页四个典型页面。其中主要介绍热点页及卡片详情页关键区域布局能力实现。以手机设备UX设计进行介绍。
热点页利用 响应式布局 中的 栅格布局 能力,结合 WaterFlow 容器,实现单列卡片变瀑布流卡片的一多布局能力。
卡片详情页利用响应式布局中的栅格布局,实现图文区域和评论区域的左右布局和上下布局,实现边看边评的图文阅读效果。
社区评论应用包含以下一多页面布局能力:侧边导航、列表重复布局、动态卡片、边看边评布局。
页面开发
布局能力
本章节选取页面关键区域进行一多页面布局能力介绍。
热点页布局能力
热点页主要提供搜索、热搜展示、信息阅读等功能。使用布局能力为:列表重复布局、动态卡片。
- 列表重复布局
竖向列表可以清晰明了地展示数据,在宽屏设备上为了展示更多的数据,设计了列表重复的布局。
在进行有序数据展示时,使用List容器进行数据排列。通过设置List组件的布局方向listDirection和lanes属性并结合断点,实现在不同断点下显示不同列数。
示意图 | sm | md | lg |
---|---|---|---|
设计能力点 | ![]() | ||
效果图 | ![]() | ![]() | ![]() |
// features/hot/src/main/ets/view/HotColumnView.ets
@Component
export struct HotColumnView {
@StorageLink('currentBreakpoint') currentBreakpoint: string = Breakpoint.BREAKPOINT_SM;
// ...
@Builder
hotListBuilder() {
List() {
ForEach(HOST_LIST_ARRAY[this.tab_index], (item: HotItemInterface, index: number) => {
if (index < Common.HOT_COLUMN_MAX_COUNT * new BreakpointType(1, 2, 3).getValue(this.currentBreakpoint)) {
ListItem() {
HotListItemView({
item: item,
showDetail: true,
// ...
})
}
.width(new BreakpointType('100%', '50%', '33%').getValue(this.currentBreakpoint))
}
}, (item: HotItemInterface) => JSON.stringify(item))
}
.width('100%')
.height('334vp')
.lanes(Common.HOT_COLUMN_MAX_COUNT)
.listDirection(Axis.Horizontal)
}
build() {
Column() {
this.hotListBuilder()
// ...
}
// ...
}
}
- 动态卡片
信息卡片作为显示内容的主体,若使用竖向单列的布局方式,在宽屏设备上容易造成大量留白,影响视觉效果。此时通过在宽屏设备上展示两列的方式充实了页面内容,并通过瀑布流的布局紧密连接了卡片,提供了更紧凑的视觉效果。
动态卡片布局主要使用WaterFlow容器,在小屏设备手机、折叠屏与宽屏设备2in1之间差异化显示。在手机及折叠屏上竖向单列展示,通过分割线进行卡片分隔。而在2in1设备上,依赖断点控制WaterFlow容器显示2列。
示意图 | sm | md | lg |
---|---|---|---|
设计能力点 | ![]() | ||
效果图 | ![]() | ![]() | ![]() |
// features/hot/src/main/ets/view/FoundView.ets
WaterFlow() {
ForEach(this.cardArrayViewModel.cardArray, (item: CardItem, index: number) => {
FlowItem() {
Column() {
MircoBlogView({
cardItem: item,
// ...
})
// ...
CommentBarView({
isShowInput: false,
// ...
})
}
// ...
}
}, (item: CardItem, index: number) => index + JSON.stringify(item))
}
.columnsTemplate(this.currentBreakpoint !== Breakpoint.BREAKPOINT_LG ?
Common.WATER_FLOW_COLUMNS_TEMPLATE_NORMAL : Common.WATER_FLOW_COLUMNS_TEMPLATE_LG)
// ...
卡片详情区域
卡片详情区域支持图文和评论在不同设备上显示上下或左右布局。
- 边看边评
为了更好的进行图文内容及图片内容的展示,以及实现同时浏览评论的功能,在不同设备上进行了图文与评论左右或上下的布局设计。手机布局为上下布局。折叠屏支持内容区和评论区上下及左右布局切换。2in1固定为左右布局。
边看边评能力主要使用栅格布局实现。在手机设备上图文区及评论区同时占满设备栅格,显示图文区在上评论区在下的效果。折叠屏上下布局实现方式同手机上实现一致。控制图文区占用栅格数为3/5,评论区栅格数为2/5,从而实现了图文与评论的左右布局。
2in1设备上固定为左右布局。使用 SideBarContainer 容器设置评论区宽度。由于栅格布局与SideBarContainer容器无法兼容,使用断点分别控制两处实现的显示隐藏。
示意图 | sm | md | lg |
---|---|---|---|
设计能力点 | ![]() | ||
效果图 | ![]() | ![]() | ![]() |
// features/detail/src/main/ets/view/DetailPage.ets
@Component
export struct DetailPage {
// ...
build() {
Stack() {
Column() {
DetailTitleView({ isShowedButton: this.isShowedButton })
Scroll() {
GridRow({
columns: {
sm: Breakpoint.GRID_ROW_COLUMNS[2],
md: Breakpoint.GRID_ROW_COLUMNS[3],
lg: Breakpoint.GRID_ROW_COLUMNS[0]
}
}) {
GridCol({
span: {
sm: Breakpoint.GRID_COLUMN_SPANS[5],
// md断点判断布局方式设置占用栅格数量
md: this.isFoldHorizontal ? Breakpoint.GRID_COLUMN_SPANS[4] : Breakpoint.GRID_COLUMN_SPANS[3],
lg: Breakpoint.GRID_COLUMN_SPANS[0]
}
}) {
if ((this.isFoldHorizontal && this.currentBreakpoint === Breakpoint.BREAKPOINT_MD)) {
Scroll() {
MircoBlogView({
cardItem: this.cardItem,
index: this.selectCardIndex
})
// ...
}
// ...
} else {
MircoBlogView({
cardItem: this.cardItem,
index: this.selectCardIndex
})
// ...
}
}
// ...
GridCol({
span: {
sm: Breakpoint.GRID_COLUMN_SPANS[5],
md: this.isFoldHorizontal ? Breakpoint.GRID_COLUMN_SPANS[6] : Breakpoint.GRID_COLUMN_SPANS[3],
lg: Breakpoint.GRID_COLUMN_SPANS[0]
}
}) {
CommentListView()
}
// ...
}
}
.visibility(this.currentBreakpoint === Breakpoint.BREAKPOINT_LG ? Visibility.None : Visibility.Visible)
// ...
Column() {
SideBarContainer() {
Column() {
CommentListView()
}
// ...
Column() {
Scroll() {
MircoBlogView({
cardItem: this.cardItem,
index: this.selectCardIndex
})
// ...
}
// ...
}
.justifyContent(FlexAlign.Start)
}
// ...
}
.visibility(this.currentBreakpoint !== Breakpoint.BREAKPOINT_LG ? Visibility.None : Visibility.Visible)
// ...
}
// ...
}
}
// ...
}
交互事件处理
鼠标事件
2in1设备支持鼠标键盘,需要进行交互事件处理。触控点击评论区触发点击事件拉起弹窗进行评论输入。鼠标点击时触发鼠标事件,更改评论区显示状态为输入状态,取消对点击事件的处理。
// features/detail/src/main/ets/view/CommentBarView.ets
@Component
export struct CommentBarView {
// ...
build() {
Column() {
Row() {
if (!this.isMouseClick) {
Row() {
Text($r('app.string.detail_write_comment'))
// ...
}
// ...
// 判断鼠标左键点击事件
.onMouse((event: MouseEvent) => {
if (event.button === MouseButton.Left && event.action === MouseAction.Press) {
this.isMouseClick = true;
}
})
// 鼠标事件触发时,点击事件内容不执行
.onClick(() => {
if (!this.isMouseClick) {
AppStorage.setOrCreate('isShowInput', true);
}
})
// ...
}
else {
// 鼠标左键点击事件触发显示该区域
Row() {
TextArea({ placeholder: $r('app.string.detail_write_comment'), text: this.content })
// ...
Image(this.isDarkMode ? $r('app.media.ic_toolbar_send_white') : $r('app.media.ic_toolbar_sent'))
// ...
.onClick(() => {
this.isMouseClick = false;
})
}
// ...
}
}
// ...
}
// ...
}
// ...
}
文字缩放
详情页正文内容支持 捏合手势 缩放文字大小。文字区域添加双指捏合手势事件,使用缩放比例计算文字大小及文字行高,实现双指缩放文字的功能。缩放事件输入方式参考 交互归一。
效果如图所示:
// features/detail/src/main/ets/view/MircoBlogView.ets
@Component
export struct MircoBlogView {
// ...
build() {
Column() {
if (this.cardItem !== undefined) {
// ...
Row() {
Text(this.cardItem.content)
.fontSize(`${this.contentFontSize}${Common.SUFFIX_FP}`)
.lineHeight(`${this.contentFontHeight}${Common.SUFFIX_VP}`)
// ...
}
.gesture(
PinchGesture({ fingers: Common.PINCH_GESTURE_FINGERS })
.onActionUpdate((event?: GestureEvent) => {
if (event && (this.isDetailPage || this.isPictureDetail)) {
let tmp = this.pinchValue * event.scale;
if (tmp > Common.MAX_SCALE) {
tmp = Common.MAX_SCALE;
}
if (tmp < Common.MIN_SCALE) {
tmp = Common.MIN_SCALE;
}
this.scaleValue = tmp;
// 计算文字大小及文字行高
this.contentFontSize = Common.CONTENT_FONT_SIZE * this.scaleValue;
this.contentFontHeight = Common.CONTENT_FONT_HEIGHT * this.scaleValue;
this.pictureMarginTop = Common.PICTURE_MARGIN_TOP * (this.scaleValue > 1 ? this.scaleValue : 1);
}
})
.onActionEnd(() => {
// 捏合手势结束后设置文字当前缩放比例
this.pinchValue = this.scaleValue;
})
)
// ...
}
}
// ...
}
// ...
}