From 705a65ae2ee1e3aeb2d2d6e2ea77ddeaece5090d Mon Sep 17 00:00:00 2001 From: laoyang Date: Mon, 12 Oct 2026 11:37:45 +0800 Subject: [PATCH] =?UTF-8?q?v0.5.0=20=E6=8A=BD=E5=B1=89=E6=94=B9=E3=80=8C?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E5=86=85=E5=B7=A6=E5=8F=B3=E6=BB=91=E5=8A=A8?= =?UTF-8?q?=E3=80=8D=E8=A7=A6=E5=8F=91=20+=20=E5=8E=BB=E6=8E=89=E6=B1=89?= =?UTF-8?q?=E5=A0=A1=E6=8C=89=E9=92=AE=20+=20=E7=BB=9F=E4=B8=80=E5=8C=BA?= =?UTF-8?q?=E5=9D=97=E8=BE=B9=E8=B7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户 2026-10-12 三条反馈。 ## ① 抽屉改「应用内水平滑动」触发(关键更正) 用户实测指出:「左滑右滑不是从边上滑,而是在应用的内部从左往右滑或者从右往左滑。 如果从边上往里边滑的话,它是系统手势,我们是没有办法去控制它的。」 ⇒ **这是之前手势完全没生效的根因**:屏幕边缘是系统手势区(返回/侧边栏), 从边缘往里滑的事件根本轮不到应用。判据改为「在应用内部水平滑动」: · 向右滑 ⇒ 抽屉从左边出;· 向左滑 ⇒ 抽屉从右边出。 (左手持机往右划、右手持机往左划,单手都顺。) 🔴 同时修掉一个真 bug:**`offsetX` 的单位是 vp**(SDK 原文 "in vp"), 我之前又除以 density,把位移缩到 1/3 ⇒ 阈值永远达不到。 ⚠️ 用 `parallelGesture`(不是 gesture):内容区还有竖向滚动与 纪念日/财务/任务的横向滚动条,用 gesture 会把它们抢掉。 阈值 80vp,轻扫横向列表不会误开。 ## ② 去掉顶栏汉堡按钮 用户:「那个三个点、三个横线的按钮就不要了,图标靠左,显得更好看一些」。 ⇒ TopBar 删掉按钮,`padding.left` 由 14 改为 `pagePad`,图标与下方内容左边线对齐。 ## ③ 🔴 统一区块边距(修掉 15 处真 bug) 用户:「页面里的方框大小不一样,有的左右充满,有的有缝隙 —— 最好都留一点距离」。 实测界面树 + 像素采样找到根因:**卡片同时写了 `.width('100%')` 和 `.margin({left, right})` ⇒ width 先把宽度占满、margin 失效,卡片顶到屏幕两侧**。 (首页个人信息卡实测 x=0..1080 满宽,而别的区块是留边 17vp 的。) 全项目扫出 **15 处**同款写法(calendar 3 / days 2 / ledger 2 / movies 1 / people 2 / settings 5),**全是卡片**(backgroundColor+borderRadius+margin)。 统一删掉那条 `.width('100%')`,宽度交给 margin 撑出。 ### 验证(真机) · 手势:向右滑 ⇒ 抽屉「家书」x=187(贴左);向左滑 ⇒ x=442(贴右)—— 双向 ✓ · 边距:像素级逐行采样,17 行里 15 行左右均 50px(17vp)完全一致; 另 2 行是"标题文字起点"(文字有额外缩进),不是边距问题 ✓ · 14 页遍历 **14/14**;深验(每页特征文本)**12/12** · 编译 **0 error / 0 warning** ### 工具链修复(都很关键) · 🔴 `hdc file recv` 在 **Git Bash** 下会被路径转换改写:`/data/local/tmp/x` 被当成 `C:/.../tmp/x` ⇒ **静默失败、界面树永远抓不到**。 三个验证脚本已统一注入 `MSYS_NO_PATHCONV=1`。 · 探针不再点汉堡按钮开抽屉(按钮已删),改用「应用内向右滑」。 · 修探针一处误判:顶栏与抽屉**都有**「家书」,原来没区分 ⇒ 两种滑动都读到顶栏那个固定 x,误报"手势没生效"(其实生效了)。 --- entry/src/main/ets/common/Store.ets | 9 +++ entry/src/main/ets/components/Drawer.ets | 58 ++++++++++++++++--- entry/src/main/ets/components/TopBar.ets | 32 +++------- .../ets/pages/calendar/FbCalendarPage.ets | 3 - entry/src/main/ets/pages/days/FbDaysPage.ets | 2 - entry/src/main/ets/pages/home/FbHomePage.ets | 7 ++- .../main/ets/pages/ledger/FbLedgerPage.ets | 2 - .../main/ets/pages/movies/FbMoviesPage.ets | 1 - .../main/ets/pages/people/FbPeoplePage.ets | 2 - .../ets/pages/settings/FbSettingsPage.ets | 5 -- 10 files changed, 73 insertions(+), 48 deletions(-) diff --git a/entry/src/main/ets/common/Store.ets b/entry/src/main/ets/common/Store.ets index 3939c18..df9589c 100644 --- a/entry/src/main/ets/common/Store.ets +++ b/entry/src/main/ets/common/Store.ets @@ -24,6 +24,15 @@ export class FbAppState { /** 抽屉是否展开(手机端滑出层;宽屏端恒为常驻,不看这个值) */ @Trace drawerOpen: boolean = false; + /** + * 抽屉从哪一侧滑出:'left' | 'right'。 + * + * 🔴 用户要求「从左边缘向右滑 → 左边出抽屉;从右边缘向左滑 → 右边出抽屉」—— + * 滑动方向决定抽屉在哪边(用户 2026-10-12 选的是这套,不用握姿传感器)。 + * 这样左手握持时用左边缘、右手握持时用右边缘,单手持机都顺。 + */ + @Trace drawerSide: string = 'left'; + /** 当前所在模块 key(home / people / movies …) */ @Trace activeModule: string = 'home'; diff --git a/entry/src/main/ets/components/Drawer.ets b/entry/src/main/ets/components/Drawer.ets index a831111..2b21844 100644 --- a/entry/src/main/ets/components/Drawer.ets +++ b/entry/src/main/ets/components/Drawer.ets @@ -1,13 +1,14 @@ /** - * 家书 FamilyBook · 左侧抽屉(鸿蒙端) + * 家书 FamilyBook · 抽屉(鸿蒙端) * * 形态对齐 Web 端顶栏 `.nav` 那一排模块入口,但**改成抽屉**(用户建议): * Web 端导航条最多放 12 个模块,手机宽度下必然溢出;抽屉竖向排列, * 还能顺带放下「模块说明 + 主题色」这层信息,一眼看清每个模块是干什么的。 * - * 两条形态,同一个组件: + * 三条形态,同一个组件: * · **常驻侧栏**(宽屏:窗口短边 ≥ 600vp)—— 固定在左边,占 264vp; - * · **滑出层**(手机竖屏)—— 覆盖在内容上,点遮罩或选中项即收起。 + * · **左滑出层**(手机,从左边缘向右滑)—— 覆盖内容,点遮罩或选中项即收起; + * · **右滑出层**(手机,从右边缘向左滑)—— 同上,只是贴右边(右手单手持机顺手)。 * * ⚠️ 这里**不用 SideBarContainer**:它的控制按钮与「滑出」是内建的一套, * 样式(图标、位置、宽度动画)很难与家书的视觉统一,且它只能有一层侧栏 @@ -205,7 +206,10 @@ export struct FbDrawerShell { @BuilderParam content: () => void; build() { - Stack({ alignContent: Alignment.TopStart }) { + // 抽屉贴哪边由 drawerSide 决定 ⇒ Stack 的 alignment 也跟着换。 + Stack({ + alignContent: this.app.drawerSide === 'right' ? Alignment.TopEnd : Alignment.TopStart + }) { // ---- 宽屏:并排 ---- if (this.app.wideLayout) { Row() { @@ -222,7 +226,7 @@ export struct FbDrawerShell { .width('100%') .height('100%') } else { - // ---- 窄屏:内容铺满 + 抽屉浮层 ---- + // ---- 窄屏:内容铺满 + 抽屉浮层(可从左或右滑出)---- Column() { this.content() } @@ -240,6 +244,9 @@ export struct FbDrawerShell { }) .transition(TransitionEffect.OPACITY.animation({ duration: 180 })) + // 抽屉本体:由 drawerSide 决定贴左还是贴右 + // ⚠️ 用 Stack 的 alignment 而不是 margin 来定位: + // 动画是 translateX,用 alignment 铺位更稳(不参与测量)。 FbDrawer({ app: this.app, overlayMode: true, @@ -247,13 +254,50 @@ export struct FbDrawerShell { this.app.drawerOpen = false; } }) - .transition(TransitionEffect.translate({ x: -FbMetric.drawerW }) - .animation({ duration: 220, curve: Curve.FastOutSlowIn })) + .transition(TransitionEffect.translate({ + x: this.app.drawerSide === 'right' ? FbMetric.drawerW : -FbMetric.drawerW + }).animation({ duration: 220, curve: Curve.FastOutSlowIn })) } } } .width('100%') .height('100%') .backgroundColor(FbColors.paper) + // 🔴 横向滑动开抽屉(用户 2026-10-12 定的规则)。 + // + // ⚠️ **不要判断"起点是否贴屏幕边缘"** —— 用户实测反馈:屏幕边缘是 + // **系统手势区**(返回/侧边栏),从边缘往里滑的事件根本轮不到应用处理, + // 我们无法控制。所以判据只能是"**在应用内部水平滑动**": + // 向右滑 → 抽屉从左边出;向左滑 → 抽屉从右边出。 + // 这样左手持机时往右划、右手持机时往左划,单手都顺。 + // + // ⚠️ `offsetX` 的单位是 **vp**(SDK 原文 "in vp"),不要再除以 density —— + // 除一次会把位移缩到 1/3,导致阈值永远达不到、手势"看起来没生效"。 + // + // ⚠️ 用 `parallelGesture` 而不是 `gesture`:内容区自己还有竖向滚动 + // (以及纪念日/财务/任务的横向滚动条),用 gesture 会把它们抢掉。 + // 代价是横向滚动条扫得猛时也可能触发 —— 所以阈值给到 80vp, + // 轻扫(浏览横向列表)不会误开。 + .parallelGesture( + PanGesture({ direction: PanDirection.Horizontal, distance: 30 }) + .onActionEnd((e: GestureEvent) => { + // 宽屏下抽屉常驻,不需要手势 + if (this.app.wideLayout) { + return; + } + const dx: number = e.offsetX; // vp + // 向右滑 ⇒ 抽屉在左 + if (dx > 80) { + this.app.drawerSide = 'left'; + this.app.drawerOpen = true; + return; + } + // 向左滑 ⇒ 抽屉在右 + if (dx < -80) { + this.app.drawerSide = 'right'; + this.app.drawerOpen = true; + } + }) + ) } } \ No newline at end of file diff --git a/entry/src/main/ets/components/TopBar.ets b/entry/src/main/ets/components/TopBar.ets index b009729..5050073 100644 --- a/entry/src/main/ets/components/TopBar.ets +++ b/entry/src/main/ets/components/TopBar.ets @@ -7,10 +7,11 @@ * · 顶栏**并入页面滚动流**,向上滑动时跟着内容一起滑走 * (所以它本身不固定、也不自己处理状态栏留白)。 * - * 🔴 与状态栏的关系:顶栏是白色,而系统栏被涂成全透明、透出的是**根容器底色**。 - * 所以要让"通知栏与顶栏融为一体",根容器在顶栏这一段必须是白色 —— - * 由 `Index.ets` 在顶部铺一层与顶栏同高的白色块来实现(页面滚动时它不动, - * 保证通知栏区域永远是白的,不会滚出内容)。 + * 🔴 **顶栏不放汉堡按钮**(用户 2026-10-12 明确要求去掉): + * 开抽屉改由**屏幕边缘滑动**触发(见 `Drawer.ets` 的 parallelGesture)—— + * 从左边缘向右滑 ⇒ 抽屉在左;从右边缘向左滑 ⇒ 抽屉在右。 + * ⇒ 图标能一直贴到最左边,视觉上更好看。 + * ⚠️ 别再加回汉堡按钮;抽屉的其它入口是「顶栏头像」与「抽屉底部账号区」。 * * ⚠️ 顶栏**不放**「设置」—— 与 Web 端一致:设置收在抽屉底部的账号区里。 */ @@ -28,25 +29,7 @@ export struct FbTopBar { build() { Row({ space: 10 }) { - // ---- 左:窄屏显示抽屉按钮 ---- - if (!this.app.wideLayout) { - Button({ type: ButtonType.Circle }) { - Image(FbIcon.ui('menu')) - .width(18) - .height(18) - .renderMode(ImageRenderMode.Template) - .fillColor(FbColors.ink) - } - .width(36) - .height(36) - .backgroundColor(Color.Transparent) - .stateEffect(true) - .onClick(() => { - this.app.drawerOpen = true; - }) - } - - // ---- 应用图标 ---- + // ---- 应用图标(贴最左:抽屉改由边缘滑动触发,这里不再有汉堡按钮)---- // ⚠️ 必须给描边:logo 是「米色底(74%) + 砖红图案(24%)」的实心方图, // 放在白色/米色上时底板与背景几乎同色,只剩砖红图案发飘、看不清。 // 一层极浅的描边就能把底板"托"出来(本机范本 SyncCalendar 也给了描边)。 @@ -114,7 +97,8 @@ export struct FbTopBar { } .width('100%') .height(FbMetric.topBarH) - .padding({ left: 14, right: 10, top: 4, bottom: 4 }) + // 左内边距=pagePad(与下面各区块的左边线对齐);右侧 10(图标可略贴边) + .padding({ left: FbMetric.pagePad, right: 10, top: 4, bottom: 4 }) .alignItems(VerticalAlign.Center) // 顶栏底色 = 白色(与下面的个人信息卡同色) .backgroundColor(FbColors.surface) diff --git a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets index c6d7bf6..f86b53a 100644 --- a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets +++ b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets @@ -147,7 +147,6 @@ export struct FbCalendarPage { .height(this.gridHeight()) .scrollBar(BarState.Off) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -205,7 +204,6 @@ export struct FbCalendarPage { .width('100%') .padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, bottom: 6 }) } - .width('100%') .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) .margin({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12 }) @@ -233,7 +231,6 @@ export struct FbCalendarPage { .width('100%') .padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, bottom: 6 }) } - .width('100%') .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) .margin({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12, bottom: 28 }) diff --git a/entry/src/main/ets/pages/days/FbDaysPage.ets b/entry/src/main/ets/pages/days/FbDaysPage.ets index 08a97ba..d2efdb3 100644 --- a/entry/src/main/ets/pages/days/FbDaysPage.ets +++ b/entry/src/main/ets/pages/days/FbDaysPage.ets @@ -98,7 +98,6 @@ export struct FbDaysPage { .width('100%') } } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -127,7 +126,6 @@ export struct FbDaysPage { this.annRow(a, idx === this.filtered().length - 1) }, (a: FbAnniversaryItem): string => 'ann_' + a.id.toString()) } - .width('100%') .padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, top: 4, bottom: 8 }) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index a510839..0200175 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -171,9 +171,12 @@ export struct FbHomePage { cAlarm: FB_DEMO_WEEK_STAT.overdue > 0 }) } - .width('100%') + // 🔴 不要 `.width('100%')` —— 它会先把宽度占满,导致后面的 margin 失效, + // 卡片于是**顶到屏幕两侧边缘**(实测 x=0..1080),而别的区块是留边 16vp 的, + // 页面里就出现"有的满宽、有的有缝"的不统一(用户 2026-10-12 的反馈)。 + // 正确做法:**只给 margin**,宽度由 margin 自动撑出(左右各 16vp)。 .padding(FbMetric.cardPad) - // 白色卡片(用户要求):与下面「今天的日程」等块同一种卡片语言 + // 白色卡片:与下面「今天的日程」等块同一种卡片语言 .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) .margin({ diff --git a/entry/src/main/ets/pages/ledger/FbLedgerPage.ets b/entry/src/main/ets/pages/ledger/FbLedgerPage.ets index 8c73e97..6a456e8 100644 --- a/entry/src/main/ets/pages/ledger/FbLedgerPage.ets +++ b/entry/src/main/ets/pages/ledger/FbLedgerPage.ets @@ -168,7 +168,6 @@ export struct FbLedgerPage { .width('100%') .alignItems(HorizontalAlign.Start) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -219,7 +218,6 @@ export struct FbLedgerPage { } .width('100%') } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) diff --git a/entry/src/main/ets/pages/movies/FbMoviesPage.ets b/entry/src/main/ets/pages/movies/FbMoviesPage.ets index fa6111d..e3c0964 100644 --- a/entry/src/main/ets/pages/movies/FbMoviesPage.ets +++ b/entry/src/main/ets/pages/movies/FbMoviesPage.ets @@ -77,7 +77,6 @@ export struct FbMoviesPage { .layoutWeight(1) .maxLines(1) } - .width('100%') .height(36) .padding({ left: 12, right: 12 }) .backgroundColor(FbColors.inputBg) diff --git a/entry/src/main/ets/pages/people/FbPeoplePage.ets b/entry/src/main/ets/pages/people/FbPeoplePage.ets index 4be7f11..0e37553 100644 --- a/entry/src/main/ets/pages/people/FbPeoplePage.ets +++ b/entry/src/main/ets/pages/people/FbPeoplePage.ets @@ -293,7 +293,6 @@ export struct FbPeoplePage { } .width('100%') } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -361,7 +360,6 @@ export struct FbPeoplePage { .width('100%') } } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) diff --git a/entry/src/main/ets/pages/settings/FbSettingsPage.ets b/entry/src/main/ets/pages/settings/FbSettingsPage.ets index 499f86b..b9caadf 100644 --- a/entry/src/main/ets/pages/settings/FbSettingsPage.ets +++ b/entry/src/main/ets/pages/settings/FbSettingsPage.ets @@ -69,7 +69,6 @@ export struct FbSettingsPage { .renderMode(ImageRenderMode.Template) .fillColor(FbColors.faint) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -82,7 +81,6 @@ export struct FbSettingsPage { this.switchRow('到期提醒', '日程开始前 10 分钟提醒', true) this.switchRow('逾期提醒', '任务逾期时提醒我', true) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -113,7 +111,6 @@ export struct FbSettingsPage { this.moduleToggleRow(m) }, (m: FbModuleDef): string => 'set_' + m.key) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -137,7 +134,6 @@ export struct FbSettingsPage { this.colorRow(m) }, (m: FbModuleDef): string => 'col_' + m.key) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -151,7 +147,6 @@ export struct FbSettingsPage { this.plainRow('数据来源', '待接入 · 当前为演示数据') this.plainRow('服务器', '未配置') } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius)