使用微信的朋友圈会发现,点击某一条评论后输入框会弹出来,然后所点击的那一项会自动地滚动到输入框上方的位置,这样如果开始所点击的评论在屏幕很下方的话,就不会被输入框遮住,虽然微信这一点在我的MX2频繁点几次后滚动的位置就完全错误了,但据说在有些机型上效果还不错,还有其他地方可能会有类似的需求,比如登录时软键盘可能会把登录按钮遮住。
要实现这个功能需要注意的地方主要有两点:
- 什么时候进行滚动操作,以及有可能还需要在输入框消失时回滚回去。
- 输入框弹出后所点击的项要滚动到输入框上方,这就需要我们计算要滚动的距离。
针对第一点,评论框出现在软键盘的上方,一般情况下软键盘出来后评论框的位置会移动,也就是会出现Layout操作,所以可以在Layout时计算滚动距离,时机就是:
view.getViewTreeObserver().addOnGlobalLayoutListener
评论框Layout时的回调,在这里计算需要滚动的距离。
接下来就是滚动距离的计算。
滚动距离=所点击的项底部的Y坐标 - 软键盘弹出后输入框顶部的Y坐标
所以只要知道这两个坐标就可以知道需要滚动的距离,获得坐标以很简单,通过getGlobalVisibleRect就可以了,当然还有其他方法,但由于是计算的差值,保证两次计算坐标时用同一个就可以了。获得坐标后直接smoothScrollBy。
原理就是这么简单,不过要实现起来,细节问题搞得人恶心。
比如说输入框初始的可见性可能是GONE,也可能是Visible,如果是GONE,那么软键盘弹出时可能会有两个过程,1.从GONE到Visible会layout一次,2.软键盘弹出又layout一次,隐藏时一样。界面刚显示时也会layout,所以这就需要判断在onGlobalLayout时是否需要过滤事件。
在MX2上实验,smoothScrollBy有两个参数,第二个是duration,如果duration过小,可能你传入的distance是600,系统却可能只会滚动500。
有时可能也需要在输入框的onFocusChange中滚动。
如果到了列表底部,计算出的距离可能和实际滚动的距离也不一样,这种情况也可以用setSelectionFromTop的方法让所点击的Item在屏幕最上方,当然也可以再计算偏移,总之异常繁琐。
如果是像登录这种情况,UI简单的,要加个ScrollView,也比较好处理,软键盘弹出时直接滚动到底部,隐藏时滚动到顶部。
总之,要实现自动滚动,首先就要有一个控件随着软键盘的弹出消失而移动位置,软键盘弹出后出现在软键盘的上方,哪怕它看不见只是作为一个anchor。
其次,需要计算滚动距离,看情况有所不同,也是最麻烦的,可能需要知道输入框的状态是隐藏,显示在屏幕底部而软键盘没出来,还是软键盘出来了。不过在输入框初始隐藏在布局最下方的情况下,这三种情况输入框的坐标也只有3个值,也可以根据这个值判断输入框的状态,当然不排除有些输入法可以调整软键盘高度而用户又很配合地在输入时调整。
反正如果有这需求就恶心死吧。在项目三个地方实现,大致方法都是一样的,细节都有差异。