• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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