DEV Community

魔眼天王
魔眼天王

Posted on

HarmonyOS Focus and Focus Navigation Technology Sharing

I. Core Mechanisms of the Focus System

1.1 Focus Chain Formation Rules

HarmonyOS adopts a tree-based focus chain mechanism. When a component gains focus, all its parent components automatically form a focus chain. For example, in a nested scenario where a Row is inside a Column, the focus chain will follow the hierarchy: Page > Column > Row > TextInput. Developers can verify the focus chain status via the onFocus callback.

1.2 Activation State Control

​Activation Condition: Triggered by the first TAB key press on an external keyboard.
​Exit Mechanism: Immediately exits activation state upon any touch event (including hover).
​Debugging Tip: Monitor the state in real time using FocusController.isActivated.

II. Focus Solutions for Complex Layouts

2.1 Precise Focus Positioning

​Problem Scenario: Need to directly position focus on a specific Button within a deeply nested List cell.
​Implementation Solution:

// Parent List sets focus navigation strategy List({ space: 20 }) { ForEach(listData, (item, index) => { ListItem() { // Critical control configuration Button("Operation") .focusable(true) .tabIndex(index === targetIndex ? 1 : -1) // Force specify TAB order .requestFocus({ id: `btn-${index}` }) // Programmatic focus .onFocus(() => console.log("Precise positioning successful")) } }) } 
Enter fullscreen mode Exit fullscreen mode

2.2 Z-shaped Focus Navigation

​Scenario Requirement: After focus is on the last element of a horizontal Row, pressing the down arrow key should jump to the first element of the lower Column.
​Configuration Method:

Row({ alignContent: Alignment.Center }) { ForEach(horizontalItems, (item) => { Item().focusable(true).tabIndex(1) }) } .tabIndex(2) // Set ROW's TAB priority .groupDefaultFocus(true) // Allow cross-level focusing Column() { // Lower container captures focus Container() .focusable(true) .tabIndex(3) .onFocus(() => console.log("Cross-level focus successful")) } 
Enter fullscreen mode Exit fullscreen mode

III. In-depth Control of Focus Styles

3.1 Style Configuration Solutions

Property Type Supported Style Types Effective Conditions
focusBox Border/background color/corner radius Requires focusable=true
focusColor Text highlight color Specific to input-type components
focusShadow Projection effect Requires elevation>0

​Solution to Style Override Issues:

// Force override parent styles Button() .focusBox({ strokeColor: Color.Red, // Red border strokeWidth: 3, // 3px line width margin: LengthMetrics.px(5) // Extended margin }) .parentStyleOverride(true) // Key override property 
Enter fullscreen mode Exit fullscreen mode

3.2 Debugging for Incomplete Style Display

​Common Issue: Custom focus box is clipped.
​Troubleshooting Steps:

  • Check if the parent container's overflow property is set to Visible.

  • Verify if focusBox.margin exceeds the parent container's boundaries.

  • Use LayoutInspector to check the actual rendered dimensions.

  • Add flexGrow: 1 to ensure container adaptability.

IV. Focus Navigation Strategies

4.1 Direction Control

​Scenario: Implement cross-shaped focus navigation in a Flex container.

Flex({ direction: FlexDirection.Row }) { ForEach(items, (item) => { Item() .flexGrow(1) .onKeyDown((event) => { if(event.key === Key.ArrowDown) { // Manually control cross-row jump nextRowItem.requestFocus() return true // Prevent default focus navigation } }) }) } 
Enter fullscreen mode Exit fullscreen mode

4.2 Dynamic Layout Adaptation

​Focus Maintenance During Dynamic List Item Loading:

List({ space: 20 }) { Scroll() { ForEach(dynamicData, (item) => { ListItem() .focusable(true) .defaultFocus(index === 0) // Default focus on first item .onFocusLost(() => { // Record position on focus loss lastFocusedIndex = index }) }) } .onScrollEnd(() => { // Restore focus after scrolling if(lastFocusedIndex !== -1) { listRef.scrollToItem(lastFocusedIndex) } }) } 
Enter fullscreen mode Exit fullscreen mode

V. Handling Special Scenarios

5.1 Modal Dialog Focus Management

Dialog() { // Force dialog to gain focus .focusable(true) .defaultFocus(true) .onDismissed(() => { // Return to original focus when closed originalFocusComponent.requestFocus() }) } 
Enter fullscreen mode Exit fullscreen mode

5.2 Cross-component Focus Transfer

​Automatic Transfer Between Parent and Child Components:

// Parent Component @Component struct Parent { @Link childFocus: boolean = false build() { Column() { Child({ onFocus: () => this.childFocus = true }) Button("Jump") .onClick(() => childComponent.requestFocus()) } } } // Child Component @Component struct Child { @Prop onFocus: () => void build() { TextInput() .onFocus(() => this.onFocus()) } } 
Enter fullscreen mode Exit fullscreen mode

VI. Performance Optimization

  • ​Focus Preloading: Preset defaultFocus during page initialization.

  • ​Invalid Focus Cleanup: Promptly call clearFocus() to release invalid focus.

  • ​Batch Operation Optimization:

// Batch set non-focusable listItems.forEach(item => { item.focusable(false) }) 
Enter fullscreen mode Exit fullscreen mode
  • ​Focus Event Throttling:
onFocus(() => { throttle(() => { // Processing logic }, 300) }) 
Enter fullscreen mode Exit fullscreen mode

VII. Debugging Tools

Focus Visualization

// Enable focus border display setGlobalFocusStyle({ showBorder: true, borderColor: Color.Blue }) 
Enter fullscreen mode Exit fullscreen mode

Focus Path Logging

FocusController.onPathChange((path) => { console.log("Current focus path:", path) }) 
Enter fullscreen mode Exit fullscreen mode

Performance Monitoring

PerformanceMonitor.start('focus') // ... Operations ... PerformanceMonitor.stop('focus') 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)