资讯动态

HarmonyOS 多设备交互实战:从触屏到键盘完整适配方案

发布时间:2026/9/29 22:41:46 来源:尧图企业网站定制
一、这玩意儿有啥用开发一次开发多端部署应用除了适配硬件差异屏幕尺寸、分辨率还得关注交互方式差异。不同设备用的输入设备不一样交互方式就不一样手机和平板用手指触控屏幕电脑用鼠标控制光标智慧屏用灵犀指向遥控控制光标。要是只适配一种交互方式应用在其他设备上体验就差了。比如视频播放应用控制播放/暂停这功能不同设备的操作方式就完全不一样直板机单击屏幕就行。平板单击屏幕、鼠标左键点击、键盘空格键都行。电脑单击屏幕、鼠标左键点击、触控板点击、键盘空格键都行。智慧屏灵犀指向遥控按OK键、灵犀悬浮触控点击触控板。穿戴设备单击屏幕。所以多设备交互开发得针对触屏、鼠标、键盘、遥控器等不同输入设备做适配保证功能一致性的基础上遵循各设备的交互规范实现自然流畅的体验。二、不同设备咋交互多设备交互这概念听着挺高大上其实就是应用在不同设备上实现同一功能适配相应的输入设备。先看看不同设备支持哪些输入设备直板机只有触控屏。折叠屏只有触控屏。阔折叠只有触控屏。三折叠触控屏和手写笔Mate XTs。平板触摸屏、鼠标、键盘、手写笔。电脑触控屏、触控板、鼠标、键盘、手柄。智慧屏灵犀指向遥控、灵犀悬浮触控、走焦类遥控、键盘、鼠标、手柄。智能穿戴触控屏、表冠。这个表得记清楚后面适配交互事件时就靠它判断哪些输入设备得适配。再看视频播放暂停/播放这个功能在不同设备上的操作方式触控屏单击屏幕。鼠标点击左键。灵犀指向遥控单击OK键。走焦类遥控器单击OK键。键盘按下空格键。这五种操作方式得都适配不然在某些设备上就没法用这功能。三、交互归一咋实现交互归一是个面向多设备输入的响应框架把不同输入设备的交互行为抽象为同一事件简化开发逻辑。比如触屏点击、触控板点击、鼠标左键单击、遥控器OK键确认这些都可以统一抽象为点击事件。遥控器功能键、键盘快捷键可以抽象为按键事件。但交互归一不是把所有输入方式简单合并为单一事件而是通过对不同设备的几十种底层交互事件进行语义抽象与归类在保证交互差异可控的前提下大幅减少事件类型数量。最终形成的是一组标准化的交互API集合开发者还得根据具体场景选择并适配相应的抽象事件实现跨设备的一致性与灵活性兼顾的交互体验。ArkUI框架提供了丰富的交互功能支持直接处理基础输入事件以及由这些事件驱动的手势事件同时支持拖拽事件、焦点事件等复杂交互。四、基础输入事件咋处理基础输入事件就是用户用输入设备触摸屏、键盘、鼠标、触控板交互时设备驱动层检测并生成原始信号操作系统捕获这些底层信号封装成标准化的基础事件传递给上层应用处理。基础事件分两类指向性事件和非指向性事件。从事件的目标如何确定来理解这两类事件。指向性事件交互动作第一次开始时手指按下、鼠标点击用户的手指或鼠标指针碰到屏幕上的哪个组件这个被命中的组件就成为整个交互过程的接收目标。包括触摸事件、鼠标事件、轴事件、手写笔事件。场景案例用手写笔在屏幕上书写手写笔第一次点击的位置比如画板画板组件就是交互目标。手写笔套件的示例代码if(canIUse(SystemCapability.Stylus.Handwrite)){// Using the canIUse interface to prevent the stylus event from not being supported by some devicesHandwriteComponent({handwriteController:this.controller,defaultPenType:PenType.PEN,defaultPenInfo:[{penType:PenType.PEN,penWidth:this.penWidth},{penType:PenType.BALLPOINT_PEN,penWidth:this.ballpointPenWidth}]asPenHspInfo[],widthRatio:1,heightRatio:1,})}else{Text($r(app.string.HandwriteDescInfo))}这段代码的意思先用 canIUse 判断设备是否支持手写笔事件支持就用 HandwriteComponent 组件设置手写笔控制器、笔类型、笔宽度不支持就显示提示文本非指向性事件事件的接收者由当前焦点所在的组件决定。包括按键事件、表冠事件、焦点轴事件。场景案例用键盘填写表单多个输入框之间可通过Tab键切换当前获得焦点的输入框会被高亮显示。用户输入的字符内容会被系统视为针对该焦点输入框的交互。按键事件的示例代码.onKeyEvent((event?:KeyEvent){if(event){if(event.typeKeyType.Down){this.eventTypeDown;}elseif(event.typeKeyType.Up){this.eventTypeUp;}this.keyTextevent.keyText;this.sourceToolevent.keySource;this.getUserTextData();}returntrue;}).onMouse((event:MouseEvent):void{if(event){// ...}})这段代码的意思用 onKeyEvent 监听按键事件判断按键类型是按下还是抬起获取按键文本和按键来源返回 true 表示已消费该事件阻止事件继续冒泡有个细节得注意onKeyEvent 事件默认是冒泡的在回调函数中如果事件已被处理建议返回 true表示已消费该事件。这可以阻止事件继续冒泡避免上层节点重复响应防止按键事件被触发多次。还有个细节灵犀手写笔不响应 onHover 事件这个特性只适用于其他型号的手写笔设备。这张图展示了常见基础输入事件在不同输入设备上的触发方式可以对照参考。五、手势事件咋识别手势是由一系列基础事件累积并满足特定条件后识别出的交互行为比如点击就是快速按下并抬起。ArkUI 中系统组件默认支持常见手势按钮支持点击事件也可以在组件上绑定一个或多个手势。默认情况下多手势识别会按照手势注册的顺序依次匹配和处理多个手势互斥执行仅一个生效用互斥识别机制避免冲突响应。多个手势同时响应用并行识别机制允许多个手势并发处理提升交互灵活性。精细控制哪些手势可参与识别与竞争用手势冲突处理机制。这张图展示了常见手势事件在不同输入设备上的触发方式可以对照参考。旋转手势的示例代码.gesture(RotationGesture().onActionUpdate((event:GestureEvent){if(event){// Obtain the rotation angle and change the rotation angle of the imagethis.anglethis.rotateValueevent.angle;this.sourceTypeevent.source;this.sourceToolevent.sourceTool;this.getUserTextData();}}).onActionEnd((){this.rotateValuethis.angle;}))这段代码的意思用 RotationGesture 创建旋转手势在手势更新时获取旋转角度更新图片旋转角度在手势结束时保存当前旋转角度典型场景宫格排列的界面元素可以通过双指触发 PinchGesture 捏合手势动态修改网格布局的 columnsTemplate 属性。比如视频应用中双指捏合实现视频元素的显示个数调整。双指捏合调整视频列数的示例代码Grid(){// ...}// ....gesture(PinchGesture({fingers:2}).onActionUpdate((event:GestureEvent){if(event.scale1this.currentWidthBreakpoint!sm){if(this.currentWidthBreakpointmd){this.getUIContext().animateTo({duration:500},(){this.videoGridColumn1fr 1fr 1fr;})}else{this.getUIContext().animateTo({duration:500},(){this.videoGridColumn1fr 1fr 1fr 1fr;})}}elseif(event.scale1this.currentWidthBreakpoint!sm){if(this.currentWidthBreakpointmd){this.getUIContext().animateTo({duration:500},(){this.videoGridColumn1fr 1fr 1fr 1fr;})}else{this.getUIContext().animateTo({duration:500},(){this.videoGridColumn1fr 1fr 1fr 1fr 1fr;})}}else{Logger.info(Two-finger operation is not supported);}}))这段代码的意思用 PinchGesture 创建双指捏合手势捏合放大时scale 1根据当前断点调整视频列数捏合缩小时scale 1根据当前断点调整视频列数sm 断点不支持双指操作这个功能挺实用的用户可以双指捏合调整视频显示个数体验更灵活。六、焦点事件咋管理用键盘、电视遥控器、车机摇杆或旋钮等非指向性输入设备与应用程序间接交互时得把页面中可操作元素设置为可获焦状态并配置获焦视觉效果保证交互体验。获焦/失焦通过 onFocus 和 onBlur 事件监听组件的焦点变化。组件获焦时遵循子组件优先原则。若子组件需获焦其所有祖先组件均需可获焦。容器组件需获焦时其子组件应不可获焦并配置点击事件。部分组件默认可获焦比如 Button、TextInput 等基础组件和 Column、Row 等容器组件。若组件有获焦能力但默认不可获焦比如 Text、Image 等组件可设置 focusable(true) 使其可获焦。部分组件为无交互行为的组件通常不可获焦比如 Blank、Circle 组件。走焦触发走焦时系统遍历组件树中可走焦的组件。当前焦点框架支持三种走焦算法线性走焦按照子节点在节点树中的挂载顺序进行焦点导航。投影走焦适用于容器内子组件尺寸不一的场景通过空间位置关系计算最佳焦点目标。自定义走焦开发者可通过 tabIndex 和 nextFocus 灵活自定义走焦逻辑满足复杂交互需求。这张图展示了使用键盘走焦的效果可以看到焦点从一个元素移动到另一个元素。这张图展示了走焦样式的设计指南获焦元素要有明显的视觉反馈。走焦常用属性有五个focusable设置当前组件是否可以获焦参数是 boolean。tabIndex自定义组件Tab键走焦顺序值参数是 number。defaultFocus设置当前组件是否为当前页面上的默认焦点参数是 boolean。tabStop设置当前容器组件是否为走焦可停留容器参数是 boolean。nextFocus设置当前容器组件的自定义走焦规则参数是 Optional。这张图展示了不同输入设备上支持触发焦点事件的方式可以对照参考。七、拖拽事件咋实现拖拽发生在两个组件之间它不是简单的单次输入而是一个过程通常包含以下步骤长按或点击组件A触发拖拽。保持按压或点击持续将组件A向组件B拖拽。抵达组件B中释放按压点击完成拖拽。也可以在未抵达组件B的中途释放按压点击取消拖拽。一个完整的拖拽事件包含多个拖拽子事件onDragStart绑定A组件触控屏长按/鼠标左键按下后移动触发。onDragEnter绑定B组件触控屏手指、鼠标移动进入B组件瞬间触发。onDragMove绑定B组件触控屏手指、鼠标在B组件内移动触发。onDragLeave绑定B组件触控屏手指、鼠标移动退出B组件瞬间触发。onDrop绑定B组件在B组件内触控屏手指抬起、鼠标左键松开时触发。这张图展示了不同输入设备上触发拖拽事件的方式可以对照参考。图片拖拽的示例代码.allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE]).onDrop((event:DragEvent){try{letdragData:UnifiedData(eventasDragEvent).getData()asUnifiedData;if(dragData!undefined){letrecords:unifiedDataChannel.UnifiedRecord[]dragData.getRecords();if(records.length0){for(leti0;irecords.length;i){lettypesrecords[i].getTypes();if(types.includes(uniformTypeDescriptor.UniformDataType.FILE_URI)){// Retrieve the image resource URLs from the record and assign themconstfileUriUdsrecords[i].getEntry(uniformTypeDescriptor.UniformDataType.FILE_URI)asuniformDataStruct.FileUri;lettypeDescriptoruniformTypeDescriptor.getTypeDescriptor(fileUriUds.fileType);if(typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.IMAGE)){this.targetImagefileUriUds.oriUri;}}}}else{hilog.info(0x0000,TAG,%{public}s,dragData arr is null);}}else{hilog.info(0x0000,TAG,%{public}s,dragData is undefined);}this.dragSuccesstrue;}catch(error){consterrerrorasBusinessError;hilog.error(0x0000,TAG,startDataLoading errorCode:${err.code}, errorMessage:${err.message});}})这段代码的意思用 allowDrop 设置允许拖入的数据类型为图片用 onDrop 监听拖拽完成事件获取拖拽数据判断数据类型如果是图片类型的文件URI就获取图片资源地址并赋值这个功能可以让用户把图片从其他应用拖入当前应用体验很方便。八、实际开发咋整实际开发中得根据业务场景明确需适配的目标设备。比如视频类应用通常需覆盖手机、平板、电脑及智慧屏。运动健康类应用侧重手机与智能穿戴设备。开发者应结合业务特点和用户高频使用场景确定适配范围。在明确目标设备后需进一步依据各设备支持的输入方式针对性地设计和适配相应的交互事件。以视频播放页的暂停/播放功能为例开发步骤第一步明确长视频类应用的目标适配设备为手机、平板、电脑及智慧屏。第二步根据设备支持的输入设备一览表可知手机、平板、电脑和智慧屏支持的输入设备包括触控屏、手写笔、鼠标、键盘、手柄、灵犀指向遥控、灵犀悬浮触控、走焦类遥控。第三步实现播放/暂停功能根据不同输入设备的交互方式适配相应的事件处理机制。触控屏、鼠标、灵犀指向遥控、走焦类遥控都可以通过 onClick 点击事件触发Flex({// ...}){Column(){// ...}.width(100%).onClick((){// 播放/暂停逻辑})键盘得监听特定按键的 onKeyEvent 按键事件.onKeyEvent((event?:KeyEvent){//If the key type is pressed, the subsequent code will not be executed, and the specific key logic will be executed when released.if(!event||event.type!KeyType.Down){return;}// Space key controls pause/play.if(event.keyCodeKeyCode.KEYCODE_SPACE){this.avPlayerUtil!.playerStateControl();}//press ESC to exit full screen.if(event.keyCodeKeyCode.KEYCODE_ESCAPE){this.windowUtil!.recover();}//Right-click fast forwardif(event.keyCodeKeyCode.KEYCODE_DPAD_RIGHT){this.avPlayerUtil!.fastForward();}//Left click to go back quicklyif(event.keyCodeKeyCode.KEYCODE_DPAD_LEFT){this.avPlayerUtil!.rewind();}})这段代码的意思用 onKeyEvent 监听按键事件判断按键类型是按下不是按下就不处理空格键控制播放/暂停ESC键退出全屏方向右键快进方向左键快退空格键的键码是 KEYCODE_SPACE其他按键的键码可通过 keyCode 枚举查询比如回车键对应 KEYCODE_ENTER。应用可根据实际交互需求灵活适配不同按键。开发者可根据业务场景明确适配的设备范围并针对不同功能所支持的输入方式灵活选择与之匹配的交互事件进行适配。多设备交互这事儿看着复杂其实就是根据不同设备的输入方式适配相应的交互事件。核心是交互归一把不同输入设备的交互行为抽象为同一事件简化开发逻辑。基础输入事件分两类指向性事件和非指向性事件。指向性事件的目标由手指或鼠标指针触碰的位置决定非指向性事件的目标由当前焦点所在的位置决定。手势事件是由基础事件累积并满足特定条件后识别出的交互行为。焦点事件用于非指向性输入设备的交互需要设置可获焦状态和获焦视觉效果。拖拽事件是一个过程包含多个子事件。实际开发时得先明确适配的设备范围然后根据设备支持的输入方式适配相应的交互事件。代码示例可以直接参考照着写就行。

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

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

免费获取报价 →
↑