Xử lý chế độ hiển thị phương thức nhập

Khi tiêu điểm nhập di chuyển vào hoặc ra khỏi một trường văn bản có thể chỉnh sửa, Android sẽ hiển thị hoặc ẩn phương thức nhập (chẳng hạn như bàn phím ảo) khi phù hợp. Hệ thống cũng quyết định cách giao diện người dùng và trường văn bản xuất hiện phía trên phương thức nhập. Ví dụ: khi không gian theo chiều dọc trên màn hình bị hạn chế, trường văn bản có thể lấp đầy toàn bộ không gian phía trên phương thức nhập.

Đối với hầu hết ứng dụng, những hành vi mặc định này là tất cả những gì cần thiết. Tuy nhiên, trong một số trường hợp, bạn có thể muốn kiểm soát nhiều hơn đối với chế độ hiển thị của phương thức nhập và cách phương thức đó tác động đến bố cục. Bài học này giải thích cách kiểm soát và phản hồi chế độ hiển thị phương thức nhập.

Hiện bàn phím mềm khi hoạt động bắt đầu

Mặc dù Android tập trung vào trường văn bản đầu tiên trong bố cục khi hoạt động bắt đầu, nhưng sẽ không hiển thị bàn phím mềm. Hành vi này phù hợp vì việc nhập văn bản có thể không phải là tác vụ chính trong hoạt động. Tuy nhiên, nếu việc nhập văn bản thực sự là tác vụ chính, chẳng hạn như trong màn hình đăng nhập, thì bạn nên để bàn phím mềm xuất hiện theo mặc định.

Để hiển thị phương thức nhập khi hoạt động của bạn bắt đầu, hãy thêm thuộc tính android:windowSoftInputMode vào phần tử <activity> chứa giá trị "stateVisible". Ví dụ:

<application ... >
    <activity
        android:windowSoftInputMode="stateVisible" ... >
        ...
    </activity>
   ...
</application>

Chỉ định cách giao diện người dùng phản hồi

Khi bàn phím mềm xuất hiện trên màn hình, điều này sẽ làm giảm không gian dành cho giao diện người dùng của ứng dụng. Hệ thống sẽ quyết định cách điều chỉnh phần hiển thị của giao diện người dùng nhưng có thể không chính xác. Để đảm bảo ứng dụng của bạn hoạt động tốt nhất, hãy chỉ định cách bạn muốn hệ thống hiển thị giao diện người dùng trong không gian còn lại.

Để khai báo phương thức xử lý ưu tiên trong một hoạt động, hãy sử dụng thuộc tính android:windowSoftInputMode trong phần tử <activity> của tệp kê khai với một trong các giá trị "adjust" (điều chỉnh).

Ví dụ: để đảm bảo rằng hệ thống đổi kích thước bố cục thành không gian còn trống – giúp bạn có thể truy cập vào tất cả nội dung bố cục, ngay cả khi nội dung bố cục yêu cầu cuộn, hãy sử dụng "adjustResize":

<application ... >
   <activity
       android:windowSoftInputMode="adjustResize" ... >
       ...
   </activity>
   ...
</application>

Bạn có thể kết hợp thông số kỹ thuật điều chỉnh với thông số kỹ thuật về chế độ hiển thị bàn phím mềm ban đầu trong phần trước:

<activity
    android:windowSoftInputMode="stateVisible|adjustResize" ... >
    ...
</activity>

Việc chỉ định "adjustResize" là rất quan trọng nếu giao diện người dùng của bạn bao gồm các chế độ điều khiển mà người dùng có thể cần truy cập ngay sau khi hoặc trong khi thực hiện nhập văn bản. Ví dụ: nếu bạn sử dụng bố cục tương đối để đặt thanh nút ở cuối màn hình, thì việc sử dụng "adjustResize" sẽ đổi kích thước bố cục để thanh nút xuất hiện phía trên bàn phím mềm.

Hiện bàn phím mềm theo yêu cầu

Nếu có một phương thức trong vòng đời của hoạt động mà bạn muốn đảm bảo phương thức nhập hiển thị, thì bạn có thể sử dụng InputMethodManager để hiển thị phương thức đó.

Ví dụ: phương thức sau sẽ sử dụng View, trong đó người dùng cần nhập nội dung nào đó, gọi requestFocus() để lấy tiêu điểm, sau đó gọi showSoftInput() để mở phương thức nhập:

Kotlin

fun showSoftKeyboard(view: View) {
   if (view.requestFocus()) {
       val imm = getSystemService(InputMethodManager::class.java)
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
   }
}

Java

public void showSoftKeyboard(View view) {
   if (view.requestFocus()) {
       InputMethodManager imm = getSystemService(InputMethodManager.class);
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
   }
}

Hiện bàn phím mềm một cách đáng tin cậy

Có một số trường hợp, chẳng hạn như khi một hoạt động bắt đầu, trong đó việc sử dụng InputMethodManager.showSoftInput() để hiển thị bàn phím mềm có thể khiến người dùng không nhìn thấy bàn phím phần mềm.

Chế độ hiển thị của bàn phím mềm khi sử dụng showSoftInput() tuỳ thuộc vào các điều kiện sau:

  • Khung hiển thị phải được kết nối với bàn phím phần mềm. (Đổi lại, điều này sẽ yêu cầu lấy cửa sổ được lấy làm tiêu điểm và khung hiển thị trình chỉnh sửa để yêu cầu tiêu điểm của khung hiển thị bằng View.requestFocus()).

  • Chế độ hiển thị cũng có thể chịu ảnh hưởng của thuộc tính android:windowSoftInputMode và các cờ mà showSoftInput() sử dụng.

Trong một số trường hợp sử dụng nhất định, chẳng hạn như khi một hoạt động đang bắt đầu, một số điều kiện bắt buộc này chưa được đáp ứng. Hệ thống không coi khung hiển thị đó là kết nối với bàn phím phần mềm, bỏ qua lệnh gọi showSoftInput() và bàn phím mềm không hiển thị với người dùng.

Để đảm bảo bàn phím phần mềm hiển thị một cách đáng tin cậy, bạn có thể sử dụng các phương án thay thế sau:

  • (Nên dùng) Sử dụng WindowInsetsControllerCompat. Đối tượng này hiển thị bàn phím mềm trong Activity.onCreate() như minh hoạ trong đoạn mã sau. Lệnh gọi đảm bảo sẽ được lên lịch sau khi cửa sổ được lấy tiêu điểm.

Kotlin

editText.requestFocus()
WindowCompat.getInsetsController(window, editText)!!.show(WindowInsetsCompat.Type.ime())

Java

editText.requestFocus();
WindowCompat.getInsetsController(getWindow(), editText).show(WindowInsetsCompat.Type.ime());
  • Đăng một tập dữ liệu có thể chạy. Điều này đảm bảo rằng ứng dụng sẽ chờ cho đến khi nhận được sự kiện lấy tiêu điểm cửa sổ từ View.onWindowFocusChanged() trước khi gọi showSoftInput().

Kotlin

class MyEditText : EditText() {
  ...
  override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
    if (hasWindowFocus) {
      requestFocus()
      post {
        val imm: InputMethodManager = getSystemService(InputMethodManager::class.java)
        imm.showSoftInput(this, 0)
      }
    }
  }
}

Java

public class MyEditText extends EditText {
  ...
  @Override
  public void onWindowFocusChanged(boolean hasWindowFocus) {
    if (hasWindowFocus) {
      requestFocus();
      post(() -> {
        InputMethodManager imm = getSystemService(InputMethodManager.class);
        imm.showSoftInput(this, 0);
      });
    }
  }
}

Xử lý cẩn thận các cờ hiển thị trong thời gian chạy

Khi bật/tắt chế độ hiển thị bàn phím mềm trong thời gian chạy, hãy cẩn thận để không truyền một số giá trị cờ vào các phương thức này. Ví dụ: nếu ứng dụng muốn bàn phím mềm sẽ xuất hiện khi gọi View.getWindowInsetsController().show(ime()) trong Activity.onCreate() trong quá trình bắt đầu hoạt động, thì nhà phát triển ứng dụng phải cẩn thận để không đặt cờ SOFT_INPUT_STATE_HIDDEN hoặc SOFT_INPUT_STATE_ALWAYS_HIDDEN trong lần chạy đầu tiên phòng trường hợp bàn phím mềm bị ẩn đột ngột.

Hệ thống thường tự động ẩn bàn phím mềm

Trong hầu hết các trường hợp, hệ thống sẽ xử lý việc ẩn bàn phím mềm. Trường hợp này có thể là bất kỳ trường hợp nào sau đây:

  • Người dùng hoàn tất tác vụ trong trường văn bản.
  • Người dùng nhấn phím quay lại hoặc cử chỉ vuốt khi sử dụng tính năng điều hướng quay lại.
  • Người dùng chuyển đến một ứng dụng khác và ứng dụng đó đã đặt cờ SOFT_INPUT_STATE_HIDDEN hoặc SOFT_INPUT_STATE_ALWAYS_HIDDEN khi khung hiển thị đó được lấy làm tâm điểm.

Ẩn bàn phím mềm theo cách thủ công dựa trên hoạt động trước đó của hệ thống

Ứng dụng phải ẩn bàn phím mềm theo cách thủ công trong một số trường hợp, chẳng hạn như khi trường văn bản bị mất tiêu điểm trong View.OnFocusChangeListener.onFocusChange. Hãy thận trọng khi sử dụng kỹ thuật này; việc đóng bàn phím mềm sẽ đột ngột ảnh hưởng xấu đến trải nghiệm người dùng.

Nếu ứng dụng của bạn ẩn bàn phím mềm theo cách thủ công, thì bạn cần biết liệu bàn phím mềm được hiện rõ ràng hay ngầm ẩn:

  • Bàn phím mềm được coi là đã hiển thị rõ ràng sau khi gọi showSoftInput().

  • Ngược lại, bàn phím mềm được xem là đã ngầm hiển thị trong một trong các điều kiện sau:

    • Hệ thống đã hiển thị bàn phím mềm trong khi áp dụng android:windowSoftInputMode.
    • Ứng dụng của bạn đã chuyển SHOW_IMPLICIT đến showSoftInput().

Thông thường, hideSoftInputFromWindow() sẽ ẩn bàn phím mềm bất kể cách thức được yêu cầu. Tuy nhiên, với HIDE_IMPLICIT_ONLY, bạn có thể chỉ đóng một bàn phím mềm được yêu cầu ngầm ẩn.

Hiện hộp thoại hoặc chế độ xem lớp phủ ở đầu bàn phím mềm

Trong một số trường hợp, hoạt động của trình chỉnh sửa có thể cần phải tạo một cửa sổ lớp phủ hoặc hộp thoại không thể chỉnh sửa ở đầu bàn phím mềm.

Ứng dụng của bạn có một vài tuỳ chọn được mô tả trong các phần sau.

Tóm lại, hãy đảm bảo xử lý chính xác các cờ cửa sổ của bàn phím mềm nhắm đến cửa sổ sao cho đáp ứng các kỳ vọng sau đây về thứ tự dọc (lớp z):

  • Không có cờ nào (chữ thường): Đằng sau lớp bàn phím mềm và có thể nhận văn bản.
  • FLAG_NOT_FOCUSABLE: Ở đầu lớp bàn phím mềm nhưng không thể nhận văn bản.
  • FLAG_ALT_FOCUSABLE_IM: Ở đầu lớp bàn phím mềm, bạn có thể lấy tiêu điểm nhưng không được kết nối với bàn phím mềm. Đồng thời chặn tất cả các khung hiển thị bên dưới khung hiển thị đó để không kết nối với bàn phím mềm. Điều này rất hữu ích khi hiện hộp thoại ứng dụng không sử dụng phương thức nhập văn bản phía trên lớp bàn phím mềm.
  • FLAG_NOT_FOCUSABLEFLAG_ALT_FOCUSABLE_IM: Đằng sau lớp bàn phím mềm nhưng không thể nhận văn bản.
  • FLAG_NOT_FOCUSABLEFLAG_NOT_TOUCH_MODAL: Ở phía trên bàn phím mềm, hãy cho phép các sự kiện chạm "di chuyển" qua cửa sổ trên bàn phím mềm.

Tạo hộp thoại

Sử dụng cờ FLAG_ALT_FOCUSABLE_IM trong cửa sổ hộp thoại để giữ hộp thoại ở đầu bàn phím mềm và ngăn bàn phím mềm lấy tiêu điểm:

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog on top of soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog on top of soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);
mDialog = builder.create();
mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

Tạo thành phần hiển thị lớp phủ

Tạo khung hiển thị lớp phủ chỉ định loại cửa sổ TYPE_APPLICATION_OVERLAY và cờ cửa sổ FLAG_ALT_FOCUSABLE_IM theo hoạt động được nhắm mục tiêu bằng bàn phím mềm.

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION, /* Overlay window type */
  WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  /* Allow touch event send to soft keyboard behind the overlay */
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
        | FLAG_NOT_TOUCH_MODAL, /* Allow touch event send to soft keyboard behind the overlay */
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);

Hiện hộp thoại hoặc chế độ xem bên dưới bàn phím mềm

Ứng dụng của bạn có thể cần tạo một hộp thoại hoặc một cửa sổ có các thuộc tính sau:

  • Xuất hiện bên dưới bàn phím mềm được hoạt động của trình chỉnh sửa yêu cầu để không bị ảnh hưởng bởi việc nhập văn bản.
  • Nhận biết được những thay đổi đối với kích thước phần lồng ghép của bàn phím mềm sẽ thay đổi để điều chỉnh bố cục của hộp thoại hoặc cửa sổ.

Trong trường hợp này, ứng dụng của bạn có một vài lựa chọn. Các phần sau đây mô tả những tuỳ chọn này.

Tạo hộp thoại

Tạo hộp thoại bằng cách đặt cả cờ cửa sổ FLAG_NOT_FOCUSABLE và cờ cửa sổ FLAG_ALT_FOCUSABLE_IM:

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog behind soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog behind soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);

mDialog = builder.create();
mDialog.getWindow()
    .addFlags(FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

Tạo thành phần hiển thị lớp phủ

Tạo khung hiển thị lớp phủ bằng cách đặt cả cờ cửa sổ FLAG_NOT_FOCUSABLE và cờ cửa sổ FLAG_ALT_FOCUSABLE_IM:

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION,  /* Overlay window type */
  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
      or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM,
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);