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 của 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 quay lại. Tính năng khôi phục tiêu điểm cho phép 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 về phần tử đó khi tiêu điểm quay lại vùng chứa.

Khôi phục tiêu điểm trong 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 đóng vai trò là 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 đối tượng 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ư vùng chứa bố cục tải từng phầ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 lấy tiêu điểm theo mặc định. Vì các thành phần này không tự chặn các sự kiện nhập tiêu điểm, nên chỉ áp dụng đối tượng sửa đổi focusRestorer là chưa đủ.

Để 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ó phần tử con nào được lấy tiêu điểm trước đó. Compose tuân theo thứ tự lấy tiêu điểm mặc định để xác định thành phần con nào cần lấy tiêu điểm.

Bạn có thể tuỳ chỉnh thành phần con nào nhận tiêu điểm trong mục đầu tiên bằng cách truyền FocusRequester dự phòng đến focusRestorer (hoặc một lambda trả về FocusRequester trên các phiên bản Compose cũ hơn):

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 đó không có thành phần con nào được lấy tiêu điểm (hoặc nếu không khôi phục được tiêu điểm), thì 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 muốn tiêu điểm nằm trên đích đến hiện được chọn khi 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ũ):

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 sau đó 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().