场景:今天遇到软键盘移动到其他位置之后,点击软键盘上的字母接收不到点击事件。
猜测一:系统默认把软键盘的触摸区域给写死了,只能在屏幕的底部。
验证猜测一:我把键盘输入部分的布局设至全屏,结果全屏的软键盘是可以接收得到点击事件的。所以软键盘的触摸区域不是写死不变的。而是根据软键盘的大小来设置的。根据平常的经验设置了触摸事件监听之后,在view的宽高范围内应该都是可以接收到触摸事件才对。但是现在即使是点击view所在的范围也不能接收到这个监听的事件。
然后我又把软键盘横向范围缩小至原来的一半,结果点击另外一半的地方出现了错误。(这个问题未深究未解决)
普及:在创建window的时候,系统会为每个window设置固定的触摸区域,这个区域是由Frame确定的。系统在获取键盘的Frame的时候只能获取到键盘按键区域的大小。所以把键盘移动之后,没有在这块设置的区域就接收不到点击事件。
Android 11的源码:WindowState.java类的3418行getTouchableRegion()方法中设置了触摸范围。
/** Get the touchable region in global coordinates. */
void getTouchableRegion(Region outRegion) {
final Rect frame =mWindowFrames.mFrame;
switch (mTouchableInsets) {
default:
case TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
break;
case TOUCHABLE_INSETS_CONTENT:
applyInsets(outRegion,frame,mGivenContentInsets);
break;
case TOUCHABLE_INSETS_VISIBLE:
applyInsets(outRegion,frame,mGivenVisibleInsets);
break;
case TOUCHABLE_INSETS_REGION: { //主要是这个case。这个case中做了触摸范围的设置。
outRegion.set(mGivenTouchableRegion);//这里设置了触摸的范围。如果要全屏接收那这个参数就得设置全屏的范围,键盘默认的不是全屏范围。
outRegion.translate(frame.left,frame.top);
break;
}
}
cropRegionToStackBoundsIfNeeded(outRegion);
subtractTouchExcludeRegionIfNeeded(outRegion);
}
修改WindowState.java类的3418行getTouchableRegion()内部设置触摸范围的方案:
outRegion.set(mGivenTouchableRegion); 把这个mGivenTouchableRegion 换成自己创建的全屏范围的Region对象即可。但是要进行包名判断,是键盘的包名才进行全屏的设置。
mAttrs.packageName这个可以获取到对应window的包名。
最终代码:frameworks/base/services/core/java/com/android/server/wm/WindowState.java 源码路径下的getTouchableRegion
/** Get the touchable region in global coordinates. */
void getTouchableRegion(Region outRegion) {
final Rect frame = mWindowFrames.mFrame;
switch (mTouchableInsets) {
default:
case TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
break;
case TOUCHABLE_INSETS_CONTENT:
applyInsets(outRegion, frame, mGivenContentInsets);
break;
case TOUCHABLE_INSETS_VISIBLE:
applyInsets(outRegion, frame, mGivenVisibleInsets);
break;
case TOUCHABLE_INSETS_REGION: {
if(inputFilter()){
outRegion.set(new Region(0, 0, frame.right, frame.bottom));//这是后添加的。解决键盘touchableRegion没有全屏的问题
}else{
outRegion.set(mGivenTouchableRegion);//这是系统默认设置的touchableRegion
}
outRegion.translate(frame.left, frame.top);
break;
}
}
cropRegionToStackBoundsIfNeeded(outRegion);
subtractTouchExcludeRegionIfNeeded(outRegion);
}
//过滤输入法的包名
private boolean inputFilter(){
if(mAttrs!=null&&mAttrs.packageName!=null
&&mAttrs.packageName.equals("com.android.inputmethod.latin")){
return true;
}
return false;
}
设置好之后,键盘随意移动到哪里都可以接收到触摸事件了。但是这种策略就改变了系统原来对应键盘的触摸策略了。