Android 软键盘的显示和隐藏

news/2024/12/2 12:42:31/
Android是一个针对触摸屏专门设计的操作系统,当点击编辑框,系统自动为用户弹出软键盘,以便用户进行输入。
    那么,弹出软键盘后必然会造成原有布局高度的减少,那么系统应该如何来处理布局的减少?我们能否在应用程序中进行自定义的控制?这些是本文要讨论的重点。


    一、软键盘显示的原理
    软件盘的本质是什么?软键盘其实是一个Dialog!
    InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参数(如Gravity)进行了设置,使之能够在底部或者全屏显示。当我们点击输入框时,系统对活动主窗口进行调整,从而为输入法腾出相应的空间,然后将该Dialog显示在底部,或者全屏显示。
    二、活动主窗口调整
    android定义了一个属性,名字为windowSoftInputMode, 用它可以让程序可以控制活动主窗口调整的方式。我们可以在AndroidManifet.xml中对Activity进行设置。如:android:windowSoftInputMode="stateUnchanged|adjustPan"
    该属性可选的值有两部分,一部分为软键盘的状态控制,另一部分是活动主窗口的调整。前一部分本文不做讨论,请读者自行查阅android文档。
    模式一,压缩模式
    windowSoftInputMode的值如果设置为adjustResize,那么该Activity主窗口总是被调整大小以便留出软键盘的空间。
我们通过一段代码来测试一下,当我们设置了该属性后,弹出输入法时,系统做了什么。
    重写Layout布局:

 1. public class ResizeLayout extends LinearLayout{ 
2.     private static int count = 0; 
3.      
4.     public ResizeLayout(Context context, AttributeSet attrs) { 
5.         super(context, attrs); 
6.     } 
7.      
8.     @Override 
9.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {     
10.         super.onSizeChanged(w, h, oldw, oldh); 
11.          
12.         Log.e("onSizeChanged " + count++, "=>onResize called! w="+w + ",h="+h+",oldw="+oldw+",oldh="+oldh); 
13.     } 
14.      
15.     @Override 
16.     protected void onLayout(boolean changed, int l, int t, int r, int b) { 
17.         super.onLayout(changed, l, t, r, b); 
18.         Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b="+b); 
19.     } 
20.      
21.     @Override 
22.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
23.         super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
24.          
25.         Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec); 
26.     } 
我们的布局设置为:
1. <com.winuxxan.inputMethodTest.ResizeLayout  
2.     xmlns:android="http://schemas.android.com/apk/res/android" 
3.     android:id="@+id/root_layout" 
4.     android:layout_width="fill_parent" 
5.     android:layout_height="fill_parent" 
6.     android:orientation="vertical" 
7.     > 
8.      
9.     <EditText 
10.         android:layout_width="fill_parent"  
11.         android:layout_height="wrap_content"  
12.     /> 
13.    
14.     <LinearLayout 
15.             android:id="@+id/bottom_layout" 
16.             android:layout_width="fill_parent"  
17.             android:layout_height="fill_parent"  
18.             android:orientation="vertical" 
19.             android:gravity="bottom">s 
20.     
21.     <TextView   
22.         android:layout_width="fill_parent"  
23.         android:layout_height="wrap_content"  
24.         android:text="@string/hello" 
25.         android:background="#77777777" 
26.       /> 
27.    </LinearLayout> 
28. </com.winuxxan.inputMethodTest.ResizeLayout> 


    AndroidManifest.xml的Activity设置属性:android:windowSoftInputMode = "adjustResize"
    运行程序,点击文本框,查看调试信息:
    E/onMeasure 6(7960): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec = 1073742024
    E/onMeasure 7(7960): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec = 1073742025
    E/onSizeChanged 8(7960): =>onSizeChanged called! w=320,h=201,oldw=320,oldh=377
    E/onLayout 9(7960): =>OnLayout called! l=0, t=0,r=320,b=201
    从调试结果我们可以看出,当我们点击文本框后,根布局调用了onMeasure,onSizeChanged和onLayout。
    实际上,当设置为adjustResize后,软键盘弹出时,要对主窗口布局重新进行measure和layout,而在layout时,发现窗口的大小发生的变化,因此调用了onSizeChanged。
    从下图的运行结果我们也可以看出,原本在下方的TextView被顶到了输入法的上方。


 模式二,平移模式
    windowSoftInputMode的值如果设置为adjustPan,那么该Activity主窗口并不调整屏幕的大小以便留出软键盘的空间。相反,当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分。这个通常是不期望比调整大小,因为用户可能关闭软键盘以便获得与被覆盖内容的交互操作。
    上面的例子中,我们将AndroidManifest.xml的属性进行更改:android: windowSoftInputMode = "adjustPan"

    重新运行,并点击文本框,查看调试信息:
    E/onMeasure 6(8378): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec=1073742200
    E/onMeasure 7(8378): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec=1073742201
    E/onLayout 8(8378): =>OnLayout called! l=0, t=0,r=320,b=377
    我们看到:系统也重新进行了measrue和layout,但是我们发现,layout过程中onSizeChanged并没有调用,这说明输入法弹出前后并没有改变原有布局的大小。
    从下图的运行结果我们可以看到,下方的TextView并没有被顶到输入法上方。


    

    事实上,当输入框不会被遮挡时,该模式没有对布局进行调整,然而当输入框将要被遮挡时,窗口就会进行平移。也就是说,该模式始终是保持输入框为可见。如下图,整个窗口,包括标题栏均被上移,以保证文本框可见。


  模式三 自动模式
    当属性windowSoftInputMode被设置为adjustUspecified时,它不被指定是否该Activity主窗口调整大小以便留出软键盘的空间,或是否窗口上的内容得到屏幕上当前的焦点是可见的。系统将自动选择这些模式中一种主要依赖于是否窗口的内容有任何布局视图能够滚动他们的内容。如果有这样的一个视图,这个窗口将调整大小,这样的假设可以使滚动窗口的内容在一个较小的区域中可见的。这个是主窗口默认的行为设置。
    也就是说,系统自动决定是采用平移模式还是压缩模式,决定因素在于内容是否可以滚动。


三、侦听软键盘的显示隐藏
    有时候,借助系统本身的机制来实现主窗口的调整并非我们想要的结果,我们可能希望在软键盘显示隐藏的时候,手动的对布局进行修改,以便使软键盘弹出时更加美观。这时就需要对软键盘的显示隐藏进行侦听。
    直接对软键盘的显示隐藏侦听的方法本人没有找到,如果哪位找到的方法请务必告诉本人一声。还有本方法针对压缩模式,平移模式不一定有效。
    我们可以借助软键盘显示和隐藏时,对主窗口进行了重新布局这个特性来进行侦听。如果我们设置的模式为压缩模式,那么我们可以对布局的onSizeChanged函数进行跟踪,如果为平移模式,那么该函数可能不会被调用。
    我们可以重写根布局,因为根布局的高度一般情况下是不发生变化的。

    假设跟布局为线性布局,模式为压缩模式,我们写一个例子,当输入法弹出时隐藏某个view,输入法隐藏时显示某个view。


   1. public class ResizeLayout extends LinearLayout{  
2.     private OnResizeListener mListener; 
3.      
4.     public interface OnResizeListener { 
5.         void OnResize(int w, int h, int oldw, int oldh); 
6.     } 
7.      
8.     public void setOnResizeListener(OnResizeListener l) { 
9.         mListener = l; 
10.     } 
11.      
12.     public ResizeLayout(Context context, AttributeSet attrs) { 
13.         super(context, attrs); 
14.     } 
15.      
16.     @Override 
17.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {     
18.         super.onSizeChanged(w, h, oldw, oldh); 
19.          
20.         if (mListener != null) { 
21.             mListener.OnResize(w, h, oldw, oldh); 
22.         } 
23.     } 
24. } 
    在我们的Activity中,通过如下方法调用:


  

   1. public class InputMethodTestActivity extends Activity { 
2.     private static final int BIGGER = 1; 
3.     private static final int SMALLER = 2; 
4.     private static final int MSG_RESIZE = 1; 
5.      
6.     private static final int HEIGHT_THREADHOLD = 30; 
7.      
8.     class InputHandler extends Handler { 
9.         @Override 
10.         public void handleMessage(Message msg) { 
11.             switch (msg.what) { 
12.             case MSG_RESIZE: { 
13.                 if (msg.arg1 == BIGGER) { 
14.                     findViewById(R.id.bottom_layout).setVisibility(View.VISIBLE); 
15.                 } else { 
16.                     findViewById(R.id.bottom_layout).setVisibility(View.GONE); 
17.                 } 
18.             } 
19.                 break; 
20.  
21.             default: 
22.                 break; 
23.             } 
24.             super.handleMessage(msg); 
25.         } 
26.     } 
27.      
28.     private InputHandler mHandler = new InputHandler(); 
29.      
30.     /** Called when the activity is first created. */ 
31.     @Override 
32.     public void onCreate(Bundle savedInstanceState) { 
33.         super.onCreate(savedInstanceState); 
34.         setContentView(R.layout.main); 
35.          
36.         ResizeLayout layout = (ResizeLayout) findViewById(R.id.root_layout); 
37.         layout.setOnResizeListener(new ResizeLayout.OnResizeListener() { 
38.              
39.             public void OnResize(int w, int h, int oldw, int oldh) { 
40.                 int change = BIGGER; 
41.                 if (h < oldh) { 
42.                     change = SMALLER; 
43.                 } 
44.                                  
45.                 Message msg = new Message(); 
46.                 msg.what = 1; 
47.                 msg.arg1 = change; 
48.                 mHandler.sendMessage(msg); 
49.             } 
50.         }); 
51.     } 
52. } 


这里特别需要注意的是,不能直接在OnResizeListener中对要改变的View进行更改,因为OnSizeChanged函数实际上是运行在View的layout方法中,如果直接在onSizeChange中改变view的显示属性,那么很可能需要重新调用layout方法才能显示正确。然而我们的方法又是在layout中调用的,因此会出现错误。因此我们在例子中采用了Handler的方法。




1.使用adjustResize属性是 如果界面中没有滚动条 需要添加一个滚动条scrollview包裹所有内容,保证resize后 能滚动显示显示不下的内容

2.全屏fullscreen模式时 adjustResize属性失效,属于是一个bug,只能使用adjustPan 来设置焦点,比较悲剧
3.全屏fullscreen模式时 webview adjustPan 偶尔也会失效~~~~~~~~,悲剧一米

有的还使用这俩接口来取得软键盘的,总之原理是一样的。在adjustSize时,布局会重新计算。但是在全屏时无效。
     resize = (ResizeLayout) findViewById(R.id.input);
ViewTreeObserver vto =  resize.getViewTreeObserver();
//        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//           
//            @Override
//            public void onGlobalLayout() {
//                // TODO Auto-generated method stub
//                  int height = resize.getMeasuredHeight();
//                int width = resize.getMeasuredWidth();
//                Log.e("width * height", width +"*"+height);
//                //取消监听
resize.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//            }
//        });
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// TODO Auto-generated method stub
int height = resize.getMeasuredHeight();
int width = resize.getMeasuredWidth();
Log.e("width * height", width +"*"+height);
return true;
}
});


那么此时怎么办呢,网上很多都提到了这个BUG,但是没有解决方法,我自己研究觉得这个方法还行,可以参考我的另一片博客

解决Android编辑框在全屏模式下无法检测布局变化的问题



备忘

      强制设置为非全屏(其实就是隐藏了软键盘上方的输入框)
               mEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);

      设置右下角的按钮为完成

                mEditText.setImeOptions(EditorInfo.IME_ACTION_DONE);

         注意:

           1.本方法对有的输入法无效,如搜狗 

            2.  有的是需要设置编辑框为单行 

           mEditText.setSingleLine(true);设置编辑框为单行,右下角的按钮如是回车换行,此时是无效的

         

      隐藏软键盘

          InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
          imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);

      显示软键盘

        ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).showSoftInput(mEditText, 0);


EditText 属性

xml文件中EditText标签有一个属性android:editable="false"和android:numeric="integer"

android:numeric="integer"表示只允许输入数字,此属性可以限制用户只能输入数字内容。

android:editable表示是否可以输入内容TRUE表示可以输入,false表示不允许输入内容;

当为android:editable="false"时,点击输入框,虚拟键盘是显示不出来的,不过当设置了 android:editable=""属性时,不管是false还是true,在其后加入android:numeric="integer"属性时,是可以输入数字内容了;这里没搞明白是怎么回事,也许是numeric把前面的属性覆盖掉了。

当android:editable="false"时,在java类里如果再规定EditText.setEnabled(true)时,虚拟键盘还是不会显示的。


参考:http://blog.csdn.net/aomandeshangxiao/article/details/6587578

http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3628157

http://www.ppmy.cn/news/638975.html

相关文章

关于软键盘

1、禁止软键盘吧控件顶上去 在配置文件中注册的类加入 android:windowSoftInputMode"adjustPan|stateHidden" 或者 在setContentView方法前加入getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); 2、禁止自动弹出软键盘 在代码中…

Android软键盘使用

imeOptions属性 ​ 点击编辑框&#xff0c;软键盘右下角有不同的图标 常用属性说明actionUnspecified切换到下一项按钮(默认)actionNone同上actionGoGO按钮actionSearch搜索按钮actionSend发送按钮actionNext下一个按钮actionDone完成按钮 使用 1 //singleLine和inputType…

软键盘

Android软键盘研究&#xff1a; 1.利用按钮弹出软键盘。 2.程序启动后直接弹出软键盘。1.由Button的OnClick事件调出软键盘和隐藏软键盘。具体代码如下&#xff1a;public class test extends Activity implements OnClickListener{Button bt;Override public void onCreate(Bu…

搜狗软键盘

html密码表单域&#xff0c;搜狗浏览器下自动加上搜狗软键盘 转载于:https://www.cnblogs.com/qiuh/archive/2013/06/03/3114933.html

andriod软键盘搜索

软键盘搜索 目标输入框的布局文件上加上一个属性android:imeOptions"actionSearch" android:imeOptions属性android手机软键盘设置时的一个重要的属性&#xff0c;主要的功能是设置软键盘上输入内容后接下来要做什么样的事情。该属性通常有7种值&#xff0c;分别是…

苹果手机ios设备管理软件iMazing 2.17.6官方版下载及常见问题解决

苹果手机ios设备管理软件iMazing 2.17.6官方版下载(ios设备管理软件)是一款管理苹果设备的软件&#xff0c; Windows / macos 系统上的一款帮助用户管理 IOS 手机的应用程序&#xff0c;软件功能非常强大&#xff0c;界面简洁明晰、操作方便快捷&#xff0c;设计得非常人性化。…

Apache seatunnel集群部署

跳转到安装目录 cd /opt/soft/seatunnel 1.设置环境变量 export SEATUNNEL_HOME/opt/soft/seatunnel export PATH$PATH:$SEATUNNEL_HOME/bin 启动服务端 ./bin/seatunnel-cluster.sh -d 启动客户端 ./bin/seatunnel.sh --config ./config/kafka2gbase_udf.conf 这样就启…

做为程序员 给大家推荐不很错的淘宝店

为我们程序员提供没有活 可以接活的一个形式 他的淘宝店 在找技术人员 只要你会程序 java 前端 后端 都可以实现你的奥迪梦 他的淘宝名称叫做 猿来很快乐 具体怎么进入技术人员接单工作群 还需要咨询他们淘宝店的客服 有一句话说的好 再苦再累也不提&#xff0c;年底默默提…