很简单的需求:打开界面就弹出软键盘
于是:
//build.gradle中
targetSdkVersion 27
//Mainfest中
android:windowSoftInputMode="adjustResize|stateVisible"
//xml中
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
很开心的就去玩耍了。
有一天,心血来潮把targetSdkVersion升级到28了。
然后就是在9.0及以上的手机上无论怎么骚键盘都不鸟你?
改适配版本还能影响代码逻辑?一万只草泥马奔腾而过
查看日志发现一句报错:
InputMethodManagerService: SOFT_INPUT_STATE_VISIBLE is ignored because there is no focused view that also returns true from View#onCheckIsTextEditor()
打开TextView的onCheckIsTextEditor方法,天真的以为需要加上inputType就行了
@Override
public boolean onCheckIsTextEditor() {
return mEditor != null && mEditor.mInputType != EditorInfo.TYPE_NULL;
}
然而现实是残酷的...(啪啪啪打脸中)
打开InputMethodManagerService,找到报错的代码(需要api28及以上的源码),如下:
if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, controlFlags)) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
missingMethods, attribute, controlFlags,
startInputReason);
didStart = true;
}
showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
} else {
Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
+ " there is no focused view that also returns true from"
+ " View#onCheckIsTextEditor()");
}
打开isSoftInputModeStateVisibleAllowed方法
public static boolean isSoftInputModeStateVisibleAllowed(
int targetSdkVersion, int controlFlags) {
if (targetSdkVersion < Build.VERSION_CODES.P) {
// for compatibility.
return true;
}
if ((controlFlags & CONTROL_WINDOW_VIEW_HAS_FOCUS) == 0) {
return false;
}
if ((controlFlags & CONTROL_WINDOW_IS_TEXT_EDITOR) == 0) {
return false;
}
return true;
}
小于28的就返回正常,否则就需要满足2个条件。又一万只草泥马奔腾而过...
controlFlags是个什么鬼玩意?
在InputMethodManager的onPostWindowFocus里有
public void onPostWindowFocus(View rootView, View focusedView,
@SoftInputModeFlags int softInputMode, boolean first, int windowFlags) {
...
int controlFlags = 0;
if (focusedView != null) {
controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
if (focusedView.onCheckIsTextEditor()) {
controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
}
}
...
}
原来是由focusedView决定的,此时灵光一现,focusedView肯定是EditText了,继续翻源码,果然在ViewRootImpl的handleWindowFocusChanged方法里出现了
private void handleWindowFocusChanged() {
...
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
...
}
此时更验证了我的猜想,于是很愉快的在EditText加上了获取焦点的方法:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:focusable="true"
/>
再次运行,脸都被打肿了...
debug才发现mView.findFocus()里的mFocused一直是null并不是EditText
//ViewGroup
@Override
public View findFocus() {
if (DBG) {
System.out.println("Find focus in " + this + ": flags="
+ isFocused() + ", child=" + mFocused);
}
if (isFocused()) {
return this;
}
if (mFocused != null) {
return mFocused.findFocus();
}
return null;
}
为什么是null
在api23的ViewRootImpl的performTraversals方法里这样写的:第一次的时候,没有焦点就默认调用获取焦点的逻辑
...
if (mFirst) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
+ mView.hasFocus());
if (mView != null) {//根布局,肯定不是null
if (!mView.hasFocus()) {
mView.requestFocus(View.FOCUS_FORWARD);
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
+ mView.findFocus());
} else {
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
+ mView.findFocus());
}
}
}
...
但在api28就变了:sAlwaysAssignFocus是false,!isInTouchMode()也是false,然后焦点就被go...吃了
...
sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
...
if (mFirst) {
if (sAlwaysAssignFocus || !isInTouchMode()) {//isInTouchMode表示手机是不是触摸模式,肯定是true,取反就变成false了
// handle first focus request
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus());
}
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: requested focused view=" + mView.findFocus());
}
} else {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: existing focused view=" + mView.findFocus());
}
}
}
}
...
仔细理一下思路:配置文件配置打开Activity要弹出键盘>api28却不给EditText焦点>逻辑判断没有焦点就不弹键盘...
******奔腾而过
经过博主最强大脑的深思熟虑,最终只要加上如下一行即可:
mEt.requestFocus();
...........................................................................................