Android 软键盘监听事件

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


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

  源码打印?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并无被顶到输入法上方。
    测试

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

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

    3、侦听软键盘的显示隐藏
    有时候,借助系统自己的机制来实现主窗口的调整并不是咱们想要的结果,咱们可能但愿在软键盘显示隐藏的时候,手动的对布局进行修改,以便使软键盘弹出时更加美观。这时就须要对软键盘的显示隐藏进行侦听。
    直接对软键盘的显示隐藏侦听的方法本人没有找到,若是哪位找到的方法请务必告诉本人一声。还有本方法针对压缩模式,平移模式不必定有效。
    咱们能够借助软键盘显示和隐藏时,对主窗口进行了从新布局这个特性来进行侦听。若是咱们设置的模式为压缩模式,那么咱们能够对布局的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的方法。code

相关文章
相关标签/搜索