diff options
| author | Adam Powell <adamp@google.com> | 2017-01-27 13:05:14 -0800 |
|---|---|---|
| committer | Adam Powell <adamp@google.com> | 2017-01-27 13:31:44 -0800 |
| commit | 995c043acfcdb6032ed7dc79b0c0aeb78b4f3b0f (patch) | |
| tree | 0c0c3c51f63e2012b24179b2b29caa766026f3ee /core/java/android | |
| parent | 39cb8895a529d6a53bc8c7f0320da1ea03d21681 (diff) | |
Fix ListView click handling under new focus rules
ListView historically uses View#hasFocusable to change signifcant
behavior around the clickability of items: an item view with any
focusable children could not be clicked via an item click
listener. Many apps therefore have sub-views in list items that are
deliberately clickable, but not focusable. This comes up in cases like
overflow menu buttons on list items.
Now that we have auto-focusability triggered when a view is set as
clickable, the expectations of apps using this pattern have changed.
Create an overload of hasFocusable that optionally can filter out
auto-focusable views in its results. Have ListView use it to preserve
its previous behavior. This isn't public API for now, but perhaps it
should be if this pattern shows up in practice in places other than
ListView.
Bug: 34756767
Change-Id: Ie71ee6e388449f634b30f9162a8b3fa578e32db8
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/view/View.java | 13 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 8 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 8 |
3 files changed, 21 insertions, 8 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0657bef2ab69..11e8b3edcbcc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6389,6 +6389,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see ViewGroup#getTouchscreenBlocksFocus() */ public boolean hasFocusable() { + return hasFocusable(true); + } + + /** + * @hide pending determination of whether this should be public or not. + * Currently used for compatibility with old focusability expectations in ListView. + */ + public boolean hasFocusable(boolean allowAutoFocus) { if (!isFocusableInTouchMode()) { for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) { final ViewGroup g = (ViewGroup) p; @@ -6397,7 +6405,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable(); + if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) { + return false; + } + return allowAutoFocus ? getFocusable() != NOT_FOCUSABLE : getFocusable() == FOCUSABLE; } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index af39eb37ff88..b135beff0c3a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1105,13 +1105,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return null; } + /** @hide Overriding hidden method */ @Override - public boolean hasFocusable() { + public boolean hasFocusable(boolean allowAutoFocus) { if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) { return false; } - if (isFocusable()) { + // TODO This should probably be super.hasFocusable, but that would change behavior + if (allowAutoFocus ? getFocusable() != NOT_FOCUSABLE : getFocusable() == FOCUSABLE) { return true; } @@ -1122,7 +1124,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; - if (child.hasFocusable()) { + if (child.hasFocusable(allowAutoFocus)) { return true; } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 8cedb1712820..47c4cf383e61 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2548,7 +2548,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private boolean isItemClickable(View view) { - return !view.hasFocusable(); + return !view.hasFocusable(false); } /** @@ -2824,7 +2824,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final View v = getChildAt(mSelectedPosition - mFirstPosition); if (v != null) { - if (v.hasFocusable()) return; + if (v.hasFocusable(false)) return; v.setPressed(true); } setPressed(true); @@ -3428,7 +3428,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mTouchMode == TOUCH_MODE_DOWN) { mTouchMode = TOUCH_MODE_TAP; final View child = getChildAt(mMotionPosition - mFirstPosition); - if (child != null && !child.hasFocusable()) { + if (child != null && !child.hasFocusable(false)) { mLayoutMode = LAYOUT_NORMAL; if (!mDataChanged) { @@ -4005,7 +4005,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final float x = ev.getX(); final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right; - if (inList && !child.hasFocusable()) { + if (inList && !child.hasFocusable(false)) { if (mPerformClick == null) { mPerformClick = new PerformClick(); } |
