diff options
| -rw-r--r-- | api/current.txt | 5 | ||||
| -rw-r--r-- | api/system-current.txt | 5 | ||||
| -rw-r--r-- | api/test-current.txt | 5 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 131 | ||||
| -rw-r--r-- | core/java/android/widget/RatingBar.java | 6 | ||||
| -rw-r--r-- | core/java/android/widget/SearchView.java | 6 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 23 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 12 |
8 files changed, 145 insertions, 48 deletions
diff --git a/api/current.txt b/api/current.txt index b964a60beead..d8110203c8cd 100644 --- a/api/current.txt +++ b/api/current.txt @@ -43826,6 +43826,7 @@ package android.view { method public float getElevation(); method public boolean getFilterTouchesWhenObscured(); method public boolean getFitsSystemWindows(); + method public int getFocusable(); method public java.util.ArrayList<android.view.View> getFocusables(int); method public void getFocusedRect(android.graphics.Rect); method public android.graphics.drawable.Drawable getForeground(); @@ -44129,6 +44130,7 @@ package android.view { method public void setFilterTouchesWhenObscured(boolean); method public void setFitsSystemWindows(boolean); method public void setFocusable(boolean); + method public void setFocusable(int); method public void setFocusableInTouchMode(boolean); method public void setFocusedByDefault(boolean); method public void setForeground(android.graphics.drawable.Drawable); @@ -44266,8 +44268,10 @@ package android.view { field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET; field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2 field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1 + field public static final int FOCUSABLE = 1; // 0x1 field public static final int FOCUSABLES_ALL = 0; // 0x0 field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1 + field public static final int FOCUSABLE_AUTO = 16; // 0x10 field protected static final int[] FOCUSED_SELECTED_STATE_SET; field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; field protected static final int[] FOCUSED_STATE_SET; @@ -44297,6 +44301,7 @@ package android.view { field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000 field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000 + field public static final int NOT_FOCUSABLE = 0; // 0x0 field public static final int NO_ID = -1; // 0xffffffff field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0 field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1 diff --git a/api/system-current.txt b/api/system-current.txt index d7c8f5aee4e4..b6b25f5abe9a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -47175,6 +47175,7 @@ package android.view { method public float getElevation(); method public boolean getFilterTouchesWhenObscured(); method public boolean getFitsSystemWindows(); + method public int getFocusable(); method public java.util.ArrayList<android.view.View> getFocusables(int); method public void getFocusedRect(android.graphics.Rect); method public android.graphics.drawable.Drawable getForeground(); @@ -47478,6 +47479,7 @@ package android.view { method public void setFilterTouchesWhenObscured(boolean); method public void setFitsSystemWindows(boolean); method public void setFocusable(boolean); + method public void setFocusable(int); method public void setFocusableInTouchMode(boolean); method public void setFocusedByDefault(boolean); method public void setForeground(android.graphics.drawable.Drawable); @@ -47615,8 +47617,10 @@ package android.view { field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET; field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2 field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1 + field public static final int FOCUSABLE = 1; // 0x1 field public static final int FOCUSABLES_ALL = 0; // 0x0 field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1 + field public static final int FOCUSABLE_AUTO = 16; // 0x10 field protected static final int[] FOCUSED_SELECTED_STATE_SET; field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; field protected static final int[] FOCUSED_STATE_SET; @@ -47646,6 +47650,7 @@ package android.view { field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000 field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000 + field public static final int NOT_FOCUSABLE = 0; // 0x0 field public static final int NO_ID = -1; // 0xffffffff field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0 field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index c2ae08194263..94b4a000ed98 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -44117,6 +44117,7 @@ package android.view { method public float getElevation(); method public boolean getFilterTouchesWhenObscured(); method public boolean getFitsSystemWindows(); + method public int getFocusable(); method public java.util.ArrayList<android.view.View> getFocusables(int); method public void getFocusedRect(android.graphics.Rect); method public android.graphics.drawable.Drawable getForeground(); @@ -44421,6 +44422,7 @@ package android.view { method public void setFilterTouchesWhenObscured(boolean); method public void setFitsSystemWindows(boolean); method public void setFocusable(boolean); + method public void setFocusable(int); method public void setFocusableInTouchMode(boolean); method public void setFocusedByDefault(boolean); method public void setForeground(android.graphics.drawable.Drawable); @@ -44558,8 +44560,10 @@ package android.view { field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET; field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2 field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1 + field public static final int FOCUSABLE = 1; // 0x1 field public static final int FOCUSABLES_ALL = 0; // 0x0 field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1 + field public static final int FOCUSABLE_AUTO = 16; // 0x10 field protected static final int[] FOCUSED_SELECTED_STATE_SET; field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; field protected static final int[] FOCUSED_STATE_SET; @@ -44589,6 +44593,7 @@ package android.view { field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000 field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000 + field public static final int NOT_FOCUSABLE = 0; // 0x0 field public static final int NO_ID = -1; // 0xffffffff field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0 field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1 diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e7f96de881c8..3bd67c77401c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -857,22 +857,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static boolean sCascadedDragDrop; + /** @hide */ + @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) + @Retention(RetentionPolicy.SOURCE) + public @interface Focusable {} + /** - * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when - * calling setFlags. + * This view does not want keystrokes. + * <p> + * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code + * android:focusable}. */ - private static final int NOT_FOCUSABLE = 0x00000000; + public static final int NOT_FOCUSABLE = 0x00000000; /** - * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling - * setFlags. + * This view wants keystrokes. + * <p> + * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code + * android:focusable}. */ - private static final int FOCUSABLE = 0x00000001; + public static final int FOCUSABLE = 0x00000001; + + /** + * This view determines focusability automatically. This is the default. + * <p> + * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code + * android:focusable}. + */ + public static final int FOCUSABLE_AUTO = 0x00000010; /** * Mask for use with setFlags indicating bits used for focus. */ - private static final int FOCUSABLE_MASK = 0x00000001; + private static final int FOCUSABLE_MASK = 0x00000011; /** * This view will adjust its padding to fit sytem windows (e.g. status bar) @@ -4136,7 +4153,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; - mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; + mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | FOCUSABLE_AUTO; // Set some flags defaults mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) | @@ -4322,6 +4339,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; + // Set default values. + viewFlagValues |= FOCUSABLE_AUTO; + viewFlagMasks |= FOCUSABLE_AUTO; + final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); @@ -4434,8 +4455,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } break; case com.android.internal.R.styleable.View_focusable: - if (a.getBoolean(attr, false)) { - viewFlagValues |= FOCUSABLE; + viewFlagValues = (viewFlagValues & ~FOCUSABLE_MASK) | getFocusableAttribute(a); + if ((viewFlagValues & FOCUSABLE_AUTO) == 0) { viewFlagMasks |= FOCUSABLE_MASK; } break; @@ -5006,7 +5027,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case GONE: out.append('G'); break; default: out.append('.'); break; } - out.append((mViewFlags&FOCUSABLE_MASK) == FOCUSABLE ? 'F' : '.'); + out.append((mViewFlags & FOCUSABLE) == FOCUSABLE ? 'F' : '.'); out.append((mViewFlags&ENABLED_MASK) == ENABLED ? 'E' : '.'); out.append((mViewFlags&DRAW_MASK) == WILL_NOT_DRAW ? '.' : 'D'); out.append((mViewFlags&SCROLLBARS_HORIZONTAL) != 0 ? 'H' : '.'); @@ -8453,20 +8474,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Set whether this view can receive the focus. - * + * <p> * Setting this to false will also ensure that this view is not focusable * in touch mode. * * @param focusable If true, this view can receive the focus. * * @see #setFocusableInTouchMode(boolean) + * @see #setFocusable(int) * @attr ref android.R.styleable#View_focusable */ public void setFocusable(boolean focusable) { - if (!focusable) { + setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE); + } + + /** + * Sets whether this view can receive focus. + * <p> + * Setting this to {@link #FOCUSABLE_AUTO} tells the framework to determine focusability + * automatically based on the view's interactivity. This is the default. + * <p> + * Setting this to NOT_FOCUSABLE will ensure that this view is also not focusable + * in touch mode. + * + * @param focusable One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE}, + * or {@link #FOCUSABLE_AUTO}. + * @see #setFocusableInTouchMode(boolean) + * @attr ref android.R.styleable#View_focusable + */ + public void setFocusable(@Focusable int focusable) { + if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) { setFlags(0, FOCUSABLE_IN_TOUCH_MODE); } - setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK); + setFlags(focusable, FOCUSABLE_MASK); } /** @@ -9056,14 +9096,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** - * Returns whether this View is able to take focus. + * Returns whether this View is currently able to take focus. * * @return True if this view can take focus, or false otherwise. - * @attr ref android.R.styleable#View_focusable */ @ViewDebug.ExportedProperty(category = "focus") public final boolean isFocusable() { - return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK); + return FOCUSABLE == (mViewFlags & FOCUSABLE); + } + + /** + * Returns the focusable setting for this view. + * + * @return One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE}, or {@link #FOCUSABLE_AUTO}. + * @attr ref android.R.styleable#View_focusable + */ + @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.IntToString(from = NOT_FOCUSABLE, to = "NOT_FOCUSABLE"), + @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"), + @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO") + }) + @Focusable + public int getFocusable() { + return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE; } /** @@ -9615,8 +9670,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) { // need to be focusable - if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE || - (mViewFlags & VISIBILITY_MASK) != VISIBLE) { + if ((mViewFlags & FOCUSABLE) != FOCUSABLE + || (mViewFlags & VISIBILITY_MASK) != VISIBLE) { return false; } @@ -11970,14 +12025,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } int privateFlags = mPrivateFlags; + // If focusable is auto, update the FOCUSABLE bit. + if (((mViewFlags & FOCUSABLE_AUTO) != 0) + && (changed & (FOCUSABLE_MASK | CLICKABLE | FOCUSABLE_IN_TOUCH_MODE)) != 0) { + int newFocus = NOT_FOCUSABLE; + if ((mViewFlags & (CLICKABLE | FOCUSABLE_IN_TOUCH_MODE)) != 0) { + newFocus = FOCUSABLE; + } else { + mViewFlags = (mViewFlags & ~FOCUSABLE_IN_TOUCH_MODE); + } + mViewFlags = (mViewFlags & ~FOCUSABLE) | newFocus; + int focusChanged = (old & FOCUSABLE) ^ (newFocus & FOCUSABLE); + changed = (changed & ~FOCUSABLE) | focusChanged; + } + /* Check if the FOCUSABLE bit has changed */ - if (((changed & FOCUSABLE_MASK) != 0) && - ((privateFlags & PFLAG_HAS_BOUNDS) !=0)) { - if (((old & FOCUSABLE_MASK) == FOCUSABLE) + if (((changed & FOCUSABLE) != 0) && ((privateFlags & PFLAG_HAS_BOUNDS) != 0)) { + if (((old & FOCUSABLE) == FOCUSABLE) && ((privateFlags & PFLAG_FOCUSED) != 0)) { /* Give up focus if we are no longer focusable */ clearFocus(); - } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE) + } else if (((old & FOCUSABLE) == NOT_FOCUSABLE) && ((privateFlags & PFLAG_FOCUSED) == 0)) { /* * Tell the view system that we are now available to take focus @@ -12120,7 +12188,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (accessibilityEnabled) { - if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0 + if ((changed & FOCUSABLE) != 0 || (changed & VISIBILITY_MASK) != 0 || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0 || (changed & CONTEXT_CLICKABLE) != 0) { if (oldIncludeForAccessibility != includeForAccessibility()) { @@ -18045,7 +18113,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static String printFlags(int flags) { String output = ""; int numFlags = 0; - if ((flags & FOCUSABLE_MASK) == FOCUSABLE) { + if ((flags & FOCUSABLE) == FOCUSABLE) { output += "TAKES_FOCUS"; numFlags++; } @@ -24670,6 +24738,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, ViewConfiguration.getLongPressTooltipHideTimeout()); } + private int getFocusableAttribute(TypedArray attributes) { + TypedValue val = new TypedValue(); + if (attributes.getValue(com.android.internal.R.styleable.View_focusable, val)) { + if (val.type == TypedValue.TYPE_INT_BOOLEAN) { + return (val.data == 0 ? NOT_FOCUSABLE : FOCUSABLE); + } else { + return val.data; + } + } else { + return FOCUSABLE_AUTO; + } + } + /** * @return The content view of the tooltip popup currently being shown, or null if the tooltip * is not showing. diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java index 3b7fe86a5fa1..70b70bcb36ea 100644 --- a/core/java/android/widget/RatingBar.java +++ b/core/java/android/widget/RatingBar.java @@ -150,7 +150,11 @@ public class RatingBar extends AbsSeekBar { */ public void setIsIndicator(boolean isIndicator) { mIsUserSeekable = !isIndicator; - setFocusable(!isIndicator); + if (isIndicator) { + setFocusable(FOCUSABLE_AUTO); + } else { + setFocusable(FOCUSABLE); + } } /** diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 9139361eda7c..38221383df3f 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -357,9 +357,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { setInputType(inputType); } - boolean focusable = true; - focusable = a.getBoolean(R.styleable.SearchView_focusable, focusable); - setFocusable(focusable); + if (getFocusable() == FOCUSABLE_AUTO) { + setFocusable(FOCUSABLE); + } a.recycle(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2f303cdba301..a11ece6608d2 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1515,26 +1515,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (hint != null) setHint(hint); /* - * Views are not normally focusable unless specified to be. + * Views are not normally clickable unless specified to be. * However, TextViews that have input or movement methods *are* - * focusable by default. + * clickable by default. By setting clickable here, we implicitly set focusable as well + * if not overridden by the developer. */ a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes); - - boolean focusable = mMovement != null || getKeyListener() != null; - boolean clickable = focusable || isClickable(); - boolean longClickable = focusable || isLongClickable(); + boolean canInputOrMove = (mMovement != null || getKeyListener() != null); + boolean clickable = canInputOrMove || isClickable(); + boolean longClickable = canInputOrMove || isLongClickable(); n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { - case com.android.internal.R.styleable.View_focusable: - focusable = a.getBoolean(attr, focusable); - break; - case com.android.internal.R.styleable.View_clickable: clickable = a.getBoolean(attr, clickable); break; @@ -1546,7 +1542,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } a.recycle(); - setFocusable(focusable); setClickable(clickable); setLongClickable(longClickable); @@ -2155,11 +2150,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void fixFocusableAndClickableSettings() { if (mMovement != null || (mEditor != null && mEditor.mKeyListener != null)) { - setFocusable(true); + setFocusable(FOCUSABLE); setClickable(true); setLongClickable(true); } else { - setFocusable(false); + setFocusable(FOCUSABLE_AUTO); setClickable(false); setLongClickable(false); } @@ -6126,7 +6121,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.mTextIsSelectable = selectable; setFocusableInTouchMode(selectable); - setFocusable(selectable); + setFocusable(FOCUSABLE_AUTO); setClickable(selectable); setLongClickable(selectable); diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index df7a5f523caa..0789241c45d2 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2274,13 +2274,16 @@ <!-- Sets the padding, in pixels, of the end edge; see {@link android.R.attr#padding}. --> <attr name="paddingEnd" format="dimension" /> - <!-- Boolean that controls whether a view can take focus. By default the user can not - move focus to a view; by setting this attribute to true the view is - allowed to take focus. This value does not impact the behavior of + <!-- Controls whether a view can take focus. By default, this is "auto" which lets the + framework determine whether a user can move focus to a view. By setting this attribute + to true the view is allowed to take focus. By setting it to "false" the view will not + take focus. This value does not impact the behavior of directly calling {@link android.view.View#requestFocus}, which will always request focus regardless of this view. It only impacts where focus navigation will try to move focus. --> - <attr name="focusable" format="boolean" /> + <attr name="focusable" format="boolean|enum"> + <enum name="auto" value="0x00000010" /> + </attr> <!-- Boolean that controls whether a view can take focus while in touch mode. If this is true for a view, that view can gain focus when clicked on, and can keep @@ -7912,7 +7915,6 @@ <attr name="queryBackground" format="reference" /> <!-- Background for the section containing the action (e.g. voice search) --> <attr name="submitBackground" format="reference" /> - <attr name="focusable" /> </declare-styleable> <declare-styleable name="Switch"> |