Nút biểu tượng

Nút biểu tượng cho thấy các hành động mà người dùng có thể thực hiện. Nút biểu tượng phải sử dụng biểu tượng có ý nghĩa rõ ràng và thường đại diện cho các thao tác phổ biến hoặc thường dùng.

Có hai loại nút biểu tượng:

  • Mặc định: Các nút này có thể mở các thành phần khác, chẳng hạn như trình đơn hoặc thanh tìm kiếm.
  • Nút bật/tắt: Các nút này có thể đại diện cho các thao tác nhị phân có thể bật hoặc tắt, chẳng hạn như "yêu thích" hoặc "đánh dấu".
5 nút biểu tượng có các biểu tượng khác nhau (cài đặt, xem thêm, v.v.). Một số biểu tượng được tô màu, cho biết lựa chọn, và một số biểu tượng được viền ngoài.
Hình 1. Các nút biểu tượng, một số nút được tô màu (cho biết lựa chọn) và có đường viền.

Nền tảng API

Sử dụng thành phần kết hợp IconButton để triển khai các nút biểu tượng tiêu chuẩn. Để tạo các kiểu hình ảnh khác nhau như có màu nền, có màu nền và tông màu hoặc có đường viền, hãy sử dụng lần lượt FilledIconButton, FilledTonalIconButton và OutlinedIconButton.

Các tham số chính cho IconButton bao gồm:

  • onClick: Hàm lambda thực thi khi người dùng nhấn vào nút biểu tượng.
  • enabled: Một boolean kiểm soát trạng thái bật của nút. Khi false, nút này không phản hồi hoạt động đầu vào của người dùng.
  • content: Nội dung có thể kết hợp bên trong nút, thường là Icon.

Ví dụ cơ bản: Nút biểu tượng bật/tắt

Ví dụ này cho bạn biết cách triển khai nút biểu tượng bật/tắt. Nút biểu tượng bật/tắt thay đổi giao diện dựa trên việc nút đó được chọn hay không được chọn.

@Preview
@Composable
fun ToggleIconButtonExample() {
    // isToggled initial value should be read from a view model or persistent storage.
    var isToggled by rememberSaveable { mutableStateOf(false) }

    IconButton(
        onClick = { isToggled = !isToggled }
    ) {
        Icon(
            painter = if (isToggled) painterResource(R.drawable.favorite_filled) else painterResource(R.drawable.favorite),
            contentDescription = if (isToggled) "Selected icon button" else "Unselected icon button."
        )
    }
}

Các điểm chính về mã

  • Thành phần kết hợp ToggleIconButtonExample xác định một IconButton có thể bật/tắt.
    • mutableStateOf(false) tạo một đối tượng MutableState chứa giá trị boolean, ban đầu là false. Điều này khiến isToggled trở thành một phần tử giữ trạng thái, nghĩa là Compose sẽ kết hợp lại giao diện người dùng bất cứ khi nào giá trị của phần tử này thay đổi.
    • rememberSaveable đảm bảo trạng thái isToggled vẫn giữ nguyên khi có các thay đổi về cấu hình, chẳng hạn như xoay màn hình.
  • Hàm lambda onClick của IconButton xác định hành vi của nút khi được nhấp vào, chuyển đổi trạng thái giữa true và false.
  • Tham số painter của thành phần kết hợp Icon tải có điều kiện một painterResource khác dựa trên trạng thái isToggled. Thao tác này sẽ thay đổi giao diện của biểu tượng.
    • Nếu isToggled là true, thì hàm này sẽ tải đối tượng có thể vẽ hình trái tim được tô màu.
    • Nếu isToggled là false, thì hàm này sẽ tải đối tượng có thể vẽ hình trái tim có đường viền.
  • contentDescription của Icon cũng cập nhật dựa trên trạng thái isToggled để cung cấp thông tin hỗ trợ tiếp cận phù hợp.

Kết quả

Hình ảnh sau đây cho thấy nút biểu tượng bật/tắt trong đoạn mã trước ở trạng thái chưa chọn:

Nút biểu tượng bật/tắt mục yêu thích (hình trái tim) ở trạng thái chưa chọn (chưa được tô màu).
Hình 2. Nút biểu tượng bật/tắt "yêu thích" ở trạng thái chưa chọn.

Ví dụ nâng cao: Thao tác lặp lại khi nhấn

Phần này minh hoạ cách tạo các nút biểu tượng liên tục kích hoạt một hành động khi người dùng nhấn và giữ các nút đó, thay vì chỉ kích hoạt một lần cho mỗi lượt nhấp.

@Composable
fun MomentaryIconButton(
    unselectedImage: Int,
    selectedImage: Int,
    contentDescription: String,
    modifier: Modifier = Modifier,
    stepDelay: Long = 100L, // Minimum value is 1L milliseconds.
    onClick: () -> Unit
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed by interactionSource.collectIsPressedAsState()
    val pressedListener by rememberUpdatedState(onClick)

    LaunchedEffect(isPressed) {
        while (isPressed) {
            delay(stepDelay.coerceIn(1L, Long.MAX_VALUE))
            pressedListener()
        }
    }

    IconButton(
        modifier = modifier,
        onClick = onClick,
        interactionSource = interactionSource
    ) {
        Icon(
            painter = if (isPressed) painterResource(id = selectedImage) else painterResource(id = unselectedImage),
            contentDescription = contentDescription,
        )
    }
}

Các điểm chính về mã

  • MomentaryIconButton lấy unselectedImage: Int, mã tài nguyên drawable cho biểu tượng khi nút không được nhấn và selectedImage: Int, mã tài nguyên drawable cho biểu tượng khi nút được nhấn.
  • Nó sử dụng interactionSource để theo dõi cụ thể các lượt tương tác "nhấn" của người dùng.
  • isPressed là true khi nút đang được nhấn và false trong trường hợp ngược lại. Khi isPressed là true, LaunchedEffect sẽ chuyển sang một vòng lặp.
    • Bên trong vòng lặp này, hàm sử dụng delay (với stepDelay) để tạo khoảng dừng giữa các hành động kích hoạt. coerceIn đảm bảo độ trễ ít nhất là 1 mili giây để ngăn chặn vòng lặp vô hạn.
    • pressedListener được gọi sau mỗi lần trì hoãn trong vòng lặp. Thao tác này sẽ lặp lại hành động.
  • pressedListener sử dụng rememberUpdatedState để đảm bảo rằng lambda onClick (hành động cần thực hiện) luôn là phiên bản mới nhất từ thành phần mới nhất.
  • Icon thay đổi hình ảnh hiển thị dựa trên việc nút hiện có được nhấn hay không.
    • Nếu isPressed là true, thì selectedImage sẽ xuất hiện.
    • Nếu không, unselectedImage sẽ xuất hiện.

Tiếp theo, hãy sử dụng MomentaryIconButton này trong một ví dụ. Đoạn mã sau đây minh hoạ hai nút biểu tượng điều khiển một bộ đếm:

@Preview()
@Composable
fun MomentaryIconButtonExample() {
    var pressedCount by remember { mutableIntStateOf(0) }

    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        MomentaryIconButton(
            unselectedImage = R.drawable.fast_rewind,
            selectedImage = R.drawable.fast_rewind_filled,
            stepDelay = 100L,
            onClick = { pressedCount -= 1 },
            contentDescription = "Decrease count button"
        )
        Spacer(modifier = Modifier)
        Text("advanced by $pressedCount frames")
        Spacer(modifier = Modifier)
        MomentaryIconButton(
            unselectedImage = R.drawable.fast_forward,
            selectedImage = R.drawable.fast_forward_filled,
            contentDescription = "Increase count button",
            stepDelay = 100L,
            onClick = { pressedCount += 1 }
        )
    }
}

Các điểm chính về mã

  • Thành phần kết hợp MomentaryIconButtonExample hiển thị một Row chứa hai thực thể MomentaryIconButton và một thành phần kết hợp Text để tạo giao diện người dùng nhằm tăng và giảm bộ đếm.
  • Hàm này duy trì một biến trạng thái có thể thay đổi pressedCount bằng cách sử dụng remember và mutableIntStateOf, được khởi tạo thành 0. Khi pressedCount thay đổi, mọi composable quan sát nó (như composable Text) sẽ recompose để phản ánh giá trị mới.
  • MomentaryIconButton đầu tiên sẽ giảm pressedCount khi được nhấp hoặc giữ.
  • MomentaryIconButton thứ hai sẽ tăng pressedCount khi được nhấp hoặc giữ.
  • Cả hai nút đều sử dụng stepDelay là 100 mili giây, nghĩa là hành động onClick lặp lại sau mỗi 100 mili giây khi người dùng nhấn giữ một nút.

Kết quả

Video sau đây cho thấy giao diện người dùng có các nút biểu tượng và bộ đếm:

Hình 3. Giao diện người dùng của bộ đếm có hai nút biểu tượng (cộng và trừ) để tăng và giảm bộ đếm.

Tài nguyên khác