指向當前應(yīng)用界面上唯一的一個可交互元素,當用戶使用鍵盤、電視遙控器、車機搖桿/旋鈕等非指向性輸入設(shè)備與應(yīng)用程序進行間接交互時,基于焦點的導航和交互是重要的輸入手段。
應(yīng)用打開或切換頁面后,若當前頁上存在可獲焦的組件,則樹形結(jié)構(gòu)的組件樹中第一個可獲焦的組件默認獲得焦點??梢允褂?a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" >自定義默認焦點進行自定義指定。
指組件獲得了焦點,同一時刻,應(yīng)用中最多只有1個末端組件是獲焦的,且此時它的所有祖宗組件(整個組件鏈)均是獲焦的。當期望某個組件獲焦,須確保該組件及其所有的祖宗節(jié)點均是可獲焦的(focusable屬性為true)。
指組件從獲焦狀態(tài)變成了非獲焦狀態(tài),失去了焦點。組件失焦時,它的所有祖宗組件(失焦組件鏈)與新的獲焦組件鏈不相同的節(jié)點都會失焦。
表示焦點在當前應(yīng)用中轉(zhuǎn)移的過程,走焦會帶來原焦點組件的失焦和新焦點組件的獲焦。應(yīng)用中焦點發(fā)生變化的方式按行為可分為兩類:
獲焦組件的樣式,不同組件的焦點態(tài)樣式大同小異,默認情況下焦點態(tài)不顯示,僅使用外接鍵盤按下TAB鍵/方向鍵時才會觸發(fā)焦點態(tài)樣式出現(xiàn)。首次觸發(fā)焦點態(tài)顯示的TAB鍵/方向鍵不會觸發(fā)走焦。當應(yīng)用接收到點擊事件時(包括手指觸屏的按下事件和鼠標左鍵的按下事件),自動隱藏焦點態(tài)樣式。焦點態(tài)樣式由后端組件定義,開發(fā)者無法修改。
走焦規(guī)則是指用戶使用“TAB鍵/SHIFT+TAB鍵/方向鍵”主動進行走焦,或焦點系統(tǒng)在執(zhí)行被動走焦時的順序規(guī)則。組件的走焦規(guī)則默認由走焦系統(tǒng)定義,由焦點所在的容器決定。
例如Row容器,使用方向鍵左右(←/→)即可將焦點在相鄰的2個可獲焦組件之間來回切換。
本文涉及到的焦點均為組件焦點,另外一個焦點的概念是:窗口焦點,指向當前獲焦的窗口。當窗口失焦時,該窗口應(yīng)用中的所有獲焦組件全部失焦。
- onFocus(event: () => void)
獲焦事件回調(diào),綁定該API的組件獲焦時,回調(diào)響應(yīng)。
- onBlur(event:() => void)
失焦事件回調(diào),綁定該API的組件失焦時,回調(diào)響應(yīng)。
onFocus和onBlur兩個接口通常成對使用,來監(jiān)聽組件的焦點變化。
以下示例代碼展示獲焦/失焦回調(diào)的使用方法:
- // xxx.ets
- @Entry
- @Component
- struct FocusEventExample {
- @State oneButtonColor: Color = Color.Gray;
- @State twoButtonColor: Color = Color.Gray;
- @State threeButtonColor: Color = Color.Gray;
- build() {
- Column({ space: 20 }) {
- // 通過外接鍵盤的上下鍵可以讓焦點在三個按鈕間移動,按鈕獲焦時顏色變化,失焦時變回原背景色
- Button('First Button')
- .width(260)
- .height(70)
- .backgroundColor(this.oneButtonColor)
- .fontColor(Color.Black)
- // 監(jiān)聽第一個組件的獲焦事件,獲焦后改變顏色
- .onFocus(() => {
- this.oneButtonColor = Color.Green;
- })
- // 監(jiān)聽第一個組件的失焦事件,失焦后改變顏色
- .onBlur(() => {
- this.oneButtonColor = Color.Gray;
- })
- Button('Second Button')
- .width(260)
- .height(70)
- .backgroundColor(this.twoButtonColor)
- .fontColor(Color.Black)
- // 監(jiān)聽第二個組件的獲焦事件,獲焦后改變顏色
- .onFocus(() => {
- this.twoButtonColor = Color.Green;
- })
- // 監(jiān)聽第二個組件的失焦事件,失焦后改變顏色
- .onBlur(() => {
- this.twoButtonColor = Color.Grey;
- })
- Button('Third Button')
- .width(260)
- .height(70)
- .backgroundColor(this.threeButtonColor)
- .fontColor(Color.Black)
- // 監(jiān)聽第三個組件的獲焦事件,獲焦后改變顏色
- .onFocus(() => {
- this.threeButtonColor = Color.Green;
- })
- // 監(jiān)聽第三個組件的失焦事件,失焦后改變顏色
- .onBlur(() => {
- this.threeButtonColor = Color.Gray ;
- })
- }.width('100%').margin({ top: 20 })
- }
- }
上述示例包含以下4步:
通過focusable接口設(shè)置組件是否可獲焦:
- focusable(value: boolean)
按照組件的獲焦能力可大致分為三類:
基礎(chǔ)組件 | 是否有獲焦能力 | focusable默認值 | 走焦規(guī)則 |
---|---|---|---|
AlphabetIndexer | 是 | true | 線性走焦 |
Blank | 否 | false | / |
Button | 是 | true | / |
Checkbox | 是 | true | / |
CheckboxGroup | 是 | true | / |
DataPanel | 否 | false | / |
DatePicker | 是 | true | 線性走焦 |
Divider | 否 | false | / |
Gauge | 否 | false | / |
Image | 是 | false | / |
ImageAnimator | 是 | false | / |
LoadingProgress | 否 | false | / |
Marquee | 否 | false | / |
Menu | 是 | true | 線性走焦 |
MenuItem | 是 | true | / |
MenuItemGroup | 是 | true | 線性走焦 |
Navigation | 否 | false | 組件自定義 |
NavRouter | 否 | false | 跟隨子容器 |
NavDestination | 否 | false | 線性走焦 |
PatternLock | 否 | false | / |
Progress | 否 | false | / |
QRCode | 否 | false | / |
Radio | 是 | true | / |
Rating | 是 | true | / |
RichText | 否 | false | / |
ScrollBar | 否 | false | / |
Search | 是 | true | / |
Select | 是 | true | 線性走焦 |
Slider | 是 | true | / |
Span | 否 | false | / |
Stepper | 是 | true | / |
StepperItem | 是 | true | / |
Text | 是 | false | / |
TextArea | 是 | true | / |
TextClock | 否 | false | / |
TextInput | 是 | true | / |
TextPicker | 是 | true | 線性走焦 |
TextTimer | 否 | false | / |
TimePicker | 是 | true | 線性走焦 |
Toggle | 是 | true | / |
Web | 是 | true | Web組件自定義 |
XComponent | 否 | false | / |
容器組件 | 是否可獲焦 | focusable默認值 | 走焦規(guī)則 |
---|---|---|---|
Badge | 否 | false | / |
Column | 是 | true | 線性走焦 |
ColumnSplit | 是 | true | / |
Counter | 是 | true | 線性走焦 |
Flex | 是 | true | 線性走焦 |
GridCol | 是 | true | 容器組件自定義 |
GridRow | 是 | true | 容器組件自定義 |
Grid | 是 | true | 容器組件自定義 |
GridItem | 是 | true | 跟隨子組件 |
List | 是 | true | 線性走焦 |
ListItem | 是 | true | 跟隨子組件 |
ListItemGroup | 是 | true | 跟隨List組件 |
Navigator | 否 | true | 容器組件自定義 |
Panel | 否 | true | 跟隨子組件 |
Refresh | 否 | false | / |
RelativeContainer | 否 | true | 容器組件自定義 |
Row | 是 | true | 線性走焦 |
RowSplit | 是 | true | / |
Scroll | 是 | true | 線性走焦 |
SideBarContainer | 是 | true | 線性走焦 |
Stack | 是 | true | 線性走焦 |
Swiper | 是 | true | 容器組件自定義 |
Tabs | 是 | true | 容器組件自定義 |
TabContent | 是 | true | 跟隨子組件 |
媒體組件 | 是否可獲焦 | focusable默認值 | 走焦規(guī)則 |
---|---|---|---|
Video | 是 | true | / |
畫布組件 | 是否可獲焦 | focusable默認值 | 走焦規(guī)則 |
---|---|---|---|
Canvas | 否 | false | / |
以下示例為大家展示focusable接口的使用方法:
- // xxx.ets
- @Entry
- @Component
- struct FocusableExample {
- @State textFocusable: boolean = true;
- @State color1: Color = Color.Yellow;
- @State color2: Color = Color.Yellow;
- build() {
- Column({ space: 5 }) {
- Text('Default Text') // 第一個Text組件未設(shè)置focusable屬性,默認不可獲焦
- .borderColor(this.color1)
- .borderWidth(2)
- .width(300)
- .height(70)
- .onFocus(() => {
- this.color1 = Color.Blue;
- })
- .onBlur(() => {
- this.color1 = Color.Yellow;
- })
- Divider()
- Text('focusable: ' + this.textFocusable) // 第二個Text設(shè)置了focusable屬性,初始值為true
- .borderColor(this.color2)
- .borderWidth(2)
- .width(300)
- .height(70)
- .focusable(this.textFocusable)
- .onFocus(() => {
- this.color2 = Color.Blue;
- })
- .onBlur(() => {
- this.color2 = Color.Yellow;
- })
- Divider()
- Row() {
- Button('Button1')
- .width(140).height(70)
- Button('Button2')
- .width(160).height(70)
- }
- Divider()
- Button('Button3')
- .width(300).height(70)
- Divider()
- }.width('100%').justifyContent(FlexAlign.Center)
- .onKeyEvent((e) => { // 綁定onKeyEvent,在該Column組件獲焦時,按下'F'鍵,可將第二個Text的focusable置反
- if (e.keyCode === 2022 && e.type === KeyType.Down) {
- this.textFocusable = !this.textFocusable;
- }
- })
- }
- }
運行效果:
上述示例包含默認獲焦和主動走焦兩部分:
默認獲焦:
主動走焦:
按鍵盤F鍵,觸發(fā)onKeyEvent,focusable置為false,Text組件變成不可獲焦,焦點自動轉(zhuǎn)移,按照被動走焦中的說明項,焦點會自動從Text組件先向上尋找下一個可獲焦組件,由于上一個組件是一個不可獲焦的Text,所以向下尋找下一個可獲焦的組件,找到并使焦點轉(zhuǎn)移到Row容器上,根據(jù)走焦至容器規(guī)則,計算Button1和Button2的位置,Button2比Button1更大,因此焦點會自動轉(zhuǎn)移到Button2上。
- defaultFocus(value: boolean)
焦點系統(tǒng)在頁面初次構(gòu)建完成時,會搜索當前頁下的所有組件,找到第一個綁定了defaultFocus(true)的組件,然后將該組件置為默認焦點,若無任何組件綁定defaultFocus(true),則將第一個找到的可獲焦的組件置為默認焦點。
以如下應(yīng)用為例,應(yīng)用布局如下:
以下是實現(xiàn)該應(yīng)用的示例代碼,且示例代碼中沒有設(shè)置defaultFocus:
- // xxx.ets
- import promptAction from '@ohos.promptAction';
- class MyDataSource implements IDataSource {
- private list: number[] = [];
- private listener: DataChangeListener;
- constructor(list: number[]) {
- this.list = list;
- }
- totalCount(): number {
- return this.list.length;
- }
- getData(index: number): any {
- return this.list[index];
- }
- registerDataChangeListener(listener: DataChangeListener): void {
- this.listener = listener;
- }
- unregisterDataChangeListener() {
- }
- }
- @Entry
- @Component
- struct SwiperExample {
- private swiperController: SwiperController = new SwiperController()
- private data: MyDataSource = new MyDataSource([])
- aboutToAppear(): void {
- let list = []
- for (let i = 1; i <= 4; i++) {
- list.push(i.toString());
- }
- this.data = new MyDataSource(list);
- }
- build() {
- Column({ space: 5 }) {
- Swiper(this.swiperController) {
- LazyForEach(this.data, (item: string) => {
- Row({ space: 20 }) {
- Column() {
- Button('1').width(200).height(200)
- .fontSize(40)
- .backgroundColor('#dadbd9')
- }
- Column({ space: 20 }) {
- Row({ space: 20 }) {
- Button('2')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- Button('3')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- }
- Row({ space: 20 }) {
- Button('4')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- Button('5')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- }
- Row({ space: 20 }) {
- Button('6')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- Button('7')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- }
- }
- }
- .width(480)
- .height(380)
- .justifyContent(FlexAlign.Center)
- .borderWidth(2)
- .borderColor(Color.Gray)
- .backgroundColor(Color.White)
- }, item => item)
- }
- .cachedCount(2)
- .index(0)
- .interval(4000)
- .indicator(true)
- .loop(true)
- .duration(1000)
- .itemSpace(0)
- .curve(Curve.Linear)
- .onChange((index: number) => {
- console.info(index.toString());
- })
- .margin({ left: 20, top: 20, right: 20 })
- Row({ space: 40 }) {
- Button('←')
- .fontSize(40)
- .fontWeight(FontWeight.Bold)
- .fontColor(Color.Black)
- .backgroundColor(Color.Transparent)
- .onClick(() => {
- this.swiperController.showPrevious();
- })
- Button('→')
- .fontSize(40)
- .fontWeight(FontWeight.Bold)
- .fontColor(Color.Black)
- .backgroundColor(Color.Transparent)
- .onClick(() => {
- this.swiperController.showNext();
- })
- }
- .width(480)
- .height(50)
- .justifyContent(FlexAlign.Center)
- .borderWidth(2)
- .borderColor(Color.Gray)
- .backgroundColor('#f7f6dc')
- Row({ space: 40 }) {
- Button('Cancel')
- .fontSize(30)
- .fontColor('#787878')
- .type(ButtonType.Normal)
- .width(140)
- .height(50)
- .backgroundColor('#dadbd9')
- Button('OK')
- .fontSize(30)
- .fontColor('#787878')
- .type(ButtonType.Normal)
- .width(140)
- .height(50)
- .backgroundColor('#dadbd9')
- .onClick(() => {
- promptAction.showToast({ message: 'Button OK on clicked' });
- })
- }
- .width(480)
- .height(80)
- .justifyContent(FlexAlign.Center)
- .borderWidth(2)
- .borderColor(Color.Gray)
- .backgroundColor('#dff2e4')
- .margin({ left: 20, bottom: 20, right: 20 })
- }.backgroundColor('#f2f2f2')
- .margin({ left: 50, top: 50, right: 20 })
- }
- }
當前應(yīng)用上無任何defaultFocus設(shè)置,所以第一個可獲焦的組件默認獲取焦點,按下TAB鍵/方向鍵讓獲焦的組件顯示焦點態(tài)樣式:
假設(shè)開發(fā)者想讓應(yīng)用打開的時候,無需執(zhí)行多余的切換焦點操作,直接點擊按鍵的空格/回車鍵,就可以執(zhí)行Button-OK的onClick回調(diào)操作,那么就可以給這個Button綁定defaultFocus(true),讓它成為該頁面上的默認焦點:
- Button('OK')
- .defaultFocus(true) // 設(shè)置Button-OK為defaultFocus
- .fontSize(30)
- .fontColor('#787878')
- .type(ButtonType.Normal)
- .width(140).height(50).backgroundColor('#dadbd9')
- .onClick(() => {
- promptAction.showToast({ message: 'Button OK on clicked' });
- })
打開應(yīng)用后按TAB鍵,Button-OK顯示了焦點態(tài),說明默認焦點變更到了Button-OK上。然后按下空格,響應(yīng)了Button-OK的onClick事件。
- tabIndex(index: number)
tabIndex用于設(shè)置自定義TAB鍵走焦順序,默認值為0。使用“TAB/Shift+TAB鍵”走焦時(方向鍵不影響),系統(tǒng)會自動獲取到所有配置了tabIndex大于0的組件,然后按照遞增/遞減排序進行走焦。
以defaultFocus提供的示例為例,默認情況下的走焦順序如下:
默認的走焦順序從第一個獲焦組件一路走到最后一個獲焦組件,會經(jīng)歷Button1->Button4->Button5->Button7->左箭頭->右箭頭->ButtonOK。這種走焦隊列比較完整,遍歷了大部分的組件。但缺點是從第一個走到最后一個所經(jīng)歷的路徑較長。
如果想實現(xiàn)快速的從第一個走到最后一個,又不想犧牲太多的遍歷完整性,就可以使用tabIndex通用屬性。
比如:開發(fā)者把白色的區(qū)域當為一個整體,黃色的區(qū)域當為一個整體,綠色的區(qū)域當為一個整體,實現(xiàn)Button1->左箭頭->ButtonOK這種隊列的走焦順序,只需要在Button1、左箭頭、ButtonOK這三個組件上依次增加tabIndex(1)、tabIndex(2)、tabIndex(3)。tabIndex的參數(shù)表示TAB走焦的順序(從大于0的數(shù)字開始,從小到大排列)。
- Button('1').width(200).height(200)
- .fontSize(40)
- .backgroundColor('#dadbd9')
- .tabIndex(1) // Button-1設(shè)置為第一個tabIndex節(jié)點
- Button('←')
- .fontSize(40)
- .fontWeight(FontWeight.Bold)
- .fontColor(Color.Black)
- .backgroundColor(Color.Transparent)
- .onClick(() => {
- this.swiperController.showPrevious();
- })
- .tabIndex(2) // Button-左箭頭設(shè)置為第二個tabIndex節(jié)點
- Button('OK')
- .fontSize(30)
- .fontColor('#787878')
- .type(ButtonType.Normal)
- .width(140).height(50).backgroundColor('#dadbd9')
- .onClick(() => {
- promptAction.showToast({ message: 'Button OK on clicked' });
- })
- .tabIndex(3) // Button-OK設(shè)置為第三個tabIndex節(jié)點
- groupDefaultFocus(value: boolean)
自定義TAB鍵走焦順序中所展示的使用tabIndex完成快速走焦的能力有如下問題:
每個區(qū)域(白色/黃色/綠色三個區(qū)域)都設(shè)置了某個組件為tabIndex節(jié)點(白色-Button1、黃色-左箭頭、綠色-ButtonOK),但這樣設(shè)置之后,只能在這3個組件上按TAB/ShiftTab鍵走焦時會有快速走焦的效果。
解決方案是給每個區(qū)域的容器設(shè)置tabIndex,但是這樣設(shè)置的問題是:第一次走焦到容器上時,獲焦的子組件是默認的第一個可獲焦組件,并不是自己想要的組件(Button1、左箭頭、ButtonOK)。
這樣便引入了groupDefaultFocus通用屬性,參數(shù):boolean,默認值:false。
用法需和tabIndex組合使用,使用tabIndex給區(qū)域(容器)綁定走焦順序,然后給Button1、左箭頭、ButtonOK綁定groupDefaultFocus(true),這樣在首次走焦到目標區(qū)域(容器)上時,它的綁定了groupDefaultFocus(true)的子組件同時獲得焦點。
- // xxx.ets
- import promptAction from '@ohos.promptAction';
- class MyDataSource implements IDataSource {
- private list: number[] = [];
- private listener: DataChangeListener;
- constructor(list: number[]) {
- this.list = list;
- }
- totalCount(): number {
- return this.list.length;
- }
- getData(index: number): any {
- return this.list[index];
- }
- registerDataChangeListener(listener: DataChangeListener): void {
- this.listener = listener;
- }
- unregisterDataChangeListener() {
- }
- }
- @Entry
- @Component
- struct SwiperExample {
- private swiperController: SwiperController = new SwiperController()
- private data: MyDataSource = new MyDataSource([])
- aboutToAppear(): void {
- let list = []
- for (let i = 1; i <= 4; i++) {
- list.push(i.toString());
- }
- this.data = new MyDataSource(list);
- }
- build() {
- Column({ space: 5 }) {
- Swiper(this.swiperController) {
- LazyForEach(this.data, (item: string) => {
- Row({ space: 20 }) { // 設(shè)置該Row組件為tabIndex的第一個節(jié)點
- Column() {
- Button('1').width(200).height(200)
- .fontSize(40)
- .backgroundColor('#dadbd9')
- .groupDefaultFocus(true) // 設(shè)置Button-1為第一個tabIndex的默認焦點
- }
- Column({ space: 20 }) {
- Row({ space: 20 }) {
- Button('2')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- Button('3')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- }
- Row({ space: 20 }) {
- Button('4')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- Button('5')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- }
- Row({ space: 20 }) {
- Button('6')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- Button('7')
- .width(100)
- .height(100)
- .fontSize(40)
- .type(ButtonType.Normal)
- .borderRadius(20)
- .backgroundColor('#dadbd9')
- }
- }
- }
- .width(480)
- .height(380)
- .justifyContent(FlexAlign.Center)
- .borderWidth(2)
- .borderColor(Color.Gray)
- .backgroundColor(Color.White)
- .tabIndex(1)
- }, item => item)
- }
- .cachedCount(2)
- .index(0)
- .interval(4000)
- .indicator(true)
- .loop(true)
- .duration(1000)
- .itemSpace(0)
- .curve(Curve.Linear)
- .onChange((index: number) => {
- console.info(index.toString());
- })
- .margin({ left: 20, top: 20, right: 20 })
- Row({ space: 40 }) { // 設(shè)置該Row組件為第二個tabIndex節(jié)點
- Button('←')
- .fontSize(40)
- .fontWeight(FontWeight.Bold)
- .fontColor(Color.Black)
- .backgroundColor(Color.Transparent)
- .onClick(() => {
- this.swiperController.showPrevious();
- })
- .groupDefaultFocus(true) // 設(shè)置Button-左箭頭為第二個tabIndex節(jié)點的默認焦點
- Button('→')
- .fontSize(40)
- .fontWeight(FontWeight.Bold)
- .fontColor(Color.Black)
- .backgroundColor(Color.Transparent)
- .onClick(() => {
- this.swiperController.showNext();
- })
- }
- .width(480)
- .height(50)
- .justifyContent(FlexAlign.Center)
- .borderWidth(2)
- .borderColor(Color.Gray)
- .backgroundColor('#f7f6dc')
- .tabIndex(2)
- Row({ space: 40 }) { // 設(shè)置該Row組件為第三個tabIndex節(jié)點
- Button('Cancel')
- .fontSize(30)
- .fontColor('#787878')
- .type(ButtonType.Normal)
- .width(140)
- .height(50)
- .backgroundColor('#dadbd9')
- Button('OK')
- .fontSize(30)
- .fontColor('#787878')
- .type(ButtonType.Normal)
- .width(140)
- .height(50)
- .backgroundColor('#dadbd9')
- .defaultFocus(true)
- .onClick(() => {
- promptAction.showToast({ message: 'Button OK on clicked' });
- })
- .groupDefaultFocus(true) // 設(shè)置Button-OK為第三個tabIndex節(jié)點的默認焦點
- }
- .width(480)
- .height(80)
- .justifyContent(FlexAlign.Center)
- .borderWidth(2)
- .borderColor(Color.Gray)
- .backgroundColor('#dff2e4')
- .margin({ left: 20, bottom: 20, right: 20 })
- .tabIndex(3)
- }.backgroundColor('#f2f2f2')
- .margin({ left: 50, top: 50, right: 20 })
- }
- }
- focusOnTouch(value: boolean)
點擊獲焦能力,參數(shù):boolean,默認值:false(輸入類組件:TextInput、TextArea、Search、Web默認值是true)。
點擊是指使用觸屏或鼠標左鍵進行單擊,默認為false的組件,例如Button,不綁定該API時,點擊Button不會使其獲焦,當給Button綁定focusOnTouch(true)時,點擊Button會使Button立即獲得焦點。
給容器綁定focusOnTouch(true)時,點擊容器區(qū)域,會立即使容器的第一個可獲焦組件獲得焦點。
示例代碼:
- // requestFocus.ets
- import promptAction from '@ohos.promptAction';
- @Entry
- @Component
- struct RequestFocusExample {
- @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N']
- build() {
- Column({ space:20 }){
- Button("id: " + this.idList[0] + " focusOnTouch(true) + focusable(false)")
- .width(400).height(70).fontColor(Color.White).focusOnTouch(true)
- .focusable(false)
- Button("id: " + this.idList[1] + " default")
- .width(400).height(70).fontColor(Color.White)
- Button("id: " + this.idList[2] + " focusOnTouch(false)")
- .width(400).height(70).fontColor(Color.White).focusOnTouch(false)
- Button("id: " + this.idList[3] + " focusOnTouch(true)")
- .width(400).height(70).fontColor(Color.White).focusOnTouch(true)
- }.width('100%').margin({ top:20 })
- }
- }
效果:
解讀:
Button-A雖然設(shè)置了focusOnTouch(true),但是同時也設(shè)置了focusable(false),該組件無法獲焦,因此點擊后也無法獲焦;
Button-B不設(shè)置相關(guān)屬性,點擊后不會獲焦;
Button-C設(shè)置了focusOnTouch(false),同Button-B,點擊后也不會獲焦;
Button-D設(shè)置了focusOnTouch(true),點擊即可使其獲焦;
由于焦點態(tài)的闡述的特性,焦點態(tài)在屏幕接收點擊事件后會立即清除。因此該示例代碼在每次點擊后,需要再次按下TAB鍵使焦點態(tài)再次顯示,才可知道當前焦點所在的組件。
- focusControl.requestFocus(id: string)
主動申請焦點能力的全局方法,參數(shù):string,參數(shù)表示被申請組件的id(通用屬性id設(shè)置的字符串)。
使用方法為:在任意執(zhí)行語句中調(diào)用該API,指定目標組件的id為方法參數(shù),當程序執(zhí)行到該語句時,會立即給指定的目標組件申請焦點。
代碼示例:
- // requestFocus.ets
- import promptAction from '@ohos.promptAction';
- @Entry
- @Component
- struct RequestFocusExample {
- @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'N']
- @State requestId: number = 0
- build() {
- Column({ space:20 }){
- Row({space: 5}) {
- Button("id: " + this.idList[0] + " focusable(false)")
- .width(200).height(70).fontColor(Color.White)
- .id(this.idList[0])
- .focusable(false)
- Button("id: " + this.idList[1])
- .width(200).height(70).fontColor(Color.White)
- .id(this.idList[1])
- }
- Row({space: 5}) {
- Button("id: " + this.idList[2])
- .width(200).height(70).fontColor(Color.White)
- .id(this.idList[2])
- Button("id: " + this.idList[3])
- .width(200).height(70).fontColor(Color.White)
- .id(this.idList[3])
- }
- Row({space: 5}) {
- Button("id: " + this.idList[4])
- .width(200).height(70).fontColor(Color.White)
- .id(this.idList[4])
- Button("id: " + this.idList[5])
- .width(200).height(70).fontColor(Color.White)
- .id(this.idList[5])
- }
- }.width('100%').margin({ top:20 })
- .onKeyEvent((e) => {
- if (e.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'});
- }
- })
- }
- }
效果:
解讀:頁面中共6個Button組件,其中Button-A組件設(shè)置了focusable(false),表示其不可獲焦,在外部容器的onKeyEvent中,監(jiān)聽按鍵事件,當按下A ~ F按鍵時,分別去申請Button A ~ F 的焦點,另外按下N鍵,是給當前頁面上不存在的id的組件去申請焦點。
更多建議: