我们在开发时,会有突发奇想或者看到好的设计,希望能够模仿,但是会有处于毫无头绪的时候,不知道该如何下手,这就相当于一个考试,给你一个题目叫你作答,但不同于校园考试,他并不是在给定你范围并且复习的情况下进行作答。它是未知的,并不是顺藤摸瓜,总会遇到你所不清楚的内容,我希望能够通过案例顺瓜摸藤进行学习,通过“瓜”来推出是什么“藤”。
这些案例都是现有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. 仔细观察会发现,不一定点击按钮才展开侧边栏,实际点击效果区域会比按钮大小要大。
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-sidebarcontainer
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-enable
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-opacity
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-appstoragev2
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
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-promptaction#promptactionopentoast18
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-expand-safe-area#expandsafearea
// 小红薯数据中心(UI状态存储)@ObservedV2export class RBTmpDataV2 {// 监听侧边栏是否显示@Trace isShowSideBar: boolean = false;}
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;})}}}
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())!;// 侧边栏区@BuildersidebarView() {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])}// 内容区@BuildercontentView() {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;})}}
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])}}
🎉 恭喜你!完成鸿蒙开发案例中的应用的自定义页签导航