当输入焦点移入或移出可编辑文本字段时,Android 会根据需要显示或隐藏输入(例如屏幕键盘)。系统还会决定您的 UI 和文本字段如何在输入法上方显示。例如,当屏幕上的垂直空间受限时,文本字段可能会填充输入法上方的所有空间。
对于大多数应用,这些默认行为已足够。但在某些情况下,您可能希望更多地控制输入法的可见性及其对布局的影响。本课将介绍如何控制和响应输入法的可见性。
在活动启动时显示软键盘
尽管 Android 会在活动启动时将焦点赋予布局中的第一个文本字段,但它不会显示软键盘。这种行为是合适的,因为输入文本可能不是活动中的主要任务。但是,如果输入文本确实是主要任务,例如在登录屏幕中,那么您可能希望软键盘默认显示。
要在活动启动时显示输入法,请将android:windowSoftInputMode
属性添加到<activity>
元素中,并使用"stateVisible"
值。例如
<application ... >
<activity
android:windowSoftInputMode="stateVisible" ... >
...
</activity>
...
</application>
指定您的 UI 应该如何响应
当软键盘出现在屏幕上时,它会减少应用 UI 可用的空间量。系统会决定如何调整 UI 的可见部分,但它可能无法正确调整。为了确保应用获得最佳行为,请指定您希望系统如何在剩余空间中显示 UI。
要在活动中声明您首选的处理方式,请在清单的<activity>
元素中使用android:windowSoftInputMode
属性,并使用其中一个“adjust”值。
例如,为了确保系统调整布局以适应可用空间(即使需要滚动,也能使所有布局内容都可访问),请使用"adjustResize"
<application ... >
<activity
android:windowSoftInputMode="adjustResize" ... >
...
</activity>
...
</application>
您可以将调整规范与上一节中的初始软键盘可见性规范结合使用
<activity
android:windowSoftInputMode="stateVisible|adjustResize" ... >
...
</activity>
如果您的 UI 包含用户可能需要在执行文本输入后或同时访问的控件,则指定"adjustResize"
非常重要。例如,如果您使用相对布局在屏幕底部放置一个按钮栏,则使用"adjustResize"
会调整布局,以便按钮栏出现在软键盘上方。
按需显示软键盘
如果在活动的生命周期中存在某个方法,您希望确保输入法可见,则可以使用InputMethodManager
来显示它。
例如,以下方法获取用户预期在其中输入内容的View
,调用requestFocus()
赋予其焦点,然后调用showSoftInput()
打开输入法
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); } }
可靠地显示软键盘
在某些情况下,例如活动启动时,使用InputMethodManager.showSoftInput()
显示软键盘可能会导致软件键盘对用户不可见。
使用showSoftInput()
时软键盘的可见性依赖于以下条件
视图必须已连接到软件键盘。(这反过来又需要窗口获得焦点,并且编辑器视图使用
View.requestFocus()
请求视图焦点)。android:windowSoftInputMode
属性和showSoftInput()
使用的标志也会影响可见性。
在某些用例中,例如活动启动时,某些必需的条件未满足。系统不认为视图已连接到软件键盘,忽略showSoftInput()
调用,并且软键盘对用户不可见。
为了确保软件键盘可靠地显示,您可以使用以下替代方法
- (推荐)使用
WindowInsetsControllerCompat
。此对象在Activity.onCreate()
期间显示软键盘,如下面的代码片段所示。该调用保证在窗口获得焦点后调度。
Kotlin
editText.requestFocus() WindowCompat.getInsetsController(window, editText)!!.show(WindowInsetsCompat.Type.ime())
Java
editText.requestFocus(); WindowCompat.getInsetsController(getWindow(), editText).show(WindowInsetsCompat.Type.ime());
- 发布一个 Runnable。这确保您的应用在从
View.onWindowFocusChanged()
接收窗口焦点事件后,再调用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); }); } } }
谨慎处理运行时可见性标志
在运行时切换软键盘可见性时,请注意不要将某些标志值传递给这些方法。例如,如果应用程序期望在活动启动期间在Activity.onCreate()
中调用View.getWindowInsetsController().show(ime())
时显示软键盘,则应用程序开发人员应注意在初始启动期间不要设置SOFT_INPUT_STATE_HIDDEN
或SOFT_INPUT_STATE_ALWAYS_HIDDEN
标志,以防软键盘意外隐藏。
系统通常会自动隐藏软键盘
在大多数情况下,系统会处理隐藏软键盘。这可能是以下任何情况
- 用户完成文本字段中的任务。
- 用户按下后退键或使用后退导航滑动。
- 用户导航到另一个应用,并且该应用在视图获得焦点时设置了
SOFT_INPUT_STATE_HIDDEN
或SOFT_INPUT_STATE_ALWAYS_HIDDEN
标志。
根据先前的系统行为手动隐藏软键盘
在某些情况下,您的应用必须手动隐藏软键盘,例如,当文本字段在View.OnFocusChangeListener.onFocusChange
中失去焦点时。谨慎使用此技术;意外关闭软键盘会影响用户体验。
如果您的应用手动隐藏软键盘,您需要知道软键盘是显式显示还是隐式显示。
调用
showSoftInput()
后,软键盘被认为是显式显示的。相反,在以下任一情况下,软键盘被认为是隐式显示的。
- 系统在应用
android:windowSoftInputMode
时显示了软键盘。 - 您的应用将
SHOW_IMPLICIT
传递给showSoftInput()
。
- 系统在应用
通常,hideSoftInputFromWindow()
会隐藏软键盘,无论它是如何被请求的,但使用HIDE_IMPLICIT_ONLY
,可以将其限制为仅关闭隐式请求的软键盘。
在软键盘上显示对话框或叠加视图
在某些情况下,编辑器活动可能需要在软键盘上创建不可编辑的对话框或叠加窗口。
您的应用有一些选项,以下部分将对此进行描述。
总之,请确保正确处理针对窗口的软键盘的窗口标志,使其满足以下关于垂直(z 层)顺序的预期
- 无标志(正常情况):位于软键盘层后面,并且可以接收文本。
FLAG_NOT_FOCUSABLE
:位于软键盘层之上,但无法接收文本。FLAG_ALT_FOCUSABLE_IM
:位于软键盘层之上,可以获得焦点,但未连接到软键盘。此外,还会阻止其下方的所有视图连接到软键盘。这对于在软键盘层上方显示不使用文本输入的应用对话框很有用。FLAG_NOT_FOCUSABLE
和FLAG_ALT_FOCUSABLE_IM
:位于软键盘层后面,但无法接收文本。FLAG_NOT_FOCUSABLE
和FLAG_NOT_TOUCH_MODAL
:位于软键盘之上,并允许触摸事件“穿过”窗口到达软键盘。
创建对话框
使用FLAG_ALT_FOCUSABLE_IM
对话框窗口标志将对话框保持在软键盘之上,并防止软键盘获得焦点。
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();
创建叠加视图
创建一个叠加视图,指定TYPE_APPLICATION_OVERLAY
窗口类型和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_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);
在软键盘下方显示对话框或视图
您的应用可能需要创建一个具有以下属性的对话框或窗口。
- 出现在编辑器活动请求的软键盘下方,使其不受文本输入的影响。
- 能够感知软键盘内嵌大小变化,并调整对话框或窗口的布局。
在这种情况下,您的应用有几个选项。以下部分将描述这些选项。
创建对话框
通过同时设置FLAG_NOT_FOCUSABLE
窗口标志和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();
创建叠加视图
通过同时设置FLAG_NOT_FOCUSABLE
窗口标志和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);