关于Android原生View默认焦点高亮的问题

本文探讨了Android原生View在特定情况下出现的焦点高亮问题,尤其是当连接蓝牙鼠标时。分析了View的源码,指出问题出现在系统默认焦点高亮设置上。解决方案包括禁用默认焦点高亮或设置特定背景属性以实现自定义高亮效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

例如:在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一定会有鼠标悬停高亮~~

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值