【Android Compose】焦點管理

官方文檔鏈接:

https://developer.android.google.cn/develop/ui/compose/touch-input/focus?hl=zh-cn

1、更改焦點遍歷順序

1.1、替換一維遍歷順序

(1)創建焦點引用對象:

/// 創建4個引用對象(二選一)
// 語法1:
val (first, second, third, fourth) = remember { FocusRequester.createRefs() }
// 語法2:數組
val focusRequesters = remember { List(4){ FocusRequester.createRefs() } }

(2)使用 focusRequester 修飾符將它們分別與 可組合項

Column {Row {// 注意:TextButton 具有默認焦點(可聚焦)// 如果此處是沒有默認聚焦的元素,比如 Box,則需要在Modifer最后面(所有焦點函數之后)// 注意是最后面加上 .focusable()TextButton({}, Modifier.focusRequester(first/*focusRequesters[0]*/)/* .focusable()*/) { Text("First field") }TextButton({}, Modifier.focusRequester(third/*focusRequesters[1]*/)) { Text("Third field") }}Row {TextButton({}, Modifier.focusRequester(second/*focusRequesters[2]*/)) { Text("Second field") }TextButton({}, Modifier.focusRequester(fourth/*focusRequesters[3]*/)) { Text("Fourth field") }}
}

(3)使用 focusProperties 修飾符指定自定義遍歷順序
因為有些布局或組件默認會帶有遍歷默認順序,比如 Column 的孩子的默認遍歷順序是從上到下。
但是有時候我們需要改變這種順序:

// 一維:它的下一個是誰、上一個是誰
Modifier.focusRequester(first).focusProperties { next = second }..../* .focusable()*/...// 二維:上下左右
Modifier.focusRequester(fourth).focusProperties {down = thirdright = second}

2 更改焦點行為

2.1 焦點小組

LazyVerticalGrid(columns = GridCells.Fixed(4)) {item(span = { GridItemSpan(maxLineSpan) }) {// 將其設置為一個焦點組:Row(modifier = Modifier.focusGroup()) {FilterChipA()FilterChipB()FilterChipC()}}items(chocolates) {SweetsCard(sweets = it)}
}

2.2 使可組合項可聚焦

var color by remember { mutableStateOf(Green) }
Box(Modifier.background(color).onFocusChanged { color = if (it.isFocused) Blue else Green }// 注意要放在焦點相關函數的最后面,.focusable()
) {Text("Focusable 1")
}

2.3 使可組合項不可聚焦

var checked by remember { mutableStateOf(false) }Switch(checked = checked,onCheckedChange = { checked = it },// Prevent component from being focusedmodifier = Modifier// 使原本可以聚焦的,變得不可聚焦....focusProperties { canFocus = false }
)

2.4 使用 FocusRequester 手動請求焦點

上面說了可以手動添加焦點引用對象。添加這個請求對象之后,可以使用焦點請求對象手動請求焦點…

// 焦點請求對象
val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }TextField(value = text,onValueChange = { text = it },// 為這個元素添加上面的焦點請求對象(覆蓋默認的焦點請求對象)modifier = Modifier.focusRequester(focusRequester)
)// 在添加焦點請求對象之后,就可以手動請求:
Button(onClick = {// 手動請求(在.focusRequester(focusRequester)之后)focusRequester.requestFocus() }) {Text("Request focus on TextField")
}

2.5 捕獲和釋放 焦點

調用 captureFocus() 方法,并且之后必須要用 freeFocus() 方法將其釋放
// 經常用在 TextField 之類的組件
// 比如下面的示例中,首先 TextField 已經獲取焦點,
// 但是我還想讓它在輸入長度達到 3 之后突出視覺效果,可以嘗試捕獲焦點…

val textField = FocusRequester()TextField(value = text,onValueChange = {text = itif (it.length > 3) {textField.captureFocus()} else {textField.freeFocus()}},modifier = Modifier.focusRequester(textField)
)

2.6 焦點修飾符的優先級

Modifier.focusProperties { right = Default }.focusProperties { right = item1 }.focusProperties { right = item2 }// 放在最后....focusable()

注意:焦點相關修飾函數順序由決定性作用,不同的順序會帶來不同的效果:

// 作用失效案例1:
Box(Modifier.focusable() // 讓焦點函數生效.focusRequester(Default)// 后聲明請求器 → 無法關聯.onFocusChanged {}// 當焦點發生變化的時候,回調// 上面這個案例會導致下面兩個焦點修飾符函數不生效。// 因為 focusable() 的作用的讓(前面已經)配置生效。// 所以先生效,再配置的邏輯順序是不符合要求的
)
// 作用失效案例2:
Box(Modifier.onFocusChanged {}// 先監聽焦點變化(無效),應該先綁定焦點.focusRequester(Default).focusable()
)// 正確順序:
Box(Modifier.focusRequester(Default) // 先聲明請求器.onFocusChanged {}       // 可放在此處(但可能不推薦).focusable()            // 后聲明可聚焦 → 請求器生效
)

2.7 進入或退出時重定向焦點

什么是進入:
一般就是 Enter 鍵觸發的行為

什么是退出:退出可組合區域,比如說焦點離開Colum1,進入Column2。
離開Column1就是退出行為。

在這里插入圖片描述

比如在第一列離開,如果想要將焦點放置在第二列,可以:

val otherComposable = remember { FocusRequester() }Modifier.focusProperties {// 離開第一列的時候,我們根據離開的方向指定下一個焦點應該到達何處exit = { focusDirection ->when (focusDirection) {Right -> Cancel// 離開且按右方向鍵,就取消焦點Down -> otherComposable// 離開且按下鍵,就移動到第二列else -> Default// 其他方向使用默認}}
}

2.8 更改焦點推進方向

val focusManager = LocalFocusManager.current
var text by remember { mutableStateOf("") }TextField(value = text,onValueChange = { text = it },modifier = Modifier.onPreviewKeyEvent {when {//  檢測到tab按鍵(并釋放抬起),焦點移至焦點中的下一個元素 列表KeyEventType.KeyUp == it.type && Key.Tab == it.key -> {// 移動焦點到默認的下一個焦點元素focusManager.moveFocus(FocusDirection.Next)true}else -> false}}
)

3、回應焦點

3.1、添加視覺效果(比如顏色)

var color by remember { mutableStateOf(Color.White) }
Card(modifier = Modifier.onFocusChanged {color = if (it.isFocused) Red else White}.border(5.dp, color)
) {}

3.2、高級視覺提示

(1)首先,創建一個 IndicationInstance,以在界面中直觀地繪制所需提示:

private class MyHighlightIndicationNode(private val interactionSource: InteractionSource) :Modifier.Node(), DrawModifierNode {private var isFocused = falseoverride fun onAttach() {coroutineScope.launch {var focusCount = 0interactionSource.interactions.collect { interaction ->when (interaction) {is FocusInteraction.Focus -> focusCount++is FocusInteraction.Unfocus -> focusCount--}val focused = focusCount > 0if (isFocused != focused) {isFocused = focusedinvalidateDraw()}}}}override fun ContentDrawScope.draw() {drawContent()if (isFocused) {drawRect(size = size, color = Color.White, alpha = 0.2f)}}
}

(2)接下來,創建一個 Indication 并記住聚焦狀態:

object MyHighlightIndication : IndicationNodeFactory {override fun create(interactionSource: InteractionSource): DelegatableNode {return MyHighlightIndicationNode(interactionSource)}override fun hashCode(): Int = -1override fun equals(other: Any?) = other === this
}

(3)通過 indication() 修飾符將 Indication 和 InteractionSource 添加到界面中:

var interactionSource = remember { MutableInteractionSource() }Card(modifier = Modifier.clickable(interactionSource = interactionSource,indication = MyHighlightIndication,enabled = true,onClick = { })
) {Text("hello")
}

3.3、焦點狀態

// 焦點一般有這三種狀態,可以直接在焦點回調里面獲取:
Modifier.onFocusChanged {val isFocused = it.isFocused// 只檢查當前元素val hasFocus = it.hasFocus// 不僅檢查當前,還檢查孩子們val isCaptured= it.isCaptured//每當獲得焦點時,isCaptured 都會返回 true// 一般出現這種清空都是當 TextField 包含不正確的數據時,就會嘗試聚焦 其他元素不會清除焦點。
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78504.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78504.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78504.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

dwj2025426

目錄 一、25. K 個一組翻轉鏈表 - 力扣(LeetCode) 二、 215. 數組中的第K個最大元素 - 力扣(LeetCode) 三、 15. 三數之和 - 力扣(LeetCode) 一、25. K 個一組翻轉鏈表 - 力扣(LeetCode&#…

C++ std::forward 詳解

在 C 11 引入的眾多特性中,std::forward占據著獨特且重要的地位。它主要用于實現所謂的 “完美轉發”,這一機制在現代 C 編程中發揮著關鍵作用,尤其是在編寫通用庫和高效代碼時。 什么是完美轉發? 完美轉發是指在函數模板中&…

如何保證線程安全(含典型手段與應用場景)

? 1. 什么是線程安全? 線程安全指的是:當多個線程同時訪問同一塊代碼時,無論運行時環境采用怎樣的調度方式或者這些線程將怎樣交替執行,代碼的行為都能正確執行,且不會出現數據不一致、臟數據或異常崩潰。 舉個簡單…

Qt/C++開發監控GB28181系統/協議解釋說明/SIP內容解釋/每一行數據什么含義

一、前言 搞gb28181開發,首要任務就是解析協議,按照gb28181的文檔來,還是非常詳細的,通過抓包工具可以查看到具體的收發數據,也可以打開網絡調試助手工具,監聽5060端口,看到上報的數據&#xf…

C++:string 1

練習題&#xff1a; 這個題的思路是從前往后&#xff0c;從后往前同時找&#xff0c;不是字母的話就繼續&#xff0c;是的話就交換。 代碼&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <string> using namespace std; //1、4個…

SMT貼片加工費控制與優化實踐指南

內容概要 SMT貼片加工費的控制與優化需建立在對成本結構的系統性認知基礎上。本節從物料采購、設備運行、工藝參數三大維度切入&#xff0c;結合BOM清單管理、鋼網使用規范等實操環節&#xff0c;構建覆蓋全流程的降本增效框架。以下表格列舉了SMT加工成本的典型構成要素及其占…

未來醫院已來:AI如何實現無死角安全監控

AI智慧醫院如何用算法守護安全與效率 ## 背景&#xff1a;醫療場景的智能化轉型需求 現代醫院作為人員密集、場景復雜的公共場所&#xff0c;面臨諸多管理痛點&#xff1a;患者跌倒可能延誤救治、醫鬧事件威脅安全、醫療垃圾處置不當引發感染風險、重點區域&#xff08;如藥…

Nuxt3中使用UnoCSS指南

Nuxt3中使用UnoCSS指南 UnoCSS是一個高度可定制的、原子化CSS引擎&#xff0c;可以輕松集成到Nuxt3項目中。下面介紹如何在Nuxt3中安裝和配置UnoCSS。 安裝步驟 安裝UnoCSS的Nuxt模塊&#xff1a; # 使用pnpm pnpm add -D unocss unocss/nuxt# 使用yarn yarn add -D unocss…

mmap詳解

mmap詳解 mmap基礎概念mmap內存映射原理mmap相關函數調用mmap的使用細節mmap和常規文件操作的區別 mmap基礎概念 mmap是一種內存映射文件的方法&#xff0c;即將一個文件或者其它對象映射到進程的地址空間&#xff0c;實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一…

Vue3的內置組件 -實現過渡動畫 TransitionGroup

Vue3的內置組件 -實現過渡動畫 TransitionGroup 是一個內置組件&#xff0c;用于對 v-for 列表中的元素或組件的插入、移除和順序改變添加動畫效果 支持和 基本相同的 props、CSS 過渡 class 和 JavaScript 鉤子監聽器&#xff0c;但有以下幾點區別&#xff1a; 默認情況下&…

【軟考-架構】14、軟件可靠性基礎

?資料&文章更新? GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目錄 軟件可靠性基本概念軟件可靠性建模軟件可靠性管理軟件可靠性設計N版本程序設計恢復塊設計&#xff08;動態冗余&#xff09;雙機容錯技術、集群技術負載均衡軟件可靠性測試…

使用Python+OpenCV對視頻抽幀保存為JPG圖像

使用PythonOpenCV對視頻抽幀保存為JPG圖像 import os import cv2 import time#視頻文件夾路徑&#xff0c;可修改 videoPath D:\\video\\ #保存的圖片文件夾路徑&#xff0c;可修改 savePath D:\\images\\ videolist os.listdir(videoPath) if not os.path.exists(savePath…

學習整理在centos7上安裝mysql8.0版本教程

學習整理在centos7上安裝mysql8.0版本教程 查看linux系統版本下載mysql數據庫安裝環境檢查解壓mysql安裝包創建MySQL需要的目錄及授權新增用戶組新增組用戶配置mysql環境變量編寫MySQL配置文件初始化數據庫初始化msyql服務啟動mysql修改初始化密碼配置Linux 系統服務工具,使My…

DeepSeek預訓練追求極致的訓練效率的做法

DeepSeek在預訓練階段通過多種技術手段實現了極致的訓練效率,其中包括采用FP8混合精度訓練框架以降低計算和內存需求 ,創新性地引入Multi-head Latent Attention(MLA)壓縮KV緩存以提升推理效率,以及基于Mixture-of-Experts(MoE)的稀疏計算架構以在保證性能的同時顯著降低…

【計算機視覺】CV項目實戰- 深度解析TorchVision_Maskrcnn:基于PyTorch的實例分割實戰指南

深度解析TorchVision_Maskrcnn&#xff1a;基于PyTorch的實例分割實戰指南 技術背景與核心原理Mask R-CNN架構解析項目特點 完整實戰流程環境準備硬件要求軟件依賴 數據準備與標注1. 圖像采集2. 數據標注3. 數據格式轉換 模型構建與訓練1. 模型初始化2. 數據加載器配置3. 訓練優…

x86系列CPU寄存器和匯編指令總結

文章目錄 概要一、寄存器1.1、8086寄存器1.2、通用寄存器1.3、擴展寄存器 二、指令集三、x86指令集常見指令使用說明四、匯編4.1、匯編語法4.2、nsam匯編 五、參考 概要 在對學習Go的過程中&#xff0c;涉及到了匯編&#xff0c;因此對X86系列CPU的背景、寄存器、匯編指令做了一…

戴維斯雙擊選股公式如何編寫?

戴維斯雙擊&#xff0c;指的是營收增長和凈利潤增長同步&#xff0c;并有超預期的財務狀況。 戴維斯雙擊是指在低市盈率&#xff08;P/E&#xff09;時買入股票&#xff0c;待公司盈利增長和市盈率提升后賣出&#xff0c;以獲取雙重收益。以下是一個簡單的通達信選股模型示例&…

前端面試寶典---vue原理

vue的Observer簡化版 class Observer {constructor(value) {if (!value || typeof value ! object) returnthis.walk(value) // 對對象的所有屬性進行遍歷并定義響應式}walk (obj) {Object.keys(obj).forEach(key > defineReactive(obj, key, obj[key]))} } // 定義核心方法…

從“聾啞設備“到超級工廠:EtherCAT轉Modbus協議網關正在重構工業未來

當全球工廠加速邁向工業4.0&#xff0c;您的生產線是否因Modbus設備“拖后腿”而被迫降速&#xff1f;無需百萬改造&#xff01;無需淘汰設備&#xff01;一套EtherCAT從站轉Modbus協議網關&#xff0c;讓30年老機床與智能工廠實時對話&#xff0c;效率飆升300%&#xff01; 一…

Tauri文件系統操作:桌面應用的核心能力(入門系列四)

今天我們來聊聊Tauri中一個超級重要的功能 - 文件系統操作。這可是Web應用和桌面應用最大的區別之一。在瀏覽器里&#xff0c;出于安全考慮&#xff0c;我們對文件系統的訪問被限制得死死的。但在Tauri桌面應用中&#xff0c;我們可以安全地訪問用戶的文件系統&#xff0c;這簡…