1# Focus Event 2 3 4## Basic Concepts 5 6- Focus 7 8 Focus points to a unique interactive element in the current window. When a user indirectly interacts with an application by using a non-directional input device such as a keyboard, a television remote control, or a vehicle-mounted joystick/knob, focus-based interaction is an important input means. 9 10- Default focus 11 12 After an application opens or switches to a page, the first focusable component (if any) in the component tree of the page is the default focus. You can [set the default focus](#setting-default-focus) as needed. 13 14- Focused 15 16 A focused component is one that has focus. Only one end-point component in the application can receive focus at one time, and all the component's ancestor components along the focus chain are focused. If you want a component to be focused, ensure that the component and all its ancestor components are focusable (the [focusable](#setting-whether-a-component-is-focusable) attribute is set to **true**). 17 18- Not focused 19 20 A component is not focused when it loses focus. In this case, all its ancestor components and the components not on the same focus chain as the focused component are not focused. 21 22- Focus navigation 23 24 Focus navigation refers to a process in which the focus is transferred in the current application. It causes the original focused component to lose focus and a previously not focused component to receive focus. Focus navigation in applications can be classified into the following types by behavior: 25 26 - Active navigation: A component is assigned focus due to subjective actions by developers or users, such as pressing the Tab or arrow keys on the external keyboard, using the [requestFocus](#focuscontrolrequestfocus) API, or clicking the component when the [focusOnTouch](#focusontouch) attribute is set to **true**. 27 - Passive navigation: A component is assigned focus due to logical operations by the system. This focus navigation mode cannot be set by developers. For example, the system may assign focus to a component when the **if-else** statement is used to delete the focused component or set the focused component (or its parent component) to be unfocusable or when the page is switched. 28 29- Focused state 30 31 The focused state refers to the style of the focused component. It is similar among different components and is not visible by default. The focused state is visible only when the Tab or arrow keys on the external keyboard are pressed to move focus. The Tab key or arrow key that triggers the focused state for the first time does not trigger focus navigation. When the application receives a touch event (including a finger press event on the screen and a press event of a left mouse button), the focused state style is automatically hidden. The focused state style is defined by the backend component and cannot be modified by developers. 32 33 34## Rules of Focus Navigation 35 36Focus navigation follows the set rules regardless of whether it is active or passive focus navigation. By default, these rules are defined by the focus system and subject to the container where focus is located. 37 38- Linear navigation: used in components where child components are arranged linearly, such as the **\<Flex>**, **\<Row>**, **\<Column>**, and **\<List>** components. The focus navigation direction is the same as the direction of the arrow keys. 39 40 **Figure 1** Linear navigation 41 42 ![en-us_image_0000001562700537](figures/en-us_image_0000001562700537.png) 43 44 For example, in the **\<Row>** container, you can use the left and right arrow keys (←/→) to move focus between two adjacent focusable components. 45 46- Cross navigation: used when the up (↑), down (↓), left (←), and right (→) arrow keys are pressed to move focus. The following figure shows a **\<Grid>** container where cross focus navigation is frequently seen. 47 48 **Figure 2** Cross focus navigation in the \<Grid> component 49 50 ![en-us_image_0000001511740580](figures/en-us_image_0000001511740580.png) 51 52 >**NOTE** 53 > - With the previous focus navigation rules, the functions of the Tab/Shift+Tab keys are the same as those of the arrow keys. Pressing the Tab key is equivalent to pressing the right arrow key and then, if the focus cannot be moved, the down arrow key. Pressing the Shift+Tab key is equivalent to pressing the left arrow key and then, if the focus cannot be moved, the up arrow key. 54 > 55 > - The key that triggers focus navigation is the press event (Down event). 56 > 57 > - After a component is deleted or set to be unfocusable, the linear navigation rule is followed. The focus automatically moves to the sibling component in front of the deleted or unfocusable component. If that component cannot receive focus, the focus is then moved to the sibling component on the rear. 58 59- tabIndex-based navigation: Focus navigation with the Tab/Shift+Tab keys becomes sequential when the [tabIndex](../reference/arkui-ts/ts-universal-attributes-focus.md) attribute is set for the components. 60 61- Area-based focus: You can define the order of sequential focus navigation and the default focused component, by setting the **tabIndex** attribute for a container component and the [groupDefaultFocus](#groupdefaultfocus) attribute. 62 63- Rule for focusing on a container component: When a container component (for which **groupDefaultFocus** is not set) receives focus for the first time, the positions of its child components are calculated to identify the child component closest to the center of the container. The focus moves to this identified child component. If the container is not focused for the first time, the focus automatically moves to the child component that is focused last time in the container. 64 65- Focus interaction: When a component is focused, the inherent click task of the component or the **onClick** callback task bound is automatically mounted to the space or carriage return key. When the key is pressed, the task is executed, just as in the case of a finger or mouse click. 66 67 68>**NOTE** 69> 70>The focus involved in this topic refers to component focus. In real-world applications, the focus can also be window focus, which points to the currently focused window. When a window loses focus, all focused components in the window lose focus. 71 72 73## Listening for Focus Changes 74 75 76```ts 77onFocus(event: () => void) 78``` 79 80 81Triggered when the bound component obtains focus. 82 83 84 85```ts 86onBlur(event:() => void) 87``` 88 89 90Triggered when the bound component loses focus. 91 92 93The **onFocus** and **onBlur** APIs are usually used in pairs to listen for the focus changes of the component. 94 95 96The following sample code shows how to use these APIs: 97 98 99 100```ts 101// xxx.ets 102@Entry 103@Component 104struct FocusEventExample { 105 @State oneButtonColor: Color = Color.Gray; 106 @State twoButtonColor: Color = Color.Gray; 107 @State threeButtonColor: Color = Color.Gray; 108 109 build() { 110 Column({ space: 20 }) { 111 // You can use the up and down arrow keys on an external keyboard to move the focus between the three buttons. When a button gains focus, its color changes. When it loses focus, its color changes back. 112 Button('First Button') 113 .width(260) 114 .height(70) 115 .backgroundColor(this.oneButtonColor) 116 .fontColor(Color.Black) 117 // Listen for the focus obtaining event of the first component and change its color when it obtains focus. 118 .onFocus(() => { 119 this.oneButtonColor = Color.Green; 120 }) 121 // Listen for the focus loss event of the first component and change its color when it loses focus. 122 .onBlur(() => { 123 this.oneButtonColor = Color.Gray; 124 }) 125 126 Button('Second Button') 127 .width(260) 128 .height(70) 129 .backgroundColor(this.twoButtonColor) 130 .fontColor(Color.Black) 131 // Listen for the focus obtaining event of the second component and change its color when it obtains focus. 132 .onFocus(() => { 133 this.twoButtonColor = Color.Green; 134 }) 135 // Listen for the focus loss event of the second component and change its color when it loses focus. 136 .onBlur(() => { 137 this.twoButtonColor = Color.Grey; 138 }) 139 140 Button('Third Button') 141 .width(260) 142 .height(70) 143 .backgroundColor(this.threeButtonColor) 144 .fontColor(Color.Black) 145 // Listen for the focus obtaining event of the third component and change its color when it obtains focus. 146 .onFocus(() => { 147 this.threeButtonColor = Color.Green; 148 }) 149 // Listen for the focus loss event of the third component and change its color when it loses focus. 150 .onBlur(() => { 151 this.threeButtonColor = Color.Gray ; 152 }) 153 }.width('100%').margin({ top: 20 }) 154 } 155} 156``` 157 158 159![en-us_image_0000001511740584](figures/en-us_image_0000001511740584.gif) 160 161 162The preceding example includes four steps: 163 164 1651. When the application is opened, the **First Button** component obtains the focus by default, its **onFocus** callback is triggered, and its background color turns green. 166 1672. When the Tab key (or the down arrow key) is pressed, **First Button** is in focused state, that is, there is a blue closed box outside the component. If no focus navigation is triggered, the focus remains on **First Button**. 168 1693. When the Tab key (or the down arrow key) is pressed, the **Second Button** component is focused, its **onFocus** callback is triggered, and its background color turns green. **First Button** loses focus, its **onBlur** callback is triggered, and its background color turns gray. 170 1714. When the Tab key (or the down arrow key) is pressed, the **Third Button** component is focused, its **onFocus** callback is triggered, and its background color turns green. **Second Button** loses focus, its **onBlur** callback is triggered, and its background color turns gray. 172 173 174## Setting Whether a Component Is focusable 175 176Use the **focusable** API to set whether a component is focusable. 177 178 179```ts 180focusable(value: boolean) 181``` 182 183Components can be classified into the following types based on their focusability: 184 185- Components that are focusable by default: These components are usually interactive components, such as **\<Button>**, **\<Checkbox>**, and **\<TextInput>**. 186 187- Components that can be focused but are unfocusable by default: Typical examples are **\<Text>** and **\<Image>**. To enable them to be focusable, use the **focusable(true)** attribute. 188 189- Components that cannot be focused: These components usually do not allow for interactions, such as **\<Blank>** and **\<Circle>**, and cannot be focused even if they use the **focusable** attribute. 190 191 192>**NOTE** 193> - If **focusable** is set to **false**, the component is unfocusable. The universal attribute [enabled](../reference/arkui-ts/ts-universal-attributes-enable.md) can also be used to make the component unfocusable. 194> 195> - When a component is in the focused state, if its **focusable** or **enabled** attribute is set to **false**, the component automatically loses focus. Then, the focus moves to other components based on the [Rules of Focus Navigation](#rules-of-focus-navigation). 196 197 **Table 1** Focusability of basic components 198 199| Basic Component | Focusable| Default Value of focusable| Rules of Focus Navigation | 200| ---------------------------------------- | ------- | ------------ | -------- | 201| [AlphabetIndexer](../reference/arkui-ts/ts-container-alphabet-indexer.md) | Yes | true | Linear navigation | 202| [Blank](../reference/arkui-ts/ts-basic-components-blank.md) | No | false | / | 203| [Button](../reference/arkui-ts/ts-basic-components-button.md) | Yes | true | / | 204| [Checkbox](../reference/arkui-ts/ts-basic-components-checkbox.md) | Yes | true | / | 205| [CheckboxGroup](../reference/arkui-ts/ts-basic-components-checkboxgroup.md) | Yes | true | / | 206| [DataPanel](../reference/arkui-ts/ts-basic-components-datapanel.md) | No | false | / | 207| [DatePicker](../reference/arkui-ts/ts-basic-components-datepicker.md) | Yes | true | Linear navigation | 208| [Divider](../reference/arkui-ts/ts-basic-components-divider.md) | No | false | / | 209| [Formcomponent](../reference/arkui-ts/ts-basic-components-formcomponent.md) | No | false | / | 210| [Gauge](../reference/arkui-ts/ts-basic-components-gauge.md) | No | false | / | 211| [Image](../reference/arkui-ts/ts-basic-components-image.md) | Yes | false | / | 212| [ImageAnimator](../reference/arkui-ts/ts-basic-components-imageanimator.md) | Yes | false | / | 213| [LoadingProgress](../reference/arkui-ts/ts-basic-components-loadingprogress.md) | No | false | / | 214| [Marquee](../reference/arkui-ts/ts-basic-components-marquee.md) | No | false | / | 215| [Menu](../reference/arkui-ts/ts-basic-components-menu.md) | Yes | true | Linear navigation | 216| [MenuItem](../reference/arkui-ts/ts-basic-components-menuitem.md) | Yes | true | / | 217| [MenuItemGroup](../reference/arkui-ts/ts-basic-components-menuitemgroup.md) | Yes | true | Linear navigation | 218| [Navigation](../reference/arkui-ts/ts-basic-components-navigation.md) | No | false | Customized | 219| [NavRouter](../reference/arkui-ts/ts-basic-components-navrouter.md) | No | false | Follows the child container | 220| [NavDestination](../reference/arkui-ts/ts-basic-components-navdestination.md) | No | false | Linear navigation | 221| [PatternLock](../reference/arkui-ts/ts-basic-components-patternlock.md) | No | false | / | 222| [PluginComponent](../reference/arkui-ts/ts-basic-components-plugincomponent.md) | No | false | / | 223| [Progress](../reference/arkui-ts/ts-basic-components-progress.md) | No | false | / | 224| [QRCode](../reference/arkui-ts/ts-basic-components-qrcode.md) | No | false | / | 225| [Radio](../reference/arkui-ts/ts-basic-components-radio.md) | Yes | true | / | 226| [Rating](../reference/arkui-ts/ts-basic-components-rating.md) | Yes | true | / | 227| [RemoteWindow](../reference/arkui-ts/ts-basic-components-remotewindow.md) | No | false | / | 228| [RichText](../reference/arkui-ts/ts-basic-components-richtext.md) | No | false | / | 229| [ScrollBar](../reference/arkui-ts/ts-basic-components-scrollbar.md) | No | false | / | 230| [Search](../reference/arkui-ts/ts-basic-components-search.md) | Yes | true | / | 231| [Select](../reference/arkui-ts/ts-basic-components-select.md) | Yes | true | Linear navigation | 232| [Slider](../reference/arkui-ts/ts-basic-components-slider.md) | Yes | true | / | 233| [Span](../reference/arkui-ts/ts-basic-components-span.md) | No | false | / | 234| [Stepper](../reference/arkui-ts/ts-basic-components-stepper.md) | Yes | true | / | 235| [StepperItem](../reference/arkui-ts/ts-basic-components-stepperitem.md) | Yes | true | / | 236| [Text](../reference/arkui-ts/ts-basic-components-text.md) | Yes | false | / | 237| [TextArea](../reference/arkui-ts/ts-basic-components-textarea.md) | Yes | true | / | 238| [TextClock](../reference/arkui-ts/ts-basic-components-textclock.md) | No | false | / | 239| [TextInput](../reference/arkui-ts/ts-basic-components-textinput.md) | Yes | true | / | 240| [TextPicker](../reference/arkui-ts/ts-basic-components-textpicker.md) | Yes | true | Linear navigation | 241| [TextTimer](../reference/arkui-ts/ts-basic-components-texttimer.md) | No | false | / | 242| [TimePicker](../reference/arkui-ts/ts-basic-components-timepicker.md) | Yes | true | Linear navigation | 243| [Toggle](../reference/arkui-ts/ts-basic-components-toggle.md) | Yes | true | / | 244| [Web](../reference/arkui-ts/ts-basic-components-web.md) | Yes | true | Customized| 245| [XComponent](../reference/arkui-ts/ts-basic-components-xcomponent.md) | No | false | / | 246 247 **Table 2** Focusability of container components 248 249| Container Component | Focusable| Default Value of focusable| Rules of Focus Navigation | 250| ---------------------------------------- | ----- | ------------ | -------- | 251| [AbilityComponent](../reference/arkui-ts/ts-container-ability-component.md) | No | false | / | 252| [Badge](../reference/arkui-ts/ts-container-badge.md) | No | false | / | 253| [Column](../reference/arkui-ts/ts-container-column.md) | Yes | true | Linear navigation | 254| [ColumnSplit](../reference/arkui-ts/ts-container-columnsplit.md) | Yes | true | / | 255| [Counter](../reference/arkui-ts/ts-container-counter.md) | Yes | true | Linear navigation | 256| [Flex](../reference/arkui-ts/ts-container-flex.md) | Yes | true | Linear navigation | 257| [GridCol](../reference/arkui-ts/ts-container-gridcol.md) | Yes | true | Customized | 258| [GridRow](../reference/arkui-ts/ts-container-gridrow.md) | Yes | true | Customized | 259| [Grid](../reference/arkui-ts/ts-container-grid.md) | Yes | true | Customized | 260| [GridItem](../reference/arkui-ts/ts-container-griditem.md) | Yes | true | Follows the child component | 261| [List](../reference/arkui-ts/ts-container-list.md) | Yes | true | Linear navigation | 262| [ListItem](../reference/arkui-ts/ts-container-listitem.md) | Yes | true | Follows the child component | 263| [ListItemGroup](../reference/arkui-ts/ts-container-listitemgroup.md) | Yes | true | Follows the **\<List>** component| 264| [Navigator](../reference/arkui-ts/ts-container-navigator.md) | No | true | Customized | 265| [Panel](../reference/arkui-ts/ts-container-panel.md) | No | true | Follows the child component | 266| [Refresh](../reference/arkui-ts/ts-container-refresh.md) | No | false | / | 267| [RelativeContainer](../reference/arkui-ts/ts-container-relativecontainer.md) | No | true | Customized | 268| [Row](../reference/arkui-ts/ts-container-row.md) | Yes | true | Linear navigation | 269| [RowSplit](../reference/arkui-ts/ts-container-rowsplit.md) | Yes | true | / | 270| [Scroll](../reference/arkui-ts/ts-container-scroll.md) | Yes | true | Linear navigation | 271| [SideBarContainer](../reference/arkui-ts/ts-container-sidebarcontainer.md) | Yes | true | Linear navigation | 272| [Stack](../reference/arkui-ts/ts-container-stack.md) | Yes | true | Linear navigation | 273| [Swiper](../reference/arkui-ts/ts-container-swiper.md) | Yes | true | Customized | 274| [Tabs](../reference/arkui-ts/ts-container-tabs.md) | Yes | true | Customized | 275| [TabContent](../reference/arkui-ts/ts-container-tabcontent.md) | Yes | true | Follows the child component | 276 277 **Table 3** Focusability of media components 278 279| Media Component | Focusable| Default Value of focusable| Rules of Focus Navigation| 280| ---------------------------------------- | ----- | ------------ | ---- | 281| [Video](../reference/arkui-ts/ts-media-components-video.md) | Yes | true | / | 282 283 **Table 4** Focusability of canvas components 284 285| Canvas Component | Focusable| Default Value of focusable| Rules of Focus Navigation| 286| ---------------------------------------- | ----- | ------------ | ---- | 287| [Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md) | No | false | / | 288 289 290The following example shows how to use the **focusable** API: 291 292 293 294```ts 295// xxx.ets 296@Entry 297@Component 298struct FocusableExample { 299 @State textFocusable: boolean = true; 300 @State color1: Color = Color.Yellow; 301 @State color2: Color = Color.Yellow; 302 303 build() { 304 Column({ space: 5 }) { 305 Text('Default Text') // The focusable attribute is not set for the first <Text> component. By default, the component is unfocusable. 306 .borderColor(this.color1) 307 .borderWidth(2) 308 .width(300) 309 .height(70) 310 .onFocus(() => { 311 this.color1 = Color.Blue; 312 }) 313 .onBlur(() => { 314 this.color1 = Color.Yellow; 315 }) 316 Divider() 317 318 Text('focusable: ' + this.textFocusable) // The focusable attribute is set for the second <Text> component. The initial value is true. 319 .borderColor(this.color2) 320 .borderWidth(2) 321 .width(300) 322 .height(70) 323 .focusable(this.textFocusable) 324 .onFocus(() => { 325 this.color2 = Color.Blue; 326 }) 327 .onBlur(() => { 328 this.color2 = Color.Yellow; 329 }) 330 331 Divider() 332 333 Row() { 334 Button('Button1') 335 .width(140).height(70) 336 Button('Button2') 337 .width(160).height(70) 338 } 339 340 Divider() 341 Button('Button3') 342 .width(300).height(70) 343 344 Divider() 345 }.width('100%').justifyContent(FlexAlign.Center) 346 .onKeyEvent((e) => { // Bind onKeyEvent. When the <Column> component is focused, pressing F can reverse the focusable attribute of the second <Text> component. 347 if (e.keyCode === 2022 && e.type === KeyType.Down) { 348 this.textFocusable = !this.textFocusable; 349 } 350 }) 351 } 352} 353``` 354 355 356Operation result: 357 358 359![en-us_image_0000001511900540](figures/en-us_image_0000001511900540.gif) 360 361 362The preceding example includes two parts: default focus and active navigation. 363 364 365**Default focus:** 366 367 368- According to the definition of the default focus, after the application is opened, the first focusable element is focused by default. 369 370- As the **focusable** attribute is not set for the first **\<Text>** component, it cannot be focused. 371 372- The **focusable** attribute of the second **\<Text>** component is explicitly set to **true**. In this case, the default focus is placed on the component. 373 374 375**Active navigation:** 376 377 378Pressing **F** on the keyboard triggers **onKeyEvent**, which sets **focusable** to **false** and makes the **\<Text>** component unfocusable. In this case, the focus automatically shifts. According to the description in passive focus, the system automatically searches for the immediate focusable component above the **\<Text>** component, which is an unfocusable **\<Text>** component. Therefore, the system searches for the next focusable component, finds and moves the focus to the **\<Row>** container, and calculates the positions of **Button1** and **Button2** based on the [rule for focusing on a container component](#rules-of-focus-navigation). Because **Button2** is larger than **Button1**, the focus automatically moves to **Button2**. 379 380 381## Setting Default Focus 382 383 384```ts 385defaultFocus(value: boolean) 386``` 387 388When the page is constructed for the first time, the focus system searches for all components on the current page, finds the first component bound to **defaultFocus(true)**, and sets the component as the default focus. If no component is bound to **defaultFocus(true)**, the first focusable component is set as the default focus. 389 390Below is an application layout. 391 392![en-us_image_0000001563060793](figures/en-us_image_0000001563060793.png) 393 394The following is the sample code for implementing the application layout, and **defaultFocus** is not set in the sample code: 395 396 397```ts 398// xxx.ets 399import promptAction from '@ohos.promptAction'; 400 401class MyDataSource implements IDataSource { 402 private list: number[] = []; 403 private listener: DataChangeListener; 404 405 constructor(list: number[]) { 406 this.list = list; 407 } 408 409 totalCount(): number { 410 return this.list.length; 411 } 412 413 getData(index: number): any { 414 return this.list[index]; 415 } 416 417 registerDataChangeListener(listener: DataChangeListener): void { 418 this.listener = listener; 419 } 420 421 unregisterDataChangeListener() { 422 } 423} 424 425@Entry 426@Component 427struct SwiperExample { 428 private swiperController: SwiperController = new SwiperController() 429 private data: MyDataSource = new MyDataSource([]) 430 431 aboutToAppear(): void { 432 let list = [] 433 for (let i = 1; i <= 4; i++) { 434 list.push(i.toString()); 435 } 436 this.data = new MyDataSource(list); 437 } 438 439 build() { 440 Column({ space: 5 }) { 441 Swiper(this.swiperController) { 442 LazyForEach(this.data, (item: string) => { 443 Row({ space: 20 }) { 444 Column() { 445 Button('1').width(200).height(200) 446 .fontSize(40) 447 .backgroundColor('#dadbd9') 448 } 449 450 Column({ space: 20 }) { 451 Row({ space: 20 }) { 452 Button('2') 453 .width(100) 454 .height(100) 455 .fontSize(40) 456 .type(ButtonType.Normal) 457 .borderRadius(20) 458 .backgroundColor('#dadbd9') 459 Button('3') 460 .width(100) 461 .height(100) 462 .fontSize(40) 463 .type(ButtonType.Normal) 464 .borderRadius(20) 465 .backgroundColor('#dadbd9') 466 } 467 468 Row({ space: 20 }) { 469 Button('4') 470 .width(100) 471 .height(100) 472 .fontSize(40) 473 .type(ButtonType.Normal) 474 .borderRadius(20) 475 .backgroundColor('#dadbd9') 476 Button('5') 477 .width(100) 478 .height(100) 479 .fontSize(40) 480 .type(ButtonType.Normal) 481 .borderRadius(20) 482 .backgroundColor('#dadbd9') 483 } 484 485 Row({ space: 20 }) { 486 Button('6') 487 .width(100) 488 .height(100) 489 .fontSize(40) 490 .type(ButtonType.Normal) 491 .borderRadius(20) 492 .backgroundColor('#dadbd9') 493 Button('7') 494 .width(100) 495 .height(100) 496 .fontSize(40) 497 .type(ButtonType.Normal) 498 .borderRadius(20) 499 .backgroundColor('#dadbd9') 500 } 501 } 502 } 503 .width(480) 504 .height(380) 505 .justifyContent(FlexAlign.Center) 506 .borderWidth(2) 507 .borderColor(Color.Gray) 508 .backgroundColor(Color.White) 509 }, item => item) 510 } 511 .cachedCount(2) 512 .index(0) 513 .interval(4000) 514 .indicator(true) 515 .loop(true) 516 .duration(1000) 517 .itemSpace(0) 518 .curve(Curve.Linear) 519 .onChange((index: number) => { 520 console.info(index.toString()); 521 }) 522 .margin({ left: 20, top: 20, right: 20 }) 523 524 Row({ space: 40 }) { 525 Button('←') 526 .fontSize(40) 527 .fontWeight(FontWeight.Bold) 528 .fontColor(Color.Black) 529 .backgroundColor(Color.Transparent) 530 .onClick(() => { 531 this.swiperController.showPrevious(); 532 }) 533 Button('→') 534 .fontSize(40) 535 .fontWeight(FontWeight.Bold) 536 .fontColor(Color.Black) 537 .backgroundColor(Color.Transparent) 538 .onClick(() => { 539 this.swiperController.showNext(); 540 }) 541 } 542 .width(480) 543 .height(50) 544 .justifyContent(FlexAlign.Center) 545 .borderWidth(2) 546 .borderColor(Color.Gray) 547 .backgroundColor('#f7f6dc') 548 549 Row({ space: 40 }) { 550 Button('Cancel') 551 .fontSize(30) 552 .fontColor('#787878') 553 .type(ButtonType.Normal) 554 .width(140) 555 .height(50) 556 .backgroundColor('#dadbd9') 557 558 Button('OK') 559 .fontSize(30) 560 .fontColor('#787878') 561 .type(ButtonType.Normal) 562 .width(140) 563 .height(50) 564 .backgroundColor('#dadbd9') 565 .onClick(() => { 566 promptAction.showToast({ message: 'Button OK on clicked' }); 567 }) 568 } 569 .width(480) 570 .height(80) 571 .justifyContent(FlexAlign.Center) 572 .borderWidth(2) 573 .borderColor(Color.Gray) 574 .backgroundColor('#dff2e4') 575 .margin({ left: 20, bottom: 20, right: 20 }) 576 }.backgroundColor('#f2f2f2') 577 .margin({ left: 50, top: 50, right: 20 }) 578 } 579} 580``` 581 582 583As **defaultFocus** is not set in the application, the first focusable component obtains the focus by default. Pressing the Tab key or arrow keys can set the focused component to enter the focused state. 584 585 586![en-us_image_0000001511421360](figures/en-us_image_0000001511421360.gif) 587 588 589Assume that you want to perform the **onClick** callback of the **OK** button without switching the focus when opening the application. In this case, you can bind **defaultFocus(true)** to the button, make it the default focus on the page. 590 591 592 593```ts 594Button('OK') 595 .defaultFocus(true) // Bind defaultFocus to the OK button. 596 .fontSize(30) 597 .fontColor('#787878') 598 .type(ButtonType.Normal) 599 .width(140).height(50).backgroundColor('#dadbd9') 600 .onClick(() => { 601 promptAction.showToast({ message: 'Button OK on clicked' }); 602 }) 603``` 604 605 606![en-us_image_0000001562940617](figures/en-us_image_0000001562940617.gif) 607 608 609When the application is opened, pressing the Tab key switches the **OK** button to the focused state, indicating that the default focus is changed to the button. After the space key is pressed, the **onClick **event of the **OK** button is triggered. 610 611 612## Setting the Order for Sequential Tab Navigation 613 614 615```ts 616tabIndex(index: number) 617``` 618 619Use **tabIndex** to set the order for sequential Tab navigation. The default value is **0**. In Tab navigation, where Tab/Shift+Tab is used (the arrow keys do not affect the navigation), the focus system automatically obtains all components whose **tabIndex** is greater than 0 and moves focus in ascending or descending order. 620 621 622Take the example provided by [defaultFocus](#setting-default-focus) as an example. The default order for sequential focus navigation is as follows: 623 624 625![en-us_image_0000001511421364](figures/en-us_image_0000001511421364.gif) 626 627 628The default order for sequential Tab navigation is from the first focusable component to the last focusable component, and the process goes through Button1 -> Button4 -> Button5 -> Button7 -> Left arrow -> Right arrow -> ButtonOK. This focus navigation queue is relatively complete and traverses most of the components. However, the disadvantage is that the path from the first to the last is long. 629 630 631If you want to quickly go from the first to the last without sacrificing too much traversal integrity, you can use the **tabIndex** attribute. 632 633 634For example, take the white area, the yellow area, and the green area each as a unit. To implement the focus navigation queue of Button1 -> Left arrow -> Button-OK, you only need to add **tabIndex(1)**, **tabIndex(2)**, and **tabIndex(3)** to the Button1, left arrow, and ButtonOK components in sequence. The **tabIndex** attribute indicates how a component participates in sequential Tab navigation. A component with a larger value gains focus later than one with a smaller value. 635 636 637 638```ts 639 Button('1').width(200).height(200) 640 .fontSize(40) 641 .backgroundColor('#dadbd9') 642 .tabIndex(1) // Set Button1 as the first tabIndex node. 643``` 644 645 646 647```ts 648 Button('←') 649 .fontSize(40) 650 .fontWeight(FontWeight.Bold) 651 .fontColor(Color.Black) 652 .backgroundColor(Color.Transparent) 653 .onClick(() => { 654 this.swiperController.showPrevious(); 655 }) 656 .tabIndex(2) // Set Button-left arrow as the second tabIndex node. 657``` 658 659 660 661```ts 662Button('OK') 663 .fontSize(30) 664 .fontColor('#787878') 665 .type(ButtonType.Normal) 666 .width(140).height(50).backgroundColor('#dadbd9') 667 .onClick(() => { 668 promptAction.showToast({ message: 'Button OK on clicked' }); 669 }) 670 .tabIndex(3) // Set Button-OK as the third tabIndex node. 671``` 672 673 674![en-us_image_0000001511580976](figures/en-us_image_0000001511580976.gif) 675 676 677>**NOTE** 678> - When the focus is on a tabIndex (greater than 0) node, after Tab/Shift+Tab is pressed, the focus system preferentially searches for the rear/front node in the tabIndex (greater than 0) queue. If the rear/front node exists, the focus system moves the focus to that node. If the node does not exist, the default focus logic is used to move the focus backward or forward. 679> 680> - When the focus is on the tabIndex (equal to 0) node, the focus system uses the default focus navigation logic. During the navigation, the tabIndex (greater than 0) and tabIndex (less than 0) nodes are skipped. 681> 682> - When the focus is on a tabIndex (less than 0) node, pressing Tab/Shift+Tab does not move the focus. 683 684 685### groupDefaultFocus 686 687 688```ts 689groupDefaultFocus(value: boolean) 690``` 691 692Using **tabIndex** to [set the order for sequential Tab navigation](#setting-the-order-for-sequential-tab-navigation) has the following issues: 693 694While a component is set as a tabIndex node (white-Button1, yellow-left arrow, and green-ButtonOK) in each area (white, yellow, and green), focus moves quicly only within these components in Tab navigation. 695 696The solution is to set **tabIndex** for the container of each area. However, when a container receives focus for the first time, the focused child component is the first focusable component by default, not the desired component (Button1, left arrow, and ButtonOK). 697 698To address this issue, the **groupDefaultFocus** attribute is introduced, whose value type is boolean and default value is **false**. 699 700This attribute must be used together with **tabIndex**. Use **tabIndex** to bind the focus sequence to the areas (containers), and then bind **groupDefaultFocus(true)** to Button1, left arrow, and ButtonOK. In this way, when the target area (container) is focused for the first time, its child components bound to **groupDefaultFocus(true)** get the focus at the same time. 701 702 703```ts 704// xxx.ets 705import promptAction from '@ohos.promptAction'; 706 707class MyDataSource implements IDataSource { 708 private list: number[] = []; 709 private listener: DataChangeListener; 710 711 constructor(list: number[]) { 712 this.list = list; 713 } 714 715 totalCount(): number { 716 return this.list.length; 717 } 718 719 getData(index: number): any { 720 return this.list[index]; 721 } 722 723 registerDataChangeListener(listener: DataChangeListener): void { 724 this.listener = listener; 725 } 726 727 unregisterDataChangeListener() { 728 } 729} 730 731@Entry 732@Component 733struct SwiperExample { 734 private swiperController: SwiperController = new SwiperController() 735 private data: MyDataSource = new MyDataSource([]) 736 737 aboutToAppear(): void { 738 let list = [] 739 for (let i = 1; i <= 4; i++) { 740 list.push(i.toString()); 741 } 742 this.data = new MyDataSource(list); 743 } 744 745 build() { 746 Column({ space: 5 }) { 747 Swiper(this.swiperController) { 748 LazyForEach(this.data, (item: string) => { 749 Row({ space: 20 }) { // Set the <Row> component as the first tabIndex node. 750 Column() { 751 Button('1').width(200).height(200) 752 .fontSize(40) 753 .backgroundColor('#dadbd9') 754 .groupDefaultFocus(true) // Set Button-1 as the default focus of the first tabIndex node. 755 } 756 757 Column({ space: 20 }) { 758 Row({ space: 20 }) { 759 Button('2') 760 .width(100) 761 .height(100) 762 .fontSize(40) 763 .type(ButtonType.Normal) 764 .borderRadius(20) 765 .backgroundColor('#dadbd9') 766 Button('3') 767 .width(100) 768 .height(100) 769 .fontSize(40) 770 .type(ButtonType.Normal) 771 .borderRadius(20) 772 .backgroundColor('#dadbd9') 773 } 774 775 Row({ space: 20 }) { 776 Button('4') 777 .width(100) 778 .height(100) 779 .fontSize(40) 780 .type(ButtonType.Normal) 781 .borderRadius(20) 782 .backgroundColor('#dadbd9') 783 Button('5') 784 .width(100) 785 .height(100) 786 .fontSize(40) 787 .type(ButtonType.Normal) 788 .borderRadius(20) 789 .backgroundColor('#dadbd9') 790 } 791 792 Row({ space: 20 }) { 793 Button('6') 794 .width(100) 795 .height(100) 796 .fontSize(40) 797 .type(ButtonType.Normal) 798 .borderRadius(20) 799 .backgroundColor('#dadbd9') 800 Button('7') 801 .width(100) 802 .height(100) 803 .fontSize(40) 804 .type(ButtonType.Normal) 805 .borderRadius(20) 806 .backgroundColor('#dadbd9') 807 } 808 } 809 } 810 .width(480) 811 .height(380) 812 .justifyContent(FlexAlign.Center) 813 .borderWidth(2) 814 .borderColor(Color.Gray) 815 .backgroundColor(Color.White) 816 .tabIndex(1) 817 }, item => item) 818 } 819 .cachedCount(2) 820 .index(0) 821 .interval(4000) 822 .indicator(true) 823 .loop(true) 824 .duration(1000) 825 .itemSpace(0) 826 .curve(Curve.Linear) 827 .onChange((index: number) => { 828 console.info(index.toString()); 829 }) 830 .margin({ left: 20, top: 20, right: 20 }) 831 832 Row({ space: 40 }) { // Set the <Row> component as the second tabIndex node. 833 Button('←') 834 .fontSize(40) 835 .fontWeight(FontWeight.Bold) 836 .fontColor(Color.Black) 837 .backgroundColor(Color.Transparent) 838 .onClick(() => { 839 this.swiperController.showPrevious(); 840 }) 841 .groupDefaultFocus(true) // Set the Button-left arrow as the default focus of the second tabIndex node. 842 Button('→') 843 .fontSize(40) 844 .fontWeight(FontWeight.Bold) 845 .fontColor(Color.Black) 846 .backgroundColor(Color.Transparent) 847 .onClick(() => { 848 this.swiperController.showNext(); 849 }) 850 } 851 .width(480) 852 .height(50) 853 .justifyContent(FlexAlign.Center) 854 .borderWidth(2) 855 .borderColor(Color.Gray) 856 .backgroundColor('#f7f6dc') 857 .tabIndex(2) 858 859 Row({ space: 40 }) { // Set the <Row> component as the third tabIndex node. 860 Button('Cancel') 861 .fontSize(30) 862 .fontColor('#787878') 863 .type(ButtonType.Normal) 864 .width(140) 865 .height(50) 866 .backgroundColor('#dadbd9') 867 868 Button('OK') 869 .fontSize(30) 870 .fontColor('#787878') 871 .type(ButtonType.Normal) 872 .width(140) 873 .height(50) 874 .backgroundColor('#dadbd9') 875 .defaultFocus(true) 876 .onClick(() => { 877 promptAction.showToast({ message: 'Button OK on clicked' }); 878 }) 879 .groupDefaultFocus(true) // Set Button-OK as the default focus of the third tabIndex node. 880 } 881 .width(480) 882 .height(80) 883 .justifyContent(FlexAlign.Center) 884 .borderWidth(2) 885 .borderColor(Color.Gray) 886 .backgroundColor('#dff2e4') 887 .margin({ left: 20, bottom: 20, right: 20 }) 888 .tabIndex(3) 889 }.backgroundColor('#f2f2f2') 890 .margin({ left: 50, top: 50, right: 20 }) 891 } 892} 893``` 894 895![en-us_image_0000001562700533](figures/en-us_image_0000001562700533.gif) 896 897 898### focusOnTouch 899 900 901```ts 902focusOnTouch(value: boolean) 903``` 904 905Sets whether a component is focusable on touch (touching or left-clicking). The parameter value type is boolean and the default value is **false**. The default value is **true** for input components: TextInput, TextArea, Search, and Web. 906 907By binding **focusOnTouch(true)** to a component whose default value is **false**, such as **\<Button>**, you enable the component to become focused on touch. 908 909When **focusOnTouch(true)** is bound to a container and the container area is clicked, the first focusable component of the container is immediately focused. 910 911The sample code is as follows: 912 913 914```ts 915// requestFocus.ets 916import promptAction from '@ohos.promptAction'; 917 918@Entry 919@Component 920struct RequestFocusExample { 921 @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N'] 922 923 build() { 924 Column({ space:20 }){ 925 Button("id: " + this.idList[0] + " focusOnTouch(true) + focusable(false)") 926 .width(400).height(70).fontColor(Color.White).focusOnTouch(true) 927 .focusable(false) 928 Button("id: " + this.idList[1] + " default") 929 .width(400).height(70).fontColor(Color.White) 930 Button("id: " + this.idList[2] + " focusOnTouch(false)") 931 .width(400).height(70).fontColor(Color.White).focusOnTouch(false) 932 Button("id: " + this.idList[3] + " focusOnTouch(true)") 933 .width(400).height(70).fontColor(Color.White).focusOnTouch(true) 934 }.width('100%').margin({ top:20 }) 935 } 936} 937``` 938 939 940![en-us_image_0000001511580980](figures/en-us_image_0000001511580980.gif) 941 942 943Interpretation: 944 945 946Because **focusOnTouch(true)** and **focusable(false)** are both set for Button-A, the component is unfocusable and cannot be focused on touch. 947 948 949No related attributes are set for Button-B, and therefore it cannot be focused on touch. 950 951 952**focusOnTouch(false)** is set for Button-C, and therefore it cannot be focused on touch, just as Button-B. 953 954 955**focusOnTouch(true)** is set for Button-D, and therefore it is focused on touch. 956 957 958>**NOTE** 959> 960>Due to the feature of the focused state, the focused state is cleared immediately after the screen receives a touch event. Therefore, each time a component is clicked, you need to press the Tab key again to display the focused state again. In this way, you can know the component where the focus is located. 961 962 963### focusControl.requestFocus 964 965 966```ts 967focusControl.requestFocus(id: string) 968``` 969 970Requests the focus to move to the specified component. This API can be used in global method statements. The parameter **id** indicates the target component to focus, which is the string bound to the component using the universal attribute **id**. 971 972 973The usage method is as follows: Invoke the API in any execution statement and specify the ID of the target component as the input parameter. When the program executes the statement, it immediately requests focus for the specified target component. 974 975 976Sample code: 977 978 979 980```ts 981// requestFocus.ets 982import promptAction from '@ohos.promptAction'; 983 984@Entry 985@Component 986struct RequestFocusExample { 987 @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N'] 988 @State requestId: number = 0 989 990 build() { 991 Column({ space:20 }){ 992 Row({space: 5}) { 993 Button("id: " + this.idList[0] + " focusable(false)") 994 .width(200).height(70).fontColor(Color.White) 995 .id(this.idList[0]) 996 .focusable(false) 997 Button("id: " + this.idList[1]) 998 .width(200).height(70).fontColor(Color.White) 999 .id(this.idList[1]) 1000 } 1001 Row({space: 5}) { 1002 Button("id: " + this.idList[2]) 1003 .width(200).height(70).fontColor(Color.White) 1004 .id(this.idList[2]) 1005 Button("id: " + this.idList[3]) 1006 .width(200).height(70).fontColor(Color.White) 1007 .id(this.idList[3]) 1008 } 1009 Row({space: 5}) { 1010 Button("id: " + this.idList[4]) 1011 .width(200).height(70).fontColor(Color.White) 1012 .id(this.idList[4]) 1013 Button("id: " + this.idList[5]) 1014 .width(200).height(70).fontColor(Color.White) 1015 .id(this.idList[5]) 1016 } 1017 }.width('100%').margin({ top:20 }) 1018 .onKeyEvent((e) => { 1019 if (e.keyCode >= 2017 && e.keyCode <= 2022) { 1020 this.requestId = e.keyCode - 2017; 1021 } else if (e.keyCode === 2030) { 1022 this.requestId = 6; 1023 } else { 1024 return; 1025 } 1026 if (e.type !== KeyType.Down) { 1027 return; 1028 } 1029 let res = focusControl.requestFocus(this.idList[this.requestId]); 1030 if (res) { 1031 promptAction.showToast({message: 'Request success'}); 1032 } else { 1033 promptAction.showToast({message: 'Request failed'}); 1034 } 1035 }) 1036 } 1037} 1038``` 1039 1040 1041![en-us_image_0000001562820905](figures/en-us_image_0000001562820905.gif) 1042 1043 1044Interpretation: There are six **\<Button>** components on the page. **Focusable(false)** is set for Button-A, indicating that Button-A cannot be focused. In **onKeyEvent** of the external container, key events are listened. When A to F keys are pressed, the focus is requested for Buttons A to F. If you press N, the focus is requested the component whose ID does not exist on the current page. 1045 1046 10471. Press the Tab key. Because the first component Button-A cannot be focused, the second component Button-B is focused by default, and Button-B is displayed in the focused state. 1048 10492. Press A on the keyboard to request the focus for Button-A. The message "Request failed" is displayed, indicating that the focus cannot be obtained. The focus position remains unchanged. 1050 10513. Press B on the keyboard to request the focus for Button-B. The message "Request success" is displayed, indicating that the focus is on Button-B. The focus position remains unchanged. 1052 10534. Press C on the keyboard to request the focus for Button-C. The message "Request success" is displayed, indicating that the focus is on Button-C. The focus position changes from Button-B to Button-C. 1054 10555. Press D on the keyboard to request the focus for Button-D. The message "Request success" is displayed, indicating that the focus is on Button-D. The focus position changes from Button-C to Button-D. 1056 10576. Press E on the keyboard to request the focus for Button-E. The message "Request success" is displayed, indicating that the focus is on Button-E. The focus position changes from Button-D to Button-E. 1058 10597. Press F on the keyboard to request the focus for Button-F. The message "Request success" is displayed, indicating that the focus is on Button-F. The focus position changes from Button-E to Button-F. 1060 10618. Press N on the keyboard to request the focus for an unknown component. The message "Request failed" is displayed, indicating that the focus cannot be obtained and the focus position remains unchanged. 1062