diff --git a/client/harmony/entry/src/main/ets/common/Surface.ets b/client/harmony/entry/src/main/ets/common/Surface.ets index af141b3..74997fb 100644 --- a/client/harmony/entry/src/main/ets/common/Surface.ets +++ b/client/harmony/entry/src/main/ets/common/Surface.ets @@ -332,11 +332,33 @@ export struct GlassCard { export class GlassCardModifier implements AttributeModifier { /** 壁纸是否开启。关着时不铺材质(退回实体系统色,与原来观感一致)。 */ private active: boolean = false; + /** + * 是否**同时**带按压反馈(默认带)。 + * + * ★★ 2026-09-20 加(用户问过两次:「各个组件带响应点击、滑动的动画了吗」 + * 「基础组件包含动画」)。 + * + * 之前按压反馈是**单独一个 modifier**,要靠调用点自己写 + * `CompositeModifier.of([GlassCardModifier.of(x), PressFeedbackModifier.of()])` + * —— 实测全仓 18 个玻璃卡站点里**只有 1 个**记得加。 + * 这不是"写的人不小心":**两个东西要一起用时,就该是一个东西**。 + * 把按压并进基础卡之后,18 个站点**自动全都有**按压反馈, + * 且不需要在每个调用点改一个字(这正是"基础组件包含动画"的意思)。 + * + * ★ 为什么默认 **true**(而不是"想按才开"): + * WebUI 那一侧是**全局**的 —— `index.css:1169` + * button, a, input, textarea, select, [role='button'] { transition: … } + * "可点就有点击反馈"在两端都该是默认,而不是逐处声明。 + * 静态信息卡(不可点的)自己传 false —— 但那**不会**有声音, + * 因为不可点本来就不该有按压感;这里只是别给它挂无用的 `onTouch`。 + */ + private pressable: boolean = true; - /** 工厂:`GlassCardModifier.of(this.bgActive)` */ - static of(active: boolean): GlassCardModifier { + /** 工厂:`GlassCardModifier.of(this.bgActive)`;静态卡传 `false` */ + static of(active: boolean, pressable: boolean = true): GlassCardModifier { const m = new GlassCardModifier(); m.active = active; + m.pressable = pressable; return m; } @@ -387,6 +409,17 @@ export class GlassCardModifier implements AttributeModifier { /* 壁纸关着时不铺任何材质(退了才不浪费一次模糊) */ instance.backgroundBlurStyle(BlurStyle.NONE); } + /* + * 按压反馈直接内联在这里(而不是让调用点再挂一个 modifier)—— + * 理由见 `pressable` 字段的注释:两个东西要一起用时,就该是一个东西。 + * + * ★ 内联而不是 `CompositeModifier.of([this, PressFeedbackModifier.of()])`: + * `CompositeModifier` 是给"调用点自己有好几个 modifier 要叠"用的; + * 而这里是**基础组件内部要知道的事**,转一层反而多一个间接。 + */ + if (this.pressable) { + PressFeedbackModifier.attach(instance); + } } } @@ -615,11 +648,26 @@ export class PressFeedbackModifier implements AttributeModifier * ★ `Cancel` 必须一起处理:手势被父容器(列表滚动)抢走时不会来 `Up`, * 只处理 `Up` 会让元素**永远停在按下的样子**。 */ + PressFeedbackModifier.attach(instance, this.color); + } + + /** + * 把按压反馈挂到任意组件的**普通态**属性上(可复用)。 + * + * ★ 抽成静态方法是为了让 `GlassCardModifier` 能在**自己的** + * `applyNormalAttribute` 里内联调用它 —— `AttributeModifier` 的 + * `instance` 参数本来就是 `CommonAttribute`,两边类型一致,不需要包一层。 + * + * @param instance 目标组件的属性集合 + * @param color 按压时叠上去的色(默认系统点击效果色) + */ + static attach(instance: CommonAttribute, color?: ResourceColor): void { + const pressColor: ResourceColor = color !== undefined ? color : $r('sys.color.ohos_id_color_click_effect'); instance.onTouch((e: TouchEvent) => { const down: boolean = e.type === TouchType.Down; const cancel: boolean = e.type === TouchType.Cancel; /* 用 `BLANK` 常量而不是 `Color.Transparent` 字面量:避免"透明色"被误当成"没设" */ - const c: ResourceColor = down ? this.color : Color.Transparent; + const c: ResourceColor = down ? pressColor : Color.Transparent; instance.backgroundColor(c); }); } diff --git a/client/harmony/entry/src/main/ets/pages/MainPage.ets b/client/harmony/entry/src/main/ets/pages/MainPage.ets index 1d8c2fc..9e8b6e1 100644 --- a/client/harmony/entry/src/main/ets/pages/MainPage.ets +++ b/client/harmony/entry/src/main/ets/pages/MainPage.ets @@ -755,16 +755,14 @@ struct InboxTab { .padding({ left: 10, right: 12 }) .alignItems(VerticalAlign.Center) /* - * ★★ 两个属性集要**合成一个**再挂(实测撞出来的)。 - * - * `.attributeModifier()` 一个组件只能挂一个,链两个是后者覆盖前者 —— - * 第一版就是链了两句,结果玻璃与按压反馈只生效一个,而编译器不报错。 - * WebUI 那边是 CSS 类名天然叠加;ArkUI 是单一插槽,叠加要显式做。 + * ★★ 2026-09-20 简:这里原来写的是 + * CompositeModifier.of([GlassCardModifier.of(...), PressFeedbackModifier.of()]) + * —— 那是"两个 modifier 要叠"的通用解法,当时**只有这一处**记得叠。 + * 现在按压反馈已经**内联进基础卡**(见 `GlassCardModifier.pressable`), + * 单独再挂一遍会挂两个 `onTouch`(第二个覆盖第一个,行为上等价但白挂)。 + * 这一处是全仓唯一"以前就叠对了"的,现在反而成了唯一的例外写法。 */ - .attributeModifier(CompositeModifier.of([ - GlassCardModifier.of(this.bgActive), - PressFeedbackModifier.of() - ])) + .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) .border({ width: 1, color: Theme.border }) .clip(true)