转自:http://blog.csdn.net/lilu_leo/article/details/6587578

看了很多这类型的文章,这篇文章最有价值,解决了我的烦恼,必须转。

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

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

  1. 1. public class ResizeLayout extends LinearLayout{
  2. 2.     private static int count = 0;
  3. 3.
  4. 4.     public ResizeLayout(Context context, AttributeSet attrs) {
  5. 5.         super(context, attrs);
  6. 6.     }
  7. 7.
  8. 8.     @Override
  9. 9.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  10. 10.         super.onSizeChanged(w, h, oldw, oldh);
  11. 11.
  12. 12.         Log.e("onSizeChanged " + count++, "=>onResize called! w="+w + ",h="+h+",oldw="+oldw+",oldh="+oldh);
  13. 13.     }
  14. 14.
  15. 15.     @Override
  16. 16.     protected void onLayout(boolean changed, int l, int t, int r, int b) {
  17. 17.         super.onLayout(changed, l, t, r, b);
  18. 18.         Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b="+b);
  19. 19.     }
  20. 20.
  21. 21.     @Override
  22. 22.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  23. 23.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  24. 24.
  25. 25.         Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec);
  26. 26.     }

我们的布局设置为:

  1. 1. <com.winuxxan.inputMethodTest.ResizeLayout
  2. 2.     xmlns:android="http://schemas.android.com/apk/res/android"
  3. 3.     android:id="@+id/root_layout"
  4. 4.     android:layout_width="fill_parent"
  5. 5.     android:layout_height="fill_parent"
  6. 6.     android:orientation="vertical"
  7. 7.     >
  8. 8.
  9. 9.     <EditText
  10. 10.         android:layout_width="fill_parent"
  11. 11.         android:layout_height="wrap_content"
  12. 12.     />
  13. 13.
  14. 14.     <LinearLayout
  15. 15.             android:id="@+id/bottom_layout"
  16. 16.             android:layout_width="fill_parent"
  17. 17.             android:layout_height="fill_parent"
  18. 18.             android:orientation="vertical"
  19. 19.             android:gravity="bottom">s
  20. 20.
  21. 21.     <TextView
  22. 22.         android:layout_width="fill_parent"
  23. 23.         android:layout_height="wrap_content"
  24. 24.         android:text="@string/hello"
  25. 25.         android:background="#77777777"
  26. 26.       />
  27. 27.    </LinearLayout>
  28. 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. 1. public class ResizeLayout extends LinearLayout{
  2. 2.     private OnResizeListener mListener;
  3. 3.
  4. 4.     public interface OnResizeListener {
  5. 5.         void OnResize(int w, int h, int oldw, int oldh);
  6. 6.     }
  7. 7.
  8. 8.     public void setOnResizeListener(OnResizeListener l) {
  9. 9.         mListener = l;
  10. 10.     }
  11. 11.
  12. 12.     public ResizeLayout(Context context, AttributeSet attrs) {
  13. 13.         super(context, attrs);
  14. 14.     }
  15. 15.
  16. 16.     @Override
  17. 17.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  18. 18.         super.onSizeChanged(w, h, oldw, oldh);
  19. 19.
  20. 20.         if (mListener != null) {
  21. 21.             mListener.OnResize(w, h, oldw, oldh);
  22. 22.         }
  23. 23.     }
  24. 24. }

在我们的Activity中,通过如下方法调用:

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

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

Android之Android软键盘的隐藏显示研究的更多相关文章

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

    http://winuxxan.blog.51cto.com/2779763/522810 全局推: android:windowSoftInputMode="adjustResize&qu ...

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

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

  3. android:windowSoftInputMode属性;界面关闭后软键盘不隐藏的解决方法;

    stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置 stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activit ...

  4. android 弹出软键盘将底部视图顶起问题

    今天要做一个搜索功能,搜索界面采用AutoCompleteTextView做搜索条,然后下面用listview来显示搜索结果,而我的主界面是在底 部用tab做了一个主界面导航,其中有一个搜索按钮,因为 ...

  5. 彻底搞定Android开发中软键盘的常见问题

    软键盘显示的原理 软件盘的本质是什么?软键盘其实是一个Dialog.        InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参 ...

  6. 调用Android中的软键盘

    我们在Android提供的EditText中单击的时候,会自动的弹 出软键盘,其实对于软键盘的控制我们可以通过InputMethodManager这个类来实现.我们需要控制软键盘的方式就是两种一个是像 ...

  7. Android 监听软键盘按键的三种方式

    前言: 我们在Android手机上面有时候会遇到监听手机软键盘按键的时候,例如:我们在浏览器输入url完毕后可以点击软键盘右下角的“Go”按键加载url页面:在点击搜索框的时候,点击右下角的searc ...

  8. Android中检测软键盘的弹出和关闭

    Android系统并没有提供明显的API来监听软键盘的弹出和关闭,但是在某些情况下我们还是有办法来检测软键盘的弹出和关闭. 从StackOverflow找到了一个不错的方法.但是这种只适用于在mani ...

  9. Android 监听软键盘搜索键

    现在很多的Android应用都有了数据搜索功能,在以往的设计上,会使用搜索框+搜索按钮来实现搜索功能: 现在呢,越来越流行的是,去除搜索按钮,直接监听软键盘搜索键,当用户输入完搜索关键字后,直接点击软 ...

随机推荐

  1. day1作业:登录接口

    作业一:编写登陆接口 1.输入用户名和密码 2.认证成功后显示欢迎信息 3.输错三次后锁定 思路: (1)用户输入用户名: (2)去锁定文件中验证用户名是否锁定: (3)去当前用户验证用户是否存在: ...

  2. 【LOJ】#2306. 「NOI2017」蔬菜

    题解 从后往前递推 如果我们知道了第i天的最优方案和第i天选择的蔬菜,加入第i天选择的蔬菜数量为S,我们只需要减去最小的S - (i - 1) * M 个蔬菜即可 所以我们只要求出最后一天的蔬菜选择 ...

  3. Linux 的文件权限与目录配置

    用户和用户组 文件所有者 (owner) 用户组概念 (group) 其他人概念 (others) Linux文件权限概念 1. Linux文件属性 要了解Linux文件属性,那么有个重要的命令必须提 ...

  4. 【原创】MySQL Replay线上流量压测工具

    一. 背景 去年做过一次mysql trace 重放的测试,由于performance schema本身采集样本的长度等限制,实际回放的成功率比较低. 最近找到一款开源的工具,基于TCPCopy实现了 ...

  5. Java 中线程安全问题

    不好意思,一个国庆假期给我放的都不知道东西南北了,放松,很放松,差一点就弃更了,感谢那些催更的小伙伴们! 虽然没有更新,但是日常的学习还是有的,以后我尽量给大家分享一些通用知识,非技术. 但是本期还是 ...

  6. String 的常用操作

    String 类,我可以不负责的说在 Java 中这个类应该是使用最频繁的类了.然而关于它的常用的操作,我却不甚了解.整理这个东西很反人性的,这些方法,你看到他的时候感觉很简单,但是真正用的时候还是不 ...

  7. python 发送邮件(收到的邮件要有发送方才能回复)

    Python使用SMTP(简单邮件传输协议)发送邮件 普通文本邮件 普通文本邮件发送的实现,关键是要将MIMEText中_subtype设置为plain ## -*- coding: UTF-8 -* ...

  8. [ 转载 ] Centos 安装mysql后启动失败 出现 ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’

    MySQL Daemon failed to start Mysql出问题一定要学会查看log https://blog.csdn.net/shuai825644975/article/details ...

  9. 关于PyCharm database查看db.sqlites文件无内容的一种可能解决方法

    初学python,学到了scrapy爬虫数据入库,在网上跟着一个视频课进行学习,但是碰到了如下问题: 这里新建了数据库文件之后,将这个.sqlite文件拖动到Database里面,然后出现了上图描述问 ...

  10. [BZOJ4592][SHOI2015]脑洞治疗仪(线段树)

    线段树基础操作题,唯一需要思考下的是将区间的前k个0覆盖为1. 线段树上二分,先递归到左子树覆盖,回溯时返回还剩多少个0未被覆盖,在根据这个信息递归到右子树.注意特判k=0的情况. 要维护的信息有:区 ...