资讯动态

长按菜单关了,蓝色选区为什么还在:HarmonyOS 7 SelectionContainerController 状态收口实战

发布时间:2026/10/19 2:09:39 来源:尧图企业网站定制
长按菜单关了蓝色选区为什么还在HarmonyOS 7 SelectionContainerController 状态收口实战在菜谱详情里跨段选中“腌制 15 分钟”“中火翻炒”和“出锅前加盐”长按菜单弹出来以后用户又打开“加入购物清单”的半模态。菜单确实消失了蓝色选区却留在正文上返回列表再进另一道菜旧选区甚至还像是上一页遗留下来的状态。这不是一个“把弹窗关掉”就能解决的问题。HarmonyOS 7 对应的 API 26 为 SelectionContainer 增加 SelectionContainerControllercloseSelectionMenu() 只关闭选择菜单并保留选区clearTextSelection() 会清空选区并在菜单存在时一并关闭。两种动作的差别决定了临时遮挡、路由切换和内容换代时该不该保留用户上下文。本文交付一套可以迁入“中式美食”的选择会话模型用内容 revision 约束选区寿命用控制器区分“暂时收起菜单”和“彻底结束选择”再用 onTextSelectionChange、onWillCopy 与 onCopy 对齐 UI、业务和日志。当前机器只有 API 24 SDK且没有连接 HDC 设备下面的 API 26 接入代码依据当前官方文档整理本文实际运行的宿主断言只验证状态模型不声称完成了 API 26 编译、真机长按或系统剪贴板验证。先把现象拆成三个独立状态很多页面只维护一个 isSelecting 布尔值菜单、选区和业务结果全靠它推断。问题是这三者并不总是同步状态用户能看到什么页面需要保存什么选区蓝色高亮与拖拽手柄当前内容 revision、选中文本片段选择菜单复制、收藏、加入清单等操作菜单是否需要暂时让位业务动作半模态、路由、筛选或内容刷新动作完成后保留还是结束选择打开半模态只是让菜单暂时让位用户回来还可能继续操作原选区切换菜谱则意味着内容身份已经变化旧选区必须失效。把两者都处理成“关闭菜单”会留下幽灵选区都处理成“清空选择”又会让临时操作丢失上下文。API 26 的控制器到底控制了什么官方 API 参考把 SelectionContainerController 的两个方法分得很清楚closeSelectionMenu()关闭自定义或默认选择菜单文本选区仍然存在。clearTextSelection()清除 SelectionContainer 内的文本选中状态若菜单正显示也会一起关闭。SelectionContainer 本身负责跨节点文本选择、复制和菜单扩展。它只支持 Stage 模型默认按 Stack 布局需要其他排版时应在内部再放 Column、Row 等容器。Repeat 生成的 Text 不支持跨节点选中参与选择的也只有 Text 节点这些边界会直接影响页面结构。EntryComponentV2struct RecipeSelectionPage{privateselectionController:SelectionContainerControllernewSelectionContainerController();LocalprivateselectedParts:string[][];LocalprivatecontentRevision:number1;build(){Column({space:12}){SelectionContainer({controller:this.selectionController}){Column({space:10}){Text(腌制 15 分钟让调味先进入肉片。)Text(锅热后下油中火翻炒至边缘变色。)Text(出锅前加盐避免肉片提前失水。)}.alignItems(HorizontalAlign.Start)}.textJoinStyle(TextJoinStyle.NEWLINE).onTextSelectionChange((parts:string[]){this.selectedParts[...parts];})Row({space:10}){Button(加入清单).onClick(()this.openIngredientSheet())Button(下一道菜).onClick(()this.switchRecipe())}}.padding(16)}privateopenIngredientSheet():void{this.selectionController.closeSelectionMenu();// 再打开业务半模态选区保留返回后仍能继续操作。}privateswitchRecipe():void{this.contentRevision;this.selectedParts[];this.selectionController.clearTextSelection();// 随后替换菜谱内容。}}这里没有用 controller 推断“当前是否选中”。控制器只执行 UI 动作业务仍通过 onTextSelectionChange 保存可观察结果这样日志、按钮可用性和异步任务都有明确的数据来源。文本拼接策略要在复制前确定onTextSelectionChange 返回按视觉顺序排列的选中文本数组只包含真正参与选择的 Text。最终复制内容如何拼接由 textJoinStyle 决定NEWLINE不同 Text 之间插入换行适合步骤、配料和说明段落。DIRECT直接连接适合被拆成多个样式节点但语义上仍是一句话的标题或单位。这不只是显示偏好。onCopy 接收到的是按该策略拼接后的结果自定义菜单和系统菜单也会受它影响。若页面把“200”“克”“牛肉”拆成三个 Text却使用 NEWLINE复制结果可能变成三行如果把三个步骤用 DIRECT 连接文本又会挤成一段。functionjoinSelectedParts(parts:string[],style:newline|direct):string{returnparts.join(stylenewline?\n:);}conststepTextjoinSelectedParts([焯水 30 秒,沥干备用,大火快炒],newline);// 焯水 30 秒\n沥干备用\n大火快炒如果需要对复制内容做业务门禁可以使用 onWillCopy。返回 false 时容器层面的复制和 onCopy 都会被阻止。不要在 onCopy 才发现敏感字段然后“假装没复制”因为复制动作已经进入了后续链路。.onWillCopy((value:string){return!value.includes(内部备注);}).onCopy((value:string){this.recordCopyMetric({revision:this.contentRevision,length:value.length});})日志只记录 revision、长度和动作结果不记录完整菜谱文本或用户剪贴板内容。案例一打开“加入购物清单”半模态菜单让位但选区保留用户跨三个步骤选中一段文本点击自定义菜单里的“提取配料”。页面要打开半模态展示识别结果。如果直接 clearTextSelection()半模态关闭后用户无法继续调整原选区如果什么都不做选择菜单会浮在半模态上方或与遮罩竞争焦点。正确策略是在打开半模态前调用 closeSelectionMenu()保留 selection snapshot半模态关闭后先确认内容 revision 没变再决定是否继续展示业务操作入口。interfaceSelectionSnapshot{revision:number;parts:string[];joinedText:string;}classSelectionSessionCoordinator{privaterevision:number1;privateparts:string[][];updateSelection(parts:string[]):void{this.parts[...parts];}beginTransientAction():SelectionSnapshot{return{revision:this.revision,parts:[...this.parts],joinedText:this.parts.join(\n)};}canRestore(snapshot:SelectionSnapshot):boolean{returnsnapshot.revisionthis.revisionthis.parts.length0;}advanceContent():void{this.revision;this.parts[];}}页面调用顺序如下privateasyncopenIngredientSheet():Promisevoid{constsnapshotthis.session.beginTransientAction();this.selectionController.closeSelectionMenu();constresultawaitthis.extractIngredients(snapshot.joinedText);if(!this.session.canRestore(snapshot)){return;}this.sheetDataresult;this.showIngredientSheettrue;}这个案例的重点不是“记住字符串”而是把字符串和当时的内容版本绑定。等待识别结果期间如果用户切换了菜谱旧结果不能覆盖新页面同一菜谱内临时打开半模态则可以保留选区上下文。验收顺序跨三个 Text 节点拖出选区并打开选择菜单。触发“加入清单”确认菜单关闭、蓝色选区仍在。关闭半模态确认页面仍可基于同一选区继续操作。在识别请求未返回时切换菜谱确认旧结果被丢弃旧选区不再出现。案例二切换菜谱或刷新正文必须同时清理选区与异步结果“下一道菜”“切换语言”“仅看关键步骤”都会改变 SelectionContainer 内的 Text 集合。此时保留旧选区没有业务意义甚至会让按钮仍然显示“已选 3 段”。这类内容身份变化应走一个统一入口先推进 revision再清理业务快照最后调用 clearTextSelection()。privateapplyRecipe(next:RecipeDetail):void{this.session.advanceContent();this.selectedParts[];this.selectionController.clearTextSelection();this.recipenext;}privateapplyDisplayFilter(filter:StepFilter):void{constchangesTextIdentityfilter!this.currentFilter;if(changesTextIdentity){this.session.advanceContent();this.selectedParts[];this.selectionController.clearTextSelection();}this.currentFilterfilter;}为什么先改 revision 再替换内容因为异步回调可能恰好在这一帧返回。revision 先推进旧回调就会被 canRestore 拒绝如果先替换正文、下一帧再清理旧结果存在短暂写回新页面的窗口。路由返回和临时遮挡不能共用同一策略触发动作文本身份是否变化控制器动作业务快照打开半模态否closeSelectionMenu保留打开普通提示框否closeSelectionMenu保留切换菜谱 ID是clearTextSelection清空并推进 revision切换语言或正文版本是clearTextSelection清空并推进 revision页面销毁是clearTextSelection清空并取消异步任务这张表可以直接放进页面设计评审。只问“要不要关菜单”是不够的必须先问文本身份是否还相同。自定义菜单的两个优先级陷阱SelectionContainer 可以通过 bindSelectionMenu 绑定自定义选择菜单也可以通过 editMenuOptions 定制系统菜单行为。官方说明里有两个容易踩坑的优先级同时设置 bindSelectionMenu 和 editMenuOptions 时editMenuOptions 不生效。应按定制深度二选一。子 Text 自己绑定的 selection menu 会被 SelectionContainer 的配置覆盖跨节点选择应由容器统一负责。此外SelectionContainer 的长按选择菜单触发时长约为 600ms而 bindContextMenu 的长按约为 800ms。二者都使用长按时选择菜单优先。若菜谱卡还需要长按弹出“收藏/分享”上下文菜单应避免把两个入口放在同一文本区域或把上下文操作移到明确按钮。SelectionContainer({controller:this.selectionController}){Column(){Text(this.recipe.title)Text(this.recipe.stepsText)}}.bindSelectionMenu(TextSpanType.TEXT,this.SelectionMenuBuilder,TextResponseType.LONG_PRESS,{onDisappear:()this.onSelectionMenuClosed()})自定义菜单内容过长时应放进 Scroll不要让菜单超过可用区域。菜单按钮也不要直接持有 Text 节点引用业务动作只接收 selection snapshot这样容器重建以后仍能判断结果是否过期。宿主侧状态模型六组断言先锁住生命周期当前环境无法编译 API 26 的 SelectionContainerController但“菜单关闭是否保留选区”“内容换代是否拒绝旧结果”属于纯状态规则可以先用 Node.js 断言验证typeSelectionEvent|{type:selection;parts:string[]}|{type:menu-close}|{type:clear}|{type:content-change}|{type:async-result;revision:number;value:string};interfaceSelectionState{revision:number;parts:string[];menuVisible:boolean;acceptedResult?:string;}functionreduceSelectionState(state:SelectionState,event:SelectionEvent):SelectionState{if(event.typeselection){return{...state,parts:[...event.parts],menuVisible:event.parts.length0};}if(event.typemenu-close){return{...state,menuVisible:false};}if(event.typeclear){return{...state,parts:[],menuVisible:false};}if(event.typecontent-change){return{revision:state.revision1,parts:[],menuVisible:false};}if(event.revision!state.revision)returnstate;return{...state,acceptedResult:event.value};}最小断言应覆盖menu-close 后 parts 不变。clear 后 parts 为空且 menuVisible 为 false。content-change 会推进 revision 并清空选区。临时半模态在同一 revision 下可继续使用 snapshot。旧 revision 的异步结果不得写回。NEWLINE 与 DIRECT 的拼接结果符合预期。宿主测试通过不等于 SelectionContainer 在设备上已经通过。目标环境还要补充长按阈值、拖拽手柄、跨节点视觉顺序、自定义菜单焦点、系统返回键和剪贴板权限边界。接入步骤把选择会话当成有寿命的数据确认正文使用普通 Text 节点避免把需要跨节点选择的内容全部放进不支持的 Repeat 结构。创建唯一的 SelectionContainerController并与对应容器生命周期绑定。用 onTextSelectionChange 保存视觉顺序的文本片段不用节点索引推断结果。根据语义选择 textJoinStyle步骤用 NEWLINE拆分样式的一句话才用 DIRECT。临时遮挡前调用 closeSelectionMenu()保留 revision 与 snapshot。内容 ID、语言、版本或过滤结果改变时先推进 revision再调用 clearTextSelection()。异步结果提交前比较 revision旧结果直接丢弃。复制前用 onWillCopy 做门禁onCopy 只记录非敏感指标。自定义菜单和系统菜单二选一验证长按与上下文菜单的优先级。在目标 API 26 SDK 与真机上补齐跨节点拖拽、半模态、路由和系统返回验收。文件级检查表页面只有一个 SelectionContainerController 实例负责当前容器。closeSelectionMenu 仅用于临时让位不被当成清空选区。clearTextSelection 用于内容身份变化和页面销毁。onTextSelectionChange 的数组按视觉顺序保存并与 revision 绑定。textJoinStyle 与正文语义一致复制结果经过示例验证。onWillCopy 在复制前阻断不允许的文本。异步任务提交结果前检查内容 revision。bindSelectionMenu 与 editMenuOptions 没有同时配置。子 Text 没有维护另一套选择菜单状态。Repeat、Stage 模型、设备形态和长按优先级边界已记录。日志不包含完整选中文本、剪贴板内容或用户隐私。测试报告区分宿主断言、API 26 编译、预览器与真机结果。证据边界与官方资料本文依据当前华为官方 SelectionContainer API 参考与 HarmonyOS 26.0.0 Release 的 ArkUI API 变更清单整理。官方资料可以证明组件、控制器方法、回调、拼接策略、菜单优先级和支持版本文中的宿主断言只能证明本文状态模型。当前环境没有完成 API 26 编译、SelectionContainer 的真实长按、拖拽手柄、自定义菜单焦点、系统复制或多设备验证接入项目后必须在目标 SDK 和设备上补齐。SelectionContainer API 参考HarmonyOS 26.0.0 Release ArkUI API 变更清单HarmonyOS 26.0.0 新特性说明Text API 参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑