Drawable selector loses hover state when clicking with a physical mouse

325 views Asked by At

I created a custom drawable (base on a drawable selector) for a regular Button and I decided to test my app with both a physical mouse and keyboard.

That's when I noticed that every time I click the button with the physical mouse, it loses the hovered state for a very brief moment and only after a few milliseconds it gains the pressed state.

Even though this happens only for a very brief moment, it produces an annoying and very noticeable "blink".

I do not know if I made myself clear, so I will reproduce the steps:

1 - Button is idle, and the mouse pointer is not over the button: button renders ok with the default state

2 - I move the mouse cursor over the button and I leave the mouse still: button renders with the hovered state

3 - I press the mouse's left button: button renders with the default state (not hovered) for a brief moment and then renders with pressed state, producing a visual blink

That weird behavior can be easily reproduced/verified by replacing the button with a custom Button class overriding drawableStateChanged():

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        final int[] states = getDrawableState();
        StringBuilder builder = new StringBuilder("@@@ ");
        if (states != null) {
            for (int i = states.length - 1; i >= 0; i--) {
                switch (states[i]) {
                case android.R.attr.state_pressed:
                    builder.append("p");
                    break;
                case android.R.attr.state_focused:
                    builder.append("f");
                    break;
                case android.R.attr.state_hovered:
                    builder.append("h");
                    break;
                }
            }
        }
        System.out.println(builder.toString());
    }

Logcat shows:

@@@     [the button is in the default state]
@@@ h   [the button is in the hovered state]

[I physically click the left mouse button]

@@@     [the button goes back to the default state even though it *is* hovered and pressed]
@@@ p   [after a visual blink, the button goes to its pressed state]
...

I tried almost everything: overriding onHoverChanged(), setHovered() and so on... I have even tried to call isHovered() within drawableStateChanged() to see if there is a difference... Nothing!!!! Android actively changes both the drawable state and the hovered attribute to false before setting the pressed state. That's on purpose!

I looked everywhere on the web, and even here at SO. Nothing! It's like no one has noticed it yet, or I must be forgetting something really simple...

FYI: I am not using any libraries (like AppCompat etc etc etc). It is a plain button, placed in a plain Activity. Tested on a Samsung A5 2017 (with USB mouse) and on an Asus Chromebook Flip C100 (using the track pad).

FYI 2: The style in effect is v21/styles.xml:

<?xml version="1.0" encoding="UTF-8"?>
<resources>

    <style name="AppBaseTheme" parent="@android:style/Theme.Material.Light.NoActionBar">
        <item name="android:colorPrimary">#ff3344bb</item>
        <item name="android:colorAccent">#ff3344bb</item>
        <item name="android:colorControlNormal">#ff6d6d6d</item>
    </style>

</resources>

FYI 3: I also tried to override onGenericMotionEvent(). Again, as soon as I click the button logcat shows @@@ HEXIT. Soon after that, logcat shows @@@ HENTER... but I never even moved the mouse cursor by one pixel... let alone moving it outside the button!

    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_HOVER_ENTER:
            System.out.println("@@@ HENTER");
            break;
        case MotionEvent.ACTION_HOVER_EXIT:
            System.out.println("@@@ HEXIT");
            break;
        }
        return super.onGenericMotionEvent(event);
    }

How can I get rid of that behavior? Does anyone know a flag or a hack to disable this blink?

Important: I do not want to use the only workaround I came up with so far: create private flags to manually control the hovered state, ignoring Android's hovered state and using timers/scheduled delays/etc.

0

There are 0 answers