Xử lý thao tác trên bàn phím

Khi người dùng đặt tiêu điểm vào một thành phần văn bản có thể chỉnh sửa, chẳng hạn như TextField, và thiết bị có gắn bàn phím phần cứng, tất cả đầu vào đều do hệ thống xử lý. Bạn có thể cung cấp phím tắt bằng cách xử lý các sự kiện chính.

Phím tắt mặc định

Các phím tắt sau đây có sẵn ngay từ đầu.

Phím tắt Thao tác Thành phần kết hợp hỗ trợ lối tắt
Shift+Ctrl+Mũi tên trái/Mũi tên phải Chọn văn bản ở đầu/cuối từ BasicTextField, TextField
Shift+Ctrl+Mũi tên lên/Mũi tên xuống Chọn văn bản vào đầu/cuối đoạn BasicTextField, TextField
Shift+Alt+Mũi tên lên/Mũi tên xuống hoặc Shift+Meta+Mũi tên trái/Mũi tên phải Chọn văn bản ở đầu/cuối văn bản BasicTextField, TextField
Shift+Mũi tên trái/Mũi tên phải Chọn ký tự BasicTextField, TextField
Ctrl+A Chọn tất cả BasicTextField, TextField
Ctrl+C/Ctrl+X/Ctrl+V Sao chép/cắt/dán BasicTextField, TextField
Ctrl+Z/Ctrl+Shift+Z Huỷ/làm lại BasicTextField, TextField
PageDown/PageUp Cuộn LazyColumn, đối tượng sửa đổi verticalScroll, đối tượng sửa đổi scrollable

Sự kiện quan trọng

Trong Compose, bạn có thể xử lý từng thao tác nhấn phím bằng đối tượng sửa đổi onKeyEvent. Đối tượng sửa đổi chấp nhận một hàm lambda được gọi khi thành phần đã sửa đổi nhận được một sự kiện chính. Sự kiện chính được mô tả là đối tượng KeyEvent. Bạn có thể lấy thông tin của từng sự kiện nhấn phím bằng cách tham chiếu đến đối tượng trong lambda được truyền đến đối tượng sửa đổi onKeyEvent.

Một thao tác nhấn phím sẽ gửi hai sự kiện chính. Một lệnh được kích hoạt khi người dùng nhấn phím; cái còn lại được kích hoạt khi khoá được nhả ra. Bạn có thể phân biệt 2 sự kiện chính bằng cách tham chiếu đến thuộc tính type của đối tượng KeyEvent.

Giá trị trả về của hàm lambda onKeyEvent cho biết liệu sự kiện nhấn phím có được xử lý hay không. Trả về true nếu ứng dụng của bạn xử lý sự kiện nhấn phím, giúp dừng việc truyền sự kiện.

Đoạn mã sau đây cho biết cách gọi hàm doSomething() khi người dùng thả phím S trên thành phần Box:

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

Phím bổ trợ

Đối tượng KeyEvent có các thuộc tính sau cho biết liệu có nhấn phím sửa đổi hay không:

Hãy mô tả cụ thể các sự kiện chính mà ứng dụng của bạn xử lý. Đoạn mã sau gọi một hàm doSomething() chỉ khi người dùng thả phím S. Nếu người dùng nhấn bất kỳ phím sửa đổi nào, chẳng hạn như phím Shift, thì ứng dụng sẽ không gọi hàm.

Box(
  modifier = Modifier.focusable().onKeyEvent{
     if(
       it.type == KeyEventType.KeyUp &&
       it.key == Key.S &&
       !it.isAltPressed &&
       !it.isCtrlPressed &&
       !it.isMetaPressed &&
       !it.isShiftPressed
     ) {
       doSomething()
       true
     } else {
       false
     }
  }
)  {
    Text("Press S key with a modifier key")
}

Sự kiện nhấp phím cách và phím Enter

Phím Phím cáchEnter cũng kích hoạt sự kiện nhấp. Ví dụ: người dùng có thể bật/tắt (phát hoặc tạm dừng) chế độ phát nội dung nghe nhìn bằng Phím cách hoặc phím Enter bằng cách xử lý các sự kiện nhấp như sau:

MoviePlayer(
   modifier = Modifier.clickable { togglePausePlay() }
)

Đối tượng sửa đổi clickable sẽ chặn các sự kiện phím và gọi lệnh gọi lại onClick() khi nhấn phím Phím cách hoặc Enter. Đó là lý do hàm togglePausePlay() được gọi bằng cách nhấn phím Phím cách hoặc Enter trong đoạn mã.

Sự kiện chính chưa được sử dụng

Các sự kiện chính chưa được sử dụng sẽ được truyền từ thành phần xảy ra sự kiện đến thành phần bên ngoài bao bọc. Trong ví dụ bên dưới, InnerComponent sử dụng các sự kiện chính khi phím S được nhả, do đó OuterComponent không nhận được bất kỳ sự kiện chính nào được kích hoạt bằng cách nhả phím S. Đó là lý do hàm actionB() không bao giờ được gọi.

Các sự kiện chính khác trên InnerComponent, chẳng hạn như phát hành phím D, OuterComponent có thể xử lý. Hàm actionC() được gọi vì sự kiện quan trọng cho việc huỷ bỏ khoá D sẽ được truyền vào OuterComponent.

OuterComponent(
    modifier = Modifier.onKeyEvent {
        when {
           it.type == KeyEventType.KeyUp && it.key == Key.S -> {
               actionB() // This function is never called.
               true
           }
           it.type == KeyEventType.KeyUp && it.key == Key.D -> {
               actionC()
               true
           }
           else -> false
        }
    }
) {
    InnerComponent(
        modifier = Modifier.onKeyEvent {
            if(it.type == KeyEventType.KeyUp && it.key == Key.S) {
                actionA()
                true
            } else {
                false
            }
        }
    )
}

Đối tượng sửa đổi onKeyPreviewEvent

Trong một số trường hợp sử dụng, bạn muốn chặn một sự kiện nhấn phím trước khi sự kiện đó kích hoạt hành động mặc định. Thêm lối tắt tuỳ chỉnh vào TextField là một ví dụ điển hình. Đoạn mã sau đây cho phép người dùng chuyển sang thành phần có thể làm tâm điểm tiếp theo bằng cách nhấn phím tab.

val focusManager = LocalFocusManager.current
var textFieldValue by remember { mutableStateOf(TextFieldValue()) }

TextField(
    textFieldValue,
    onValueChange = {
        textFieldValue = it
    },
    modifier = Modifier.onPreviewKeyEvent {
        if (it.type == KeyEventType.KeyUp && it.key == Key.Tab) {
            focusManager.moveFocus(FocusDirection.Next)
            true
        } else {
            false
        }
    }
)

Theo mặc định, thành phần TextField sẽ thêm một ký tự tab mỗi khi người dùng nhấn phím Tab, ngay cả khi sự kiện phím được xử lý bằng đối tượng sửa đổi onKeyEvent. Để di chuyển tiêu điểm bàn phím mà không cần thêm ký tự tab nào, hãy xử lý sự kiện nhấn phím trước khi kích hoạt các thao tác liên kết với sự kiện nhấn phím, như trong đoạn mã. Hàm lambda onKeyPreviewEvent() chặn sự kiện chính bằng cách trả lại true.

Thành phần mẹ có thể chặn sự kiện chính xảy ra trên thành phần con. Trong đoạn mã sau, hàm previewSKey() được gọi khi người dùng nhấn phím S thay vì gọi hàm actionForPreview().

Column(
  modifier = Modifier.onPreviewKeyEvent{
    if(it.key == Key.S){
      previewSKey()
      true
    }else{
      false
    }
  }
) {
  Box(
    modifier = Modifier
        .focusable()
        .onPreviewKeyEvent {
            actionForPreview(it)
            false
        }
        .onKeyEvent {
            actionForKeyEvent(it)
            true
        }
  ) {
    Text("Press any key")
  }
}

Lambda onPreviewKeyEvent() cho thành phần Box cũng không được kích hoạt khi người dùng nhấn phím Tab. Trước tiên, hàm lambda onPreviewKeyEvent() được gọi trên thành phần mẹ, sau đó onPreviewKeyEvent() trong thành phần con được gọi. Bạn có thể triển khai phím tắt trên toàn màn hình bằng cách tận dụng hành vi này.

Tài nguyên khác

  • Trình trợ giúp phím tắt: Màn hình hệ thống cho phép người dùng tìm kiếm các phím tắt mà ứng dụng của bạn cung cấp.