Khôi phục tiêu điểm

Khi người dùng thao tác trên giao diện người dùng phức tạp bằng bàn phím phần cứng hoặc D-pad, họ thường di chuyển giữa các phần khác nhau trên màn hình, chẳng hạn như chuyển từ thanh điều hướng bên sang danh sách nội dung, tương tác với một mục rồi chuyển về. Tính năng khôi phục tiêu điểm cho phép các vùng chứa ghi nhớ phần tử con đã được lấy làm tiêu điểm trước đó và khôi phục tiêu điểm cho phần tử đó khi tiêu điểm quay lại vùng chứa.

Khôi phục tiêu điểm trong các vùng chứa có thể cuộn

Trong Jetpack Compose, các vùng chứa có thể cuộn như LazyColumn, LazyRow và LazyVerticalGrid sẽ tự động hoạt động như các nhóm tiêu điểm. Để bật tính năng khôi phục tiêu điểm trên danh sách hoặc lưới tải từng phần, hãy đính kèm hệ số sửa đổi focusRestorer:

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

Khi tiêu điểm thoát khỏi LazyColumn, vùng chứa sẽ ghi lại mục nào đang hoạt động. Khi người dùng quay lại danh sách, tiêu điểm sẽ được khôi phục về mục đó thay vì bắt đầu lại từ đầu.

Khôi phục tiêu điểm trong Hàng và Cột

Không giống như các vùng chứa bố cục trì hoãn, các vùng chứa bố cục không thể cuộn tiêu chuẩn như Row và Column không thiết lập ranh giới tiêu điểm theo mặc định. Vì bản thân các thành phần này không chặn các sự kiện nhập tiêu điểm, nên chỉ áp dụng bộ sửa đổi focusRestorer là không đủ.

Để bật tính năng khôi phục tiêu điểm trên Row hoặc Column, hãy kết hợp focusGroup với 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")
    }
}

Chỉ định mục tiêu lấy tiêu điểm dự phòng

Khi tiêu điểm vào một vùng chứa lần đầu tiên, không có thành phần con nào được lấy tiêu điểm trước đó. Compose tuân theo thứ tự lấy nét mặc định để xác định phần tử con nào cần lấy nét.

Bạn có thể tuỳ chỉnh thành phần con nào nhận được tiêu điểm khi nhập lần đầu bằng cách truyền một FocusRequester dự phòng đến focusRestorer (hoặc một biểu thức lambda trả về FocusRequester trên các phiên bản Compose trước đó):

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)
        }
    }
}

Nếu trước đó chưa có thành phần con nào được lấy tiêu điểm (hoặc nếu quá trình khôi phục tiêu điểm không thành công), Compose sẽ di chuyển tiêu điểm đến mục tiêu dự phòng được liên kết.

Trong các thành phần điều hướng như NavigationRail, NavigationBar, NavigationDrawer hoặc TabRow, người dùng mong đợi tiêu điểm sẽ nằm trên đích đến hiện được chọn khi họ truy cập vào thành phần từ vùng nội dung.

Bạn có thể chuyển tiêu điểm đến đích đến đang hoạt động bằng cách kết hợp Modifier.focusGroup() với focusProperties và đặt thuộc tính onEnter (hoặc enter trên các phiên bản Compose cũ hơn):

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)
    )
}

Khôi phục tiêu điểm trong quá trình chuyển đổi màn hình

Khi người dùng nhấp vào một mục để chuyển đến màn hình chi tiết rồi quay lại bằng nút Quay lại hoặc cử chỉ, bạn có thể khôi phục tiêu điểm cho mục mà họ đã nhấp vào.

Cách duy trì và khôi phục tiêu điểm trong quá trình chuyển đổi màn hình:

  1. Liên kết vùng chứa với một FocusRequester.
  2. Trước khi kích hoạt sự kiện điều hướng, hãy gọi saveFocusedChild() trên FocusRequester của vùng chứa.
  3. Khi người dùng quay lại màn hình, hãy yêu cầu tiêu điểm ban đầu trên vùng chứa hoặc gọi restoreFocusedChild().