一、前言

如果有需要用到输入的地方,通常会有需要自动弹出或者收起软键盘的需求。开篇明义,本文会讲讲弹出和收起软键盘的一些细节,最终还会从源码进行分析。

想要操作软键盘,需要使用到 InputMethodManager ,它是一个系统服务,可以使用 Context.getSystemService() 获取到它。而很多关键的逻辑代码,都是在 InputMethodManagerService 中实现的。

特别说明:本文的所有分析的源码,都是基于 Android 26 的源码。

二、操作软键盘

2.1 InputMethodManager

前面提到,想要操作软键盘,需要使用 InputMethodManager ,它是一个系统服务,想要获取它,可以使用 getSystemService() ,获取到它。

毕竟是系统服务,使用的时候为了安全,还是要判空,避免空指针。

2.2 显示软键盘

在 InputMethodManager 中,有两个方法 showSoftInput()showSoftInputFromInputMethod() ,而实际上,只有 showSoftInput() 是有效的。

它有两个重载方法,而通常我们会使用它的两个参数的方法。

这里我们只需要传递两个参数。它首先需要一个 View ,使用软键盘就是为了输入,而输入就需要有接收输入内容的 View ,这里接收输入的 View ,最好是一个 EditText(但这不是必须的)。

而第二个参数 flags 就是个标志位,从上面截图的方法签名上的文档上可以看到,它接收 0 或者 SHOW_INPYT_IMPLICIT 两个参数,但是实际上,它有第三个参数,另外一个是 SHOW_FORCED。

可以看到 1、2 都是有特殊含义的,实际上它们并不影响显示,只是在隐藏的时候,会有一些限制,这些后面看源码的时候再说,一般没有特别需要的话,我们直接传递 0 就好了。

现在,简单总结一下调用 showSoftInput() 会生效的关键点:

1、第一个参数,最好是 EditText 或者它的子类。

考虑到软键盘就是为了输入,EditText 就是一个接收输入的控件。而这不是绝对的,如果不是一个 EditText ,就必须要求这个 View 有两个属性,分别是:android:focusable="true"android:focusableInTouchMode="true"

2、第一个参数,必须是可获取焦点的,并且当前已经获取到焦点。

EditText 默认是允许获取焦点的,但是假如布局中,存在多个可获取焦点的控件,就需要提前让我们传递进去的 View 获取到焦点。获取焦点可以使用 requestFocus() 方法。

3、布局必须加载完成。

onCreate() 中,如果立即调用 showSoftInput() 是不会生效的。想要在页面一启动的时候就弹出键盘,可以在 Activity 上,设置 android:windowSoftInputMode 属性来完成,或者做一个延迟加载,View.postDelayed() 也是一个解决方案。

所以最终,完整的显示软键盘的代码就如下所示了。

2.3 隐藏软键盘

虽然 showSoftInput() 方法是有效的,但是想要隐藏软键盘,就没有提供对应的 hideSoftInput() 方法,但是却有一个 hideSoftInputFromWindow() 方法,可以用来隐藏软键盘。

先来看看这个方法的签名,它同样有两个方法可以调用。

它接收两个参数,第一个参数是一个 IBinder ,可以直接传递一个 View.getWindowToken() 的 windowToken 对象就可以了。而第二个参数,就是隐藏软键盘的标志位,如果没有特殊要求的话,直接传递 0 就好了。

注意这里虽然原则上需要传递一个之前弹出键盘传递的时候,传递的 View 的 windowToken ,但是实际情况是你只需要传递一个存在于当前布局 ViewTree 中,随意一个 View 的 windowToken 就可以了。

最终隐藏软件的代码就是这样的。

2.4 切换键盘的弹出和隐藏

在 InputMethodManager 中,还提供了一个 toggleSoftInput() 方法,如同它的名字一样,它可以让软键盘在显示和隐藏之间切换。

该方法,接收两个 flags ,分别是控制 show 和 hide 时候的标识,它们的含义和前面介绍的 showSoftInput()hideSoftInputFromWindow() 一致,所以没有特殊要求,直接传递 0 就好了。

toggleSoftInput() 方法不要求传递一个 View 或者 windowToken,所以它并没有 showSoftInput() 中的一些限制,但是依然还有需要在布局绘制完成之后调用才会有效果。

虽然这个方法,限制很少,但是我们基本上不会使用它。主要原因在于,它是一个开关的方法,会根据当前的状态做相反的操作。这就导致很多时候,我们在代码中,无法直接根据 InputMethodManager 提供的方法判断当前软键盘的显示状态,这样也就无法确定调用它的时候的效果了。

三、源码分析

3.1 flag 的细节

前面的一些方法,都需要传递一个 flag 值,文档中描述的并不详细,我们就从源码的角度,来分析一下这些 flag 的含义。

先来看看 showSoftInput() 方法。

它最终会调用 mService.showSoftInput() 方法,最终的源码,就需要查看 InputMethodManagerService 中的代码了。而 showSoftInput() 方法,最终会调用 showCurrentInputLocked()

这个方法的代码很长,我们只关心和 flag 相关的代码。

可以看到,flag 会影响两个字段,mShowExplicitlyRequested 和 mShowForced,而 SHOW_FORCED 会更强势一点。

hideSoftInputFromWindow() 方法,最终也会调用 InputMethodManagerService 中的 hideCurrentInputLocked() 方法。

DEBUG == true 会输出的 Log 中,已经可以看到含义了。这里会根据显示和隐藏传递的两个 flag 来进行比对,也就是说,如果 flag 使用不正确,可能导致这里直接返回 false ,从而无法隐藏软键盘,这些细节对照代码就清晰了,就不在文章里屡这些细节了。

所以这就是为什么前面提到,如果没有特殊要求,直接传递 0 就好了,可以规避这个限制。

3.2 如何判断软键盘是否弹出

既然 toggleSoftInput() 可以根据当前软键盘的状态,进行不同的操作,那么肯定是有办法确定当前软键盘的状态的。

那我们继续追踪 toggleSoftInput() 的方法源码。

该方法,最终会调用到 InputMethodService 的 onToggleSoftInput() 方法。

在这个方法中,是根据 isInputViewShow() 方法来判定当前软键盘是否处于显示弹出的状态。但是我们并没有办法,直接和 InputMethodService 进行交互,我们也就没办法直接拿到当前键盘是否显示。

如果想要监听键盘的弹出和收起,可以使用 ViewTreeObserver.OnGlobalLayoutListener 这个监听,来监听布局的调整,从而判断出键盘的弹出和隐藏。这些细节有时间再聊。

四、KeyboardUtils

既然已经清楚了软键盘的收起和弹出的方法细节,那我们来写一个帮助类,来解决这个问题。让你们拿到就可用。

这里提供一下 Java 版和 Kotlin 版。

4.1 Java 版

public class KeyboardUtils {

    public static void showKeyboard(View view) {
InputMethodManager imm = (InputMethodManager) view.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
view.requestFocus();
imm.showSoftInput(view, 0);
}
} public static void hideKeyboard(View view){
InputMethodManager imm = (InputMethodManager) view.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(),0);
}
}
public static void toggleSoftInput(View view){
InputMethodManager imm = (InputMethodManager) view.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.toggleSoftInput(0,0);
}
}
}

4.2 Kotlin 版

object KeyboardktUtils{
fun showKeyboard(view: View) {
val imm = view.context
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (imm != null) {
view.requestFocus()
imm.showSoftInput(view, 0)
}
} fun hideKeyboard(view: View) {
val imm = view.context
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm?.hideSoftInputFromWindow(view.windowToken, 0)
} fun toggleSoftInput(view: View) {
val imm = view.context
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm?.toggleSoftInput(0, 0)
}
}

今天在承香墨影公众号的后台,回复『成长』。我会送你一些我整理的学习资料,包含:Android反编译、算法、设计模式、虚拟机、Linux、Kotlin、Python、爬虫、Web项目源码。

推荐阅读:

Android 软键盘的显示和隐藏,这样操作就对了的更多相关文章

  1. android 软键盘的显示与隐藏问题的研究

    在android中,常常会和输入法的软件键盘交互.在Manifest文件中,系统给activity的一个属性-windowSoftInputMode来控制输入法的显示方式. 该属性提供了Activit ...

  2. android软键盘的显示和隐藏

    显示: InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); imm.toggle ...

  3. EditText获取和失去焦点,软键盘的关闭,和软键盘的显示和隐藏的监听

    软键盘显示和隐藏的监听: 注: mReplayRelativeLayout是EditText的父布局 //监听软键盘是否显示或隐藏 mReplayRelativeLayout.getViewTreeO ...

  4. 日积月累:EditText软键盘的显示和隐藏

    在工作过程中,常常会遇见需要根据自己的需求,控制文本框的键盘显示和隐藏. 通过查阅Android文档,介绍可以通过在清单文件中<activity>元素中添加android:windowSo ...

  5. Android软键盘的隐藏显示、事件监听的代码

    把开发过程中重要的一些内容片段做个珍藏,如下资料是关于Android软键盘的隐藏显示.事件监听的内容,应该是对小伙伴们有所用途. public class ResizeLayout extends L ...

  6. Android之Android软键盘的隐藏显示研究

    转自:http://blog.csdn.net/lilu_leo/article/details/6587578 看了很多这类型的文章,这篇文章最有价值,解决了我的烦恼,必须转. Android是一个 ...

  7. Android手动控制软键盘的开启和关闭,判断软键盘是否显示;

    工具类,拿走就能用: import android.annotation.TargetApi; import android.app.Activity; import android.content. ...

  8. android软键盘弹出隐藏的监听

    通过网上搜索关于软键盘的隐藏弹出的监听,有几种方式,其中最有效的方式是在View的Onlayout()里面做文章 具体代码: 将布局视图自定义,重写onlayout()方法,然后在主Activity里 ...

  9. Android 软键盘弹出时把原来布局顶上去的解决方法

    键盘弹出时,会将布局底部的导航条顶上去. 解决办法: 在mainfest.xml中,在和导航栏相关的activity中加: <activity            android:name=& ...

随机推荐

  1. 自学 Python 3 最好的 入门 书籍 推荐(附 免费 在线阅读 下载链接)

    请大家根据自己的实际情况对号入座,挑选适合自己的 Python 入门书籍: 完全没有任何编程基础:01 号书 少量编程基础,不求全,只希望能以最快的速度入门:02 号书 少量编程基础,有一定的英文阅读 ...

  2. Servlet 笔记-读取表单数据

    Servlet 处理表单数据,这些数据会根据不同的情况使用不同的方法自动解析: getParameter():您可以调用 request.getParameter() 方法来获取表单参数的值. get ...

  3. ClassLoader 工作机制

    ClassLoader 采用上级委托接待机制加载 class JVM 平台提供三层 ClassLoader 1.Bootstrap ClassLoader:主要加载 JVM 自身工作需要的类 2.Ex ...

  4. react-native绑定优酷SDK播放视频-附效果和git源码

    ReactNative绑定优酷SDK需要用到两部分知识: 优酷本身的sdk绑定: RN与原生界面的交互: 效果: RN版本:0.49.3 代码更新日期:2017.10.26 下文也根据绑定优酷需要的两 ...

  5. Java基础总结--IO总结1

    1.IO流(数据流)主要应用概述数据来源:存储在设备里面* IO流用来处理设备间数据之间的传输* Java对数据的操作是通过流的方式* Java用于对流的操作的对象都在IO包* 流按照流向分为:输出流 ...

  6. 面试中常用排序算法实现(Java)

    当我们进行数据处理的时候,往往需要对数据进行查找操作,一个有序的数据集往往能够在高效的查找算法下快速得到结果.所以排序的效率就会显的十分重要,本篇我们将着重的介绍几个常见的排序算法,涉及如下内容: 排 ...

  7. xml入门视频

    XML 是可扩展标记语言(Extensible Markup Language)的缩写,其中的 标记(markup)是关键部分.您可以创建内容,然后使用限定标记标记它,从而使每个单词.短语或块成为可识 ...

  8. 版本12.2.0.1.0数据库,复制种子数据库快速创建租户数据库PDB

    实验测试:快速创建一个数据库PDB2: 实验环境:12.2.0.1.0版本数据库,dbca图形化安装,现有环境,CDB容器数据库ORCL,PDB可插拔数据库ABC   ---查询CDB名称,状态 SQ ...

  9. web前端工程师全套教程免费分享

    这是我自己早前听课时整理的前端全套知识点,适用于初学者,也可以适用于中级的程序员,你们可以下载下来.我自认为还是比较系统全面的,可以抵得上市场上90%的学习资料.讨厌那些随便乱写的资料还有拿出来卖钱的 ...

  10. 蒙特卡罗算法(Monte Carlo method)

    蒙特卡罗方法概述 蒙特卡罗方法又称统计模拟法.随机抽样技术,是一种随机模拟方法,以概率和统计理论方法为基础的一种计算方法,是使用随机数(或更常见的伪随机数)来解决很多计算问题的方法.将所求解的问题同一 ...