[Android] 关于Window Overscan
Overscan的概念
对于电视机,有一个Overscan的概念,如下图,所谓Overscan区域,就是电视机屏幕四周某些不可见的区域,这是电视机的特性。并且,Overscan的具体值也没有一个明确的标准,不同的电视机厂家的Overscan的值也各不相同。
而对于普通的LCD,由于并没有Overscan的概念,所以设想,将一块没有设置Overscan的Framebuffer显示到有Overscan的电视机上,必定四周有一部分Overscan的区域被切除到无法显示出来。
所以在Android 4.3以后,Google加入了Overscan的API,并且提供了wm工具,可以让用户设定Overscan的值,来满足不同的电视机。
上图中,包含了Overscan的整个区域我们称之为Overscan Screen;
去处Overscan区域后称之为 Unrestricted Area,由绿色和紫色区域组成,绿色区域用于显示Status Bar和Navigation Bar;
剩下的紫色区域为 Restricted Area,这里显示的为mContentFrame。
关于WM Tool
Android 4.3之后,加入了Overscan的概念,并且提供了wm工具来设定Overscan的值。(参考git commit:c652de8141f5b8e3c6bcf8916842b6e106413b1a)
代码调用流程:
简单来讲就是,当我们设置了Overscan值后,WindowManagerService、PhoneWindowManager等会对当前显示的View的参数做重新计算,将计算完的数值会传给View,让其重新Layout。
关于输入法的Bug
一个存在的Bug是,当我们设置完Overscan后,google 的虚拟键盘并没有跟着Overscan的变化而缩放,虚拟键盘的一部分会被Overscan的区域遮挡无法显示。
原因:虚拟键盘是通过Canvas绘制到屏幕上,而虚拟键盘上每一个组件的大小是根据屏幕的实际像素和深度来计算的,代码:
packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
final Resources res = mThemeContext.getResources(); builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor), res.getConfiguration().orientation, res.getDisplayMetrics().widthPixels);
所以,Keyboard会撑满Overscan Screen,而我们期望的是让Keyboard撑满Unrestricted Area,故我们做以下修改:
1. 让Display.java中的getWidth方法返回除去Overscan长度后的值。
frameworks/base/core/java/android/view/Display.java
public int getWidth() { synchronized (this) { updateCachedAppSizeIfNeededLocked(); updateDisplayInfoLocked(); return mCachedAppWidthCompat - mDisplayInfo.overscanLeft - mDisplayInfo.overscanRight ; } }
2. 在LatinIME中添加mWidth变量,用于存放Keyboard宽度。
packages/inputmethods/LatinIME/java/src/com/android/inputmethod/latin/SettingsValues.java
public static int mWidth;
public SettingsValues(final SharedPreferences prefs, final InputAttributes inputAttributes, final Context context) {
if(mWidth == 0){
mWidth = res.getDisplayMetrics().widthPixels;
}
}
3. 每次虚拟键盘弹出前,比较当前的Width和设定Width是否相同,不同和替代,并重新初始化Keyboard。注,此处InputManagerService的getMaxWidth方法最终会调用Step1的getWidth方法。
packages/inputmethods/LatinIME/java/src/com/android/inputmethod/latin/LatinIME.java
private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInputView(editorInfo, restarting); // Deal with overscan area if(mCurrentSettings.mWidth != getMaxWidth()){ mCurrentSettings.mWidth = getMaxWidth(); loadKeyboard(); } }
4.将初始化键盘宽度的值从res.getDisplayMetrics().widthPixels改成settingsValues.mWidth。
packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) { final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( mThemeContext, editorInfo); final Resources res = mThemeContext.getResources(); // builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor), // res.getConfiguration().orientation, res.getDisplayMetrics().widthPixels); builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor), res.getConfiguration().orientation, settingsValues.mWidth); }
参考
http://blog.csdn.net/cjj7905150/article/details/17888377