Postpone hover state resolution in DropDownListView

Prevents a HOVER_EXIT => TOUCH_DOWN transition from triggering two
Drawable.setState() calls in the same frame.

Bug: 25849904
Change-Id: Ib10f127fd68e433992b6b11e62ac2263d790ef52
This commit is contained in:
Alan Viverette
2016-01-20 11:26:06 -05:00
parent 485a8d140a
commit fb4cbc4a06

View File

@@ -74,6 +74,12 @@ public class DropDownListView extends ListView {
/** Helper for drag-to-open auto scrolling. */
private AbsListViewAutoScroller mScrollHelper;
/**
* Runnable posted when we are awaiting hover event resolution. When set,
* drawable state changes are postponed.
*/
private ResolveHoverRunnable mResolveHoverRunnable;
/**
* Creates a new list view wrapper.
*
@@ -100,19 +106,37 @@ public class DropDownListView extends ListView {
return isHovered() || super.shouldShowSelector();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mResolveHoverRunnable != null) {
// Resolved hover event as hover => touch transition.
mResolveHoverRunnable.cancel();
}
return super.onTouchEvent(ev);
}
@Override
public boolean onHoverEvent(@NonNull MotionEvent ev) {
final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_EXIT && mResolveHoverRunnable == null) {
// This may be transitioning to TOUCH_DOWN. Postpone drawable state
// updates until either the next frame or the next touch event.
mResolveHoverRunnable = new ResolveHoverRunnable();
mResolveHoverRunnable.post();
}
// Allow the super class to handle hover state management first.
final boolean handled = super.onHoverEvent(ev);
final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_ENTER
|| action == MotionEvent.ACTION_HOVER_MOVE) {
final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
if (position != INVALID_POSITION && position != mSelectedPosition) {
final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
if (hoveredItem.isEnabled()) {
// Force a focus so that the proper selector state gets used when we update.
// Force a focus so that the proper selector state gets
// used when we update.
requestFocus();
positionSelector(position, hoveredItem);
@@ -122,7 +146,8 @@ public class DropDownListView extends ListView {
updateSelectorState();
}
} else {
// Do not cancel the selected position if the selection is visible by other reasons.
// Do not cancel the selected position if the selection is visible
// by other means.
if (!super.shouldShowSelector()) {
setSelectedPositionInt(INVALID_POSITION);
setNextSelectedPositionInt(INVALID_POSITION);
@@ -132,6 +157,13 @@ public class DropDownListView extends ListView {
return handled;
}
@Override
protected void drawableStateChanged() {
if (mResolveHoverRunnable == null) {
super.drawableStateChanged();
}
}
/**
* Handles forwarded events.
*
@@ -196,12 +228,14 @@ public class DropDownListView extends ListView {
}
/**
* Sets whether the list selection is hidden, as part of a workaround for a touch mode issue
* (see the declaration for mListSelectionHidden).
* @param listSelectionHidden
* Sets whether the list selection is hidden, as part of a workaround for a
* touch mode issue (see the declaration for mListSelectionHidden).
*
* @param hideListSelection {@code true} to hide list selection,
* {@code false} to show
*/
public void setListSelectionHidden(boolean listSelectionHidden) {
this.mListSelectionHidden = listSelectionHidden;
public void setListSelectionHidden(boolean hideListSelection) {
mListSelectionHidden = hideListSelection;
}
private void clearPressedItem() {
@@ -312,4 +346,25 @@ public class DropDownListView extends ListView {
public boolean hasFocus() {
return mHijackFocus || super.hasFocus();
}
/**
* Runnable that forces hover event resolution and updates drawable state.
*/
private class ResolveHoverRunnable implements Runnable {
@Override
public void run() {
// Resolved hover event as standard hover exit.
mResolveHoverRunnable = null;
drawableStateChanged();
}
public void cancel() {
mResolveHoverRunnable = null;
removeCallbacks(this);
}
public void post() {
DropDownListView.this.post(this);
}
}
}