赞
踩
基本概念
指向当前应用界面上唯一的一个可交互元素,当用户使用键盘、电视遥控器、车机摇杆/旋钮等非指向性输入设备与应用程序进行间接交互时,基于焦点的导航和交互是重要的输入手段。
应用打开或切换页面后,若当前页上存在可获焦的组件,则树形结构的组件树中第一个可获焦的组件默认获得焦点。可以使用自定义默认焦点指定。
指组件获得了焦点,同一时刻,应用中最多只有1个末端组件是获焦的,且此时它的所有祖宗组件(整个组件链)均是获焦的。当期望某个组件获焦,须确保该组件及其所有的祖宗节点均是可获焦的(focusable属性为true)。
指组件从获焦状态变成了非获焦状态,失去了焦点。组件失焦时,它的所有祖宗组件(失焦组件链)与新的获焦组件链不相同的节点都会失焦。
表示焦点在当前应用中转移的过程,走焦会带来原焦点组件的失焦和新焦点组件的获焦。应用中焦点发生变化的方式按行为可分为两类:
获焦组件的样式,不同组件的焦点态样式大同小异,默认情况下焦点态不显示,仅使用外接键盘按下TAB键/方向键时才会触发焦点态样式出现。首次触发焦点态显示的TAB键/方向键不会触发走焦。当应用接收到点击事件时(包括手指触屏的按下事件和鼠标左键的按下事件),自动隐藏焦点态样式。焦点态样式由后端组件定义,开发者无法修改。
走焦规则是指用户使用“TAB键/SHIFT+TAB键/方向键”主动进行走焦,或焦点系统在执行被动走焦时的顺序规则。组件的走焦规则默认由走焦系统定义,由焦点所在的容器决定。
图1 线性走焦示意图
例如Row容器,使用方向键左右(←/→)即可将焦点在相邻的2个可获焦组件之间来回切换。
图2 Grid组件十字走焦示意图
说明:
TAB/SHIFT+TAB键在以上两种走焦规则上的功能和方向键一致。TAB键等同于“先执行方向键右,若无法走焦,再执行方向键下”,SHIFT+TAB键等同于“先执行方向键左,若无法走焦,再执行方向键上”。
触发走焦的按键是按下事件(DOWN事件)。
删除组件、设置组件无法获焦后,会使用线性走焦规则,自动先往被删除/Unfocusable组件的前置兄弟组件上走焦,无法走焦的话,再向后置兄弟组件上走焦。
说明:
本文涉及到的焦点均为组件焦点,另外一个焦点的概念是:窗口焦点,指向当前获焦的窗口。当窗口失焦时,该窗口应用中的所有获焦组件全部失焦。
import List from '@ohos.util.List';
import promptAction from '@ohos.promptAction';
onFocus(event: () => void)
获焦事件回调,绑定该API的组件获焦时,回调响应。
onBlur(event:() => void)
失焦事件回调,绑定该API的组件失焦时,回调响应。
onFocus和onBlur两个接口通常成对使用,来监听组件的焦点变化。
以下示例代码展示获焦/失焦回调的使用方法:
// xxx.ets @Entry @Component struct FocusEventExample { @State oneButtonColor: Color = Color.Gray; @State twoButtonColor: Color = Color.Gray; @State threeButtonColor: Color = Color.Gray; build() { Column({ space: 20 }) { // 通过外接键盘的上下键可以让焦点在三个按钮间移动,按钮获焦时颜色变化,失焦时变回原背景色 Button('First Button') .defaultFocus(true) .width(260) .height(70) .backgroundColor(this.oneButtonColor) .fontColor(Color.Black) // 监听第一个组件的获焦事件,获焦后改变颜色 .onFocus(() => { this.oneButtonColor = Color.Green; }) // 监听第一个组件的失焦事件,失焦后改变颜色 .onBlur(() => { this.oneButtonColor = Color.Gray; }) Button('Second Button') .width(260) .height(70) .backgroundColor(this.twoButtonColor) .fontColor(Color.Black) // 监听第二个组件的获焦事件,获焦后改变颜色 .onFocus(() => { this.twoButtonColor = Color.Green; }) // 监听第二个组件的失焦事件,失焦后改变颜色 .onBlur(() => { this.twoButtonColor = Color.Grey; }) Button('Third Button') .width(260) .height(70) .backgroundColor(this.threeButtonColor) .fontColor(Color.Black) // 监听第三个组件的获焦事件,获焦后改变颜色 .onFocus(() => { this.threeButtonColor = Color.Green; }) // 监听第三个组件的失焦事件,失焦后改变颜色 .onBlur(() => { this.threeButtonColor = Color.Gray ; }) }.width('100%').margin({ top: 20 }) } }
上述示例包含以下4步:
通过focusable接口设置组件是否可获焦:
focusable(value: boolean)
按照组件的获焦能力可大致分为三类:
说明:
focusable为false表示组件不可获焦,同样可以使组件变成不可获焦的还有通用属性enabled。
当某组件处于获焦状态时,将其的focusable属性或enabled属性设置为false,会自动使该组件失焦,然后焦点按照走焦规则将焦点转移给其他组件。
对于没有配置focusable属性,有获焦能力但默认不可获焦的组件,为其配置onClick或是单指单击的Tap手势,该组件会隐式地成为可获焦组件。如果其focusable属性被设置为false,即使配置了上述事件,该组件依然不可获焦。
表1 基础组件获焦能力
以下示例展示focusable接口的使用方法:
// xxx.ets @Entry @Component struct FocusableExample { @State textFocusable: boolean = true; @State color1: Color = Color.Yellow; @State color2: Color = Color.Yellow; build() { Column({ space: 5 }) { Text('Default Text') // 第一个Text组件未设置focusable属性,默认不可获焦 .borderColor(this.color1) .borderWidth(2) .width(300) .height(70) .onFocus(() => { this.color1 = Color.Blue; }) .onBlur(() => { this.color1 = Color.Yellow; }) Divider() Text('focusable: ' + this.textFocusable) // 第二个Text设置了focusable属性,初始值为true .borderColor(this.color2) .borderWidth(2) .width(300) .height(70) .focusable(this.textFocusable) .onFocus(() => { this.color2 = Color.Blue; }) .onBlur(() => { this.color2 = Color.Yellow; }) Divider() Row() { Button('Button1') .width(140).height(70) Button('Button2') .width(160).height(70) } Divider() Button('Button3') .width(300).height(70) Divider() }.width('100%').justifyContent(FlexAlign.Center) .onKeyEvent((e) => { // 绑定onKeyEvent,在该Column组件获焦时,按下'F'键,可将第二个Text的focusable置反 if(e){ if (e.keyCode === 2022 && e.type === KeyType.Down) { this.textFocusable = !this.textFocusable; } } }) } }
运行效果:
上述示例包含默认获焦和主动走焦两部分:
默认获焦:
按键盘F键,触发onKeyEvent,focusable置为false,Text组件变成不可获焦,焦点自动转移。按照被动走焦中的说明项,焦点会自动从Text组件先向上寻找下一个可获焦组件,由于上一个组件是一个不可获焦的Text,所以向下寻找下一个可获焦的组件,找到并使焦点转移到Row容器上。根据走焦至容器规则,使用Tab键走焦时应该满足Z字型走焦顺序,因此焦点会自动转移到Button1上。
defaultFocus(value: boolean)
焦点系统在页面初次构建完成时,会搜索当前页下的所有组件,找到第一个绑定了defaultFocus(true)的组件,然后将该组件置为默认焦点,若无任何组件绑定defaultFocus(true),则将第一个找到的可获焦的组件置为默认焦点。
以如下应用为例,应用布局如下:
以下是实现该应用的示例代码,且示例代码中没有设置defaultFocus:
// xxx.ets import promptAction from '@ohos.promptAction'; class MyDataSource implements IDataSource { private list: number[] = []; private listener: DataChangeListener | undefined = undefined; constructor(list: number[]) { this.list = list; } totalCount(): number { return this.list.length; } getData(index: number): number { return this.list[index]; } registerDataChangeListener(listener: DataChangeListener): void { this.listener = listener; } unregisterDataChangeListener() { } } class swcf { swiperController: SwiperController | undefined fun(index: number) { if (this.swiperController) { if (index == 0) { this.swiperController.showPrevious(); } else { this.swiperController.showNext(); } } } } class TmpM { left: number = 20 bottom: number = 20 right: number = 20 } let MarginTmp: TmpM = new TmpM() @Entry @Component struct SwiperExample { private swiperController: SwiperController = new SwiperController() @State tmp: promptAction.ShowToastOptions = { 'message': 'Button OK on clicked' } private data: MyDataSource = new MyDataSource([]) aboutToAppear(): void { let list: number[] = [] for (let i = 1; i <= 4; i++) { list.push(i); } this.data = new MyDataSource(list); } build() { Column({ space: 5 }) { Swiper(this.swiperController) { LazyForEach(this.data, (item: string) => { Row({ space: 10 }) { Column() { Button('1').width(120).height(120) .fontSize(40) .backgroundColor('#dadbd9') } Column({ space: 20 }) { Row({ space: 20 }) { Button('2') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') Button('3') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') } Row({ space: 20 }) { Button('4') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') Button('5') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') } Row({ space: 20 }) { Button('6') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') Button('7') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') } } } .width(320) .height(300) .justifyContent(FlexAlign.Center) .borderWidth(2) .borderColor(Color.Gray) .backgroundColor(Color.White) }, (item: string): string => item) } .cachedCount(2) .index(0) .interval(4000) .indicator(true) .loop(true) .duration(1000) .itemSpace(0) .curve(Curve.Linear) .onChange((index: number) => { console.info(index.toString()); }) .margin({ left: 20, top: 20, right: 20 }) Row({ space: 40 }) { Button('←') .fontSize(40) .fontWeight(FontWeight.Bold) .fontColor(Color.Black) .backgroundColor(Color.Transparent) .onClick(() => { let swf = new swcf() swf.fun(0) }) Button('→') .fontSize(40) .fontWeight(FontWeight.Bold) .fontColor(Color.Black) .backgroundColor(Color.Transparent) .onClick(() => { let swf = new swcf() swf.fun(1) }) } .width(320) .height(50) .justifyContent(FlexAlign.Center) .borderWidth(2) .borderColor(Color.Gray) .backgroundColor('#f7f6dc') Row({ space: 20 }) { Button('Cancel') .fontSize(30) .fontColor('#787878') .type(ButtonType.Normal) .width(140) .height(50) .backgroundColor('#dadbd9') Button('OK') .fontSize(30) .fontColor('#787878') .type(ButtonType.Normal) .width(140) .height(50) .backgroundColor('#dadbd9') .onClick(() => { promptAction.showToast(this.tmp); }) } .width(320) .height(80) .justifyContent(FlexAlign.Center) .borderWidth(2) .borderColor(Color.Gray) .backgroundColor('#dff2e4') .margin(MarginTmp) }.backgroundColor('#f2f2f2') .margin({ left: 50, top: 50, right: 50 }) } }
当前应用上无任何defaultFocus设置,所以第一个可获焦的组件默认获取焦点,按下TAB键/方向键让获焦的组件显示焦点态样式:
假设开发者想让应用打开的时候,无需执行多余的切换焦点操作,直接点击按键的空格/回车键,就可以执行Button-OK的onClick回调操作,那么就可以给这个Button绑定defaultFocus(true),让它成为该页面上的默认焦点:
import promptAction from '@ohos.promptAction'; @Entry @Component struct MouseExample { @State Tmp: promptAction.ShowToastOptions = {'message':'Button OK on clicked'} build() { Column() { Button('OK') .defaultFocus(true) // 设置Button-OK为defaultFocus .fontSize(30) .fontColor('#787878') .type(ButtonType.Normal) .width(140).height(50).backgroundColor('#dadbd9') .onClick(() => { promptAction.showToast(this.Tmp); }) } } }
打开应用后按TAB键,Button-OK显示了焦点态,说明默认焦点变更到了Button-OK上。然后按下空格,响应了Button-OK的onClick事件。
tabIndex(index: number)
tabIndex用于设置自定义TAB键走焦顺序,默认值为0。使用“TAB/Shift+TAB键”走焦时(方向键不影响),系统会自动获取到所有配置了tabIndex大于0的组件,然后按照递增/递减排序进行走焦。
以defaultFocus提供的示例为例,默认情况下的走焦顺序如下:
默认的走焦顺序从第一个获焦组件一路走到最后一个获焦组件,会经历Button1->Button2->Button3->Button4->Button5->Button6->Button7->左箭头->右箭头->ButtonCancel->ButtonOK。这种走焦队列比较完整,遍历了大部分的组件。但缺点是从第一个走到最后一个所经历的路径较长。
如果想实现快速的从第一个走到最后一个,又不想牺牲太多的遍历完整性,就可以使用tabIndex通用属性。
比如:开发者把白色的区域当为一个整体,黄色的区域当为一个整体,绿色的区域当为一个整体,实现Button1->左箭头->ButtonOK这种队列的走焦顺序,只需要在Button1、左箭头、ButtonOK这三个组件上依次增加tabIndex(1)、tabIndex(2)、tabIndex(3)。tabIndex的参数表示TAB走焦的顺序(从大于0的数字开始,从小到大排列)。
@Entry
@Component
struct MouseExample {
build() {
Column() {
Button('1').width(120).height(120)
.fontSize(40)
.backgroundColor('#dadbd9')
.tabIndex(1) // Button-1设置为第一个tabIndex节点
}
}
}
class swcf{ swiperController:SwiperController|undefined fun(){ if(this.swiperController){ this.swiperController.showPrevious(); } } } @Entry @Component struct MouseExample { build() { Column() { Button('←') .fontSize(40) .fontWeight(FontWeight.Bold) .fontColor(Color.Black) .backgroundColor(Color.Transparent) .onClick(() => { let swf = new swcf() swf.fun() }) .tabIndex(2) // Button-左箭头设置为第二个tabIndex节点 } } }
import promptAction from '@ohos.promptAction'; @Entry @Component struct MouseExample { @State Tmp:promptAction.ShowToastOptions = {'message':'Button OK on clicked'} build() { Column() { Button('OK') .fontSize(30) .fontColor('#787878') .type(ButtonType.Normal) .width(140).height(50).backgroundColor('#dadbd9') .onClick(() => { promptAction.showToast(this.Tmp); }) .tabIndex(3) // Button-OK设置为第三个tabIndex节点 } } }
说明:
当焦点处于tabIndex(大于0)节点上时,TAB/ShiftTAB会优先在tabIndex(大于0)的队列中寻找后置/前置的节点,存在则走焦至相应的tabIndex节点。若不存在,则使用默认的走焦逻辑继续往后/往前走焦。
当焦点处于tabIndex(等于0)节点上时,TAB/ShiftTAB使用默认的走焦逻辑走焦,走焦的过程中会跳过tabIndex(大于0)和tabIndex(小于0)的节点。
当焦点处于tabIndex(小于0)节点上时,TAB/ShiftTAB无法走焦。
groupDefaultFocus(value: boolean)
自定义TAB键走焦顺序中所展示的使用tabIndex完成快速走焦的能力有如下问题:
每个区域(白色/黄色/绿色三个区域)都设置了某个组件为tabIndex节点(白色-Button1、黄色-左箭头、绿色-ButtonOK),但这样设置之后,只能在这3个组件上按TAB/ShiftTab键走焦时会有快速走焦的效果。
解决方案是给每个区域的容器设置tabIndex,但是这样设置的问题是:第一次走焦到容器上时,获焦的子组件是默认的第一个可获焦组件,并不是自己想要的组件(Button1、左箭头、ButtonOK)。
这样便引入了groupDefaultFocus通用属性,参数:boolean,默认值:false。
用法需和tabIndex组合使用,使用tabIndex给区域(容器)绑定走焦顺序,然后给Button1、左箭头、ButtonOK绑定groupDefaultFocus(true),这样在首次走焦到目标区域(容器)上时,它的绑定了groupDefaultFocus(true)的子组件同时获得焦点。
// xxx.ets import promptAction from '@ohos.promptAction'; class MyDataSource implements IDataSource { private list: number[] = []; private listener: DataChangeListener|undefined = undefined; constructor(list: number[]) { this.list = list; } totalCount(): number { return this.list.length; } getData(index: number): number { return this.list[index]; } registerDataChangeListener(listener: DataChangeListener): void { this.listener = listener; } unregisterDataChangeListener() { } } @Entry @Component struct SwiperExample { private swiperController: SwiperController = new SwiperController() private data: MyDataSource = new MyDataSource([]) @State tmp:promptAction.ShowToastOptions = {'message':'Button OK on clicked'} aboutToAppear(): void { let list: number[] = [] for (let i = 1; i <= 4; i++) { list.push(i); } this.data = new MyDataSource(list); } build() { Column({ space: 5 }) { Swiper(this.swiperController) { LazyForEach(this.data, (item: string) => { Row({ space: 10 }) { // 设置该Row组件为tabIndex的第一个节点 Column() { Button('1').width(120).height(120) .fontSize(40) .backgroundColor('#dadbd9') .groupDefaultFocus(true) // 设置Button-1为第一个tabIndex的默认焦点 } Column({ space: 20 }) { Row({ space: 20 }) { Button('2') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') Button('3') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') } Row({ space: 20 }) { Button('4') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') Button('5') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') } Row({ space: 20 }) { Button('6') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') Button('7') .width(70) .height(70) .fontSize(40) .type(ButtonType.Normal) .borderRadius(20) .backgroundColor('#dadbd9') } } } .width(320) .height(300) .justifyContent(FlexAlign.Center) .borderWidth(2) .borderColor(Color.Gray) .backgroundColor(Color.White) .tabIndex(1) }, (item:string):string => item) } .cachedCount(2) .index(0) .interval(4000) .indicator(true) .loop(true) .duration(1000) .itemSpace(0) .curve(Curve.Linear) .onChange((index: number) => { console.info(index.toString()); }) .margin({ left: 20, top: 20, right: 20 }) Row({ space: 40 }) { // 设置该Row组件为第二个tabIndex节点 Button('←') .fontSize(40) .fontWeight(FontWeight.Bold) .fontColor(Color.Black) .backgroundColor(Color.Transparent) .onClick(() => { this.swiperController.showPrevious(); }) .groupDefaultFocus(true) // 设置Button-左箭头为第二个tabIndex节点的默认焦点 Button('→') .fontSize(40) .fontWeight(FontWeight.Bold) .fontColor(Color.Black) .backgroundColor(Color.Transparent) .onClick(() => { this.swiperController.showNext(); }) } .width(320) .height(50) .justifyContent(FlexAlign.Center) .borderWidth(2) .borderColor(Color.Gray) .backgroundColor('#f7f6dc') .tabIndex(2) Row({ space: 20 }) { // 设置该Row组件为第三个tabIndex节点 Button('Cancel') .fontSize(30) .fontColor('#787878') .type(ButtonType.Normal) .width(140) .height(50) .backgroundColor('#dadbd9') Button('OK') .fontSize(30) .fontColor('#787878') .type(ButtonType.Normal) .width(140) .height(50) .backgroundColor('#dadbd9') .defaultFocus(true) .onClick(() => { promptAction.showToast(this.tmp); }) .groupDefaultFocus(true) // 设置Button-OK为第三个tabIndex节点的默认焦点 } .width(320) .height(80) .justifyContent(FlexAlign.Center) .borderWidth(2) .borderColor(Color.Gray) .backgroundColor('#dff2e4') .margin({ left: 20, bottom: 20, right: 20 }) .tabIndex(3) }.backgroundColor('#f2f2f2') .margin({ left: 50, top: 50, right: 50 }) } }
focusOnTouch(value: boolean)
点击获焦能力,参数:boolean,默认值:false(输入类组件:TextInput、TextArea、Search、Web默认值是true)。
点击是指使用触屏或鼠标左键进行单击,默认为false的组件,例如Button,不绑定该API时,点击Button不会使其获焦,当给Button绑定focusOnTouch(true)时,点击Button会使Button立即获得焦点。
给容器绑定focusOnTouch(true)时,点击容器区域,会立即使容器的第一个可获焦组件获得焦点。
示例代码:
// requestFocus.ets @Entry @Component struct RequestFocusExample { @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N'] build() { Column({ space:20 }){ Button("id: " + this.idList[0] + " focusOnTouch(true) + focusable(false)") .width(400).height(70).fontColor(Color.White).focusOnTouch(true) .hoverEffect(HoverEffect.Scale) .focusable(false) Button("id: " + this.idList[1] + " default") .width(400).height(70).fontColor(Color.White) .hoverEffect(HoverEffect.Scale) Button("id: " + this.idList[2] + " focusOnTouch(false)") .width(400).height(70).fontColor(Color.White).focusOnTouch(false) .hoverEffect(HoverEffect.Scale) Button("id: " + this.idList[3] + " focusOnTouch(true)") .width(400).height(70).fontColor(Color.White).focusOnTouch(true) .hoverEffect(HoverEffect.Scale) }.width('100%').margin({ top:20 }) } }
解读:
Button-A虽然设置了focusOnTouch(true),但是同时也设置了focusable(false),该组件无法获焦,因此点击后也无法获焦;
Button-B不设置相关属性,点击后不会获焦;
Button-C设置了focusOnTouch(false),同Button-B,点击后也不会获焦;
Button-D设置了focusOnTouch(true),点击即可使其获焦;
说明:
焦点态在屏幕接收点击事件后会立即清除,因此该示例代码在每次点击后,需要再次按下TAB键使焦点态再次显示,才可知道当前焦点所在的组件。
focusControl.requestFocus(id: string)
主动申请焦点能力的全局方法,参数:string,参数表示被申请组件的id(通用属性id设置的字符串)。
使用方法为:在任意执行语句中调用该API,指定目标组件的id为方法参数,当程序执行到该语句时,会立即给指定的目标组件申请焦点。
代码示例:
// requestFocus.ets import promptAction from '@ohos.promptAction'; @Entry @Component struct RequestFocusExample { @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N'] @State requestId: number = 0 build() { Column({ space:20 }){ Row({space: 5}) { Button("id: " + this.idList[0] + " focusable(false)") .width(200).height(70).fontColor(Color.White) .id(this.idList[0]) .focusable(false) Button("id: " + this.idList[1]) .width(200).height(70).fontColor(Color.White) .id(this.idList[1]) } Row({space: 5}) { Button("id: " + this.idList[2]) .width(200).height(70).fontColor(Color.White) .id(this.idList[2]) Button("id: " + this.idList[3]) .width(200).height(70).fontColor(Color.White) .id(this.idList[3]) } Row({space: 5}) { Button("id: " + this.idList[4]) .width(200).height(70).fontColor(Color.White) .id(this.idList[4]) Button("id: " + this.idList[5]) .width(200).height(70).fontColor(Color.White) .id(this.idList[5]) } }.width('100%').margin({ top:20 }) .onKeyEvent((e) => { if(e){ if (e.keyCode >= 2017 && e.keyCode <= 2022) { this.requestId = e.keyCode - 2017; } else if (e.keyCode === 2030) { this.requestId = 6; } else { return; } if (e.type !== KeyType.Down) { return; } } let res = focusControl.requestFocus(this.idList[this.requestId]); let tmps:promptAction.ShowToastOptions = {'message':'Request success'} let tmpf:promptAction.ShowToastOptions = {'message':'Request failed'} if (res) { promptAction.showToast(tmps); } else { promptAction.showToast(tmpf); } }) } }
解读:页面中共6个Button组件,其中Button-A组件设置了focusable(false),表示其不可获焦,在外部容器的onKeyEvent中,监听按键事件,当按下A ~ F按键时,分别去申请Button A ~ F 的焦点,另外按下N键,是给当前页面上不存在的id的组件去申请焦点。
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.基本概念
2.构建第一个ArkTS应用
3.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。