Dùng Jetpack Compose để thêm tính năng hỗ trợ bàn phím, chuột, bàn di chuột và bút cảm ứng

1. Giới thiệu

Khi dùng được trên điện thoại tiêu chuẩn, ứng dụng của bạn sẽ dùng được trên các thiết bị có màn hình lớn, chẳng hạn như máy tính bảng, thiết bị có thể gập lại và thiết bị ChromeOS.

Khi dùng ứng dụng trên màn hình lớn, người dùng mong đợi sẽ có trải nghiệm tương đương hoặc tốt hơn so với khi dùng trên màn hình nhỏ.

Người dùng cũng có nhiều khả năng sẽ dùng ứng dụng của bạn với một thiết bị trỏ (chẳng hạn như chuột hoặc bàn di chuột) và bàn phím thực trên thiết bị có màn hình lớn. Một số thiết bị có màn hình lớn (ví dụ: Chromebook) có thiết bị trỏ và bàn phím thực. Những thiết bị khác kết nối với bàn phím và thiết bị trỏ qua cổng USB hoặc Bluetooth. Khi dùng ứng dụng với thiết bị trỏ và bàn phím thực, người dùng mong đợi có thể hoàn thành cùng các tác vụ như khi dùng màn hình cảm ứng.

Điều kiện tiên quyết

  • Có kinh nghiệm dùng Compose để tạo ứng dụng
  • Có kiến thức cơ bản về Kotlin, bao gồm cả lambda và coroutine

Sản phẩm bạn sẽ tạo ra

Bạn sẽ thêm tính năng hỗ trợ cho chuột và bàn phím thực vào ứng dụng được tạo bằng Jetpack Compose. Các bước thực hiện như sau:

  1. Kiểm tra ứng dụng theo các tiêu chí nêu trong nguyên tắc về chất lượng ứng dụng cho màn hình lớn
  2. Xem xét kết quả kiểm tra và tìm ra các vấn đề liên quan đến tính năng hỗ trợ chuột và bàn phím thực
  3. Khắc phục vấn đề

Cụ thể hơn, bạn sẽ cập nhật các tính năng sau đây cho ứng dụng mẫu:

  • Điều hướng bằng bàn phím
  • Phím tắt để cuộn xuống và cuộn lên
  • Trình trợ giúp phím tắt

Kiến thức bạn sẽ học được

  • Cách kiểm tra ứng dụng để hỗ trợ thiết bị ảo
  • Cách dùng Compose để quản lý tính năng điều hướng bằng bàn phím
  • Cách dùng Compose để thêm phím tắt

Bạn cần có

  • Android Studio Hedgehog trở lên
  • Bất cứ thiết bị nào sau đây để chạy ứng dụng mẫu:
  • Một thiết bị có màn hình lớn cùng với chuột và bàn phím thực
  • Một thiết bị Android ảo có cấu hình thuộc danh mục định nghĩa Thiết bị máy tính

2. Thiết lập

  1. Nhân bản kho lưu trữ large-screen-codelabs trên GitHub:
git clone https://github.com/android/large-screen-codelabs

Ngoài ra, bạn có thể tải xuống và giải nén tệp zip trong kho lưu trữ large-screen-codelabs. Cách làm như sau:

  1. Chuyển đến thư mục add-keyboard-and-mouse-support-with-compose.
  2. Trong Android Studio, hãy mở dự án. Thư mục add-keyboard-and-cursor-support-with-compose chứa một dự án.
  3. Nếu bạn không dùng thiết bị có thể gập lại hay máy tính bảng Android hoặc thiết bị ChromeOS có chuột và bàn phím thực, hãy mở Device Manager (Trình quản lý thiết bị) trong Android Studio rồi tạo thiết bị ảo bất kỳ trong danh mục Desktop (Máy tính).

Các thiết bị ảo trong danh mục Desktop (Máy tính)

3. Khám phá ứng dụng

Ứng dụng mẫu hiển thị một danh sách các bài viết. Người dùng có thể đọc một bài viết đã chọn trong danh sách này.

Ứng dụng cập nhật bố cục theo cách thích ứng với chiều rộng cửa sổ của ứng dụng. Có 3 lớp cửa sổ để phân loại chiều rộng cửa sổ của ứng dụng, đó là: thu gọn, trung bình và mở rộng.

Các lớp kích thước cửa sổ theo chiều rộng của cửa sổ: thu gọn, trung bình và mở rộng. Chiều rộng cửa sổ của ứng dụng nhỏ hơn 600 dp thì được phân loại là thu gọn. Chiều rộng cửa sổ được phân loại là mở rộng khi lớn hơn hoặc bằng 640 dp. Cửa sổ không thuộc loại thu gọn hay mở rộng, nên lớp kích thước cửa sổ của ứng dụng là trung bình.

Bố cục cho lớp kích thước cửa sổ thu gọn và trung bình

Ứng dụng dùng bố cục một ngăn. Trên màn hình chính, ứng dụng hiển thị một danh sách các bài viết. Khi người dùng chọn một bài viết trong danh sách này thì sẽ có hiệu ứng chuyển màn hình và bài viết đó sẽ hiển thị.

Ứng dụng dùng ngăn điều hướng để điều hướng chung.

Ứng dụng đang chạy trên trình mô phỏng Máy tính trong một cửa sổ thu gọn. Màn hình hiển thị danh sách bài viết.

Bố cục cho lớp kích thước cửa sổ mở rộng

Ứng dụng dùng bố cục danh sách-chi tiết. Ngăn danh sách hiện danh sách bài viết. Ngăn chi tiết hiện bài viết đã chọn.

Ứng dụng dùng thanh điều hướng để điều hướng chung.

Ứng dụng đang chạy trên trình mô phỏng Máy tính trong lớp kích thước cửa sổ mở rộng.

4. Thông tin khái quát

Compose cung cấp nhiều API để giúp ứng dụng của bạn xử lý các sự kiện từ chuột và bàn phím thực. Một số API cho phép xử lý sự kiện chuột và bàn phím tương tự như xử lý sự kiện chạm. Do đó, trong nhiều trường hợp sử dụng, ứng dụng sẽ hỗ trợ chuột và bàn phím thực mà bạn không cần phải làm gì thêm.

Ví dụ điển hình là đối tượng sửa đổi clickable giúp phát hiện lượt nhấp. Thao tác nhấn ngón tay được phát hiện là một lượt nhấp. Thao tác nhấp chuột và nhấn phím Enter cũng được phát hiện là lượt nhấp. Ứng dụng cho phép người dùng tương tác với các thành phần bất kể thiết bị đầu vào nếu phát hiện thấy lượt nhấp thông qua đối tượng sửa đổi clickable.

Tuy nhiên, mặc dù có sự hỗ trợ của API ở mức cao như vậy, bạn vẫn cần bỏ ra chút công sức để phát triển tính năng hỗ trợ chuột và bàn phím thực. Lý do là bạn cần kiểm thử ứng dụng để tìm ra các trường hợp không như dự kiến. Bạn cũng cần nỗ lực để giảm bớt trở ngại cho người dùng do các đặc điểm của thiết bị, chẳng hạn như:

  • Người dùng không biết mình có thể nhấp vào thành phần nào
  • Người dùng không thể di chuyển tiêu điểm bàn phím như mong đợi
  • Người dùng không thể cuộn lên hoặc xuống khi dùng bàn phím thực

Tiêu điểm bàn phím

Tiêu điểm bàn phím là điểm khác biệt chính giữa hoạt động tương tác với bàn phím thực và các thao tác chạm vào màn hình. Người dùng có thể nhấn vào thành phần bất kỳ trên màn hình, bất kể vị trí của thành phần mà họ đã chạm vào trước đó. Ngược lại, khi dùng bàn phím, người dùng cần chọn thành phần để tương tác trước khi bắt đầu tương tác thực tế. Lựa chọn này có tên là tiêu điểm bàn phím.

Người dùng có thể di chuyển tiêu điểm bàn phím bằng phím Tab và các phím định hướng (hoặc mũi tên). Theo mặc định, tiêu điểm bàn phím chỉ di chuyển đến các thành phần lân cận.

Hầu hết trở ngại đối với bàn phím thực đều liên quan đến tiêu điểm bàn phím. Sau đây là danh sách các vấn đề thường gặp:

  • Người dùng không thể di chuyển tiêu điểm bàn phím đến thành phần mà họ muốn tương tác
  • Thành phần không phát hiện thấy lượt nhấp khi người dùng nhấn phím Enter
  • Tiêu điểm bàn phím di chuyển khác với mong muốn của người dùng.
  • Người dùng cần nhấn nhiều phím để di chuyển tiêu điểm bàn phím đến thành phần mà họ muốn tương tác sau khi chuyển đổi màn hình.
  • Người dùng không thể xác định thành phần nào có tiêu điểm bàn phím vì không có dấu hiệu trực quan chỉ ra tiêu điểm bàn phím
  • Người dùng không thể xác định thành phần mặc định có tiêu điểm khi điều hướng đến một màn hình mới

Chỉ báo trực quan cho tiêu điểm bàn phím đóng vai trò rất quan trọng. Nếu không, người dùng có thể bị lạc trong ứng dụng và không hiểu được điều gì xảy ra khi nhấn phím Enter. Cách làm nổi bật thành phần là một dấu hiệu trực quan điển hình để chỉ ra tiêu điểm bàn phím. Người dùng có thể thấy nút ở thẻ bên phải có tiêu điểm bàn phím vì nút này được làm nổi bật.

53ee7662b764f2dd.png

Phím tắt

Người dùng mong muốn có thể sử dụng các phím tắt thông dụng khi dùng ứng dụng của bạn với bàn phím thực. Theo mặc định, một số thành phần hỗ trợ các phím tắt tiêu chuẩn. BasicTextField là một ví dụ điển hình. Thành phần này cho phép người dùng sử dụng các phím tắt tiêu chuẩn để chỉnh sửa văn bản, bao gồm:

Phím tắt

Tính năng

Ctrl+C

Sao chép

Ctrl+X

Cắt

Ctrl+V

Dán

Ctrl+Z

Huỷ

Ctrl+Y

Làm lại

Ứng dụng có thể xử lý các sự kiện chính để thêm phím tắt. Bạn có thể giám sát các sự kiện chính thông qua đối tượng sửa đổi onKeyEventonPreviewKeyEvent.

Thiết bị trỏ: Chuột, bàn di chuột và bút cảm ứng

Ứng dụng của bạn có thể xử lý chuột, bàn di chuột và bút cảm ứng theo cùng một cách. Thao tác nhấn vào bàn di chuột được phát hiện là một lượt nhấp thông qua đối tượng sửa đổi clickable. Thao tác nhấn bằng bút cảm ứng cũng được phát hiện là một lượt nhấp.

Giao diện ứng dụng phải cho người dùng biết liệu họ có thể nhấp vào một thành phần hay không. Đó là lý do trạng thái di chuột được đề cập đến trong nguyên tắc về chất lượng ứng dụng cho màn hình lớn.

Theo mặc định, các thành phần Material 3 hỗ trợ trạng thái di chuột. Material 3 cung cấp hiệu ứng trực quan cho trạng thái di chuột. Bạn có thể áp dụng hiệu ứng này cho thành phần tương tác thông qua đối tượng sửa đổi indication.

Thao tác cuộn

Theo mặc định, các vùng chứa có thể cuộn hỗ trợ thao tác cuộn con lăn chuột, cử chỉ cuộn trên bàn di chuột, cũng như thao tác cuộn bằng các phím Page upPage down.

Đối với thao tác cuộn ngang, sẽ rất dễ cho người dùng nếu ứng dụng của bạn hiện các nút mũi tên trái và phải ở trạng thái di chuột, nhờ vậy, người dùng có thể cuộn nội dung bằng cách nhấp vào các nút đó.

17feb4d3bf08831e.png

Cấu hình thay đổi do việc tháo lắp thiết bị

Người dùng có thể lắp hoặc tháo chuột và bàn phím trong khi ứng dụng của bạn đang chạy. Người dùng có thể kết nối bàn phím thực khi thấy một trường văn bản cần nhập lượng lớn nội dung. Chuột kết nối qua Bluetooth sẽ ngắt kết nối khi chuột chuyển sang chế độ ngủ. Bàn phím kết nối qua USB có thể bị tháo rời do vô tình.

Hoạt động lắp hoặc tháo phần cứng ngoại vi sẽ dẫn đến các thay đổi về cấu hình. Ứng dụng nên giữ nguyên trạng thái trong suốt quá trình thay đổi cấu hình. Để biết thêm thông tin, hãy xem bài viết Lưu trạng thái giao diện người dùng.

5. Kiểm tra ứng dụng mẫu dùng bàn phím và chuột

Để bắt đầu phát triển tính năng hỗ trợ chuột và bàn phím thực, hãy khởi động ứng dụng mẫu và xác nhận những thông tin sau đây:

  • Người dùng phải có thể di chuyển tiêu điểm bàn phím đến mọi thành phần tương tác
  • Người dùng phải có thể "nhấp" vào thành phần được lấy làm tâm điểm bằng phím Enter
  • Các thành phần tương tác phải hiện chỉ báo khi lấy tiêu điểm bàn phím
  • Tâm điểm bàn phím di chuyển theo mong đợi của người dùng (tức là theo quy ước đã thiết lập) khi dùng phím Tab, Shift+Tab và các phím định hướng (mũi tên)
  • Các thành phần tương tác phải có một trạng thái di chuột
  • Người dùng phải có thể nhấp vào các thành phần tương tác
  • Trình đơn theo bối cảnh xuất hiện khi có lượt nhấp chuột phải (nhấp vào nút điều khiển phụ) vào các thành phần thích hợp, chẳng hạn như những thành phần mà trình đơn theo bối cảnh sẽ xuất hiện khi nhấn giữ hoặc chọn văn bản

Bạn nên xem hết tất cả các mục trong lớp học lập trình này 2 lần, trong đó: 1 lần cho bố cục một ngăn và 1 lần cho bố cục danh sách-chi tiết.

Vấn đề cần khắc phục trong lớp học lập trình này

Bạn sẽ phát hiện thấy các vấn đề. Trong lớp học lập trình này, bạn sẽ khắc phục những vấn đề sau đây:

  • Người dùng không thể đọc toàn bộ bài viết nếu chỉ dùng bàn phím thực vì không thể cuộn bài viết xuống
  • Người dùng không thể xác định liệu ngăn chi tiết có tiêu điểm bàn phím hay không

6. Cho phép người dùng đọc toàn bộ bài viết trong ngăn chi tiết

Ngăn chi tiết hiện bài viết đã chọn. Một số bài viết quá dài nên không đọc được toàn bộ bài viết mà không cần cuộn. Tuy nhiên, người dùng không thể cuộn bài viết lên và xuống chỉ bằng bàn phím thực.

4627289223e5cfbc.gif

Vùng chứa có thể cuộn (chẳng hạn như LazyColumn) cho phép người dùng cuộn xuống bằng phím Page down. Căn nguyên của vấn đề này là do người dùng không thể di chuyển tiêu điểm bàn phím đến ngăn chi tiết.

Thành phần phải có khả năng lấy tâm điểm bàn phím thì mới nhận được sự kiện từ bàn phím. Đối tượng sửa đổi focusable cho phép thành phần đã sửa đổi lấy tâm điểm bàn phím.

Để khắc phục vấn đề nêu trên, hãy làm theo các bước sau đây:

  1. Truy cập vào hàm có khả năng kết hợp PostContent trong tệp ui/article/PostContent.kt
  2. Dùng đối tượng sửa đổi focusable để sửa đổi hàm có khả năng kết hợp LazyColumn
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .focusable(),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

Chỉ ra bài viết có tâm điểm bàn phím

Giờ đây, người dùng có thể đọc toàn bộ bài viết khi dùng phím Page down để cuộn xuống dưới bài viết. Tuy nhiên, rất khó để biết được liệu thành phần PostContent có tiêu điểm bàn phím hay không vì chẳng có hiệu ứng trực quan nào chỉ ra điều đó.

Ứng dụng của bạn có thể liên kết Indication với các thành phần để chỉ ra tiêu điểm bàn phím một cách trực quan. Chỉ báo sẽ tạo một đối tượng để hiển thị các hiệu ứng trực quan theo hoạt động tương tác. Ví dụ: Chỉ báo mặc định cho Material 3 là làm nổi bật thành phần khi thành phần đó có tiêu điểm bàn phím.

Ứng dụng mẫu có một Indication tên là BorderIndication. Chỉ báo này cho thấy một đường bên cạnh thành phần có tiêu điểm bàn phím (như trong ảnh chụp màn hình sau đây). Mã được lưu trữ trong tệp ui/components/BorderIndication.kt.

Một đường màu xám nhạt sẽ hiển thị ở bên cạnh bài viết khi bài viết có tiêu điểm bàn phím.

Để thành phần kết hợp PostConent cho thấy BorderIndication khi có tiêu điểm bàn phím, hãy làm theo các bước sau đây:

  1. Truy cập vào hàm có khả năng kết hợp PostContent trong tệp ui/article/PostContent.kt
  2. Khai báo giá trị interactionSource được liên kết với giá trị trả về của hàm remember()
  3. Gọi hàm MutableInteractionSource() trong hàm remember() để đối tượng MutableInteractionSource đã tạo được liên kết với giá trị interactionSource
  4. Truyền giá trị interactionSource vào đối tượng sửa đổi focusable thông qua tham số InteractionSource
  5. Thay đổi đối tượng sửa đổi của thành phần kết hợp PostContent để gọi đối tượng sửa đổi focusable sau khi gọi đối tượng sửa đổi indication
  6. Truyền giá trị interactionSource và giá trị mà hàm BorderIndication trả về vào đối tượng sửa đổi chỉ báo
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    val interactionSource = remember { MutableInteractionSource() }

    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .indication(interactionSource, BorderIndication())
            .focusable(interactionSource = interactionSource),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

Thêm phím tắt để cuộn lên và xuống

Đây là một tính năng phổ biến cho phép người dùng cuộn lên và xuống bằng Spacebar. Ứng dụng của bạn có thể triển khai tính năng này bằng cách thêm phím tắt như bảng sau đây:

Phím tắt

Tính năng

Spacebar

Cuộn bài viết xuống

Shift + Spacebar

Cuộn bài viết lên

Đối tượng sửa đổi onKeyEvent cho phép ứng dụng xử lý các sự kiện chính xảy ra trên một thành phần đã sửa đổi. Đối tượng sửa đổi này lấy một hàm lambda do đối tượng KeyEvent gọi để mô tả sự kiện chính. Hàm lambda đó sẽ trả về một giá trị Boolean cho biết liệu sự kiện chính có được sử dụng hay không.

Vị trí cuộn của LazyColumnLazyRow được ghi lại trong đối tượng LazyListState. Ứng dụng có thể kích hoạt tính năng cuộn bằng cách gọi phương thức tạm ngưng animateScrollBy() qua đối tượng LazyListState. Phương thức này sẽ cuộn LazyColumn xuống theo số lượng pixel được chỉ định. Khi hàm tạm ngưng được gọi cùng với một giá trị âm có độ chính xác đơn, hàm này sẽ cuộn LazyColumn lên.

Để triển khai những phím tắt này, hãy làm theo các bước sau đây:

  1. Truy cập vào hàm có khả năng kết hợp PostContent trong tệp ui/article/PostContent.kt
  2. Dùng đối tượng sửa đổi onKeyEvent để sửa đổi hàm có khả năng kết hợp LazyColumn
  3. Thêm một biểu thức if vào hàm lambda được truyền vào đối tượng sửa đổi onKeyEvent như sau:
  • Trả về true nếu thoả mãn các điều kiện sau đây:
  • Phím Spacebar được nhấn. Bạn có thể phát hiện phím này bằng cách kiểm tra xem thuộc tính type có phải là KeyType.KeyDown và thuộc tính key có phải là Key.Spacebar hay không
  • Thuộc tính isCtrlPressed là false để đảm bảo phím Ctrl không được nhấn
  • Thuộc tính isAltPressed là false để đảm bảo phím Alt không được nhấn
  • Thuộc tính isMetaPressed là false để đảm bảo phím Meta không được nhấn (xem ghi chú)
  • Nếu không, hãy trả về false
  1. Xác định mức cuộn khi nhấn phím Spacebar như sau:
  • -0.4f khi nhấn phím Shift (được mô tả bằng thuộc tính isShiftPressed của đối tượng KeyEvent cụ thể)
  • Nếu không thì là 0.4f
  1. Gọi phương thức launch() qua coroutineScope (tham số của hàm có khả năng kết hợp PostContent)
  2. Tính mức cuộn thực tế bằng cách nhân mức cuộn tương đối đã tính ở bước trước với thuộc tính state.layoutInfo.viewportSize.height trong tham số lambda của phương thức launch. Thuộc tính này biểu thị chiều cao của LazyColumn được gọi trong hàm có khả năng kết hợp PostContent.
  3. Gọi phương thức state.animateScrollBy() trong hàm lambda cho phương thức launch() để kích hoạt thao tác cuộn dọc
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    val interactionSource = remember { MutableInteractionSource() }

    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .onKeyEvent {
                if (
                    it.type == KeyEventType.KeyDown &&
                    it.key == Key.Spacebar &&
                    !it.isCtrlPressed &&
                    !it.isAltPressed &&
                    !it.isMetaPressed
                ) {

                    val relativeAmount = if (it.isShiftPressed) {
                        -0.4f
                    } else {
                        0.4f
                    }
                    coroutineScope.launch {
                        state.animateScrollBy(relativeAmount * state.layoutInfo.viewportSize.height)
                    }
                    true
                } else {
                    false
                }
            }
            .indication(interactionSource, BorderIndication())
            .focusable(interactionSource = interactionSource),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

Thông báo cho người dùng về phím tắt

Người dùng sẽ không thể khai thác tối đa bàn phím đã thêm nếu không biết các phím tắt. Ứng dụng của bạn có thể thông báo cho người dùng về các phím tắt hiện có thông qua trình trợ giúp phím tắt trên giao diện người dùng hệ thống của Android. Người dùng có thể sử dụng tổ hợp phím Meta+/ để mở trình trợ giúp phím tắt này.

Trình trợ giúp phím tắt cho thấy các phím tắt mà bạn đã thêm trong phần trước.

Ứng dụng sẽ ghi đè phương thức onProvideKeyboardShortcuts() trong hoạt động chính của ứng dụng để cung cấp danh sách phím tắt cho trình trợ giúp phím tắt.

Cụ thể hơn, ứng dụng sẽ cung cấp một số đối tượng KeyboardShortcutGroup bằng cách thêm các đối tượng đó vào danh sách có thể thay đổi được truyền vào onProvideKeyboardShortcuts(). Mỗi KeyboardShortcutGroup biểu thị một danh mục phím tắt được đặt tên, cho phép ứng dụng nhóm các phím tắt có sẵn theo mục đích hoặc bối cảnh.

Ứng dụng mẫu có 2 phím tắt là SpacebarShift+Spacebar.

Để 2 phím tắt này hiện trong trình trợ giúp phím tắt, hãy làm theo các bước sau đây:

  1. Mở tệp MainActivity.kt
  2. Ghi đè phương thức onProvideKeyboardShortcuts() trong MainActivity
  3. Đảm bảo phiên bản SDK Android là Android 7.0 (API cấp 24) trở lên để có thể sử dụng trình trợ giúp phím tắt.
  4. Xác nhận rằng tham số đầu tiên của phương thức không phải là null
  5. Tạo một đối tượng KeyboardShortcutInfo cho phím Spacebar bằng các tham số sau đây:
  • Nội dung mô tả
  • android.view.KeyEvent.KEYCODE_SPACE
  • 0 (cho biết không có đối tượng sửa đổi nào)
  1. Tạo một KeyboardShortcutInfo khác cho Shift+Spacebar bằng các tham số sau đây:
  • Nội dung mô tả
  • android.view.KeyEvent.KEYCODE_SPACE
  • android.view.KeyEvent.META_SHIFT_ON
  1. Tạo một danh sách không thể thay đổi chứa 2 đối tượng KeyboardShortcutInfo
  2. Tạo một đối tượng KeyboardShortcutGroup bằng các tham số sau đây:
  • Tên nhóm ở dạng văn bản
  • Danh sách không thể thay đổi ở bước trước
  1. Thêm đối tượng KeyboardShortcutGroup vào danh sách có thể thay đổi được truyền dưới dạng tham số đầu tiên của phương thức onProvideKeyboardShortcuts()

Phương thức bị ghi đè sẽ có dạng như sau:

   override fun onProvideKeyboardShortcuts(
        data: MutableList<KeyboardShortcutGroup>?,
        menu: Menu?,
        deviceId: Int
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && data != null) {
            val shortcutGroup = KeyboardShortcutGroup(
                "To read articles",
                listOf(
                    KeyboardShortcutInfo("Scroll down", KeyEvent.KEYCODE_SPACE, 0), // 0 means no modifier key is pressed
                    KeyboardShortcutInfo("Scroll up", KeyEvent.KEYCODE_SPACE, KeyEvent.META_SHIFT_ON),
                )
            )
            data.add(shortcutGroup)
        }
    }

Chạy ứng dụng

Giờ đây, người dùng có thể đọc toàn bộ bài viết ngay cả khi sử dụng phím Spacebar để cuộn bài viết. Bạn có thể thử bằng cách dùng phím Tab hoặc các phím định hướng để di chuyển tiêu điểm bàn phím đến bài viết. Bạn có thể thấy thông báo khuyến khích mình nhấn phím Spacebar.

Trình trợ giúp phím tắt cho thấy 2 phím tắt mà bạn đã thêm (nhấn tổ hợp phím Meta+/). Các phím tắt đã thêm được liệt kê ở thẻ Current app (Ứng dụng hiện tại).

7. Tăng tốc độ điều hướng bằng bàn phím trong ngăn chi tiết

Khi ứng dụng đang chạy trong lớp kích thước cửa sổ mở rộng, người dùng cần nhấn phím Tab nhiều lần để di chuyển tiêu điểm bàn phím đến ngăn chi tiết. Với phím định hướng bên phải, người dùng có thể di chuyển tiêu điểm bàn phím từ danh sách bài viết sang bài viết chỉ bằng một thao tác. Tuy nhiên, họ vẫn cần di chuyển tiêu điểm bàn phím. Tiêu điểm ban đầu không hỗ trợ mục tiêu chính của người dùng là đọc các bài viết.

Ứng dụng của bạn có thể yêu cầu di chuyển tiêu điểm bàn phím đến thành phần cụ thể thông qua đối tượng FocusRequester. Đối tượng sửa đổi focusRequester liên kết một đối tượng FocusRequester với thành phần đã sửa đổi. Ứng dụng có thể gọi phương thức requestFocus() của đối tượng FocusRequester để gửi yêu cầu thực tế về việc di chuyển tiêu điểm.

Gửi yêu cầu di chuyển tiêu điểm bàn phím là một hệ quả phụ của thành phần này. Ứng dụng phải gọi phương thức theo cách phù hợp thông qua hàm LaunchedEffect.

Để đặt thành phần kết hợp PostContent sẽ lấy tiêu điểm bàn phím khi người dùng chọn một bài viết trong danh sách bài viết, hãy làm theo các bước sau đây:

  1. Truy cập vào hàm có khả năng kết hợp PostContent trong tệp ui/article/ PostContent.kt.
  2. Liên kết giá trị focusRequester với hàm có khả năng kết hợp LazyColumn thông qua đối tượng sửa đổi focusRequester. Giá trị focusRequester được chỉ định làm tham số không bắt buộc của hàm có khả năng kết hợp PostContent.
  3. Gọi LaunchedEffect bằng post (tham số đầu tiên của hàm có khả năng kết hợp PostContent) để hàm lambda đã truyền được gọi khi người dùng chọn một bài viết.
  4. Gọi phương thức focusRequester.requestFocus() trong hàm lambda được truyền vào hàm LaunchedEffect.

Thành phần kết hợp PostContent sau khi cập nhật sẽ có dạng như sau:

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    val interactionSource = remember { MutableInteractionSource() }

    LaunchedEffect(post) {
        focusRequester.requestFocus()
    }

    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .onKeyEvent {
                if (it.type == KeyEventType.KeyDown && it.key == Key.Spacebar) {
                    val relativeAmount = if (it.isShiftPressed) {
                        -0.4f
                    } else {
                        0.4f
                    }
                    coroutineScope.launch {
                        state.animateScrollBy(relativeAmount * state.layoutInfo.viewportSize.height)
                    }
                    true
                } else {
                    false
                }
            }
            .focusRequester(focusRequester),
            .indication(interactionSource, BorderIndication())
            .focusable(interactionSource = interactionSource),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

Chạy ứng dụng

Giờ đây, tiêu điểm bàn phím sẽ di chuyển đến bài viết khi người dùng chọn một bài viết trong danh sách bài viết. Bạn có thể thấy thông báo khuyến khích mình dùng phím Spacebar để cuộn bài viết xuống khi chọn một bài viết.

8. Xin chúc mừng

Rất tốt! Bạn đã thêm tính năng hỗ trợ chuột và bàn phím thực vào ứng dụng mẫu. Do đó, người dùng có thể chọn một bài viết trong danh sách bài viết và chỉ cần dùng chuột hoặc bàn phím thực để đọc bài viết đã chọn.

Bạn đã tìm hiểu những kiến thức cần thiết sau đây để thêm tính năng hỗ trợ chuột và bàn phím thực:

  • Cách kiểm tra xem ứng dụng có hỗ trợ chuột và bàn phím thực hay không, kể cả khi dùng trình mô phỏng
  • Cách dùng Compose để quản lý tính năng điều hướng bằng bàn phím
  • Cách dùng Compose để thêm phím tắt

Bạn cũng đã sửa đổi mã một chút để thêm tính năng hỗ trợ chuột và bàn phím thực.

Bạn đã sẵn sàng dùng Compose để thêm tính năng hỗ trợ chuột và bàn phím thực vào ứng dụng chính thức rồi đó.

Nếu học hỏi thêm chút nữa, bạn sẽ có thể thêm phím tắt cho các chức năng sau đây:

  • Đánh dấu bài viết đã chọn là đã thích.
  • Đánh dấu trang bài viết đã chọn.
  • Chia sẻ bài viết đã chọn với các ứng dụng khác.

Tìm hiểu thêm