Android 10 的Box方案, 默认使用的是TvSettings作为系统设置,输入操作的习惯上是使用鼠标,键盘,遥控,日常的场景是没有问题,也不会出现本文中提及的问题。当外接的USB触摸屏后,出现无法点击WIFI密码框换出输入法进行输入密码操作。
问题:
从图1开始添加WIFI,并准备输入密码时,外接的USB触摸屏没有办法点击唤起输入法。
在前面的一些文章中,对于无法点击或响应按键操作的问题,一般可以先查查焦点的问题:
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