【鸿蒙OH实战开发】一多开发实例(社区评论)


📚往期笔录记录✏️:

✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?

✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

✏️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?

✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

✏️ 记录一场鸿蒙开发岗位面试经历~

✏️ 持续更新中……


概述

本文选择社区评论行业应用作为典型案例介绍 “一多” 在实际开发中的应用。社区评论行业应用的核心功能为社区新闻浏览以及热搜榜单查看。根据这些核心功能,案例实现推荐热搜、热搜榜单、卡片详情、图片查看、输入评论等典型页面。文章主要介绍关键布局能力的功能及实现。

架构设计

HarmonyOS的分层架构主要包括三个层次:产品定制层、基础特性层和公共能力层,为开发者构建了一个清晰、高效、可扩展的设计架构。

本示例应用包含热点页、热搜榜单页、卡片详情页、图片查看页四个典型页面。其中主要介绍热点页及卡片详情页关键区域布局能力实现。以手机设备UX设计进行介绍。

热点页利用 响应式布局 中的 栅格布局 能力,结合 WaterFlow 容器,实现单列卡片变瀑布流卡片的一多布局能力。

卡片详情页利用响应式布局中的栅格布局,实现图文区域和评论区域的左右布局和上下布局,实现边看边评的图文阅读效果。

社区评论应用包含以下一多页面布局能力:侧边导航、列表重复布局、动态卡片、边看边评布局。

页面开发

布局能力

本章节选取页面关键区域进行一多页面布局能力介绍。

热点页布局能力

热点页主要提供搜索、热搜展示、信息阅读等功能。使用布局能力为:列表重复布局、动态卡片。

  • 列表重复布局

竖向列表可以清晰明了地展示数据,在宽屏设备上为了展示更多的数据,设计了列表重复的布局。

在进行有序数据展示时,使用List容器进行数据排列。通过设置List组件的布局方向listDirection和lanes属性并结合断点,实现在不同断点下显示不同列数。

示意图smmdlg
设计能力点
效果图
// 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列。

示意图smmdlg
设计能力点
效果图
// 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容器无法兼容,使用断点分别控制两处实现的显示隐藏。

示意图smmdlg
设计能力点
效果图
// 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;
            })
        )
        // ...
      }
    }
    // ...
  }

  // ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值