Android TvSettings Bug: 密码框无法点击唤起输入法

概述

       Android 10 的Box方案, 默认使用的是TvSettings作为系统设置,输入操作的习惯上是使用鼠标,键盘,遥控,日常的场景是没有问题,也不会出现本文中提及的问题。当外接的USB触摸屏后,出现无法点击WIFI密码框换出输入法进行输入密码操作。

问题:
Android TvSettings Bug: 密码框无法点击唤起输入法_第1张图片
Android TvSettings Bug: 密码框无法点击唤起输入法_第2张图片
从图1开始添加WIFI,并准备输入密码时,外接的USB触摸屏没有办法点击唤起输入法。

正常的情况应该如下图所示:
Android TvSettings Bug: 密码框无法点击唤起输入法_第3张图片

过程

     在前面的一些文章中,对于无法点击或响应按键操作的问题,一般可以先查查焦点的问题:

View Hierarchy:
      DecorView@661bdd7[WifiConnectionActivity]
        android.widget.LinearLayout{abf86c4 V.E...... ........ 0,0-1920,1080}
          android.view.ViewStub{e3449ad G.E...... ......I. 0,0-0,0 #10201a2 android:id/action_mode_bar_stub}
          android.widget.FrameLayout{38444e2 V.E...... ........ 0,0-1920,1080 #1020002 android:id/content}
            android.widget.FrameLayout{51a6073 V.E...... ........ 0,0-1920,1080 #7f0a0229 app:id/wifi_container}
              androidx.leanback.app.GuidedStepRootLayout{1904b30 V.E...... ........ 0,0-1920,1080 #7f0a0105 app:id/guidedstep_root}
                android.widget.FrameLayout{878d3a9 V.E...... ........ 0,0-1920,1080 #7f0a0104 app:id/guidedstep_background_view_root}
                  androidx.leanback.widget.NonOverlappingView{9a5cd2e V.ED..... ........ 0,0-1920,1080 #7f0a0103 app:id/guidedstep_background}
                  android.widget.LinearLayout{fb090cf V.E...... ........ 0,0-1920,1080 #7f0a00a0 app:id/content_frame}
                    androidx.leanback.widget.NonOverlappingFrameLayout{966ea5c V.E...... ........ 0,0-1231,1080 #7f0a009f app:id/content_fragment}
                      com.android.tv.settings.connectivity.setup.GuidanceRelativeLayout{b620165 V.E...... ........ 0,0-1231,1080}
                        android.widget.TextView{e458e3a V.ED..... ........ 168,374-1147,447 #7f0a00f1 app:id/guidance_title}
                        android.widget.TextView{fcf6aeb V.ED..... ........ 168,474-1147,503 #7f0a00ef app:id/guidance_description}
                    androidx.leanback.widget.NonOverlappingFrameLayout{aa65048 V.E...... ........ 1231,0-1919,1080 #7f0a0044 app:id/action_fragment_root}
                      androidx.leanback.widget.NonOverlappingView{4f64ee1 V.ED..... ........ 0,0-688,1080 #7f0a0043 app:id/action_fragment_background}
                      androidx.leanback.widget.NonOverlappingLinearLayout{bee5406 VFE...... ........ 0,0-688,1080 #7f0a0042 app:id/action_fragment}
                        android.widget.RelativeLayout{4b7cac7 V.E...... ........ 0,0-688,1080 #7f0a00ff app:id/guidedactions_root}
                          androidx.leanback.widget.NonOverlappingView{ef428f4 G.ED..... ......ID 0,0-0,0 #7f0a00fd app:id/guidedactions_list_background}
                          androidx.leanback.widget.GuidedActionsRelativeLayout{327f81d V.E...... ........ 0,0-688,1080 #7f0a00f3 app:id/guidedactions_content}
                            androidx.leanback.widget.VerticalGridView{90baa92 V.E...... ........ 0,0-348,1080 #7f0a00fb app:id/guidedactions_list}
                              android.widget.LinearLayout{6dc4c63 VFE...C.. ........ 0,103-348,528}
                                android.widget.FrameLayout{2a4e060 V.E...... ........ 0,293-348,365 #7f0a020f app:id/text_input_wrapper}
                                  android.widget.EditText{6d0f919 VFED..... ........ 24,0-324,72 #7f0a00fa app:id/guidedactions_item_title}
                                android.widget.CheckBox{535ddde VFED..C.. ........ 14,377-146,425 #7f0a01ac app:id/password_checkbox}
                            androidx.leanback.widget.NonOverlappingView{93d4bbf G.ED..... ......ID 0,0-0,0 #7f0a0102 app:id/guidedactions_sub_list_background}
                            androidx.leanback.widget.VerticalGridView{e7a28c IFE...... ......I. 0,432-688,432 #7f0a0101 app:id/guidedactions_sub_list}
                        android.widget.RelativeLayout{a6f0dd5 V.E...... ........ 688,0-688,1080 #7f0a0100 app:id/guidedactions_root2}
                          androidx.leanback.widget.NonOverlappingView{f3df9ea V.ED..... ......ID 0,0-0,1080 #7f0a00fe app:id/guidedactions_list_background2}
                          androidx.leanback.widget.NonOverlappingFrameLayout{484e4db V.E...... ......ID 0,0-0,1080 #7f0a00f4 app:id/guidedactions_content2}
                            androidx.leanback.widget.VerticalGridView{6b25b78 V.E...... ......ID 0,0-0,1080 #7f0a00fc app:id/guidedactions_list2}

很明显android.widget.EditText{6d0f919 VFED… … 24,0-324,72 #7f0a00fa app:id/guidedactions_item_title} 不可点击.

通过ID找到相关的代码:

$ grep -r "guidedactions_item_title" packages/apps/TvSettings/Settings
packages/apps/TvSettings/Settings/res/layout/setup_text_input_item.xml:            android:id="@+id/guidedactions_item_title"
packages/apps/TvSettings/Settings/res/layout/setup_password_item.xml:            android:id="@+id/guidedactions_item_title"
packages/apps/TvSettings/Settings/src/com/android/tv/settings/connectivity/util/GuidedActionsAlignUtil.java:                        androidx.leanback.R.id.guidedactions_item_title);
packages/apps/TvSettings/Settings/src/com/android/tv/settings/util/GuidedActionsAlignUtil.java:                        androidx.leanback.R.id.guidedactions_item_title);

packages/apps/TvSettings/Settings/res/layout/setup_password_item.xml



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:orientation="vertical">

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/text_input_wrapper"
        android:layout_width="match_parent"
        android:layout_height="@dimen/setup_list_item_height"
        android:layout_alignParentStart="true"
        android:layout_marginTop="@dimen/setup_action_vertical_offset"
        android:background="@drawable/setup_text_input_background"
        android:elevation="@dimen/setup_elevation"
        android:gravity="start|center_vertical"
        android:paddingEnd="@dimen/setup_list_item_padding"
        android:paddingStart="@dimen/setup_list_item_padding">

        <EditText
            android:id="@+id/guidedactions_item_title"
            style="@style/Setup.Action.TextInput"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
	    	android:focusable="true"
	    	android:gravity="center_vertical"
            android:imeOptions="actionNext|flagNoExtractUi"
            android:inputType="text">
        EditText>
    FrameLayout>

    <CheckBox
        android:id="@+id/password_checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/setup_obfuscation_margin_start"
        android:layout_marginTop="@dimen/setup_obfuscation_margin_top"
        android:buttonTint="@color/setup_list_item_background_focused"
        android:buttonTintMode="src_in"
        android:text="@string/text_obfuscation_toggle_caption" />
LinearLayout>

        @Override
        public GuidedActionsStylist onCreateActionsStylist() {
            return new GuidedActionsStylist() {
                @Override
                public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
                    super.onBindViewHolder(vh, action);
                    if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
                        PasswordViewHolder viewHolder = (PasswordViewHolder) vh;
                        mTextInput = (EditText) viewHolder.getTitleView();
						mTextInput.setClickable(true);
						mTextInput.setFocusableInTouchMode(true);
                        mCheckBox = viewHolder.mCheckbox;
                        mCheckBox.setOnClickListener(view -> {
                            updatePasswordInputObfuscation();
                            EnterPasswordFragment.this.openInEditMode(action);
                        });
                        mCheckBox.setChecked(mUserChoiceInfo.isPasswordHidden());
                        updatePasswordInputObfuscation();
                        openInEditMode(action);
                    }
                }
             }
			@Override
            public int onProvideItemLayoutId() {
                return R.layout.setup_password_item;
            }
         }

增加
mTextInput.setClickable(true);
mTextInput.setFocusableInTouchMode(true);

编译,解决!


另一种尝试
在这之前,尝试过在XML中设置:

        <EditText
            android:clickable="true"
			android:focusableInTouchMode="true"
		>

结果:无效??
反复确认了layout文件的正确性无果。
从代码中去一个个追查并不现实,取了个巧,自定一个EditText替换了默认的,并重写了setClickable函数,从中打印出调用的堆栈得知,在GuidedActionsStylist.onBindViewHolder中确实调用了。

GuidedActionsStylist 位于androidx中,

packages/apps/TvSettings/Settings/Android.mk

LOCAL_PACKAGE_NAME := TvSettings
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
LOCAL_PROGUARD_ENABLED := disabled

LOCAL_PRIVILEGED_MODULE := true

LOCAL_STATIC_ANDROID_LIBRARIES := \
    androidx.recyclerview_recyclerview \
    androidx.preference_preference \
    androidx.appcompat_appcompat \
    androidx.legacy_legacy-preference-v14 \
    androidx.leanback_leanback-preference \
    androidx.leanback_leanback \
    androidx.lifecycle_lifecycle-extensions \
    androidx-constraintlayout_constraintlayout \

对应的jar包路径:

out/target/common/obj/JAVA_LIBRARIES/androidx.leanback_leanback_intermediates/classes.jar

顺便看下源码:

GuidedActionsStylist.java

		public ViewHolder(@NonNull View v, boolean isSubAction) {
            super(v);

            mContentView = v.findViewById(R.id.guidedactions_item_content);
            mTitleView = (TextView) v.findViewById(R.id.guidedactions_item_title);
            mActivatorView = v.findViewById(R.id.guidedactions_activator_item);
            mDescriptionView = (TextView) v.findViewById(R.id.guidedactions_item_description);
            mIconView = (ImageView) v.findViewById(R.id.guidedactions_item_icon);
            mCheckmarkView = (ImageView) v.findViewById(R.id.guidedactions_item_checkmark);
            mChevronView = (ImageView) v.findViewById(R.id.guidedactions_item_chevron);
            mIsSubAction = isSubAction;

            v.setAccessibilityDelegate(mDelegate);
        }
    /**
     * Binds a {@link ViewHolder} to a particular {@link GuidedAction}.
     * @param vh The view holder to be associated with the given action.
     * @param action The guided action to be displayed by the view holder's view.
     * @return The view to be added to the caller's view hierarchy.
     */
    public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
        vh.mAction = action;
        if (vh.mTitleView != null) {
            vh.mTitleView.setInputType(action.getInputType());
            vh.mTitleView.setText(action.getTitle());
            vh.mTitleView.setAlpha(action.isEnabled() ? mEnabledTextAlpha : mDisabledTextAlpha);
            vh.mTitleView.setFocusable(false);
            vh.mTitleView.setClickable(false);
            vh.mTitleView.setLongClickable(false);
            if (BuildCompat.isAtLeastP()) {
                if (action.isEditable()) {
                    vh.mTitleView.setAutofillHints(action.getAutofillHints());
                } else {
                    vh.mTitleView.setAutofillHints((String[]) null);
                }
            } else if (VERSION.SDK_INT >= 26) {
                // disable autofill below P as dpad/keyboard is not supported
                vh.mTitleView.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
            }
        }
		//....
    }

从上面的代码可以看出,使用了GuidedActionsStylist后,guidedactions_item_title这个ID的控件会被置为不可点击。
刚好就是前面提及的文本输入框,对头!

引用

android:focusable和android:focusableInTouchMode的区别
GuidedActionsStylist
AndroidX GitHub
AOSP AndroidX Contribution Guide

你可能感兴趣的:(android,android,bug,TvSettings,输入法,点击)