ArkUI 学习之焦点控制

一、前言

自定义组件的走焦效果,可设置组件是否走焦和具体的走焦顺序,tab键或者方向键切换焦点。

🔈说明

自定义组件无获焦能力,当设置focusableenabled等属性为false,或者设置visibility属性为Hidden、None时,也不影响其子组件的获焦。
组件主动获取焦点不受窗口焦点的控制。

二、属性

1. focusable

🔈:设置当前组件是否可以获焦。
组件类型获焦能力默认焦点设定
默认可获焦组件 有交互行为的组件,例如Button、Checkbox,TextInput组件,此类组件无需设置任何属性,默认即可获焦。
有获焦能力但默认不可获焦组件 Text、Image组件等,缺省情况下无法获焦,可使用focusable(true)使能。
无获焦能力的组件 无交互行为的展示类组件,例如Blank、Circle组件,即使使用focusable属性也无法使其可获焦。
focusable(value: boolean)

参数

参数名类型必填
value boolean

说明

存在默认交互逻辑的组件例如ButtonTextInput等,默认即为可获焦,TextImage等组件则默认状态为不可获焦。不可获焦状态下,无法触发焦点事件

示例

点击“tab”切换获取焦点

@Entry
@Component
struct RequestFocusExample {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 20 }) {
        /** ! 默认存在交互逻辑的组件 意思是这些组件默认focusable为true */
        Column() {
          /** 输入框 */
          TextInput({ placeholder: '输入框' })
          /** 按钮 */
          Button("按钮")
        }

        /** ! 默认不存在交互逻辑的组件 意思是这些组件默认focusable为false */
        Column({ space: 10 }) {
          /** 文本 */
          Text('文本').fontSize(24).fontWeight(500)
          /** 图片 */
          Image($r('app.media.test')).size({ width: '100', height: '100' })
        }

      }.width('100%').padding({ left: 20, right: 20 })
    }.height('100%')
  }
}

2. tabIndex

🔈:自定义组件tab键走焦能力。
tabIndex(index: number)

参数

参数名类型必填默认值
index number 0

说明:

🌾:若有配置了tabIndex大于0的组件,则tab键走焦只会在tabIndex大于0的组件内按照tabIndex的值从小到大并循环依次走焦。

1. tabIndex >= 0:表示元素是可聚焦的,并且可以通过tab键走焦来访问到该元素。

@Entry
@Component
struct RequestFocusExample {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 20 }) {
        /** ! 自定义tabIndex控制走焦顺序 */
        TextInput({ placeholder: '输入框一' })
          .tabIndex(3)
        TextInput({ placeholder: '输入框二' })
          .tabIndex(1)
        TextInput({ placeholder: '输入框三' })
          .tabIndex(4)
        TextInput({ placeholder: '输入框四' })
          .tabIndex(0)
        TextInput({ placeholder: '输入框五' })
          .tabIndex(-1)
      }.width('100%').padding({ left: 20, right: 20 })
    }.height('100%')
  }
}

🔈:设置了 "TabIndex" 焦点不会按照系统走焦进行了,会按照“输入框二” -> “输入框三” -> “输入框四”,且不会走“输入框四”,“输入五”。
🌾:若没有配置tabIndex大于0的组件,则tabIndex等于0的组件按照组件预设的走焦规则走焦。

2. tabIndex < 0(通常是tabIndex = -1):表示元素是可聚焦的,但是不能通过tab键走焦来访问到该元素。

@Entry
@Component
struct RequestFocusExample {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 20 }) {
        /** ! 自定义tabIndex控制走焦顺序 */
        TextInput({ placeholder: '输入框一' })
          .tabIndex(0)
        TextInput({ placeholder: '输入框二' })
          .tabIndex(0)
        TextInput({ placeholder: '输入框三' })
          .tabIndex(0)
        TextInput({ placeholder: '输入框四' })
          .tabIndex(-1)
        TextInput({ placeholder: '输入框五' })
          .tabIndex(-5)
      }.width('100%').padding({ left: 20, right: 20 })
    }.height('100%')
  }
}

🔈:会按照系统走焦进行了,会按照“输入框一” -> “输入框二” -> “输入框三”,且不会走“输入框四”,“输入框五”。因为“输入框四”,“输入框五”设置tabIndex < 0
⚠️ UiExtension组件未适配tabIndex,在含有UiExtension组件的页面使用tabIndex会导致走焦错乱。

3. defaultFocus

🔈:设置当前组件是否为当前页面上的默认焦点。仅在初次创建的页面第一次进入时生效。
defaultFocus(value: boolean)

参数

参数名类型必填默认值
value boolean

false

说明:值为true则表示为默认焦点,值为false无效。

🌾:若页面内无任何组件设置defaultFocus(true),页面的默认焦点就是页面的根容器。
@Entry
@Component
struct RequestFocusExample {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 20 }) {
        /** ! defaultFocus:不设置 */
        TextInput({ placeholder: '输入框一' })
        TextInput({ placeholder: '输入框二' })
        TextInput({ placeholder: '输入框三' })
      }.width('100%').padding({ left: 20, right: 20 })
    }.height('100%')
  }
}

🌾:若某页面内有多个组件设置了defaultFocus(true),则以组件树深度遍历找到的第一个组件为默认焦点。
@Entry
@Component
struct RequestFocusExample {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 20 }) {
        /** ! defaultFocus:设置 */
        TextInput({ placeholder: '输入框一' })
          .defaultFocus(true)
        TextInput({ placeholder: '输入框二' })
          .defaultFocus(true)
        TextInput({ placeholder: '输入框三' })
          .defaultFocus(true)
      }.width('100%').padding({ left: 20, right: 20 })
    }.height('100%')
  }
}

4. groupDefaultFocus

🔈:设置当前组件是否为当前组件所在容器获焦时的默认焦点,仅在初次创建容器节点第一次获焦时生效。
groupDefaultFocus(value: boolean)

参数

参数名类型必填默认值
value boolean

 

false

 

说明

🌾:必须与tabIndex联合使用,当某个容器设置了tabIndex,且容器内某子组件或容器自身设置了groupDefaultFocus(true),当该容器首次TAB键获焦时,会自动将焦点转移至该指定的组件上。若容器内(包含容器本身)有多个组件设置了groupDefaultFocus(true),则以组件树深度遍历找到的第一个组件为最终结果。
@Entry
@Component
struct RequestFocusExample {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 20 }) {
        /** ! groupDefaultFocus:设置 */
        /** 父容器设置tabIndex=3, 输入框三 设置groupDefaultFocus */
        Column() {
          TextInput({ placeholder: '输入框一' })
          TextInput({ placeholder: '输入框二' })
          TextInput({ placeholder: '输入框三' })
            .groupDefaultFocus(true)
        }
        .tabIndex(3)

        /** 父容器设置tabIndex=1, 输入框二 设置groupDefaultFocus */
        Column() {
          TextInput({ placeholder: '输入框四' })
          TextInput({ placeholder: '输入框五' })
            .groupDefaultFocus(true)
          TextInput({ placeholder: '输入框六' })
        }
        .tabIndex(1)

        /** 父容器设置tabIndex=2, 输入框一 设置groupDefaultFocus */
        Column() {
          TextInput({ placeholder: '输入框七' })
            .groupDefaultFocus(true)
          TextInput({ placeholder: '输入框八' })
          TextInput({ placeholder: '输入框九' })
        }
        .tabIndex(2)
      }.width('100%').padding({ left: 20, right: 20 })
    }.height('100%')
  }
}

5. focusOnTouch

🔈:设置当前组件是否支持点击获焦能力。仅在组件可点击时才能正常获取焦点。
focusOnTouch(value: boolean)

参数

参数名类型必填默认值
value boolean

false


说明

点击是指使用触屏或鼠标左键进行单击,默认为false的组件,例如Button,不绑定该API时,点击Button不会使其获焦,当给Button绑定focusOnTouch(true)时,点击Button会使Button立即获得焦点。

// requestFocus.ets
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct RequestFocusExample {
  /** MARK: 成员变量 */
  @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N']

  /** MARK: 构建函数 */
  build() {
    Column({ space: 20 }) {
      Button("id: " + this.idList[0] + " focusOnTouch(true) + focusable(false)")
        .width(300)
        .height(70)
        .fontColor(Color.White)
        .focusOnTouch(true)
        .focusable(false)
      Button("id: " + this.idList[1] + " default")
        .width(300).height(70).fontColor(Color.White)
      Button("id: " + this.idList[2] + " focusOnTouch(false)")
        .width(300).height(70).fontColor(Color.White).focusOnTouch(false)
      Button("id: " + this.idList[3] + " focusOnTouch(true)")
        .width(300).height(70).fontColor(Color.White).focusOnTouch(true)
    }.width('100%').margin({ top: 20 })
  }
}

6. focusBox

🔈:设置当前组件系统焦点框样式。
focusBox(style: FocusBoxStyle): T

参数

参数名类型必填
style FocusBoxStyle

FocusBoxStyle

名称参数类型必填描述
margin LengthMetrics

焦点框相对组件边缘的距离。

正数代表外侧,负数代表内侧。不支持百分比。

strokeColor ColorMetrics 焦点框颜色。
strokeWidth LengthMetrics

焦点框宽度。

不支持负数与百分比。

说明

该样式仅影响走焦状态下展示了系统焦点框的组件。

import { ColorMetrics, LengthMetrics } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  /** MARK: 构建函数 */
  build() {
    Row() {
      Column({ space: 30 }) {
        /** 系统焦点样式 */
        Button("small black focus box")
        /** 自定义焦点样式 */
        Button("large red focus box")
          .focusBox({
            margin: LengthMetrics.px(20),
            strokeColor: ColorMetrics.rgba(255, 0, 0),
            strokeWidth: LengthMetrics.px(10)
          })
      }
      .alignItems(HorizontalAlign.Center)
      .width('100%')
    }.height('100%')
  }
}

7.requestFocus

在任意执行语句中调用该API,指定目标组件的id为方法参数,当程序执行到该语句时,会立即给指定的目标组件申请焦点。

focusControl.requestFocus(id: string)

示例:

// requestFocus.ets
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct RequestFocusExample {
  /** MARK: 成员变量 */
  @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N']
  @State requestId: number = 0

  /** MARK: 构建函数 */
  build() {
    Column({ space: 20 }) {
      Row({ space: 5 }) {
        Button("id: " + this.idList[0] + " focusable(false)")
          .width(150)
          .height(70)
          .fontColor(Color.White)
          .id(this.idList[0])
          .focusable(false)
        Button("id: " + this.idList[1])
          .width(150).height(70).fontColor(Color.White)
          .id(this.idList[1])
      }

      Row({ space: 5 }) {
        Button("id: " + this.idList[2])
          .width(150).height(70).fontColor(Color.White)
          .id(this.idList[2])
        Button("id: " + this.idList[3])
          .width(150).height(70).fontColor(Color.White)
          .id(this.idList[3])
      }

      Row({ space: 5 }) {
        Button("id: " + this.idList[4])
          .width(150).height(70).fontColor(Color.White)
          .id(this.idList[4])
        Button("id: " + this.idList[5])
          .width(150).height(70).fontColor(Color.White)
          .id(this.idList[5])
      }
    }
    .width('100%').margin({ top: 20 })
    .onKeyEvent((e) => {
      if (e.keyCode >= 2017 && e.keyCode <= 2022) {
        this.requestId = e.keyCode - 2017;
      } else if (e.keyCode === 2030) {
        this.requestId = 6;
      } else {
        return;
      }
      if (e.type !== KeyType.Down) {
        return;
      }
      let res = focusControl.requestFocus(this.idList[this.requestId]);
      if (res) {
        promptAction.showToast({ message: 'Request success' });
      } else {
        promptAction.showToast({ message: 'Request failed' });
      }
    })
  }
}

依次按下 TAB、A、B、C、D、E、F、N

更多属性阅读API文档

三、焦点事件

1. 基本概念

焦点事件基本概念是指在用户界面中,焦点在不同控件之间切换时,触发的相关事件。下面是一些焦点事件的基本概念:

焦点(Focus):焦点是指用户当前正在与之交互的控件或元素。例如,在一个表单中,焦点可能位于输入框、复选框或按钮等控件上。焦点通常用来表示哪个控件可以接收用户的输入。

默认焦点(Default Focus):默认焦点是指用户在进入一个界面或打开一个应用程序时,自动设置在界面中某个控件上的焦点。默认焦点通常是用来提高用户交互的效率,使用户可以直接开始输入或选择操作。

获焦(Focus Gained):获焦是指当一个控件或元素成为焦点时触发的事件。获焦事件通常可以用来执行一些初始化操作,例如设置焦点控件的样式或加载数据。

失焦(Focus Lost):失焦是指当一个控件或元素不再是焦点时触发的事件。失焦事件通常可以用来执行一些清理操作,例如保存用户输入或验证输入数据。

走焦(Traversal):走焦是指焦点在控件之间切换的过程。焦点可以通过按下Tab键或者使用方向键来在不同的控件之间移动。

焦点态(Focus State):焦点态是指控件或元素在成为焦点或失去焦点时,其外观或状态发生的变化。焦点态可以用来提高用户交互的可见性,例如高亮显示焦点控件或显示输入光标。

2. 走焦规则

走焦规则描述
线性走焦 焦点按照一定的顺序在可焦点元素之间依次切换。例如,按下Tab键时,焦点会自动切换到下一个可焦点元素。
十字走焦 焦点可以在四个方向上进行切换,类似于使用方向键进行导航。例如,按下方向键时,焦点会根据按键的方向进行相应的切换。
tabIndex走焦 通过在元素上设置tabIndex属性来指定焦点切换的顺序。具有较小tabIndex值的元素会优先获取焦点。
区域走焦 将焦点限制在某个区域内进行切换。例如,在一个表单中,焦点只能在表单内的元素之间进行切换,而不能切换到其他区域。
走焦至容器组件规则 在一个容器组件中的焦点切换规则。例如,在一个可滚动的容器中,焦点可以在可见的子元素之间切换,当焦点切换到不可见的子元素时,容器会自动滚动使其可见。
焦点交互 焦点获取和失去时的交互行为。例如,当焦点切换到一个输入框时,可以自动选中其中的文字,方便用户直接输入;当焦点离开输入框时,可以进行输入内容的验证。

3. 监听组件的焦点变化

接口

onFocus(event: () => void)//获焦事件回调
onBlur(event:() => void)//失焦事件回调

案例

// xxx.ets
@Entry
@Component
struct Index {
  /** MARK: 成员变量 */
  @State oneButtonColor: Color = Color.Gray;
  @State twoButtonColor: Color = Color.Gray;
  @State threeButtonColor: Color = Color.Gray;

  /** MARK: 构建函数 */
  build() {
    Column({ space: 20 }) {
      // 通过外接键盘的上下键可以让焦点在三个按钮间移动,按钮获焦时颜色变化,失焦时变回原背景色
      Button('First Button')
        .width(260)
        .height(70)
        .backgroundColor(this.oneButtonColor)
        .fontColor(Color.Black)// 监听第一个组件的获焦事件,获焦后改变颜色
        .onFocus(() => {
          this.oneButtonColor = Color.Green;
        })// 监听第一个组件的失焦事件,失焦后改变颜色
        .onBlur(() => {
          this.oneButtonColor = Color.Gray;
        })

      Button('Second Button')
        .width(260)
        .height(70)
        .backgroundColor(this.twoButtonColor)
        .fontColor(Color.Black)// 监听第二个组件的获焦事件,获焦后改变颜色
        .onFocus(() => {
          this.twoButtonColor = Color.Green;
        })// 监听第二个组件的失焦事件,失焦后改变颜色
        .onBlur(() => {
          this.twoButtonColor = Color.Grey;
        })

      Button('Third Button')
        .width(260)
        .height(70)
        .backgroundColor(this.threeButtonColor)
        .fontColor(Color.Black)// 监听第三个组件的获焦事件,获焦后改变颜色
        .onFocus(() => {
          this.threeButtonColor = Color.Green;
        })// 监听第三个组件的失焦事件,失焦后改变颜色
        .onBlur(() => {
          this.threeButtonColor = Color.Gray;
        })
    }.width('100%').margin({ top: 20 })
  }
}

posted on 2025-03-07 23:41  梁飞宇  阅读(98)  评论(0)    收藏  举报