协慌网

登录 贡献 社区

关闭 / 隐藏 Android 软键盘

我的布局中有一个EditText和一个Button

在编辑字段中写入并单击Button ,我想隐藏虚拟键盘。我假设这是一段简单的代码,但我在哪里可以找到它的一个例子?

答案

您可以使用InputMethodManager强制 Android 隐藏虚拟键盘,调用hideSoftInputFromWindow ,传入包含焦点视图的窗口的标记。

// Check if no view has focus:
View view = this.getCurrentFocus();
if (view != null) {  
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

这将强制键盘在所有情况下都被隐藏。在某些情况下,您需要传入InputMethodManager.HIDE_IMPLICIT_ONLY作为第二个参数,以确保您只在用户未明确强制显示键盘时隐藏键盘(通过按住菜单)。

注意:如果要在 Kotlin 中执行此操作,请使用: context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

Kotlin 语法

// Check if no view has focus:
 val view = this.currentFocus
 view?.let { v ->
  val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager 
  imm?.let { it.hideSoftInputFromWindow(v.windowToken, 0) }
 }

为了帮助澄清这种疯狂,我想首先代表所有 Android 用户道歉,谷歌对软键盘的彻头彻尾的荒谬处理。对于同样简单的问题,有这么多答案的原因有很多,因为这个 API 与 Android 中的许多其他 API 一样,设计非常糟糕。我认为没有礼貌的方式来陈述它。

我想隐藏键盘。我期望为 Android 提供以下声明: Keyboard.hide() 。结束。非常感谢你。但 Android 存在问题。您必须使用InputMethodManager来隐藏键盘。好的,很好,这是 Android 的键盘 API。但!您需要具有Context才能访问 IMM。现在我们遇到了问题。我可能想要从没有任何使用或不需要任何Context的静态或实用程序类中隐藏键盘。或者更糟糕的是,IMM 要求您指定要隐藏键盘 FROM 的View (或更糟糕的是,什么Window )。

这使得隐藏键盘变得如此具有挑战性。亲爱的谷歌:当我查找蛋糕的配方时,地球上没有RecipeProvider会拒绝向我提供配方,除非我第一次回答世界卫生组织,蛋糕将被吃掉,哪里会被吃掉!

这个悲伤的故事以丑陋的事实结束:要隐藏 Android 键盘,您需要提供两种形式的识别: Context以及ViewWindow

我已经创建了一个静态实用程序方法,只要你从一个Activity调用它就可以非常稳定地完成工作。

public static void hideKeyboard(Activity activity) {
    InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    //Find the currently focused view, so we can grab the correct window token from it.
    View view = activity.getCurrentFocus();
    //If no view currently has focus, create a new one, just so we can grab a window token from it
    if (view == null) {
        view = new View(activity);
    }
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

请注意,此实用程序方法仅在从Activity调用时才有效!上面的方法调用目标Activity getCurrentFocus来获取正确的窗口标记。

但是假设您想要从DialogFragment托管的EditText隐藏键盘?你不能使用上面的方法:

hideKeyboard(getActivity()); //won't work

这将无法工作,因为你会被传递一个参考Fragment的主机Activity ,这将有没有集中控制,同时该Fragment显示!哇!所以,为了从键盘中隐藏键盘,我采用较低级别,更常见,更丑陋:

public static void hideKeyboardFrom(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

以下是从追求此解决方案浪费的更多时间中收集的一些其他信息:

关于 windowSoftInputMode

还有另一个争论点需要注意。默认情况下,Android 会自动将初始焦点分配给Activity的第一个EditText或 focusable 控件。由此可见,InputMethod(通常是软键盘)将通过显示自身来响应焦点事件。 AndroidManifest.xmlwindowSoftInputMode属性设置为stateAlwaysHidden ,指示键盘忽略此自动分配的初始焦点。

<activity
    android:name=".MyActivity"
    android:windowSoftInputMode="stateAlwaysHidden"/>

几乎令人难以置信的是,当您触摸控件时,似乎无法阻止键盘打开(除非将focusable="false"和 / 或focusableInTouchMode="false"分配给控件)。显然,windowSoftInputMode 设置仅适用于自动焦点事件,而不适用于触摸由触摸事件触发的事件。

因此, stateAlwaysHidden名字确实非常糟糕。它也许应该被称为ignoreInitialFocus

希望这可以帮助。


更新:获取窗口令牌的更多方法

如果没有焦点视图(例如,如果您刚刚更改了片段,则会发生),还有其他视图将提供有用的窗口令牌。

if (view == null) view = new View(activity);这些是上述代码的替代品if (view == null) view = new View(activity);这些并未明确提及您的活动。

在片段类中:

view = getView().getRootView().getWindowToken();

给定片段fragment作为参数:

view = fragment.getView().getRootView().getWindowToken();

从您的内容正文开始:

view = findViewById(android.R.id.content).getRootView().getWindowToken();

更新 2:如果您从后台打开应用程序,请清除焦点以避免再次显示键盘

将此行添加到方法的末尾:

view.clearFocus();

隐藏软键盘也很有用:

getWindow().setSoftInputMode(
    WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
);

这可用于在用户实际触摸 editText 视图之前抑制软键盘。

您可以使用InputMethodManager强制 Android 隐藏虚拟键盘,调用hideSoftInputFromWindow ,传入包含焦点视图的窗口的标记。

// Check if no view has focus:
View view = this.getCurrentFocus();
if (view != null) {  
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

这将强制键盘在所有情况下都被隐藏。在某些情况下,您需要传入InputMethodManager.HIDE_IMPLICIT_ONLY作为第二个参数,以确保您只在用户未明确强制显示键盘时隐藏键盘(通过按住菜单)。

注意:如果要在 Kotlin 中执行此操作,请使用: context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

Kotlin 语法

// Check if no view has focus:
 val view = this.currentFocus
 view?.let { v ->
  val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager 
  imm?.let { it.hideSoftInputFromWindow(v.windowToken, 0) }
 }

为了帮助澄清这种疯狂,我想首先代表所有 Android 用户道歉,谷歌对软键盘的彻头彻尾的荒谬处理。对于同样简单的问题,有这么多答案的原因有很多,因为这个 API 与 Android 中的许多其他 API 一样,设计非常糟糕。我认为没有礼貌的方式来陈述它。

我想隐藏键盘。我期望为 Android 提供以下声明: Keyboard.hide() 。结束。非常感谢你。但 Android 存在问题。您必须使用InputMethodManager来隐藏键盘。好的,很好,这是 Android 的键盘 API。但!您需要具有Context才能访问 IMM。现在我们遇到了问题。我可能想要从没有任何使用或不需要任何Context的静态或实用程序类中隐藏键盘。或者更糟糕的是,IMM 要求您指定要隐藏键盘 FROM 的View (或更糟糕的是,什么Window )。

这使得隐藏键盘变得如此具有挑战性。亲爱的谷歌:当我查找蛋糕的配方时,地球上没有RecipeProvider会拒绝向我提供配方,除非我第一次回答世界卫生组织,蛋糕将被吃掉,哪里会被吃掉!

这个悲伤的故事以丑陋的事实结束:要隐藏 Android 键盘,您需要提供两种形式的识别: Context以及ViewWindow

我已经创建了一个静态实用程序方法,只要你从一个Activity调用它就可以非常稳定地完成工作。

public static void hideKeyboard(Activity activity) {
    InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    //Find the currently focused view, so we can grab the correct window token from it.
    View view = activity.getCurrentFocus();
    //If no view currently has focus, create a new one, just so we can grab a window token from it
    if (view == null) {
        view = new View(activity);
    }
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

请注意,此实用程序方法仅在从Activity调用时才有效!上面的方法调用目标Activity getCurrentFocus来获取正确的窗口标记。

但是假设您想要从DialogFragment托管的EditText隐藏键盘?你不能使用上面的方法:

hideKeyboard(getActivity()); //won't work

这将无法工作,因为你会被传递一个参考Fragment的主机Activity ,这将有没有集中控制,同时该Fragment显示!哇!所以,为了从键盘中隐藏键盘,我采用较低级别,更常见,更丑陋:

public static void hideKeyboardFrom(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

以下是从追求此解决方案浪费的更多时间中收集的一些其他信息:

关于 windowSoftInputMode

还有另一个争论点需要注意。默认情况下,Android 会自动将初始焦点分配给Activity的第一个EditText或 focusable 控件。由此可见,InputMethod(通常是软键盘)将通过显示自身来响应焦点事件。 AndroidManifest.xmlwindowSoftInputMode属性设置为stateAlwaysHidden ,指示键盘忽略此自动分配的初始焦点。

<activity
    android:name=".MyActivity"
    android:windowSoftInputMode="stateAlwaysHidden"/>

几乎令人难以置信的是,当您触摸控件时,似乎无法阻止键盘打开(除非将focusable="false"和 / 或focusableInTouchMode="false"分配给控件)。显然,windowSoftInputMode 设置仅适用于自动焦点事件,而不适用于触摸由触摸事件触发的事件。

因此, stateAlwaysHidden名字确实非常糟糕。它也许应该被称为ignoreInitialFocus

希望这可以帮助。


更新:获取窗口令牌的更多方法

如果没有焦点视图(例如,如果您刚刚更改了片段,则会发生),还有其他视图将提供有用的窗口令牌。

if (view == null) view = new View(activity);这些是上述代码的替代品if (view == null) view = new View(activity);这些并未明确提及您的活动。

在片段类中:

view = getView().getRootView().getWindowToken();

给定片段fragment作为参数:

view = fragment.getView().getRootView().getWindowToken();

从您的内容正文开始:

view = findViewById(android.R.id.content).getRootView().getWindowToken();

更新 2:如果您从后台打开应用程序,请清除焦点以避免再次显示键盘

将此行添加到方法的末尾:

view.clearFocus();

隐藏软键盘也很有用:

getWindow().setSoftInputMode(
    WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
);

这可用于在用户实际触摸 editText 视图之前抑制软键盘。