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,如需转载请自行联系原作者

目录
相关文章
|
2月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
122 1
|
2月前
|
前端开发 JavaScript 测试技术
android做中大型项目完美的架构模式是什么?是MVVM吗?如果不是,是什么?
android做中大型项目完美的架构模式是什么?是MVVM吗?如果不是,是什么?
127 2
|
2月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
206 0
安卓项目:app注册/登录界面设计
|
3月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
1月前
|
前端开发 JavaScript 测试技术
android做中大型项目完美的架构模式是什么?是MVVM吗?如果不是,是什么?
在 Android 开发中,选择合适的架构模式对于构建中大型项目至关重要。常见的架构模式有 MVVM、MVP、MVI、Clean Architecture 和 Flux/Redux。每种模式都有其优缺点和适用场景,例如 MVVM 适用于复杂 UI 状态和频繁更新,而 Clean Architecture 适合大型项目和多平台开发。选择合适的架构应考虑项目需求、团队熟悉度和可维护性。
55 6
|
2月前
|
前端开发 JavaScript 测试技术
Android适合构建中大型项目的架构模式全面对比
Android适合构建中大型项目的架构模式全面对比
52 2
|
3月前
|
Java Maven 开发工具
第一个安卓项目 | 中国象棋demo学习
本文是作者关于其第一个安卓项目——中国象棋demo的学习记录,展示了demo的运行结果、爬坑记录以及参考资料,包括解决Android Studio和maven相关问题的方法。
第一个安卓项目 | 中国象棋demo学习
|
2月前
|
编译器 Android开发
配置环境变量,使CMakeLists.txt可直接使用Android NDK工具链编译项目
配置环境变量,使CMakeLists.txt可直接使用Android NDK工具链编译项目
|
3月前
|
JavaScript 前端开发 Android开发
让Vite+Vue3项目在Android端离线打开(不需要起服务)
让Vite+Vue3项目在Android端离线打开(不需要起服务)
123 10
|
3月前
|
IDE 开发工具 Android开发
安卓与iOS开发对比:平台选择对项目成功的影响
【9月更文挑战第10天】在移动应用开发的世界中,选择正确的平台是至关重要的。本文将深入探讨安卓和iOS这两大主要移动操作系统的开发环境,通过比较它们的市场份额、开发工具、编程语言和用户群体等方面,为开发者提供一个清晰的指南。我们将分析这两个平台的优势和劣势,并讨论如何根据项目需求和目标受众来做出最佳选择。无论你是初学者还是有经验的开发者,这篇文章都将帮助你更好地理解每个平台的特性,并指导你做出明智的决策。