Tạo phương thức nhập

Trình chỉnh sửa phương thức nhập (IME) là một chế độ kiểm soát người dùng cho phép người dùng nhập văn bản. Android cung cấp khung phương thức nhập có thể mở rộng, cho phép các ứng dụng cung cấp cho người dùng các phương thức nhập thay thế, chẳng hạn như bàn phím ảo hoặc phương thức nhập bằng lời nói. Sau khi cài đặt IME, người dùng có thể chọn một IME trong phần cài đặt hệ thống và sử dụng trên toàn bộ hệ thống. Bạn chỉ có thể bật một IME mỗi lần.

Để thêm một IME vào hệ thống Android, hãy tạo một ứng dụng Android chứa một lớp mở rộng InputMethodService. Ngoài ra, bạn thường tạo một hoạt động "cài đặt" để chuyển các tuỳ chọn đến dịch vụ IME. Bạn cũng có thể xác định giao diện người dùng cài đặt hiển thị trong phần cài đặt hệ thống.

Trang này bao gồm các chủ đề sau:

Nếu bạn chưa làm việc với các IME, hãy đọc bài viết giới thiệu Phương thức nhập trên màn hình trước.

Vòng đời IME

Sơ đồ dưới đây mô tả vòng đời của một IME:

Hình ảnh minh hoạ vòng đời của một IME.
Hình 1. Vòng đời của một IME.

Các phần sau đây mô tả cách triển khai giao diện người dùng và mã liên kết với một IME tuân theo vòng đời này.

Khai báo thành phần của IME trong tệp kê khai

Trong hệ thống Android, IME là một ứng dụng Android có chứa dịch vụ IME đặc biệt. Tệp kê khai của ứng dụng phải khai báo dịch vụ, yêu cầu các quyền cần thiết, cung cấp bộ lọc ý định khớp với hành động action.view.InputMethod và cung cấp siêu dữ liệu xác định các đặc điểm của IME. Ngoài ra, để cung cấp giao diện cài đặt cho phép người dùng sửa đổi hành vi của IME, bạn có thể xác định hoạt động "cài đặt" có thể chạy từ phần Cài đặt hệ thống.

Đoạn mã sau đây khai báo dịch vụ IME. Phương thức này yêu cầu quyền BIND_INPUT_METHOD để cho phép dịch vụ kết nối IME với hệ thống, thiết lập bộ lọc ý định khớp với hành động android.view.InputMethod và xác định siêu dữ liệu cho IME:

<!-- Declares the input method service. -->
<service android:name="FastInputIME"
    android:label="@string/fast_input_label"
    android:permission="android.permission.BIND_INPUT_METHOD">
    <intent-filter>
        <action android:name="android.view.InputMethod" />
    </intent-filter>
    <meta-data android:name="android.view.im"
               android:resource="@xml/method" />
</service>

Đoạn mã tiếp theo khai báo hoạt động cài đặt cho IME. Bộ lọc ý định này có bộ lọc ý định cho ACTION_MAIN cho biết rằng hoạt động này là điểm truy cập chính cho ứng dụng IME:

<!-- Optional: an activity for controlling the IME settings. -->
<activity android:name="FastInputIMESettings"
    android:label="@string/fast_input_settings">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>

Bạn cũng có thể cung cấp quyền truy cập vào các chế độ cài đặt của IME trực tiếp từ giao diện người dùng.

API phương thức nhập

Bạn có thể tìm thấy các lớp dành riêng cho IME trong gói android.inputmethodserviceandroid.view.inputmethod. Lớp KeyEvent đóng vai trò quan trọng trong việc xử lý các ký tự trên bàn phím.

Phần trung tâm của IME là một thành phần dịch vụ — một lớp mở rộng InputMethodService. Ngoài việc triển khai vòng đời dịch vụ thông thường, lớp này còn có các lệnh gọi lại để cung cấp giao diện người dùng của IME, xử lý hoạt động đầu vào của người dùng và phân phối văn bản đến trường có tâm điểm. Theo mặc định, lớp InputMethodService cung cấp hầu hết các phương thức triển khai để quản lý trạng thái và chế độ hiển thị của IME cũng như giao tiếp với trường nhập dữ liệu hiện tại.

Các lớp sau đây cũng quan trọng:

BaseInputConnection
Xác định kênh giao tiếp từ InputMethod quay lại ứng dụng đang nhận đầu vào. Bạn sử dụng nó để đọc văn bản xung quanh con trỏ, chuyển văn bản vào hộp văn bản và gửi các sự kiện chính thô đến ứng dụng. Các ứng dụng phải mở rộng lớp này thay vì triển khai giao diện cơ sở InputConnection.
KeyboardView
Phần mở rộng của View kết xuất bàn phím và phản hồi các sự kiện do người dùng nhập. Bố cục bàn phím được chỉ định bằng một thực thể của Keyboard mà bạn có thể xác định trong tệp XML.

Thiết kế giao diện người dùng cho phương thức nhập

Có hai phần tử hình ảnh chính cho một IME: khung hiển thị đầu vào và khung hiển thị đề xuất. Bạn chỉ phải triển khai các phần tử có liên quan đến phương thức nhập dữ liệu mà mình đang thiết kế.

Chế độ xem nhập dữ liệu

Khung hiển thị nhập dữ liệu là giao diện người dùng mà người dùng nhập văn bản dưới dạng nhấp phím, viết tay hoặc cử chỉ. Khi IME hiển thị lần đầu tiên, hệ thống sẽ gọi lệnh gọi lại onCreateInputView(). Trong quá trình triển khai phương thức này, hãy tạo bố cục mà bạn muốn hiển thị trong cửa sổ IME và trả bố cục về hệ thống. Đoạn mã sau đây cho thấy một ví dụ về cách triển khai phương thức onCreateInputView():

Kotlin

override fun onCreateInputView(): View {
    return layoutInflater.inflate(R.layout.input, null).apply {
        if (this is MyKeyboardView) {
            setOnKeyboardActionListener(this@MyInputMethod)
            keyboard = latinKeyboard
        }
    }
}

Java

@Override
public View onCreateInputView() {
    MyKeyboardView inputView =
        (MyKeyboardView) getLayoutInflater().inflate(R.layout.input, null);

    inputView.setOnKeyboardActionListener(this);
    inputView.setKeyboard(latinKeyboard);

    return inputView;
}

Trong ví dụ này, MyKeyboardView là một thực thể của phương thức triển khai tuỳ chỉnh của KeyboardView để kết xuất một Keyboard.

Chế độ xem đề xuất

Khung hiển thị đề xuất là giao diện người dùng trong đó IME hiển thị các bản sửa đổi hoặc đề xuất từ có thể có để người dùng chọn. Trong vòng đời IME, hệ thống sẽ gọi onCreateCandidatesView() khi đã sẵn sàng hiển thị khung hiển thị đề xuất. Trong quá trình triển khai phương thức này, hãy trả về một bố cục cho thấy các từ đề xuất hoặc trả về giá trị rỗng nếu bạn không muốn hiển thị bất kỳ nội dung nào. Phản hồi rỗng là hành vi mặc định, vì vậy, bạn không cần phải triển khai phản hồi này nếu không cung cấp nội dung đề xuất.

Những điều cần cân nhắc khi thiết kế giao diện người dùng

Phần này mô tả một số cân nhắc về thiết kế giao diện người dùng cho IME.

Xử lý nhiều kích thước màn hình

Giao diện người dùng cho IME của bạn phải có khả năng điều chỉnh tỷ lệ cho nhiều kích thước màn hình, đồng thời xử lý được cả hướng ngang lẫn hướng dọc. Ở chế độ IME không toàn màn hình, hãy để lại đủ không gian để ứng dụng hiển thị trường văn bản và mọi ngữ cảnh liên quan sao cho IME không chiếm quá một nửa màn hình. Ở chế độ IME toàn màn hình, đây không phải là vấn đề.

Xử lý nhiều loại dữ liệu đầu vào

Các trường văn bản Android cho phép bạn đặt một loại dữ liệu nhập cụ thể, chẳng hạn như văn bản dạng tự do, số, URL, địa chỉ email và chuỗi tìm kiếm. Khi bạn triển khai một IME mới, hãy phát hiện loại dữ liệu đầu vào của từng trường và cung cấp giao diện thích hợp cho trường đó. Tuy nhiên, bạn không cần phải thiết lập IME để kiểm tra xem người dùng có nhập văn bản hợp lệ cho loại dữ liệu nhập hay không. Đây là trách nhiệm của ứng dụng sở hữu trường văn bản.

Ví dụ: đây là giao diện mà IME Latinh cung cấp cho phương thức nhập văn bản trên nền tảng Android:

Hình ảnh hiển thị dữ liệu đã nhập văn bản trên một IME tiếng Latinh
Hình 2. Nhập văn bản IME tiếng Latinh.

Sau đây là giao diện mà IME tiếng Latinh cung cấp cho phương thức nhập bằng số trên nền tảng Android:

Hình ảnh hiển thị một dữ liệu đầu vào dạng số trên một IME tiếng Latinh
Hình 3. Dữ liệu đầu vào dạng số IME tiếng Latinh.

Khi một trường nhập dữ liệu nhận được tiêu điểm và IME của bạn bắt đầu, hệ thống sẽ gọi onStartInputView(), truyền một đối tượng EditorInfo chứa thông tin chi tiết về loại dữ liệu đầu vào và các thuộc tính khác của trường văn bản. Trong đối tượng này, trường inputType chứa loại dữ liệu đầu vào của trường văn bản.

Trường inputType là một int chứa các mẫu bit cho nhiều chế độ cài đặt loại dữ liệu đầu vào. Để kiểm thử trường đó cho loại dữ liệu đầu vào của trường văn bản, hãy che trường đó bằng hằng số TYPE_MASK_CLASS, như sau:

Kotlin

inputType and InputType.TYPE_MASK_CLASS

Java

inputType & InputType.TYPE_MASK_CLASS

Mẫu bit loại dữ liệu đầu vào có thể có một trong các giá trị sau:

TYPE_CLASS_NUMBER
Một trường văn bản để nhập số. Như minh hoạ trong hình 3, IME tiếng Latinh hiển thị một bàn phím số cho các trường thuộc loại này.
TYPE_CLASS_DATETIME
Trường văn bản để nhập ngày và giờ.
TYPE_CLASS_PHONE
Một trường văn bản để nhập số điện thoại.
TYPE_CLASS_TEXT
Một trường văn bản để nhập ký tự bất kỳ được hỗ trợ.

Các hằng số này được mô tả chi tiết hơn trong tài liệu tham khảo cho InputType.

Trường inputType có thể chứa các bit khác cho biết một biến thể của loại trường văn bản, chẳng hạn như:

TYPE_TEXT_VARIATION_PASSWORD
Một biến thể của TYPE_CLASS_TEXT để nhập mật khẩu. Phương thức nhập sẽ hiện hoạ tiết in trên giấy thay vì văn bản thực tế.
TYPE_TEXT_VARIATION_URI
Một biến thể của TYPE_CLASS_TEXT để nhập URL của trang web và các Giá trị nhận dạng tài nguyên thống nhất (URI) khác.
TYPE_TEXT_FLAG_AUTO_COMPLETE
Một biến thể của TYPE_CLASS_TEXT để nhập văn bản mà ứng dụng tự động hoàn thành qua từ điển, kết quả tìm kiếm hoặc công cụ khác.

Hãy che inputType bằng hằng số thích hợp khi bạn kiểm thử các biến thể này. Các hằng số mặt nạ hiện có được liệt kê trong tài liệu tham khảo về InputType.

Gửi văn bản đến ứng dụng

Khi người dùng nhập văn bản bằng IME, bạn có thể gửi văn bản đến ứng dụng bằng cách gửi từng sự kiện phím hoặc bằng cách chỉnh sửa văn bản xung quanh con trỏ trong trường văn bản của ứng dụng. Trong cả hai trường hợp, hãy sử dụng một thực thể của InputConnection để gửi văn bản. Để nhận phiên bản này, hãy gọi InputMethodService.getCurrentInputConnection().

Chỉnh sửa văn bản quanh con trỏ

Khi xử lý việc chỉnh sửa văn bản hiện có, bạn có thể sử dụng một số phương thức hữu ích trong BaseInputConnection sau đây:

getTextBeforeCursor()
Trả về CharSequence chứa số lượng ký tự đã yêu cầu trước vị trí con trỏ hiện tại.
getTextAfterCursor()
Trả về CharSequence chứa số ký tự yêu cầu sau vị trí con trỏ hiện tại.
deleteSurroundingText()
Xoá số ký tự chỉ định trước và sau vị trí con trỏ hiện tại.
commitText()
Xác nhận CharSequence vào trường văn bản và đặt vị trí con trỏ mới.

Ví dụ: đoạn mã sau đây cho biết cách thay thế bốn ký tự ở bên trái của con trỏ bằng văn bản "Hello!":

Kotlin

currentInputConnection.also { ic: InputConnection ->
    ic.deleteSurroundingText(4, 0)
    ic.commitText("Hello", 1)
    ic.commitText("!", 1)
}

Java

InputConnection ic = getCurrentInputConnection();
ic.deleteSurroundingText(4, 0);
ic.commitText("Hello", 1);
ic.commitText("!", 1);

Hỗ trợ soạn văn bản trước khi gửi

Nếu IME dự đoán văn bản hoặc yêu cầu nhiều bước để soạn một ký tự hoặc từ, bạn có thể hiển thị tiến trình trong trường văn bản cho đến khi người dùng gửi từ đó, sau đó bạn có thể thay thế kết hợp một phần bằng văn bản hoàn chỉnh. Bạn có thể xử lý đặc biệt cho văn bản bằng cách thêm span vào văn bản đó khi truyền văn bản đến setComposingText().

Đoạn mã sau đây minh hoạ cách hiển thị tiến trình trong một trường văn bản:

Kotlin

currentInputConnection.also { ic: InputConnection ->
    ic.setComposingText("Composi", 1)
    ic.setComposingText("Composin", 1)
    ic.commitText("Composing ", 1)
}

Java

InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
ic.setComposingText("Composin", 1);
ic.commitText("Composing ", 1);

Chặn sự kiện liên quan đến phím cứng

Mặc dù cửa sổ phương thức nhập không có tiêu điểm rõ ràng, nhưng trước tiên, cửa sổ này sẽ nhận được các sự kiện liên quan đến phím phần cứng và có thể sử dụng hoặc chuyển tiếp các sự kiện đó đến ứng dụng. Ví dụ: bạn có thể sử dụng các phím định hướng để điều hướng trong giao diện người dùng nhằm lựa chọn đề xuất trong quá trình kết hợp. Bạn cũng nên giữ lại phím quay lại để đóng mọi hộp thoại bắt nguồn từ cửa sổ phương thức nhập.

Để chặn các phím phần cứng, hãy ghi đè onKeyDown()onKeyUp().

Gọi phương thức super() cho các khoá mà bạn không muốn tự xử lý.

Tạo loại phụ IME

Các loại phụ cho phép IME hiển thị nhiều chế độ nhập và ngôn ngữ được IME hỗ trợ. Loại phụ có thể đại diện cho những nội dung sau:

  • Ngôn ngữ, chẳng hạn như en_US hoặc fr_FR
  • Chế độ nhập, chẳng hạn như giọng nói, bàn phím hoặc chữ viết tay
  • Các kiểu nhập, biểu mẫu hoặc thuộc tính khác dành riêng cho IME, chẳng hạn như bố cục bàn phím 10 phím hoặc QWERTY

Chế độ này có thể là văn bản bất kỳ, chẳng hạn như "bàn phím" hoặc "giọng nói". Loại phụ cũng có thể hiển thị tổ hợp các thuộc tính này.

Thông tin loại phụ được dùng cho hộp thoại trình chuyển đổi IME có trên thanh thông báo và trong các chế độ cài đặt IME. Thông tin này cũng cho phép khung hiển thị trực tiếp một loại phụ cụ thể của IME. Khi bạn tạo một IME, hãy sử dụng tiện ích loại phụ vì tiện ích này giúp người dùng xác định và chuyển đổi giữa nhiều ngôn ngữ và chế độ IME.

Xác định các loại phụ ở một trong các tệp tài nguyên XML của phương thức nhập bằng phần tử <subtype>. Đoạn mã sau đây xác định một IME có hai loại phụ: một loại phụ bàn phím cho ngôn ngữ tiếng Anh (Mỹ) và một loại phụ bàn phím khác cho ngôn ngữ tiếng Pháp tại Pháp:

<input-method xmlns:android="http://schemas.android.com/apk/res/android"
        android:settingsActivity="com.example.softkeyboard.Settings"
        android:icon="@drawable/ime_icon">
    <subtype android:name="@string/display_name_english_keyboard_ime"
            android:icon="@drawable/subtype_icon_english_keyboard_ime"
            android:languageTag="en-US"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="somePrivateOption=true" />
    <subtype android:name="@string/display_name_french_keyboard_ime"
            android:icon="@drawable/subtype_icon_french_keyboard_ime"
            android:languageTag="fr-FR"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="someVariable=30,someInternalOption=false" />
    <subtype android:name="@string/display_name_german_keyboard_ime" ... />
</input-method>

Để đảm bảo các loại phụ được gắn nhãn chính xác trong giao diện người dùng, hãy sử dụng `%s` để lấy nhãn loại phụ giống với nhãn ngôn ngữ của loại phụ. Điều này được minh hoạ trong hai đoạn mã tiếp theo. Đoạn mã đầu tiên cho thấy một phần tệp XML của phương thức nhập:

<subtype
    android:label="@string/label_subtype_generic"
    android:imeSubtypeLocale="en_US"
    android:icon="@drawable/icon_en_us"
    android:imeSubtypeMode="keyboard" />

Đoạn mã tiếp theo là một phần của tệp strings.xml của IME. Tài nguyên chuỗi label_subtype_generic (dùng theo định nghĩa giao diện người dùng của phương thức nhập để đặt nhãn của kiểu phụ) được khai báo như sau:

<string name="label_subtype_generic">%s</string>

Chế độ cài đặt này giúp tên hiển thị của loại phụ khớp với chế độ cài đặt ngôn ngữ. Ví dụ: đối với mọi ngôn ngữ tiếng Anh, tên hiển thị sẽ là "Tiếng Anh (Hoa Kỳ)".

Chọn loại phụ IME từ thanh thông báo

Hệ thống Android quản lý mọi loại phụ mà mọi IME hiển thị. Các loại phụ IME được coi là các chế độ của IME mà chúng thuộc về. Người dùng có thể di chuyển từ thanh thông báo hoặc ứng dụng Cài đặt đến trình đơn gồm các loại phụ IME có sẵn, như trong hình sau:

Hình ảnh hiển thị trình đơn Hệ thống ngôn ngữ và nhập liệu
Hình 4. Trình đơn hệ thống Ngôn ngữ và nhập liệu.

Chọn loại phụ IME từ phần Cài đặt hệ thống

Người dùng cũng có thể kiểm soát cách sử dụng các loại phụ trong bảng cài đặt Language & input (Ngôn ngữ và phương thức nhập) trong phần cài đặt hệ thống:

Hình ảnh hiển thị trình đơn chọn Ngôn ngữ
Hình 5. Trình đơn hệ thống Ngôn ngữ

Chuyển đổi giữa các loại phụ IME

Bạn có thể cho phép người dùng dễ dàng chuyển đổi giữa các loại phụ IME bằng cách cung cấp một phím chuyển đổi, chẳng hạn như biểu tượng ngôn ngữ hình quả cầu trên bàn phím. Điều này giúp cải thiện khả năng hữu dụng của bàn phím và thuận tiện cho người dùng. Để bật tính năng chuyển đổi này, hãy thực hiện các bước sau:

  1. Khai báo supportsSwitchingToNextInputMethod = "true" trong tệp tài nguyên XML của phương thức nhập. Nội dung khai báo của bạn phải giống với đoạn mã sau:
    <input-method xmlns:android="http://schemas.android.com/apk/res/android"
            android:settingsActivity="com.example.softkeyboard.Settings"
            android:icon="@drawable/ime_icon"
            android:supportsSwitchingToNextInputMethod="true">
    
  2. Gọi phương thức shouldOfferSwitchingToNextInputMethod().
  3. Nếu phương thức trả về true (đúng), hãy hiển thị phím chuyển đổi.
  4. Khi người dùng nhấn vào phím chuyển đổi, hãy gọi switchToNextInputMethod(), truyền giá trị false. Giá trị false sẽ yêu cầu hệ thống xử lý tất cả các loại phụ như nhau, bất kể chúng thuộc IME nào. Thao tác chỉ định true sẽ yêu cầu hệ thống chuyển đổi qua các loại phụ trong IME hiện tại.

Lưu ý chung về IME

Sau đây là một số yếu tố khác cần cân nhắc khi triển khai IME:

  • Cung cấp cho người dùng cách để đặt các tuỳ chọn trực tiếp từ giao diện người dùng của IME.
  • Cung cấp cho người dùng cách để chuyển sang một IME khác ngay trên giao diện người dùng phương thức nhập, vì có thể nhiều IME được cài đặt trên thiết bị.
  • Hiển thị nhanh giao diện người dùng của IME. Tải trước hoặc tải theo yêu cầu bất kỳ tài nguyên lớn nào để người dùng thấy IME ngay khi họ nhấn vào một trường văn bản. Tài nguyên và khung hiển thị lưu vào bộ nhớ đệm cho các lệnh gọi tiếp theo của phương thức nhập.
  • Giải phóng các cơ cấu phân bổ bộ nhớ lớn ngay sau khi ẩn cửa sổ phương thức nhập, giúp các ứng dụng có đủ bộ nhớ để chạy. Sử dụng một thông báo bị trì hoãn để giải phóng tài nguyên nếu IME bị ẩn trong vài giây.
  • Đảm bảo người dùng có thể nhập nhiều ký tự nhất có thể cho ngôn ngữ hoặc khu vực liên kết với IME. Người dùng có thể sử dụng dấu câu trong mật khẩu hoặc tên người dùng. Vì vậy, IME của bạn phải cung cấp nhiều ký tự để người dùng có thể nhập mật khẩu và truy cập vào thiết bị.