例如:在8.1 Launcher3界面,连接蓝牙鼠标,点击Folder或者进入Allapss界面,会发现个别应用或者Folder会有背景高亮显示
分析:
通过本地对Launche3的代码布局进行check,并没有设置任何背景,因此,初步怀疑为谷歌原生View中在某种状态下设置了什么东东,以至于在满足某种情形下,如鼠标悬停,会有个别View背景高亮显示。
下一步对谷歌原生View.java进行check,发现View的成员变量初始值默认为true。
/Anrdroid8.1/frameworks/base/core/java/android/view/View.java
4244 * Whether this View should use a default focus highlight when it gets focused but doesn't 4245 * have {@link android.R.attr#state_focused} defined in its background. 4246 */ 4247 boolean mDefaultFocusHighlightEnabled = true;
该变量的含义:是否该视图获取焦点后,应该使用一个默认焦点高亮;而此高亮并非在背景中定义的state_facused,也就是说不是用户自己定义的View的background里设置的。系统将设置一个默认的高亮。
接着往下看,View的构造器中,初始化数据时,会获取TypedArray值:
4720 public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
4781 switch (attr) {
5236 case R.styleable.View_defaultFocusHighlightEnabled:
5237 if (a.peekValue(attr) != null) { 5238 setDefaultFocusHighlightEnabled(a.getBoolean(attr, true)); 5239 } 5240 break;
也就是说,如果定义的View的布局中设置了如下属性值,将会读取保存给成员 mDefaultFocusHighlightEnabled
<item name="android:defaultFocusHighlightEnabled">false</item>
有set就有get方法:
10431 public final boolean getDefaultFocusHighlightEnabled() { 10432 return mDefaultFocusHighlightEnabled; 10433 } 10434
看看get方法哪里用的:
20189 /** 20190 * Check whether we need to draw a default focus highlight when this view gets focused, 20191 * which requires: 20192 * <ul> 20193 * <li>In both background and foreground, {@link android.R.attr#state_focused} 20194 * is not defined.</li> 20195 * <li>This view is not in touch mode.</li> 20196 * <li>This view doesn't opt out for a default focus highlight, via 20197 * {@link #setDefaultFocusHighlightEnabled(boolean)}.</li> 20198 * <li>This view is attached to window.</li> 20199 * </ul> 20200 * @return {@code true} if a default focus highlight is needed. 20201 * @hide 20202 */ 20203 @TestApi 20204 public boolean isDefaultFocusHighlightNeeded(Drawable background, Drawable foreground) { 20205 final boolean lackFocusState = (background == null || !background.isStateful() 20206 || !background.hasFocusStateSpecified()) 20207 && (foreground == null || !foreground.isStateful() 20208 || !foreground.hasFocusStateSpecified()); 20209 return !isInTouchMode() && getDefaultFocusHighlightEnabled() && lackFocusState 20210 && isAttachedToWindow() && sUseDefaultFocusHighlight; 20211 }
根据注释,当视图获取焦点时,核对是否需要绘制一个默认焦点高亮显示,成立条件:
(1) view没有设置自己的background/foreground,或者没有焦点状态
(2) 不是在触摸模式
(3) 视图没有setDefaultFocusHighlightEnabled设置禁用默认焦点高亮
(4) 视图已添加到窗口
很明显,发生问题的场景,连接蓝牙鼠标,视图无定义background/foreground,亦无设置mDefaultFocusHighlightEnabled值,就满足条件了,返回true。
那么,就看看谁调用该接口的:
20213 /** 20214 * When this view is focused, switches on/off the default focused highlight. 20215 * <p> 20216 * This always happens when this view is focused, and only at this moment the default focus 20217 * highlight can be visible. 20218 */ 20219 private void switchDefaultFocusHighlight() { 20220 if (isFocused()) { 20221 final boolean needed = isDefaultFocusHighlightNeeded(mBackground, 20222 mForegroundInfo == null ? null : mForegroundInfo.mDrawable); 20223 final boolean active = mDefaultFocusHighlight != null; 20224 if (needed && !active) { 20225 setDefaultFocusHighlight(getDefaultFocusHighlightDrawable()); 20226 } else if (!needed && active) { 20227 // The highlight is no longer needed, so tear it down. 20228 setDefaultFocusHighlight(null); 20229 } 20230 } 20231 }
噢,当视图获取焦点时,打开/关闭默认的焦点高亮呀~
鼠标点击Launcher3某Folder或Allapp界面,某视图想当然会获取到焦点呀,满足了条件if。。。。。。
等下,,,active会不会true,,因为初始值为null,没有人设置过private setDefaultFocusHighlight,,,那肯定会是false了第一次。。。。
看看设置的Drawable是什么鬼。。
20146 /** 20147 * Create a default focus highlight if it doesn't exist. 20148 * @return a default focus highlight. 20149 */ 20150 private Drawable getDefaultFocusHighlightDrawable() { 20151 if (mDefaultFocusHighlightCache == null) { 20152 if (mContext != null) { 20153 final int[] attrs = new int[] { android.R.attr.selectableItemBackground }; 20154 final TypedArray ta = mContext.obtainStyledAttributes(attrs); 20155 mDefaultFocusHighlightCache = ta.getDrawable(0); 20156 ta.recycle(); 20157 } 20158 } 20159 return mDefaultFocusHighlightCache; 20160 }
噢,原来是获取的系统的一个默认的属性值: android.R.attr.selectableItemBackground
看看其他地方有用的没,嗖嗖源码:
packages/apps/Settings/res/layout/dashboard_tile.xml
17<LinearLayout 18 xmlns:android="http://schemas.android.com/apk/res/android" 19 android:id="@+id/dashboard_tile" 20 android:layout_width="match_parent" 21 android:layout_height="wrap_content" 22 android:background="?android:attr/selectableItemBackground" 23 android:gravity="center_vertical" 24 android:minHeight="@dimen/dashboard_tile_minimum_height" 25 android:clickable="true" 26 android:focusable="true"
怪不得系统设置应用鼠标放上去显示的贼对贼对,自己偷偷设置背景了。。。
其实鼠标悬停设置如下,也可以动态改变背景:
<item name="android:state_hovered">true</item>
java中可以通过如下监听是否悬停于View之上:
this.setOnHoverListener(new OnHoverListener() { @Override public boolean onHover(View v, MotionEvent event) { return false; } });
就可以实现VIew的鼠标悬停高亮~~~
先不说这个,原问题的解决方法:
设置:<item name="android:defaultFocusHighlightEnabled">false</item>
将屏蔽系统默认高亮
设置:android:background="?android:attr/selectableItemBackground"
将使该View一定会有鼠标悬停高亮~~