我的布局中有EditTextButton.

在编辑字段中写入并单击Button之后,我想在touch 键盘外部时隐藏虚拟键盘.我假设这是一段简单的代码,但在哪里可以找到它的示例?

推荐答案

为了澄清这种疯狂,我想首先代表所有安卓用户为谷歌对软键盘的荒谬处理道歉.对于同一个简单的问题,有如此多的答案,每个答案都不同,原因是这个API和Android中的许多其他API一样,设计得非常糟糕.我想不出有礼貌的说法.

我想把键盘藏起来.我希望为Android提供以下声明:Keyboard.hide().结束了.非常感谢你.但安卓有一个问题.你必须使用InputMethodManager隐藏键盘.好的,这是Android的键盘API.但是您必须拥有Context才能访问IMM.现在我们有问题了.我可能想对一个静态类或实用类隐藏键盘,这个类没有任何用途或需要.或者更糟糕的是,IMM要求您指定要隐藏键盘的内容View(甚至更糟,是Window).

这就是隐藏键盘如此具有挑战性的原因.亲爱的谷歌:当我在查找蛋糕的配方时,地球上没有RecipeProvider家公司会拒绝向我提供配方,除非我首先回答谁会吃蛋糕以及在哪里吃蛋糕!!

这个悲惨的故事以丑陋的事实结束:要隐藏Android键盘,你需要提供两种身份证明:ContextViewWindow.

我已经创建了一个静态实用程序方法,可以非常可靠地完成这项工作,只要你从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调用时有效!上述方法调用目标ActivitygetCurrentFocus来获取正确的窗口令牌.

但是,假设您想在DialogFragment中托管的EditText中隐藏键盘?您不能使用上述方法:

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

这不会起作用,因为您将传递一个对Fragment的宿主Activity的引用,在显示Fragment时,宿主Activity将没有焦点控件!哇!因此,为了从碎片中隐藏键盘,我求助于更低级别、更常见、更难看的键盘:

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

以下是从花费更多时间追求此解决方案中收集到的一些附加信息:

About windowSoftInputMode

还有另一个争论点需要注意.默认情况下,Android会自动将初始焦点分配给Activity中的前EditText个或可聚焦控件.很自然地,InputMethod(通常是软键盘)会通过显示自己来响应聚焦事件.AndroidManifest.xml中的windowSoftInputMode属性设置为stateAlwaysHidden时,指示键盘忽略自动分配的初始焦点.

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

几乎令人难以置信的是,当你touch 控件时,它似乎没有阻止键盘打开(除非为控件指定了focusable="false"和/或focusableInTouchMode="false").显然,WindowsofInputMode设置仅适用于自动对焦事件,而不适用于由touch 事件触发的对焦事件.

因此,stateAlwaysHidden确实是一个非常糟糕的名字.它或许应该被称为ignoreInitialFocus.


UPDATE: More ways to get a window token

如果没有聚焦视图(例如,如果您只是更改了片段,可能会发生这种情况),那么还有其他视图将提供有用的窗口标记.

这些都是上述代码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();

UPDATE 2: Clear focus to avoid showing keyboard again if you open the app from the background

将这一行添加到方法的末尾:

view.clearFocus();

Android相关问答推荐

将HTML约束布局转换为Jetpack编写约束布局-链样式、偏差

关于BLE扫描工作原理的说明

如何go 除回调流中不可用的状态?

Android Gradle/Groovy,如何将文件复制到APK

如何从URI中获取图像大小

当按下通知时,将Android应用程序置于前台

如何在Jetpack Compose中将对象的移动从一个路径平滑地切换到另一个路径?

更改选定的切换轨道 colored颜色

Android v31 及更低版本中 ImageView 中的圆角

使用 Gadle kotlin 为多模块 Android 代码库设置 jacoco

如何删除房间数据库?

Kotlin 协程、 retrofit 、android

安卓模拟器打不开

Jetpack compose :使用 rememberSaveable 时未应用待处理的合成

如何限制键盘输入键不允许在下一行输入(Android Jetpack Compose 中的 TextField)

如何在最后一个可见项目之后计算惰性列中的空白空间

Hilt 依赖注入重复绑定错误

GridLayout 和 GridView 有什么好用和区别

并行运行两个挂起函数并在第一个返回时返回

IconButton 中可绘制的图标是黑色的,尽管它是白色的