還原焦點

使用者透過實體鍵盤或 D-pad 瀏覽複雜的使用者介面時,經常會在畫面的不同部分之間移動,例如從側邊導覽欄跳到內容清單、與項目互動,然後再跳回。焦點還原可讓容器記住先前聚焦的子項元素,並在焦點重新進入容器時,將焦點還原至該元素。

還原可捲動容器中的焦點

在 Jetpack Compose 中,LazyColumn、LazyRow 和 LazyVerticalGrid 等可捲動容器會自動做為焦點群組。如要在 Lazy 清單或格線啟用焦點還原功能,請附加 focusRestorer 修飾符:

LazyColumn(
    modifier = Modifier.focusRestorer()
) {
    items(items) { item ->
        Button(onClick = { /* Handle click */ }) {
            Text(item)
        }
    }
}

當焦點離開 LazyColumn 時,容器會記錄哪個項目處於啟用狀態。當使用者返回清單時,焦點會還原至該項目,而不是從頂端重新開始。

還原列與欄的焦點

與延遲版面配置容器不同,標準不可捲動版面配置容器 (例如 Row 和 Column) 預設不會建立焦點邊界。由於這類元件不會自行攔截焦點進入事件,因此單獨套用 focusRestorer 修飾符是不夠的。

如要在 Row 或 Column 上啟用焦點還原功能,請將 focusGroup 與 focusRestorer 結合:

Row(
    modifier = Modifier
        .focusGroup()
        .focusRestorer()
) {
    Button(onClick = { /* Action 1 */ }) {
        Text("First item")
    }
    Button(onClick = { /* Action 2 */ }) {
        Text("Second item")
    }
    Button(onClick = { /* Action 3 */ }) {
        Text("Third item")
    }
}

指定備用焦點目標

焦點首次進入容器時,不存在先前聚焦的子項。Compose 會按照預設焦點順序,決定要聚焦於哪個子項。

您可以將備用 focusRestorer 傳遞至 FocusRequester (或在舊版 Compose 中傳遞傳回 focusRestorer 的 lambda),自訂首次進入時要聚焦的子項:FocusRequester

val firstItemRequester = remember { FocusRequester() }

LazyColumn(
    modifier = Modifier.focusRestorer(firstItemRequester)
) {
    itemsIndexed(items) { index, item ->
        val itemModifier = if (index == 0) {
            Modifier.focusRequester(firstItemRequester)
        } else {
            Modifier
        }
        Button(
            onClick = { /* Handle click */ },
            modifier = itemModifier
        ) {
            Text(item)
        }
    }
}

如果先前沒有聚焦任何子項 (或焦點還原失敗),Compose 會將焦點移至相關聯的備用目標。

在 NavigationRail、NavigationBar、NavigationDrawer 或 TabRow 等導覽元件中,使用者從內容區域進入元件時,會預期焦點落在目前選取的目的地。

您可以將 Modifier.focusGroup() 與 focusProperties 結合,並設定 onEnter 屬性 (或在舊版 Compose 中設定 enter),將焦點導向有效目的地:

val (homeRequester, searchRequester, settingsRequester) = remember { FocusRequester.createRefs() }

val selectedRequester = when (currentDestination) {
    "home" -> homeRequester
    "search" -> searchRequester
    "settings" -> settingsRequester
    else -> homeRequester
}

NavigationRail(
    modifier = modifier
        .focusProperties {
            // Redirect focus to the selected item when focus enters the navigation rail
            onEnter = { selectedRequester.requestFocus() }
        }
        .focusGroup()
) {
    NavigationRailItem(
        selected = currentDestination == "home",
        onClick = { onNavigate("home") },
        icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
        label = { Text("Home") },
        modifier = Modifier.focusRequester(homeRequester)
    )
    NavigationRailItem(
        selected = currentDestination == "search",
        onClick = { onNavigate("search") },
        icon = { Icon(Icons.Default.Search, contentDescription = "Search") },
        label = { Text("Search") },
        modifier = Modifier.focusRequester(searchRequester)
    )
    NavigationRailItem(
        selected = currentDestination == "settings",
        onClick = { onNavigate("settings") },
        icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") },
        label = { Text("Settings") },
        modifier = Modifier.focusRequester(settingsRequester)
    )
}

在畫面轉換時還原焦點

當使用者點選項目前往詳細資料畫面,然後使用返回按鈕或手勢返回時,您可以將焦點還原至他們點選的項目。

如要在畫面轉換時保留及還原焦點,請按照下列步驟操作:

  1. 將容器與 FocusRequester 建立關聯。
  2. 觸發導覽事件前,請先在容器的 FocusRequester 上呼叫 saveFocusedChild()。
  3. 當使用者返回畫面時,請要求容器取得初始焦點,或呼叫 restoreFocusedChild()。