去年夏天,我寫了一篇關於使用 Compose 進行專注管理的部落格文章。從那時起,我就一直保留著這篇文章的草稿,但直到現在才最終定稿。這篇部落格文章的標題是:[It's All About (Accessibility) Focus And Compose][4]。

所以,在這篇文章中,我們將討論焦點指示器以及如何使用 Compose 讓它們更容易存取。但首先,讓我們來談談焦點指示器的基本概念。

重點指標

焦點指示器顧名思義,用於顯示鍵盤焦點在使用者介面中的目前位置。它們必須可見,以便鍵盤和鍵盤模擬裝置使用者能夠輕鬆地在應用程式中進行導航。

需要注意的是,我這裡所說的焦點指示器並非指螢幕閱讀器(例如 TalkBack)的焦點。螢幕閱讀器的焦點是由系統層級處理的。

Web 內容無障礙指南(儘管名稱如此,但它是無障礙立法背後的標準,也用於應用程式)對無障礙焦點指示器有一些要求。根據 [SC 2.4.13 焦點外觀][1],焦點指示器需要滿足以下條件之一:

  • 使用者代理(Android 系統)的預設樣式

  • 至少 2 個像素厚,且聚焦狀態和未聚焦狀態下相同像素的對比度至少為 3:1。

Android 的預設焦點指示器是漣漪效果,但它不太顯眼。從技術上講,這樣勉強夠用,但如果你想讓應用更易於存取,就需要提高焦點指示器的可見性。接下來,我們將討論如何使用 Compose 建立更顯眼(從而更易於存取)的焦點指示器。

使用 Compose 建立更易於使用的焦點指示器

建立焦點指示器有多種方法。例如,您可以根據焦點狀態新增邊框,如 Appt.org 在其程式碼片段中建議的那樣:[Jetpack Compose 中的輔助功能焦點指示器][2]。但如果您需要更複雜的功能,則需要使用 Indication API。

[指示 API][3] 搭配DrawModifierNode可以繪製複雜的焦點指示器。在本篇部落格文章中,我們將在目前焦點專案(首先是一個按鈕)下方繪製一條簡單的線條:

立柱上有一個按鈕和一個開關。按鈕處於選取狀態,下方有一條清晰可見的藍線。

然後是一個換行行:

一列上有一個按鈕和一個開關。開關所在的行處於選取狀態,其下方有一條可見的藍線。

焦點指示器

我們本質上是在建立一個修飾符,它可以用於任何互動式元件。我們希望盡可能將邏輯封裝在這個修飾符和其他元件中,以便盡可能簡化在 Compose 程式碼中的使用。

為此,我們需要三樣東西:

  • 修飾符(我們稱之為focusIndication

  • IndicationNodeFactory用於建立指示( FocusIndication

  • 最後,使用DrawModifierNode實際繪製焦點指示器( FocusNode )。

建構修飾符

讓我們從清單的末尾開始。

為了建立實際的指示,我們需要定義一個類,該類接受交互源和顏色作為參數。它繼承了Modifier.Node()DrawModifierNode ,並重寫了兩個方法: onAttachContentDrawScope.draw()

class FocusNode(
    val interactionSource: InteractionSource,
    val color: Color
) : Modifier.Node(), DrawModifierNode {
    override fun onAttach() {
        …
    }

    override fun ContentDrawScope.draw() {
        …
    }
}

onAttach函數處理交互。讓我們加入一個內部變數來儲存元件的焦點狀態,並將其儲存在onAttach函數中:

private var isFocused by mutableStateOf(false)

override fun onAttach() {
    coroutineScope.launch {
        interactionSource.interactions.collect { interaction ->
            when (interaction) {
                is FocusInteraction.Focus -> isFocused = true
                is FocusInteraction.Unfocus -> isFocused = false
            }
        }
    }
}

這裡,我們使用建構函式中傳入的interactionSource來收集元件的互動資訊。我們現在只專注於焦點交互,但interactionSource.interactions也包含例如按下交互,因此如果您想建立自訂的按下樣式,也可以在這裡處理它們。

然後,在ContentDrawScope.draw中,我們繪製焦點指示器:

override fun ContentDrawScope.draw() {
    drawContent()
    if (isFocused) {
        drawRect(
            color = color,
            topLeft = Offset(
                x = 0f,
                y = size.height - 8f
            ),
            size = Size(
                width = size.width,
                height = 12f
            )
        )
    }
}

我們首先使用drawContent繪製內容,然後,如果isFocused為真,則在元件下方繪製一個 12 像素高的矩形。我們希望稍微偏移它的位置,使其正確顯示。至於顏色,我們使用建構函數中傳入的color

下一步是使用這個FocusNode 。我們將建立一個繼承自IndicationNodeFactory資料類別:

private data class FocusIndication(
    val color: Color
) : IndicationNodeFactory {
    override fun create(
        interactionSource: InteractionSource
    ): Modifier.Node {
        return FocusNode(interactionSource, color)
    }
}

在這個範例中,我們重寫了create函數並傳回我們建立的FocusNode實例。最後,我們定義了一個接受interactionSource的修飾符,並將其命名為focusIndication

@Composable
fun Modifier.focusIndication(
    interactionSource: MutableInteractionSource
): Modifier {
    val focusColor = MaterialTheme.colorScheme.surfaceTint
    val focusIndication = remember {
        FocusIndication(
            color = focusColor
        )
    }
    return indication(interactionSource, focusIndication)
}

首先,我們有focusColor變數,在本例中,它取自主題顏色中的surfaceTint 。如文章開頭所述,它與非聚焦狀態下相同像素的顏色對比至少應為 3:1。這意味著最好分別設定淺色和深色主題顏色,因為很難找到一種顏色能夠同時滿足兩種模式的要求。

之後,我們記住先前建立的FocusIndication ,並將focusColor作為color參數傳遞。最後,我們傳回一個包含interactionSourcefocusIndication indication修飾符。

觸控模式下隱藏指示器

有時,我們希望在觸控模式下隱藏焦點指示器,因為它在使用者與互動元件互動時過於頻繁地顯示,或者出於某種原因需要手動管理焦點。 InputModeManager 可以幫助InputModeManager實現這一點。

首先,對於FocusNode ,我們再增加一個它擴充的接口,即CompositionLocalConsumerModifierNode

class FocusNode(
    …
) : Modifier.Node(), 
DrawModifierNode, 
CompositionLocalConsumerModifierNode {
    …
}

這樣,我們就可以使用LocalInputModeManager的值,並讀取其輸入模式:

override fun ContentDrawScope.draw() {
    drawContent()
    val inputMode = currentValueOf(LocalInputModeManager).inputMode
    if (isFocused && inputMode == InputMode.Keyboard) {
        …
    }
}

我們使用currentValueOf(LocalInputModeManager).inputMode讀取值,檢查模式是否為InputMode.Keyboard ,然後才繪製焦點指示。

好了,現在我們已經準備好了對焦指示器。我們該如何使用它?

使用焦點指示修飾符

具體用法取決於元件。對於具有內建互動的元件,例如按鈕或文字字段,使用起來很簡單。我們定義一個互動來源,將其傳遞給元件的interactionSource參數,然後使用相同的interactionSource呼叫focusIndication修飾符:


val buttonInteractionSource = remember { MutableInteractionSource() }
Button(
    modifier = Modifier.focusIndication(buttonInteractionSource),
    interactionSource = buttonInteractionSource,
    onClick = {}
) {
    Text("A Button")
}

對於使用諸如clickabletoggleableselectable等修飾符進行互動的自訂元件,新增自訂焦點指示器需要更多步驟。

在以下 Switch 行的範例中,為了清楚起見,我省略了嚴格超出焦點指示範圍的部分:


val switchInteractionSource = remember { MutableInteractionSource() }

Row(
    modifier = Modifier
        .focusIndication(switchInteractionSource)
        .toggleable(
            …
            indication = ripple(),
            interactionSource = switchInteractionSource
        ),

) {
    Text(
        "A switch"
    )
    Switch(
        …
        interactionSource = switchInteractionSource,
    )
}

我們定義一個互動來源,並將其命名為switchInteractionSource 。然後,我們將該交互源傳遞給一個toggleable修飾符,該修飾符用於使整行可切換。我們也傳入了指示函數ripple() -否則,觸摸時就不會出現漣漪效果。

最後,我們也將互動來源傳遞給Switch ,以便如果使用者點擊開關元件,漣漪效果就會在整行中可見。

總結

在這篇文章中,我們討論瞭如何為互動式 Compose 元件新增自訂焦點指示器。我們研究了Indication API 及其使用方法,並建立了一個自訂修飾符來封裝邏輯,以便更輕鬆地使用。您可以在[此 Github gist][5]中找到完整的程式碼。

我有一個後續文章的想法:建立不同類型的關注指標,以表明你實際上可以對它們進行一些創意設計。

部落格文章中的連結

  • [一切皆關乎(輔助功能)專注與構圖][4]

  • [SC 2.4.13 焦點外觀][1]

  • [Jetpack Compose 中的輔助功能焦點指示器][2]

  • [適應症 API][3]

  • [完整程式碼在此Github gist中][5]


原文出處:https://dev.to/eevajonnapanula/more-accessible-focus-indicators-with-compose-1ca4


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝11   💬8  
235
🥈
我愛JS
💬2  
8
🥉
Gigi
2
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登