遇到这么个需求,先看图:

其实是一个软件的登录界面,初始是第一个图的样子,当软键盘弹出后变为第二个图的样子,因为登录界面有用户名、密码、登录按钮,不这样的话软键盘弹出后会遮住登录按钮(其实之前的实现放到了scrollview里面,监听软键盘弹出后滚动到底部,软键盘隐藏后滚动到顶部,也是可以的)。
最简单的方法就是多加几个冗余的view,根据软键盘的状态隐藏不需要的view,显示需要的view,但这样感觉太挫了,然后就想起了前两年研究的relativelayout布局,relativelayout中子控件的布局都是相对位置,只需要在软键盘弹出隐藏时改变应用的位置规则就行了。
先来看一下布局文件
<relativelayoutxmlns:android="http://schemas.android.com/apk/res/android"
软键盘的弹出隐藏用ongloballayoutlistener监听实现,对activity应用android:windowsoftinputmode="statehidden|adjustresize",这样开始时软键盘不显示,当软键盘弹出时布局被resize。
接下来是代码,所有的代码都在这里了
当activity启动时也会进行layout,此时用rootbottom记录了初始时最外层布局底部的位置,此后当软键盘弹出时,布局被压缩,再次获取同一个view底部的位置,如果比rootbottom小说明软键盘弹出了,如果大于或等于rootbottom说明软键盘隐藏了。
所有的代码都在上面,也有详细注释,有两点需要注意一下:
activity启动时会进行layout,此时会调用ongloballayout,而且一般会调用两次,这样第二次时会进入else语句,要注意过滤
软键盘弹出或隐藏时进入ongloballayout,此时根据需要缩放logo的大小,并改变logo和label的位置,这些操作会引起再次ongloballayout,需要将之后的ongloballayout过滤掉,不然就无限循环了。
可以看到上面代码中的过滤条件,以else语句中的为例,activity启动时会进入else,此时logo是水平居中状态,会跳过else里面的if语句,这样就处理掉了第一种情况。
当因为软键盘收起进入else时,logo已经因为if语句块变为了显示在左上角,所以会进入else中的if语句,重新改变logo为水平居中,由于修改了logo的大小和位置,会导致再次进入ongloballayout,仍是进入else,但此时已经设置logo为水平居中了,不会再次进入else中的if语句,这样通过一个条件判断就处理了上面提到的两点注意事项。
关于addrule
relativelayout中每一个子控件所应用的规则都是通过数组保存的,如下所示:
以某一规则的索引为下标,值就是规则对应的anchor,如果是相对于另一个子控件,值就是另一个子控件的id,如果是相对于父控件,值就是`true`,即-1,如果没有应用某一规则值就是0,可以看到,removerule就是把相应位置的值改为了0:
removerule是api 17才加的方法,为了在api 17前也能使用,可以使用它的等价方法,像上面的例子中的一样,使用addrule(verb, 0)。