變更焦點行為

有時必須覆寫元素的預設焦點行為 。例如,您可能想要將可組合函式分組避免 聚焦於特定可組合項,明確要求焦點對其中一個可組合項。 擷取或發布焦點,或在進入或離開時重新導向焦點。這個 部分說明當非預設焦點時,如何變更焦點行為 需求。

對焦點群組提供一致的導覽功能

有時候,Jetpack Compose 無法立即針對 分頁導覽,尤其是複雜的父項 Composables 例如分頁和 最新、最重要

聚焦搜尋通常會遵循 Composables 的宣告順序, 在某些情況下是不可能的,例如當物件的一個 Composables 中的其中一個 階層是一種水平捲動式,並未完整顯示。顯示位置: 範例。

Jetpack Compose 可能會決定將焦點放在最接近開頭的下一個項目 此時請勿繼續前往預期路徑 單向導覽:

動畫顯示應用程式顯示頂端水平導覽,下方列出項目清單。
圖 1. 動畫:應用程式顯示頂端水平導覽,下方列出項目清單

在本例中,開發人員明顯未將重點放在 先從「Chocolates」分頁移至下方的第一張圖片,然後再回到第一張圖片 「測驗」分頁。反之,他們希望焦點繼續放在標籤上,直到 ,然後把焦點放在內部內容:

動畫顯示應用程式顯示頂端水平導覽,下方列出項目清單。
圖 2. 動畫:應用程式顯示頂端水平導覽,下方列出項目清單

在必須讓一組可組合項成為焦點的情況下 按照上一個範例的 Tab 列,您需要將 在具有 focusGroup() 修飾符的父項中,找到 Composable

LazyVerticalGrid(columns = GridCells.Fixed(4)) {
    item(span = { GridItemSpan(maxLineSpan) }) {
        Row(modifier = Modifier.focusGroup()) {
            FilterChipA()
            FilterChipB()
            FilterChipC()
        }
    }
    items(chocolates) {
        SweetsCard(sweets = it)
    }
}

雙向導覽會尋找距離指定 方向:其他群組中的元素距離不完全可見 目前群組中的項目,導覽功能會選擇最接近的項目。為了避免這種情況 行為,您可以套用 focusGroup() 修飾符。

FocusGroup 能讓整個群組在焦點上看起來像單一實體 但群組本身不會獲得焦點,相反地,距離最近的孩子 提高專注力如此一來,導覽會知道要前往不完全可見 再離開群組

在此情況下,系統會將 FilterChip 的三個例項置於 SweetsCard 項目 (即使 SweetsCards 可完整顯示) 系統可能會隱藏部分FilterChip。這是因為 focusGroup 修飾符會指示焦點管理工具調整項目的順序 以便瀏覽,導覽內容與 UI 更為一致。

如果沒有 focusGroup 修飾符,如果 FilterChipC 不可見,請聚焦 導航才會接起不過,新增這類修飾符 只有可搜尋,但會在 FilterChipB後取得焦點,例如 讓他們感到安心

將可組合項設為可聚焦

某些可組合函式的設計可聚焦,例如按鈕或含有 附加的 clickable 修飾符若您想要 可組合函式可聚焦的行為,您可以使用 focusable 修飾符:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

將可組合項設為無法聚焦

在某些情況下,您的部分元素可能不應參與互動 焦點在這種極少數的情況下,您可以使用 canFocus property 排除 Composable,使其無法聚焦。

var checked by remember { mutableStateOf(false) }

Switch(
    checked = checked,
    onCheckedChange = { checked = it },
    // Prevent component from being focused
    modifier = Modifier
        .focusProperties { canFocus = false }
)

使用「FocusRequester」要求鍵盤焦點

在某些情況下,您可能會想明確要求焦點做為回應 互動。舉例來說,您可以詢問使用者是否要重新啟動 填寫表單時,如果按下「是」你要重新將焦點移至第一個欄位 該表單的內容

首先,請將 FocusRequester 物件與 要移入鍵盤焦點的可組合函式。在下列程式碼中 程式碼片段時,FocusRequester 物件會設定為與 TextField 建立關聯 名為 Modifier.focusRequester 的修飾符:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

您可以呼叫 FocusRequester 的 requestFocus 方法,以傳送實際的焦點要求。建議您在 Composable 結構定義外叫用這個方法 (否則,它會在每次重組時重新執行)。下列程式碼片段 顯示如何在按下按鈕時,要求系統移動鍵盤焦點 已點擊:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

Button(onClick = { focusRequester.requestFocus() }) {
    Text("Request focus on TextField")
}

擷取並釋放焦點

您可以運用焦點來引導使用者為應用程式提供合適的資料 必須執行其工作,例如取得有效的電子郵件地址或電話 號碼。雖然錯誤狀態可通知使用者發生問題, 則可能得等到含有錯誤資訊的領域,才能集中精神 通常也會自動修復

如要擷取焦點,您可以叫用 captureFocus() 方法,以及 之後改用 freeFocus() 方法即可發布,如下所示 範例:

val textField = FocusRequester()

TextField(
    value = text,
    onValueChange = {
        text = it

        if (it.length > 3) {
            textField.captureFocus()
        } else {
            textField.freeFocus()
        }
    },
    modifier = Modifier.focusRequester(textField)
)

焦點修飾符的優先順序

Modifiers 可能會顯示為只有一個子項的元素,因此當您將佇列加入佇列時 而左側的每個Modifier左側 (或頂端) 則會納入Modifier 右方 (或下方) 結束。也就是說,第二個 Modifier 包含在 第一個,因此在宣告兩個 focusProperties 時,只有最頂層 其中一個工作,因為以下項目位於頂層。

如要進一步瞭解這個概念,請參閱下列程式碼:

Modifier
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

在此情況下,focusProperties 表示item2是適當的焦點 如前文所述,不必使用;因此,item1 會是 只使用一個專案

家長也可以運用這個方法,將行為重設為預設值 使用 FocusRequester.Default

Modifier
    .focusProperties { right = Default }
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

父項不一定要屬於同一個修飾符鏈結。家長 可組合函式可以覆寫子項可組合函式的焦點屬性。例如: 考慮這個 FancyButton,讓按鈕成為不焦點:

@Composable
fun FancyButton(modifier: Modifier = Modifier) {
    Row(modifier.focusProperties { canFocus = false }) {
        Text("Click me")
        Button(onClick = { }) { Text("OK") }
    }
}

使用者可以將 canFocus 設為 true,讓這個按鈕再次成為焦點:

FancyButton(Modifier.focusProperties { canFocus = true })

和每個 Modifier 一樣,聚焦相關項目的行為會根據順序而有不同 您宣告這些函式例如,如下所示的程式碼會使 Box 可聚焦,但 FocusRequester 與這個可聚焦項目建立關聯 是在可聚焦物件之後宣告

Box(
    Modifier
        .focusable()
        .focusRequester(Default)
        .onFocusChanged {}
)

請務必注意,focusRequester 會與第一個 位於階層中的項目下方,因此這個 focusRequester 指向 第一個可聚焦的孩子如果沒有可用的選項,就無法指向任何頁面。 不過,由於 Box 可聚焦 (感謝 focusable() 修飾符), 但您可透過雙向導覽進行瀏覽

再舉一例,下列任一指令可以使用,onFocusChanged() 修飾符是指第一個可聚焦的元素 focusable()focusTarget() 修飾符。

Box(
    Modifier
        .onFocusChanged {}
        .focusRequester(Default)
        .focusable()
)
Box(
    Modifier
        .focusRequester(Default)
        .onFocusChanged {}
        .focusable()
)

進入或離開時重新導向焦點

有時候,您需要提供一種非常具體的導覽方式,例如 顯示於下方動畫中:

動畫:螢幕顯示兩個並排的按鈕欄,並將焦點從某一欄移至另一欄。
圖 3. 動畫:畫面上並排放置兩個按鈕欄,並將焦點從某一欄移至另一欄

在深入探討如何建立此資料庫之前,您必須先瞭解 焦點搜尋的行為如未進行任何修改,一旦焦點搜尋 抵達 Clickable 3 項目,在 D-Pad (或同等項目) 上按下 DOWN 方向鍵),即可將焦點移至 Column 下方顯示的任何項目。 退出群組並忽略右側的群組如果沒有 可聚焦的項目不會在任何位置移動,而是停留在 Clickable 3

如要變更此行為並提供正確的導覽功能,您可以使用 focusProperties 修飾符,協助您管理焦點所在指令時會發生什麼事 搜尋會進入或結束 Composable

val otherComposable = remember { FocusRequester() }

Modifier.focusProperties {
    exit = { focusDirection ->
        when (focusDirection) {
            Right -> Cancel
            Down -> otherComposable
            else -> Default
        }
    }
}

每次將焦點移至特定 Composable 或離開階層的某一部分,例如當使用者介面有兩個元素 您想確保每次處理第一個資料欄時 焦點就會切換到第二個:

動畫:螢幕顯示兩個並排的按鈕欄,並將焦點從某一欄移至另一欄。
圖 4. 動畫:畫面上並排放置兩個按鈕欄,並將焦點從某一欄移至另一欄

在這張 GIF 中,當焦點在 Column 1 中的 Clickable 3 Composable 時, 下一個目前聚焦的項目是另一個 Column 中的 Clickable 4。這項行為 結合 focusDirectionenterexit 即可達成 focusProperties 修飾符內的值兩者都需要有 lambda 做為參數,以做為焦點的來源方向 FocusRequester。這個 lambda 的行為有三種不同方式:傳回 FocusRequester.Cancel 會讓焦點停止繼續,而 FocusRequester.Default 不會變更其行為。請改為提供 將 FocusRequester 附加至另一個 Composable 可讓焦點跳至該部分 特定 Composable

變更焦點前進方向

如要將焦點移到下一個項目或精確的方向,您可以 使用 onPreviewKey 修飾符,並將 LocalFocusManager 設為 使用 moveFocus 修飾符將焦點移至下一個項目。

以下範例顯示聚焦機制的預設行為: 偵測到 tab 鍵,焦點會前進至焦點中的下一個元素 請參考閱讀清單,進一步瞭解 如何選擇 Kubeflow Pipelines SDK 或 TFX雖然這並非您通常必須設定的項目,但 以便瞭解系統的內部運作機制,進而 行為

val focusManager = LocalFocusManager.current
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.onPreviewKeyEvent {
        when {
            KeyEventType.KeyUp == it.type && Key.Tab == it.key -> {
                focusManager.moveFocus(FocusDirection.Next)
                true
            }

            else -> false
        }
    }
)

在這個範例中,focusManager.moveFocus() 函式會將焦點前進至 項目,或函式參數中隱含的方向。