Android全屏状态下弹出输入法adjustResize无效的修复方案及踩坑指南
Posted 苦逼程序员_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android全屏状态下弹出输入法adjustResize无效的修复方案及踩坑指南相关的知识,希望对你有一定的参考价值。
android全屏状态下弹出输入法adjustResize无效的修复方案及踩坑指南
输入框被输入法遮挡的问题相信很多人都遇见过,这次记录的是Activity在全屏FullScreen状态下windowSoftInputMode的adjustResize不生效问题。
下面是WindowManager类关于SOFT_INPUT_ADJUST_RESIZE的官方注释,在FullScreen状态下adjustResize flag会被忽略。
/** Adjustment option for @link #softInputMode: set to allow the
* window to be resized when an input
* method is shown, so that its contents are not covered by the input
* method. This can <em>not</em> be combined with
* @link #SOFT_INPUT_ADJUST_PAN; if
* neither of these are set, then the system will try to pick one or
* the other depending on the contents of the window. If the window's
* layout parameter flags include @link #FLAG_FULLSCREEN, this
* value for @link #softInputMode will be ignored; the window will
* not resize, but will stay fullscreen.
*/
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
直接Google搜索FullScreen AdjustResize失效的相关资料,可以看到很多相关资料都指向Google的bug issue 5497,并且有现成的解决方案,大概有3种:
- 在输入法弹出的时候改变rootView或decorView的高度,使view的高度 = 屏幕高度 - 输入法高度
- 在输入法弹出的时候改变rootView的底部padding边距(当输入框置于view的底部时),padding值跟输入法高度相等
- 在输入法弹出的时候使rootView垂直向上平移,平移高度跟输入法高度相等
以上3种方案选择哪一种需要看具体需求,如果需求需要View完整显示出来而且可以压缩显示时,选择1、2方案,否则选择方案3。
鸡汁的我,直接选择方案1,并且使用网上各路大神发出来的现成工具类AndroidBug5497Workaround,运行一遍,完美,直接把测试报过来的BUG关闭掉,文章到此结束。
。
。
。
太天真了,小米2S出问题,小米MIX3出问题,三星S8出问题,而且表现各不相同。。
三种机型出现的问题的表现
-
小米2S:弹出输入法时,如下图所示,内容区域高度缩小正常,但内容延伸到了屏幕外围,输入法与输入框之间出现一大段空白间距,间距基本上等于输入法的高度。
-
小米MIX3:弹出输入法时,如下图所示,内容区域高度缩小失败,部分内容延伸到屏幕外围,输入框部分区域被输入法遮盖,输入框光标刚好展示完,基本上跟AdjustPan的效果一致。
-
三星S8:输入法缩小收起时,输入框监听界面布局变化回调失效,输入框没有相应进行隐藏操作,并出现部分输入框显示在页面底端,部分延伸到屏幕外。
兼容性事故现场分析:
此处通过AS的布局分析神器Layout Inspector大量分析,得出以下结论,分析过程本篇略过。
-
小米2S的表现在部分5.0或以下机器中会出现,通过分析得到在输入法弹出后decorView有一个纵向向上的偏移,偏移距离大致等于输入法高度,这个偏移其实就是AdjustPan的逻辑。再查看rootView的高度,发现高度是我们设进去的 (屏幕总高度 - 输入法高度)。所以中间空白区域的出现原因就是系统在AdjustPan之后我们又再次设定了rootView高度,而rootView是默认布局在顶部的 (top to top of parent),导致rootView大部分内容显示在屏幕外,下方则是空白的decorView。
-
小米MIX3的表现,基本上就是标准的AdjustPan了,我们自己设定的高度失效,在页面弹出输入法的时候可以看到rootView有一瞬间的闪烁,看上去效果就像内容区域往上偏移后再恢复回来。经过分析,它的执行顺序跟小米2S相反,小米2S是先执行AdjustPan,再设置rootView的高度,此机型则是先设置高度,再执行AdjustPan,AdjustPan之后高度会被强行恢复回来,所以会看到弹出输入法时又一瞬间的闪烁。
-
三星S8的问题通过断点调试,发现原来输入框监听界面变化时的回调并没有被调用,但AndroidBug5497Workaroun类里面的监听则是正常,为什么旧的监听会被新加入的监听截断,这里没有详细分析,只要在AndroidBug5497Workaroun里面增加一个输入法隐藏的接口到外部页面即可解决这个问题。
问题1和问题2属于同一类型的问题,解决思路是使系统不执行AdjustPan逻辑。接下来就是各种试验,给Activity加上AdjustNothing,发现行不通;代码设置AdjustNothing,行不通;甚至还想过要不要根据系统版本来区别执行逻辑,如果是5.0以下则给输入框增加一点底部边距,后来也否了这个方案。
在多次试验后,发现如果输入框不显示,多个机型弹出输入法后,rootView的高度都是能正常缩减的,刚好布满输入法以上的显示区域。然后把输入框的显示逻辑慢慢拆减处理,最后问题定位到输入框的焦点上。如果在弹出输入法之前,焦点已经被输入框获得,并且输入法弹出后会遮盖输入框的焦点,部分机型会执行AdjustPan逻辑,至于执行后是什么表现,这个就要看机子的脾气了~
弹出输入法 -> AndroidBug5497Workaroun进行rootView高度缩减 -> 显示输入框 -> 给予输入框焦点
最后我把代码执行顺序进行调整,把输入框的焦点获取放到最后,问题解决。下面是正在使用的全屏输入法AdjustResize问题工具类,基于AndroidBug5497Workaroun类修改。需要注意的是,输入框的焦点必须放到InputShowListener回调内执行,即可修复部分机型的兼容性问题。
/**
* 用于修复全屏状态下adjustResize不生效的问题,当弹出输入法时重新设定内容view的高度,使输入框正常显示
*/
public class FullscreenInputWorkaround
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
private static final String TAG = "AndroidBug5497Workaroun";
public static FullscreenInputWorkaround assistActivity(Activity activity, View contentView, InputShowListener inputShowListener)
return new FullscreenInputWorkaround(activity, contentView, inputShowListener);
private Activity activity;
private View mChildOfContent;
private int usableHeightPrevious;
private ViewGroup.LayoutParams layoutParams;
private FullscreenInputWorkaround(Activity activity, View contentView, InputShowListener inputShowListener)
this.activity = activity;
this.inputShowListener = inputShowListener;
mChildOfContent = contentView;
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
public void onGlobalLayout()
possiblyResizeChildOfContent();
);
layoutParams = mChildOfContent.getLayoutParams();
private void possiblyResizeChildOfContent()
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious)
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4))
// keyboard probably just became visible
layoutParams.height = usableHeightSansKeyboard - heightDifference;
if (inputShowListener != null)
inputShowListener.inputShow(true);
else
// keyboard probably just became hidden
layoutParams.height = usableHeightSansKeyboard;
if (inputShowListener != null)
inputShowListener.inputShow(false);
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
private int computeUsableHeight()
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
//这个判断是为了解决19之后的版本在弹出软键盘时,键盘和推上去的布局(adjustResize)之间有黑色区域的问题
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT)
return (r.bottom - r.top) + statusBarHeight;
return (r.bottom - r.top);
private InputShowListener inputShowListener;
public interface InputShowListener
void inputShow(boolean show);
以上是关于Android全屏状态下弹出输入法adjustResize无效的修复方案及踩坑指南的主要内容,如果未能解决你的问题,请参考以下文章