Android api 28 9.0 EditText无法自动弹出软键盘(windowSoftInputMod stateAlwaysVisible targetSdkVersion)

很简单的需求:打开界面就弹出软键盘

于是:

//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();

...........................................................................................

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值