Android项目:输入法软键盘显示/隐藏的监听和控制,InputMethodManager用法研究

简介:

    在项目开发中,用到编辑框的地方经常涉及到要监听或者控制软键盘的显示/隐藏状态。本以为这是很容易解决的一个小问题,没想到当初碰到这个问题才明白还得花点小心思才能整好。现将针对软键盘的显示/隐藏状态的监听/监控方法做一些总结,以备后用。


一、点击空白处隐藏软键盘

这是具有编辑框焦点的页面对输入法软键盘状态监听的一般需求和解决方法.

首先获得InputMethodManager:

1
        InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);


监听点击:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
     /**
      * 点击监听
      */
     @Override
     public  boolean  onTouchEvent(MotionEvent event)
     {
         onHideSoftInput(event);
         return  super .onTouchEvent(event);
     }
     
     /**
      * 点击空白处,关闭输入法软键盘
      */
     public  void  onHideSoftInput(MotionEvent event)
     {
         if  (event.getAction() == MotionEvent.ACTION_DOWN)
         {
             if  (getCurrentFocus() !=  null  && getCurrentFocus().getWindowToken() !=  null )
             {
                 manager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
             }
         }
     }



二、popwindow与输入法软键盘的结合

    先说下自己想实现的效果:点击顶部按钮,打开编辑菜单popwindow并自动弹出软键盘;再次点击顶部按钮,或者点击编辑菜单popwindow上面的底部按钮,关闭菜单并隐藏软键盘;菜单打开状态,点击返回键,若菜单已显示先关闭软键盘,再点击则关闭菜单。

    大致效果图如下:

wKioL1P1iuXSwcC_AAFS9YVSCmM556.gif




1.重写根布局,监听根布局高度变化

对于这个需求,简单的用上面第一点的方法是无效的。这里没法直接通过getCurrentFocus()方法判断页面是否获取焦点来控制,需要通过对popwindow的高度变化进行判断。同时也试过下面的方法,同样无效。


1
2
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
boolean  isOpen=imm.isActive(); //isOpen若返回true,则表示输入法打开


    popwindow的根布局我这里用的是RelativeLayout,RelativeLayout类可以通过重写onSizeChanged方法来监听布局大小变化。重写一个RelativeLayout类便实现了对popwindow的高度变化的监听了。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
  * 监听输入法软键盘显示状态的自定义RelativeLayout
 
  * @author zeng
 
  */
public  class  ResizeRelativeLayout  extends  RelativeLayout
{
     
     public  ResizeRelativeLayout(Context context, AttributeSet attrs)
     {
         super (context, attrs);
     }
     
     @Override
     protected  void  onSizeChanged( int  w,  int  h,  int  oldw,  int  oldh)
     {
         super .onSizeChanged(w, h, oldw, oldh);
         if  (mListener !=  null )
         {
             mListener.OnResizeRelative(w, h, oldw, oldh);
         }
     }
     
     // 监听接口
     private  OnResizeRelativeListener mListener;
     
     public  interface  OnResizeRelativeListener
     {
         void  OnResizeRelative( int  w,  int  h,  int  oldw,  int  oldh);
     }
     
     public  void  setOnResizeRelativeListener(OnResizeRelativeListener l)
     {
         mListener = l;
     }
     
}


2.配置布局文件,初始化UI等,代码如下:

初始化UI代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
     @Override
     protected  void  onCreate(Bundle savedInstanceState)
     {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         
         initUI();
     }
 
     private  void  initUI()
     {
         mBtn_open = findViewById(R.id.button1);
         mBtn_open.setOnClickListener( this );
         
         // 编辑窗口
         LayoutInflater inflater = getLayoutInflater();
         View menuLayout = inflater.inflate(R.layout.menu_window,  null );
         
         mEditMenuWindow =  new  PopupWindow(menuLayout, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,  true );
         mEditMenuWindow.setBackgroundDrawable(getResources().getDrawable(R.color.white));
         mEditMenuWindow.setTouchable( true );
         mEditMenuWindow.setFocusable( true );
         mEditMenuWindow.setAnimationStyle(R.style.MenuAnimation);
         mEditMenuWindow.setOutsideTouchable( false );
         mEditMenuWindow.update();
         //监听菜单消失
         mEditMenuWindow.setOnDismissListener( this );
         
         
         // 菜单控件
         mEt_menu = (EditText) menuLayout.findViewById(R.id.menu_edit);
         TextView btn_send = (TextView) menuLayout.findViewById(R.id.menu_send);
         btn_send.setOnClickListener( this );
         
         
         // 监听主布局大小变化,监控输入法软键盘状态
         listenerKeyBoardState(menuLayout);
     }


其中menu_window.xml文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version= "1.0"  encoding= "utf-8" ?>
<com.example.d_popwindow_inputkeyboard.ResizeRelativeLayout 
     xmlns:android= "http://schemas.android.com/apk/res/android"
     android:id= "@+id/menu_layout"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "vertical"
     android:padding= "8dp"  >
 
     <EditText
         android:id= "@+id/menu_edit"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_above= "@+id/menu_send"
         android:layout_marginBottom= "8dp"
         android:background= "#ffffffff"
         android:enabled= "true"
         android:focusable= "true"
         android:focusableInTouchMode= "true"
         android:gravity= "top|left"
         android:inputType= "none"
         android:padding= "8dp"
         android:textSize= "18sp"  >
 
         <requestFocus />
     </EditText>
 
     <TextView
         android:id= "@+id/menu_send"
         android:layout_width= "fill_parent"
         android:layout_height= "60dp"
         android:layout_alignParentBottom= "true"
         android:layout_gravity= "center"
         android:background= "#ff0f0f0f"
         android:gravity= "center"
         android:text= "发送"
         android:textColor= "#ffFF6004"
         android:textSize= "20sp"
         android:textStyle= "bold"  />
 
 
</com.example.d_popwindow_inputkeyboard.ResizeRelativeLayout>



3.打开或关闭编辑窗口,同时自动显示或隐藏输入法软键盘。

  此方法两者的控制顺序:先显示软键盘,再打开编辑窗口;先关闭编辑窗口,若软键盘当前已显示则再隐藏软键盘。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
     //点击顶部发送按钮,打开/关闭编辑窗口
     private  void  clickTopSend()
     {
         if  (mEditMenuWindow.isShowing())
         {
             //先关闭窗口再隐藏软键盘
             mEditMenuWindow.dismiss();
             
             // 隐藏输入法软键盘
//            hideKeyBoard();
         }
         else
         {
             // 窗口显示前显示输入法软键盘
             showKeyBoard();
             
             // 显示输入窗口
             mEditMenuWindow.showAsDropDown(mBtn_open,  0 0 );
         }
     }
 
     
     // 窗口显示前显示输入法软键盘
     private  void  showKeyBoard()
     {
         InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED,  0 ); // 调用此方法才能自动打开输入法软键盘
         mEditMenuWindow.setSoftInputMode(
                 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
         mEditMenuWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);  // 在显示popupwindow之后调用,否则输入法会在窗口底层
     }
     
     // 隐藏输入法软键盘
     private  void  hideKeyBoard()
     {
         InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         inputMgr.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS,  0 ); // 输入法软键盘打开时关闭,关闭时打开
         mEditMenuWindow.setSoftInputMode(
                 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
         mEditMenuWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);  // 在显示popupwindow之后调用,否则输入法会在窗口底层
 
         //此方法无效
         //        if(this.getCurrentFocus().getWindowToken() != null)
//        {
//            ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(),
//                    InputMethodManager.HIDE_NOT_ALWAYS);// 关闭输入法软键盘
//        }
         
     }
 
     
     //点击底部发送按钮,关闭编辑窗口
     private  void  closeButtomSend()
     {
         mEditMenuWindow.dismiss();        
     }
 
     //编辑窗口关闭时,隐藏输入法软键盘
     @Override
     public  void  onDismiss()
     {
         // 如果软键盘打开,隐藏输入法软键盘
         if (mIsKeyboardOpened)
         {
             hideKeyBoard();
         }
     }



4.判断软键盘的显示/隐藏状态。

  通过对编辑窗口的根布局高度变化来判断软键盘是否处于显示状态,在接口方法OnResizeRelative(int w, int h, int oldw, int oldh)里实现对高度变化的判断。这里主要判断以下几种高度变化场景:


(1)布局的当前高度小于上一次的高度,即h < oldh,因为布局被软键盘顶上去了,高度变小了。这种场景同样适用于点击按钮第一次打开窗口时的场景,虽然点击按钮时,肉眼看到的是窗口一下就充满了大半个屏幕,也就是当前h > oldh(oldh = 0)。但事实上是,第一次打开窗口时,窗口菜单首先是充满整个屏幕然后再根据软键盘高度自动缩进的。以下是点击【打开/发送】按钮后,根布局的高度变化日志:

wKioL1P1sEyT8t62AADfC5tJP2w121.jpg

可以看出,首次打开时,窗口高度先是1038,然后自动缩进成544,并非一打开便已计算好填充高度。


(2)还有一种特殊情况,就是三星输入法在软键盘初次打开时,输入字符后软键盘高度会产生变化,同时造成根布局高度变小;若再清除已输入的字符,此时软键盘高度变小,根布局高度变大。而这两种情况下,也就是h < oldh 或者 h > oldh时,软键盘都是处于显示状态。

wKiom1P1lZGRrTh0AAOPbpgoicg133.gif


针对这种情况,我的解决方法是记录下初次打开时根布局的初始高度值,由于h < oldh时在第(1)步已经进行了判断了,所以这里只要判断 h > oldh时的情况。而无论 h > oldh变化多大,只要h不超过初始高度值(且初始高度值不为0),那么便可认为当前软键盘仍是处于打开状态。


此方法运行后的日志如下:

wKiom1P1r4aBlKMUAAEvosZSRHE960.jpg

两种状况下,软键盘都是显示状态。


以下是监听高度变化判断的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
     /**
      * 监听主布局大小变化,监控输入法软键盘状态
      * @param menuLayout
      */
     private  void  listenerKeyBoardState(View menuLayout)
     {
         ResizeRelativeLayout mMenuLayout = (ResizeRelativeLayout) menuLayout.findViewById(R.id.menu_layout);
         mMenuLayout.setOnResizeRelativeListener( new  ResizeRelativeLayout.OnResizeRelativeListener()
         {
             @Override
             public  void  OnResizeRelative( int  w,  int  h,  int  oldw,  int  oldh)
             {
                 mIsKeyboardOpened =  false ;
                 Log.e( "菜单高度" "h = "  + h +  ",oldh = "  + oldh);
                 
                 //记录第一次打开输入法时的布局高度
                 if  (h < oldh &&  oldh >  0  && mMenuOpenedHeight ==  0 )
                 {
                     mMenuOpenedHeight = h;
                 }
                 
                 // 布局的高度小于之前的高度
                 if  (h < oldh )
                 {
                     mIsKeyboardOpened =  true ;
                 }
                 //或者输入法打开情况下, 输入字符后再清除(三星输入法软键盘在输入后,软键盘高度增加一行,清除输入后,高度变小,但是软键盘仍是打开状态)
                 else  if ((h <= mMenuOpenedHeight) && (mMenuOpenedHeight !=  0 ))
                 {
                     mIsKeyboardOpened =  true ;
                 }
 
                 Log.e( "是否打开" "软键盘  = "  + mIsKeyboardOpened);
             }
         });
     }


最后附上DEMO源码,详见附件。




三、InputMethodManager的一些相关方法(未有效使用过,仅作笔记)

1.调用显示系统默认的输入法

方法一、

1
2
3
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
 
imm.showSoftInput(m_receiverView,InputMethodManager.SHOW_FORCED);

m_receiverView(接受软键盘输入的视图(View)

InputMethodManager.SHOW_FORCED(提供当前操作的标记,SHOW_FORCED表示强制显示)


方法二、

1
2
InputMethodManager m=(InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
m.toggleSoftInput( 0 , InputMethodManager.HIDE_NOT_ALWAYS);


这个InputMethodManager类里面的toggleSoftInput方法的API中的解释是:

1
This method toggles the input method window display. If the input window is already displayed, it gets hidden. If not the input window will be displayed.

这个方法在界面上切换输入法的功能,如果输入法出于现实状态,就将他隐藏,如果处于隐藏状态,就显示输入法。


2.调用隐藏系统默认的输入法

1
((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(WidgetSearchActivity. this .getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);

(WidgetSearchActivity是当前的Activity)


3.获取输入法打开的状态

1
2
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
boolean  isOpen=imm.isActive();

isOpen若返回true,则表示输入法打开



四、SoftInputMode输入法软键盘模式相关说明


输入法软键盘模式选项:

public int softInputMode;


以下与输入法模式有关的各选项说明:


软输入区域是否可见。

1
public  static  final  int  SOFT_INPUT_MASK_STATE =  0x0f ;



未指定状态。

1
public  static  final  int  SOFT_INPUT_STATE_UNSPECIFIED =  0 ;


不要修改软输入法区域的状态。

1
public  static  final  int  SOFT_INPUT_STATE_UNCHANGED =  1 ;


隐藏输入法区域(当用户进入窗口时)。

1
public  static  final  int  SOFT_INPUT_STATE_HIDDEN =  2 ;


当窗口获得焦点时,隐藏输入法区域。

1
public  static  final  int  SOFT_INPUT_STATE_ALWAYS_HIDDEN =  3 ;


显示输入法区域(当用户进入窗口时)。

1
public  static  final  int  SOFT_INPUT_STATE_VISIBLE =  4 ;


当窗口获得焦点时,显示输入法区域。

1
public  static  final  int  SOFT_INPUT_STATE_ALWAYS_VISIBLE =  5 ;


窗口应当主动调整,以适应软输入窗口。

1
public  static  final  int  SOFT_INPUT_MASK_ADJUST =  0xf0 ;


未指定状态,系统将根据窗口内容尝试选择一个输入法样式。

1
public  static  final  int  SOFT_INPUT_ADJUST_UNSPECIFIED =  0x00 ;


当输入法显示时,允许窗口重新计算尺寸,使内容不被输入法所覆盖。

不可与SOFT_INPUT_ADJUSP_PAN混合使用;如果两个都没有设置,系统将根据窗口内容自动设置一个选项。

1
public  static  final  int  SOFT_INPUT_ADJUST_RESIZE =  0x10 ;


输入法显示时平移窗口。它不需要处理尺寸变化,框架能够移动窗口以确保输入焦点可见。

不可与SOFT_INPUT_ADJUST_RESIZE混合使用;如果两个都没有设置,系统将根据窗口内容自动设置一个选项。

1
public  static  final  int  SOFT_INPUT_ADJUST_PAN =  0x20 ;


当用户转至此窗口时,由系统自动设置,所以你不要设置它。

当窗口显示之后该标志自动清除。

1
public  static  final  int  SOFT_INPUT_IS_FORWARD_NAVIGATION =  0x100 ;








本文转自 glblong 51CTO博客,原文链接:http://blog.51cto.com/glblong/1543172,如需转载请自行联系原作者

目录
相关文章
|
4月前
|
Web App开发 移动开发 小程序
"项目中mpaas升级到10.2.3 适配Android 14之后 app中的H5以及小程序都访问不了,
"项目中mpaas升级到10.2.3 适配Android 14之后 app中的H5以及小程序都访问不了,显示“网络不给力,请稍后再试”,预发内网版本不能使用,线上版本可以正常使用,这个是什么原因啊,是某些参数没有配置吗,还是说是一些参数改错了?
56 2
|
2月前
|
Java Android开发 C++
安卓SO层开发 -- 第一个NDK项目
安卓SO层开发 -- 第一个NDK项目
16 0
|
2月前
|
Java Android开发 数据安全/隐私保护
安卓逆向 -- IDA基本用法
安卓逆向 -- IDA基本用法
41 0
|
3月前
|
IDE 开发工具 Android开发
Android Studio 下发布项目成APK文件
Android Studio 下发布项目成APK文件
117 1
|
3月前
|
Android开发
解决在Android Compose中点击空白处收回软键盘
解决在Android Compose中点击空白处收回软键盘
|
3月前
|
JavaScript Android开发
Cordova 打包 Vue 项目到Android打开白屏
Cordova 打包 Vue 项目到Android打开白屏
|
4月前
|
开发工具 Android开发 开发者
Android 项目编译 Gradle 配置说明
Android 项目编译 Gradle 配置说明
142 0
|
4月前
|
Web App开发 JSON Android开发
【Android App】实战项目之仿微信的视频通话(附源码和演示 超详细必看)
【Android App】实战项目之仿微信的视频通话(附源码和演示 超详细必看)
79 0
|
4月前
|
传感器 人工智能 自动驾驶
【Android App】物联网实战项目之自动驾驶的智能小车(附源码和演示 超详细)
【Android App】物联网实战项目之自动驾驶的智能小车(附源码和演示 超详细)
89 0
|
4月前
|
JSON 定位技术 Android开发
【Android App】实战项目之仿微信的附近的人(附源码和演示 超详细)
【Android App】实战项目之仿微信的附近的人(附源码和演示 超详细)
32 0