還原焦點

使用者透過硬體鍵盤或 D-Pad 瀏覽複雜的使用者介面時,經常會在螢幕的不同區段之間移動,例如從側邊導覽列跳到內容清單、與項目互動,然後跳回。焦點還原功能可讓容器記住先前焦點所在的子項元素,並在焦點重新進入容器時還原至該元素。

在可捲動的容器中還原焦點

在 Jetpack Compose 中,LazyColumn、LazyRow 和 LazyVerticalGrid 等可捲動的容器會自動做為焦點群組。如要在延遲載入的清單或格線中啟用焦點還原功能,請附加 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 會依預設焦點順序決定要將焦點放在哪個子項。

您可以傳遞後備 FocusRequester 至 focusRestorer (或在較早的 Compose 版本中傳遞傳回 FocusRequester 的 lambda),自訂首次輸入時要將焦點移至哪個子項:

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()。