当前位置:首页>鸿蒙APP>【鸿蒙开发—顺 “瓜” 摸 “藤”】3. 应用的侧边栏(仿小红书)

【鸿蒙开发—顺 “瓜” 摸 “藤”】3. 应用的侧边栏(仿小红书)

  • 2026-11-02 10:11:18
【鸿蒙开发—顺 “瓜” 摸 “藤”】3. 应用的侧边栏(仿小红书)

1. 引子

我们在开发时,会有突发奇想或者看到好的设计,希望能够模仿,但是会有处于毫无头绪的时候,不知道该如何下手,这就相当于一个考试,给你一个题目叫你作答,但不同于校园考试,他并不是在给定你范围并且复习的情况下进行作答。它是未知的,并不是顺藤摸瓜,总会遇到你所不清楚的内容,我希望能够通过案例顺瓜摸藤进行学习,通过“瓜”来推出是什么“藤”。

2. 注

这些案例都是现有APP的形式进行推论,因本人能力有限,可能不会是最优方案,如果您有更优方案,欢迎指出并讨论。

从顺“瓜”摸“藤”系列第三个案例开始,不再开发状态管理V1版本,只开发V2版本,若不分基础与进阶表示该案例处于相对复杂的案例。

该系列的代码全部开源

  • 在github 

    https://github.com/JinnyWang-Space/HMOS_Space

  • 或者gitee

    https://gitee.com/jinnywang/HMOS_Space上均可查看,下载使用

  • 文章也可在个人网站查看

    https://www.jinnyspace.online

3. 案例

应用的侧边栏

小红书的案例是侧边栏的一种,后续会持续更新其他类型侧边栏,以其他APP为案例。

案例分析

1. 应用的侧边栏通过左上角的控制按钮,侧边栏展开时覆盖当前整个页面。

2. 侧边栏展开时,只能通过返回键 / 系统滑动返回或者点击侧边栏以外的页面进行关闭,侧边栏是不可滑动关闭。

3. 侧边栏展开时,被覆盖的页面可以看出有明显的变暗效果,并且内容区处于不可交互状态。

4. 侧边栏展开时,无论什么情况,一旦应用进入前台,则会自动关闭侧边栏。

5. 仔细观察会发现,不一定点击按钮才展开侧边栏,实际点击效果区域会比按钮大小要大。 

4. 知识点

4.1 SideBarContainer (侧边栏容器)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-sidebarcontainer

4.2 enabled(禁用控制)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-enable

4.3 opacity(透明度设置)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-opacity

4.4 AppStorageV2(应用全局UI状态存储)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-appstoragev2

4.5 onDidForeground(UIAbility生命周期)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-app-ability-uiability#ondidforeground20

新手可能对于生命周期不太了解,想了解 UIAbility生命周期,请查看该文档:

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-lifecycle

4.6 自定义构建函数(@Builder)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder

4.7 @ohos.promptAction(弹窗)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-promptaction#promptactionopentoast18

4.8 expandSafeArea(拓展安全区)

文档链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-expand-safe-area#expandsafearea

5. 代码

5.1 状态管理V2版

5.1.1 数据中心

// 小红薯数据中心(UI状态存储)@ObservedV2export class RBTmpDataV2 {  // 监听侧边栏是否显示  @Trace isShowSideBar: boolean = false;}

5.1.2 UIAbility组件

用户控制在应用进入前台时,进行数据变化(即:侧边栏处于开启时,关闭)

export default class EntryAbility extends UIAbility {   rbTmpDataV2: RBTmpDataV2 = AppStorageV2.connect<RBTmpDataV2>(RBTmpDataV2, () => new RBTmpDataV2())!;  // 其他生命周期,不要去掉,去错会出现问题  ...  // 应用进入前台(随时)  onDidForeground(): void {    // 如果当前控制栏处于打开状,则关闭    if (this.rbTmpDataV2.isShowSideBar) {      animateToImmediately({        duration: 200,        curve: Curve.Linear      }, () => {        this.rbTmpDataV2.isShowSideBar = false;      })    }  }}

5.1.3 视图页面

import { AppStorageV2, promptAction } from "@kit.ArkUI";import { RBTmpDataV2 } from "../viewmodel/RBTmpData";// 3. 应用的侧边栏(小红书)// V2 版代码@ComponentV2export struct SideBarRedBookV2 {  // 创建 APPStorageV2 存储小红书全局 UI 的数据中心  @Local rbTmpDataV2: RBTmpDataV2 = AppStorageV2.connect<RBTmpDataV2>(RBTmpDataV2, () => new RBTmpDataV2())!;  // 侧边栏区  @Builder  sidebarView() {    Column({ space: 8 }) {      Row({ space: 12 }) {        SymbolGlyph($r('sys.symbol.calendar_01_a'))          .fontSize(24)          .fontColor(['#DADADA'])        Text('发现好友')          .fontSize(14)          .fontColor('#DADADA')      }      .width('100%')      .backgroundColor('#1A191E')      .borderRadius(12)      .padding({        top: 16,        bottom: 16,        left: 12,        right: 12      })      Row({ space: 12 }) {        SymbolGlyph($r('sys.symbol.calendar_01_a'))          .fontSize(24)          .fontColor(['#DADADA'])        Text('创作者中心')          .fontSize(14)          .fontColor('#DADADA')      }      .width('100%')      .backgroundColor('#1A191E')      .borderRadius(12)      .padding({        top: 16,        bottom: 16,        left: 12,        right: 12      })      Column() {        ForEach(['我的草稿', '我的评论', '浏览记录'], (item: string) => {          Row({ space: 12 }) {            SymbolGlyph($r('sys.symbol.calendar_01_a'))              .fontSize(24)              .fontColor(['#DADADA'])            Text(item)              .fontSize(14)              .fontColor('#DADADA')          }          .width('100%')          .padding({            top: 15,            bottom: 15,            left: 12,            right: 12          })        })      }      .backgroundColor('#1A191E')      .borderRadius(12)      Column() {        ForEach(['订单', '购物车', '钱包'], (item: string) => {          Row({ space: 12 }) {            SymbolGlyph($r('sys.symbol.calendar_01_a'))              .fontSize(24)              .fontColor(['#DADADA'])            Text(item)              .fontSize(14)              .fontColor('#DADADA')          }          .width('100%')          .padding({            top: 15,            bottom: 15,            left: 12,            right: 12          })        })      }      .backgroundColor('#1A191E')      .borderRadius(12)      Row({ space: 12 }) {        SymbolGlyph($r('sys.symbol.calendar_01_a'))          .fontSize(24)          .fontColor(['#DADADA'])        Text('社区公约')          .fontSize(14)          .fontColor('#DADADA')      }      .width('100%')      .backgroundColor('#1A191E')      .borderRadius(12)      .padding({        top: 16,        bottom: 16,        left: 12,        right: 12      })      Blank()      Row() {        ForEach(['扫一扫', '帮助与客服', '设置'], (item: string) => {          Column({ space: 12 }) {            Row() {              SymbolGlyph($r('sys.symbol.calendar_01_a'))                .fontSize(18)                .fontColor(['#999999'])            }            .size({ width: 40, height: 40 })            .justifyContent(FlexAlign.Center)            .borderRadius(20)            .backgroundColor('#171719')            Text(item)              .fontSize(12)              .fontColor('#959595')          }          .layoutWeight(1)        })      }      .width('100%')      .margin({ bottom: 24 })    }    .layoutWeight(1)    .backgroundColor('#0E0E01')    .padding({ top: 16, left: 12, right: 12 })    .expandSafeArea([SafeAreaType.SYSTEM])  }  // 内容区  @Builder  contentView() {    Column() {      // 标题栏      Row() {        // 侧边栏按钮        Row() {          SymbolGlyph($r('sys.symbol.line_3_horizontal'))            .fontSize(22)            .fontColor(['#DADADA'])        }        .width(38)        .height('100%')        .justifyContent(FlexAlign.End)        .onClick(() => {          this.sideBarAnimation(true);        })        Blank()        // 搜索按钮        Row() {          SymbolGlyph($r('sys.symbol.magnifyingglass'))            .fontSize(22)            .fontColor(['#DADADA'])        }        .width(38)        .height('100%')        .justifyContent(FlexAlign.Start)      }      .width('100%')      .height(44)      .backgroundColor('#1A191E')      // 下边框      .border({        width: {          bottom: 0.6        }, color: {          bottom: '#2A292E'        }      })      Column() {        Text('用来进行测试,当侧边栏展开时,内容区的内容是处于不可交互的状态,当前文字点击就不会出现提示框,而是直接关闭侧边栏;侧边栏关闭时,内容区就可进行交互')          .fontSize(24)          .fontWeight(FontWeight.Bold)          .fontColor('#DADADA')          .onClick(() => {            promptAction.openToast({ message: '可交互' })          })      }      // 控制交互      .enabled(this.rbTmpDataV2.isShowSideBar ? false : true)      .width('100%')      .layoutWeight(1)      .justifyContent(FlexAlign.Center)      .backgroundColor('#141318')      .expandSafeArea([SafeAreaType.SYSTEM])    }    .width('100%')    .layoutWeight(1)    .onClick(() => {      this.sideBarAnimation(false);    })    // 内容区跟随侧边栏的开关而有不同效果    .opacity(this.rbTmpDataV2.isShowSideBar ? 0.7: 1)    .expandSafeArea([SafeAreaType.SYSTEM])  }  build() {    // 侧边栏容器    SideBarContainer(SideBarContainerType.Overlay) {      // 侧边栏区      this.sidebarView();      // 内容区      this.contentView();    }    .width('100%')    .layoutWeight(1)    .backgroundColor('#1A191E')    // 是否显示侧边    .showSideBar(this.rbTmpDataV2.isShowSideBar)    // 是否显示侧边栏按钮    .showControlButton(false)    // 侧边栏最小宽度    .minSideBarWidth(280)    // 侧边栏拖拽到最小宽度,是否自动隐藏    .autoHide(false)    // 侧边栏切换回调    .onChange((isShow: boolean) => {      this.rbTmpDataV2.isShowSideBar = isShow;    })    // 扩展安全区    .expandSafeArea([SafeAreaType.SYSTEM])  }  // 侧边栏 开/关 时的动画,以及属性变化  private sideBarAnimation(isShow: boolean): void {    this.getUIContext().animateTo({      duration: 200,      curve: Curve.Linear    }, () => {      this.rbTmpDataV2.isShowSideBar = isShow;    })  }}

5.1.4 入口页面

import { AppStorageV2 } from '@kit.ArkUI';import { SideBarRedBookV2 } from '../view/SideBarView';import { RBTmpDataV2 } from '../viewmodel/RBTmpData';@Entry@ComponentV2struct Index {  // 创建 APPStorageV2 存储小红薯全局 UI 的数据中心  @Local rbTmpDataV2: RBTmpDataV2 = AppStorageV2.connect<RBTmpDataV2>(RBTmpDataV2, () => new RBTmpDataV2())!;  // 系统返回操作  // true 代表拦截返回操作,false 代表不拦截返回操作  onBackPress(): boolean | void {    if (this.rbTmpDataV2.isShowSideBar) {      this.getUIContext().animateTo({        duration: 200,        curve: Curve.Linear      }, () => {        this.rbTmpDataV2.isShowSideBar = false;      })    }    else {      return false;    }    return true;  }  build() {    Stack() {      /***       * 3. 应用的侧边栏(小红书案例版)       *    1. V2       */      SideBarRedBookV2();    }    .width('100%')    .height('100%')    .expandSafeArea([SafeAreaType.SYSTEM])  }}

6. 实现效果

实现效果我们可以看到:

1. 点击右上角按钮,侧边栏展开并且覆盖当前页面。

2. 侧边栏展开时,通过你返回键 / 系统滑动返回或者点击侧边栏以外的页面进行关闭,侧边栏不可滑动关闭。

3. 侧边栏展开时,被覆盖的页面可以看出有明显的变暗效果,并且内容区处于不可交互状态,只有侧边栏关闭时,内容区才可进行交互。

4. 侧边栏展开时,无论什么情况,一旦应用进入前台,则会自动关闭侧边栏。

已关注
关注
重播 分享 赞

🎉 恭喜你!完成鸿蒙开发案例中的应用的自定义页签导航

最新文章

随机文章