事件
应对软键盘遮挡的问题,可以处理四个不同的事件,事件和调用顺序如下:
- onSizeChanged(Activity需要是AdjustResize的)
- onLayout(不用监听onMeasure,回调的位置太多了)
- addOnLayoutChangeListener监听layout的回调事件
- getViewTreeObserver().addOnGlobalLayoutListener监听全局的layout事件
问题
- 在某些手机上,AdjustResize会导致键盘动画时闪一下黑底或者stack中下一个Activity的部分页面。非常尴尬。
- 这个listener是个不会被销毁的引用,会有内存泄露。特别是绝大部分都是内部类的情况下,简直了。
- 为了获得全局的可见空间,会调用getWindowVisibleDisplayFrame。这个官方文档中标明了,可能会有性能问题。
- 如果要更改某些View的大小,很可能会出现在layout中requestLayout的尴尬场景。
怎么办
如何防露底
实现对View的AdjustResize。AdjustResize实际上就是在getWindow().getDecorView().getWindowVisibleDisplayFrame()变化时,调整Activity的RootView的LayoutParams。
为了避免直接使用AdjustResize会导致键盘动画过程中的露底问题,需要避免使用AdjustResize,而使用AdjustPan实现对Activity中某个View调用。这样动画过程中漏出来的是Activity的RootView,不会露底。
如何监听变化
一般都是说用globalLayoutListener,未尝不可,但是一定要注意removeListener,否则一定会影响性能,同时有可能出现内存泄露。
可以使用开头提到的其他三种方法。如果不想继承,比较好用的是给RootView设置一个OnLayoutChangeListener。但是,如果不使用AdjustResize,listener的回调参数是无用的;对RootView调用下面这几个函数也是没用的:
- getDrawingRect
- getGlobalVisibleRect
- getLocalVisibleRect
也就是说,只能用getWindowVisibleDisplayFrame。
由于这个RootView是Activity维护的,当Activity可回收的时候,RootView一定可以回收,RootView持有的Listener,以及内部类间接持有的Activity都可以回收了,这样就不会有内存泄露了。
在大索尼上居然有兼容性问题,globalLayoutListener是ok的,目测是在做显示优化的时候如果大小不变就不进行layout,或者是硬件加速的鬼。
其他坑
- 不要更改RootView大小,否则会露底。
- 不要更改被addOnLayoutChangeListener的View的LayoutParams,因为setLayoutParams会调用requestLayout,进而会重新调用OnLayoutChangeListener。
- 在OnLayoutChangeListener内部尽量早做判断终止逻辑,这个回调的执行实在是太多了。