鸿蒙 HarmonyOS 6 | TextPickerDialog 迁移实战

张开发
2026/4/21 13:38:23 15 分钟阅读

分享文章

鸿蒙 HarmonyOS 6 | TextPickerDialog 迁移实战
文章目录前言一、这次迁移先要改掉全局调用思路二、UIContext 在哪里拿取决于代码站在哪一层三、跨模块和异步调用别再用单例硬兜四、兼容层可以留但别把旧接口继续当主路径总结前言很多老项目升到鸿蒙 6 API 20 之后会先看到一条很扎眼的告警TextPickerDialog.show已经进入废弃路径。华为开发者文档给出的口径很明确这个接口从 API version 8 开始支持从 API version 18 开始废弃推荐改用showTextPickerDialog调用前要先拿到UIContext。这次迁移表面上看只是一个弹窗接口换写法真正牵动的是 UI 调用方式。Stage 模型下UI 实例和窗口是关联的UIContext也跟具体窗口绑定。文档里对这一层关系写得很直接WindowStage 或 Window 通过loadContent加载页面并创建 UI 实例页面内容渲染到关联窗口上所以 UI 实例和窗口是一一关联的。一、这次迁移先要改掉全局调用思路TextPickerDialog.show以前用起来很顺手传入选项、默认值和回调弹窗就出来了。问题在复杂场景里会开始冒头。窗口变多之后系统需要知道这个弹窗该挂到哪一个窗口、哪一个 UI 实例上。UIContext.showTextPickerDialog解决的就是这件事它要求开发者把上下文交清楚系统按这个上下文去显示弹窗。所以这轮迁移里最先要丢掉的习惯就是把文本选择弹窗当成一个随手能调的全局能力。项目里只要还在沿着这个思路写异步回调、跨页面调用、多窗口场景都会越来越难控。把显示动作收回到UIContext上之后弹窗归属、渲染位置和后续排查都会清楚很多。二、UIContext在哪里拿取决于代码站在哪一层如果代码就在自定义组件内部最直接的方式就是this.getUIContext()。自定义组件内置方法文档已经把这个能力列出来了getUIContext会返回当前组件对应的UIContext实例。这种场景下迁移代码可以直接写成这样EntryComponentstruct PickerDemo{StateselectedText:string未选择privateshowPicker(){constuiContextthis.getUIContext()uiContext.showTextPickerDialog({range:[选项一,选项二,选项三],selected:0,onAccept:(value:string,index:number){this.selectedText${value}-${index}}})}build(){Column({space:12}){Text(this.selectedText)Button(打开选择器).onClick(()this.showPicker())}.padding(16)}}如果代码在UIAbility或窗口初始化流程里就不要硬从组件层去找上下文。更稳的写法是在windowStage.loadContent(...)完成后再从主窗口拿UIContext。文档里对这一点说得很明确getUIContext需要在windowStage.loadContent之后调用这样UIContext才已经初始化完成调用过早返回结果可能不准确。对应代码可以这样写import{UIAbility}fromkit.AbilityKitimport{window}fromkit.ArkUIexportdefaultclassEntryAbilityextendsUIAbility{privateappUIContext?:UIContextonWindowStageCreate(windowStage:window.WindowStage):void{windowStage.loadContent(pages/Index,(err){if(errerr.code){console.error(loadContent failed:${JSON.stringify(err)})return}constmainWindowwindowStage.getMainWindowSync()this.appUIContextmainWindow.getUIContext()this.appUIContext.showTextPickerDialog({range:[北京,上海,深圳],selected:0,onAccept:(value:string,index:number){console.info(accept value${value}, index${index})}})})}}这两种拿法已经覆盖了绝大多数业务代码。一个在组件里就近拿一个在窗口真正就绪后从主窗口拿。三、跨模块和异步调用别再用单例硬兜之前有一个全局服务容器去保存UIContextService。这种写法在单窗口项目里短期能跑放到多窗口应用里风险会明显变大。因为UIContext本身是和具体窗口绑定的窗口一多全局只存一个上下文很容易把弹窗弹到错误窗口或者后来的窗口把前面的上下文覆盖掉。所以跨模块适配更稳的做法是按窗口持有自己的弹窗服务或者把UIContext显式传给业务模块。这样写虽然多传一个参数边界是清楚的后面排查问题也不会绕。先看一个窗口级的服务封装classTextPickerDialogService{constructor(privatereadonlyuiContext:UIContext){}show(options:TextPickerDialogOptions|TextPickerDialogOptionsExt):void{this.uiContext.showTextPickerDialog(options)}}然后在窗口初始化完成后创建它import{UIAbility}fromkit.AbilityKitimport{window}fromkit.ArkUIexportdefaultclassEntryAbilityextendsUIAbility{privatedialogService?:TextPickerDialogServiceonWindowStageCreate(windowStage:window.WindowStage):void{windowStage.loadContent(pages/Index,(err){if(errerr.code){console.error(loadContent failed:${JSON.stringify(err)})return}constuiContextwindowStage.getMainWindowSync().getUIContext()this.dialogServicenewTextPickerDialogService(uiContext)})}}异步链路里也尽量走同一个原则。不要在深层业务代码里临时猜一个当前窗口出来也不要继续保留一个全局静态弹窗入口。更稳的写法是在发起异步任务的那一层把UIContext或对应服务一起传下去。asyncfunctionloadOptionsAndShow(dialogService:TextPickerDialogService):Promisevoid{constoptionsawaitPromise.resolve([苹果,橙子,香蕉])dialogService.show({range:options,selected:0,onAccept:(value:string,index:number){console.info(accept value${value}, index${index})}})}这样写的一个直接好处是 UI 归属关系从入口到弹出点都没有丢。后面即便业务模块拆得更多弹窗属于哪个窗口代码里一眼就能看出来。四、兼容层可以留但别把旧接口继续当主路径如果项目还要兼容更低版本保留一个适配层是合理的。TextPickerDialog.show从 API version 18 开始废弃低版本项目里它仍然存在UIContext.showTextPickerDialog 则已经进入明确推荐路径。这类兼容层更适合写成过渡方案不适合长期继续做主路径。代码可以像这样classTextPickerAdapter{staticshow(options:TextPickerDialogOptions|TextPickerDialogOptionsExt,uiContext?:UIContext,apiVersion:number20):void{if(apiVersion18uiContext){uiContext.showTextPickerDialog(options)return}if(typeofTextPickerDialog?.showfunction){TextPickerDialog.show(options)return}console.error(当前环境没有可用的文本选择弹窗接口)}}这种做法解决的是版本兼容不是新的日常写法。项目一旦以 API 20 为主线就应该把组件内和窗口级代码逐步迁到UIContext.showTextPickerDialog上。继续长期依赖旧接口只会把后面的多窗口、复杂页面和跨模块问题越堆越多。总结这次从TextPickerDialog.show到UIContext.showTextPickerDialog的迁移弹窗显示动作要和具体 UI 实例绑定。TextPickerDialog.show从 API version 18 开始废弃推荐路径已经切到UIContextUIContext本身和窗口绑定窗口初始化完成后再拿结果才稳定。组件内部可以直接this.getUIContext()窗口级逻辑可以在loadContent完成后从主窗口拿。工程里更值得注意的是跨模块和异步调用的写法。全局单例服务在多窗口场景里容易埋雷按窗口持有服务或者显式传递UIContext后面会稳很多。兼容层可以留用来照顾旧版本API 20 主线代码还是应该尽快收口到UIContext.showTextPickerDialog上。

更多文章