Chromebook cung cấp cho người dùng nhiều lựa chọn đầu vào: bàn phím, chuột, bàn di chuột, màn hình cảm ứng, bút cảm ứng, MIDI và tay điều khiển trò chơi/tay điều khiển Bluetooth. Điều này có nghĩa là cùng một thiết bị có thể trở thành trạm phát nhạc của DJ, bức vẽ của nghệ sĩ hoặc nền tảng ưa thích của game thủ để phát trực tuyến các trò chơi AAA.
Là nhà phát triển, bạn có cơ hội tạo ra trải nghiệm ứng dụng linh hoạt và thú vị cho người dùng, tận dụng các thiết bị đầu vào mà họ đã có trong tầm tay, từ bàn phím gắn ngoài đến bút cảm ứng và tay cầm chơi game Stadia. Tuy nhiên, tất cả những khả năng này cũng đòi hỏi bạn phải suy nghĩ về giao diện người dùng để mang đến trải nghiệm mượt mà và hợp lý cho ứng dụng. Điều này đặc biệt đúng nếu ứng dụng hoặc trò chơi của bạn được thiết kế cho điện thoại di động. Ví dụ: nếu trò chơi của bạn có một cần điều khiển cảm ứng trên màn hình dành cho điện thoại, thì có thể bạn sẽ muốn ẩn cần điều khiển này khi người dùng chơi bằng bàn phím.
Trên trang này, bạn sẽ thấy những vấn đề chính cần lưu ý khi nghĩ đến nhiều nguồn đầu vào và các chiến lược để giải quyết những vấn đề đó.
Người dùng khám phá các phương thức nhập được hỗ trợ
Lý tưởng nhất là ứng dụng của bạn sẽ phản hồi liền mạch mọi phương thức nhập mà người dùng chọn sử dụng. Thường thì việc này rất đơn giản và bạn không cần cung cấp thêm thông tin cho người dùng. Ví dụ: một nút sẽ hoạt động nếu người dùng nhấp vào nút đó bằng chuột, bàn di chuột, màn hình cảm ứng, bút cảm ứng, v.v. và bạn không cần cho người dùng biết rằng họ có thể sử dụng những thiết bị khác nhau này để kích hoạt nút.
Tuy nhiên, có những trường hợp phương thức nhập có thể cải thiện trải nghiệm của người dùng và bạn nên cho họ biết ứng dụng của bạn hỗ trợ phương thức này. Một số ví dụ:
- Một ứng dụng phát nội dung nghe nhìn có thể hỗ trợ nhiều tổ hợp phím tắt hữu ích mà người dùng khó đoán được.
- Nếu bạn đã tạo một ứng dụng DJ, thì lúc đầu người dùng có thể sử dụng màn hình cảm ứng và có thể không nhận ra rằng bạn đã cho phép họ sử dụng bàn phím/bàn di chuột để cung cấp quyền truy cập xúc giác vào một số tính năng. Tương tự, họ có thể không nhận ra rằng bạn hỗ trợ một số bộ điều khiển MIDI DJ và việc khuyến khích họ kiểm tra phần cứng được hỗ trợ có thể mang lại trải nghiệm DJing chân thực hơn.
- Trò chơi của bạn có thể hoạt động tốt với màn hình cảm ứng và bàn phím/chuột, nhưng người dùng có thể không nhận ra rằng trò chơi này cũng hỗ trợ một số tay điều khiển trò chơi qua Bluetooth. Việc kết nối một trong những ứng dụng này có thể giúp tăng mức độ hài lòng và tương tác của người dùng.
Bạn có thể giúp người dùng khám phá các lựa chọn nhập bằng cách gửi thông báo vào thời điểm thích hợp. Mỗi ứng dụng sẽ có cách triển khai khác nhau. Dưới đây là một số ví dụ:
- Cửa sổ bật lên khi chạy lần đầu hoặc cửa sổ bật lên mẹo trong ngày
- Các lựa chọn cấu hình trong bảng cài đặt có thể cho người dùng biết rằng có hỗ trợ. Ví dụ: thẻ "tay điều khiển trò chơi" trong bảng cài đặt của trò chơi cho biết trò chơi có hỗ trợ tay điều khiển.
- Thông báo theo bối cảnh. Ví dụ: nếu phát hiện thấy bàn phím thực và thấy người dùng đang nhấp vào một thao tác bằng chuột hoặc màn hình cảm ứng, bạn có thể muốn hiện một gợi ý hữu ích đề xuất lối tắt bàn phím
- Danh sách phím tắt. Khi phát hiện thấy bàn phím thực, việc cung cấp thông tin chỉ dẫn trong giao diện người dùng về cách hiển thị danh sách các phím tắt hiện có sẽ phục vụ hai mục đích: quảng cáo rằng ứng dụng có hỗ trợ bàn phím và cung cấp cho người dùng một cách dễ dàng để xem và ghi nhớ các phím tắt được hỗ trợ
Bố cục/gắn nhãn giao diện người dùng cho biến thể đầu vào
Lý tưởng nhất là giao diện trực quan của bạn không cần thay đổi nhiều nếu bạn sử dụng một thiết bị đầu vào khác, tất cả các đầu vào có thể có đều sẽ "hoạt động bình thường". Tuy nhiên, có những trường hợp ngoại lệ quan trọng. Hai trong số những yếu tố chính là giao diện người dùng dành riêng cho thao tác chạm và lời nhắc trên màn hình.
Giao diện người dùng dành riêng cho thao tác chạm
Bất cứ khi nào ứng dụng của bạn có các phần tử trên giao diện người dùng dành riêng cho thao tác chạm (ví dụ: cần điều khiển trên màn hình trong một trò chơi), bạn nên cân nhắc trải nghiệm người dùng khi không sử dụng thao tác chạm. Trong một số trò chơi di động, các nút điều khiển chiếm một phần đáng kể trên màn hình. Các nút này cần thiết cho lối chơi dựa trên thao tác chạm, nhưng không cần thiết nếu người dùng đang sử dụng tay điều khiển trò chơi hoặc bàn phím để chơi. Ứng dụng hoặc trò chơi của bạn phải cung cấp logic để phát hiện phương thức nhập đang được sử dụng và điều chỉnh giao diện người dùng cho phù hợp. Hãy xem phần Triển khai bên dưới để biết một số ví dụ về cách thực hiện việc này.
Lời nhắc trên màn hình
Ứng dụng của bạn có thể cung cấp lời nhắc hữu ích trên màn hình cho người dùng. Hãy cẩn thận để đảm bảo những thao tác này không phụ thuộc vào thiết bị đầu vào. Ví dụ:
- Vuốt để…
- Nhấn vào vị trí bất kỳ để đóng lại
- Chụm để thu phóng
- Nhấn "X" để...
- Nhấn và giữ để kích hoạt
Một số ứng dụng có thể điều chỉnh từ ngữ để không phụ thuộc vào phương thức nhập. Bạn nên dùng cách này khi thấy hợp lý, nhưng trong nhiều trường hợp, tính cụ thể là rất quan trọng và bạn có thể thấy mình cần phải hiển thị các thông báo khác nhau tuỳ thuộc vào phương thức nhập đang được sử dụng, đặc biệt là trong các chế độ thuộc loại hướng dẫn như khi chạy ứng dụng lần đầu.
Những điểm cần lưu ý về nhiều ngôn ngữ
Nếu ứng dụng của bạn hỗ trợ nhiều ngôn ngữ, bạn sẽ muốn suy nghĩ kỹ về cấu trúc chuỗi của mình. Ví dụ: nếu bạn hỗ trợ 3 chế độ nhập và 5 ngôn ngữ, thì có thể bạn sẽ cần 15 phiên bản khác nhau cho mỗi thông báo trên giao diện người dùng. Điều này sẽ làm tăng lượng công việc cần thiết để thêm các tính năng mới và tăng khả năng xảy ra lỗi chính tả.
Một cách tiếp cận là coi các thao tác trên giao diện là một tập hợp riêng biệt gồm các chuỗi. Ví dụ: nếu bạn xác định thao tác "đóng" là biến chuỗi riêng với các biến thể dành riêng cho thiết bị đầu vào như: "Nhấn vào vị trí bất kỳ để đóng", "Nhấn phím "Esc" để đóng", "Nhấp vào vị trí bất kỳ để đóng", "Nhấn nút bất kỳ để đóng", thì tất cả thông báo trên giao diện người dùng cần cho người dùng biết cách đóng một nội dung nào đó đều có thể sử dụng biến chuỗi "đóng" duy nhất này. Khi phương thức nhập thay đổi, bạn chỉ cần thay đổi giá trị của một biến này.
Phương thức nhập bằng bàn phím mềm / IME
Hãy nhớ rằng nếu người dùng đang sử dụng một ứng dụng mà không có bàn phím thực, thì họ có thể nhập văn bản thông qua bàn phím ảo. Hãy nhớ kiểm thử để đảm bảo các phần tử giao diện người dùng cần thiết không bị che khuất khi bàn phím ảo xuất hiện. Hãy xem tài liệu về chế độ hiển thị IME trên Android để biết thêm thông tin.
Triển khai
Trong hầu hết các trường hợp, ứng dụng hoặc trò chơi phải phản hồi chính xác mọi dữ liệu đầu vào hợp lệ, bất kể nội dung nào xuất hiện trên màn hình. Nếu người dùng đang sử dụng màn hình cảm ứng trong 10 phút, nhưng sau đó đột ngột chuyển sang sử dụng bàn phím, thì phương thức nhập bằng bàn phím sẽ hoạt động, bất kể các lời nhắc hoặc chế độ điều khiển trực quan trên màn hình. Nói cách khác, chức năng phải được ưu tiên hơn hình ảnh/văn bản.Điều này giúp đảm bảo ứng dụng/trò chơi của bạn sẽ sử dụng được ngay cả khi logic phát hiện đầu vào của bạn gặp lỗi hoặc xảy ra tình huống không mong muốn.
Bước tiếp theo là hiện giao diện người dùng phù hợp cho phương thức nhập hiện đang được dùng. Làm cách nào để phát hiện chính xác điều này? Điều gì xảy ra nếu người dùng chuyển đổi giữa các phương thức nhập khác nhau trong khi sử dụng ứng dụng của bạn? Điều gì xảy ra nếu họ sử dụng nhiều phương thức cùng lúc?
Máy trạng thái được ưu tiên dựa trên các sự kiện đã nhận
Một cách tiếp cận là theo dõi "trạng thái đầu vào đang hoạt động" hiện tại (thể hiện các lời nhắc đầu vào mà người dùng hiện thấy trên màn hình) bằng cách theo dõi các sự kiện đầu vào thực tế mà ứng dụng nhận được và chuyển đổi giữa các trạng thái bằng cách sử dụng logic được ưu tiên.
Ưu tiên
Tại sao nên ưu tiên các trạng thái đầu vào? Người dùng tương tác với thiết bị của họ theo nhiều cách và ứng dụng của bạn phải hỗ trợ lựa chọn của họ. Ví dụ: nếu người dùng chọn sử dụng đồng thời màn hình cảm ứng và chuột Bluetooth, thì ứng dụng của bạn phải hỗ trợ việc này. Nhưng bạn nên hiển thị những lời nhắc và chế độ điều khiển nào trên màn hình? Chuột hay cảm ứng?
Việc xác định từng nhóm câu lệnh là "trạng thái đầu vào" rồi ưu tiên chúng có thể giúp bạn quyết định một cách nhất quán.
Các sự kiện đầu vào nhận được sẽ xác định trạng thái
Tại sao chỉ hành động dựa trên các sự kiện đầu vào đã nhận được? Bạn có thể nghĩ rằng bạn có thể theo dõi các kết nối Bluetooth để cho biết liệu bộ điều khiển Bluetooth có được gắn hay không hoặc theo dõi các kết nối USB cho các thiết bị USB. Đây không phải là phương pháp nên dùng vì 2 lý do chính.
Trước hết, thông tin bạn có thể đoán về các thiết bị được kết nối dựa trên các biến API không nhất quán và số lượng thiết bị bluetooth/phần cứng/bút cảm ứng liên tục tăng lên.
Lý do thứ hai để sử dụng các sự kiện đã nhận thay vì trạng thái kết nối là người dùng có thể đã kết nối chuột, tay điều khiển Bluetooth, tay điều khiển MIDI, v.v. nhưng không chủ động sử dụng để tương tác với ứng dụng hoặc trò chơi của bạn.
Bằng cách phản hồi các sự kiện đầu vào mà ứng dụng của bạn đã chủ động nhận được, bạn đảm bảo rằng bạn đang phản hồi các hành động thực của người dùng theo thời gian thực, chứ không phải cố gắng đoán ý định của họ bằng thông tin không đầy đủ.
Ví dụ: trò chơi có hỗ trợ cảm ứng, bàn phím/chuột và tay điều khiển
Giả sử bạn đã phát triển một trò chơi đua xe cho điện thoại di động dựa trên thao tác chạm. Người chơi có thể tăng tốc, giảm tốc, rẽ phải, rẽ trái hoặc sử dụng nitro để tăng tốc.
Giao diện màn hình cảm ứng hiện tại bao gồm một cần điều khiển trên màn hình ở dưới cùng bên trái màn hình để điều khiển tốc độ và hướng, cùng một nút ở dưới cùng bên phải để dùng nitro. Người dùng có thể thu thập bình nitro trên đường đua và khi thanh nitro ở cuối màn hình đầy, một thông báo sẽ xuất hiện phía trên nút có nội dung "Nhấn để dùng Nitro!". Nếu đây là lần đầu tiên người dùng chơi hoặc không nhận được thông tin đầu vào trong một thời gian, văn bản "hướng dẫn" sẽ xuất hiện phía trên cần điều khiển để hướng dẫn người dùng cách di chuyển xe.
Bạn muốn thêm tính năng hỗ trợ bàn phím và tay điều khiển trò chơi qua Bluetooth. Bạn nên bắt đầu từ đâu?
Trạng thái đầu vào
Bắt đầu bằng cách xác định tất cả trạng thái đầu vào mà trò chơi của bạn có thể đang chạy, sau đó liệt kê tất cả các tham số mà bạn muốn thay đổi trong mỗi trạng thái.
| Cảm ứng | Bàn phím/Chuột | Trình điều khiển trò chơi | |
|---|---|---|---|
|
Phản ứng với |
Tất cả dữ liệu đầu vào |
Tất cả dữ liệu đầu vào |
Tất cả dữ liệu đầu vào |
|
Các chế độ điều khiển trên màn hình |
– Cần điều khiển trên màn hình |
– Không có cần điều khiển |
– Không có cần điều khiển |
|
Văn bản |
Nhấn để dùng Nitro! |
Nhấn "N" để dùng Nitro! |
Nhấn "A" để dùng Nitro! |
|
Hướng dẫn |
Hình ảnh về cần điều khiển tốc độ/hướng |
Hình ảnh các phím mũi tên và WASD để điều chỉnh tốc độ/hướng |
Hình ảnh tay cầm chơi game cho tốc độ/hướng |
Theo dõi trạng thái "đầu vào đang hoạt động", sau đó cập nhật giao diện người dùng khi cần, dựa trên trạng thái đó.
Lưu ý: Xin lưu ý rằng trò chơi/ứng dụng của bạn phải phản hồi tất cả các phương thức nhập, bất kể trạng thái. Ví dụ: nếu người dùng đang lái xe bằng màn hình cảm ứng nhưng nhấn N trên bàn phím, thì hành động tăng tốc phải được kích hoạt.
Các thay đổi về trạng thái được ưu tiên
Một số người dùng có thể sử dụng đồng thời màn hình cảm ứng và bàn phím. Một số người có thể bắt đầu dùng trò chơi/ứng dụng của bạn ở chế độ máy tính bảng trên ghế sofa, rồi chuyển sang dùng bàn phím trên bàn. Những người khác có thể kết nối hoặc ngắt kết nối tay điều khiển trò chơi khi đang chơi.
Tốt nhất là bạn không nên có các phần tử không chính xác trên giao diện người dùng, chẳng hạn như yêu cầu người dùng nhấn phím n khi họ đang sử dụng màn hình cảm ứng. Đồng thời, trong trường hợp người dùng sử dụng đồng thời nhiều thiết bị đầu vào, chẳng hạn như màn hình cảm ứng và bàn phím, bạn không muốn giao diện người dùng liên tục chuyển đổi qua lại giữa hai trạng thái.
Một cách để xử lý vấn đề này là thiết lập mức độ ưu tiên của loại dữ liệu đầu vào và tạo độ trễ giữa các thay đổi về trạng thái. Đối với trò chơi ô tô ở trên, bạn luôn muốn đảm bảo cần điều khiển trên màn hình hiển thị bất cứ khi nào nhận được sự kiện chạm màn hình, nếu không, người dùng có thể thấy trò chơi không dùng được. Thao tác này sẽ đặt màn hình cảm ứng làm thiết bị có mức độ ưu tiên cao nhất.
Nếu nhận được đồng thời các sự kiện từ bàn phím và màn hình cảm ứng, thì trò chơi sẽ vẫn ở chế độ màn hình cảm ứng, mặc dù vẫn phản hồi với dữ liệu đầu vào từ bàn phím. Nếu không nhận được dữ liệu đầu vào từ màn hình cảm ứng sau 5 giây và vẫn nhận được các sự kiện bàn phím, thì có thể các nút điều khiển trên màn hình sẽ mờ đi và trò chơi sẽ chuyển sang trạng thái bàn phím.
Đầu vào của bộ điều khiển trò chơi sẽ tuân theo một mẫu tương tự: trạng thái giao diện người dùng của bộ điều khiển sẽ được ưu tiên thấp hơn so với bàn phím/chuột và thao tác chạm, đồng thời chỉ xuất hiện nếu đầu vào của bộ điều khiển trò chơi được nhận chứ không phải các dạng đầu vào khác. Trò chơi sẽ luôn phản hồi dữ liệu đầu vào của bộ điều khiển.
Dưới đây là sơ đồ trạng thái và bảng chuyển đổi cho ví dụ này. Điều chỉnh ý tưởng cho phù hợp với ứng dụng hoặc trò chơi của bạn.
| #1 Màn hình cảm ứng | #2 Bàn phím | #3 Gamepad | |
|---|---|---|---|
|
Di chuyển đến vị trí số 1 |
Không áp dụng |
– Nhận được tín hiệu đầu vào bằng cách chạm |
– Nhận được tín hiệu đầu vào bằng cách chạm |
|
Chuyển đến #2 |
– Không chạm trong 5 giây |
Không áp dụng |
– Nhận được dữ liệu nhập bằng bàn phím |
|
Di chuyển đến #3 |
– Không chạm trong 5 giây |
– Không có bàn phím trong 5 giây |
Không áp dụng |
Lưu ý: Hãy chú ý cách việc ưu tiên giúp xác định rõ loại dữ liệu đầu vào nào sẽ chiếm ưu thế. Trạng thái đầu vào ngay lập tức chuyển sang mức độ ưu tiên "cao":
3. Tay cầm chơi game -> 2. Bàn phím -> 1. Cảm ứng
ngay khi một thiết bị có mức độ ưu tiên cao hơn được sử dụng, nhưng mức độ ưu tiên của thiết bị đó sẽ từ từ giảm xuống, chỉ sau một khoảng thời gian trễ và chỉ khi thiết bị có mức độ ưu tiên thấp hơn đang được sử dụng.
Sự kiện nhập
Sau đây là một số mã ví dụ về cách phát hiện sự kiện đầu vào từ nhiều loại thiết bị đầu vào bằng cách sử dụng các API Android tiêu chuẩn. Hãy dùng những sự kiện này để điều khiển máy trạng thái của bạn, như ở trên. Bạn nên điều chỉnh khái niệm chung cho phù hợp với các loại sự kiện đầu vào mà bạn mong đợi và cho phù hợp với ứng dụng hoặc trò chơi của bạn.
Bàn phím và các nút trên bộ điều khiển
// Drive the state machine based on the received input type // onKeyDown drives the state machine, but does not trigger game actions // Both keyboard and game controller events come through as key events override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (event != null) { // Check input source val outputMessage = when (event.source) { SOURCE_KEYBOARD -> { MyStateMachine.KeyboardEventReceived() "Keyboard event" } SOURCE_GAMEPAD -> { MyStateMachine.ControllerEventReceived() "Game controller event" } else -> "Other key event: ${event.source}" } // Do something based on source type findViewById(R.id.text_message).text = outputMessage } // Pass event up to system return super.onKeyDown(keyCode, event) }
// Trigger game events based on key release // Both keyboard and game controller events come through as key events override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { when(keyCode) { KeyEvent.KEYCODE_N -> { MyStateMachine.keyboardEventReceived() engageNitro() return true // event handled here, return true } } // If event not handled, pass up to system return super.onKeyUp(keyCode, event) }
Lưu ý: đối với KeyEvents, bạn có thể chọn sử dụng onKeyDown() hoặc onKeyUp(). Ở đây, onKeyDown() được dùng để kiểm soát máy trạng thái, còn onKeyUp() được dùng để kích hoạt các sự kiện trong trò chơi.
Nếu người dùng nhấn và giữ một nút, onKeyUp() sẽ chỉ được kích hoạt một lần cho mỗi lần nhấn phím, trong khi onKeyDown() sẽ được gọi nhiều lần. Nếu muốn phản ứng với thao tác nhấn xuống, bạn nên xử lý các sự kiện trò chơi trong onKeyDown() và triển khai logic để giải quyết các sự kiện lặp lại. Hãy xem tài liệu Xử lý thao tác trên bàn phím để biết thêm thông tin.
Thao tác chạm và bút cảm ứng
// Touch and stylus events come through as touch events override fun onTouchEvent(event: MotionEvent?): Boolean { if (event != null) { // Get tool type val pointerIndex = event.action and ACTION_POINTER_INDEX_MASK shr ACTION_POINTER_INDEX_SHIFT val toolType = event.getToolType(pointerIndex) // Check tool type val outputMessage = when (toolType) { TOOL_TYPE_FINGER -> { MyStateMachine.TouchEventReceived() "Touch event" } TOOL_TYPE_STYLUS -> { MyStateMachine.StylusEventReceived() "Stylus event" } else -> "Other touch event: ${toolType}" } // Do something based on tool type, return true if event handled findViewById(R.id.text_message).text = outputMessage } // If event not handled, pass up to system return super.onGenericMotionEvent(event) }
Chuột và cần điều khiển
// Mouse and joystick events come through as generic events override fun onGenericMotionEvent(event: MotionEvent?): Boolean { if (event != null) { // Check input source val outputMessage = when (event.source) { SOURCE_JOYSTICK -> { MyStateMachine.ControllerEventReceived() "Controller event" } SOURCE_MOUSE -> { MyStateMachine.MouseEventReceived() "Mouse event" } else -> "Other generic event: ${event.source}" } // Do something based on source type, return true if event handled findViewById(R.id.text_message).text = outputMessage } // If event not handled, pass up to system return super.onGenericMotionEvent(event) }