自动弹出软键盘(windowSoftInputMod stateAlwaysVisible targetSdkVersion)"/>
Android api 28 9.0 EditText无法自动弹出软键盘(windowSoftInputMod stateAlwaysVisible targetSdkVersion)
很简单的需求:打开界面就弹出软键盘
于是:
//build.gradle中
targetSdkVersion 27//Mainfest中
android:windowSoftInputMode="adjustResize|stateVisible"//xml中
<EditTextandroid: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就行了
@Overridepublic 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加上了获取焦点的方法:
<EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:focusableInTouchMode="true"android:focusable="true"/>
再次运行,脸都被打肿了...
debug才发现mView.findFocus()里的mFocused一直是null并不是EditText
//ViewGroup@Overridepublic 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 requestif (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="+ mView.hasFocus());if (mView != null) {//根布局,肯定不是nullif (!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 requestif (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();
...........................................................................................
更多推荐
Android api 28 9.0 EditText无法自动弹出软键盘(windowSoftInputMod stateAlwaysVisible targe
发布评论