Loading api/current.xml +35 −0 Original line number Diff line number Diff line Loading @@ -6730,6 +6730,17 @@ visibility="public" > </field> <field name="nextFocusForward" type="int" transient="false" volatile="false" value="16843594" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> <field name="nextFocusLeft" type="int" transient="false" Loading Loading @@ -206743,6 +206754,17 @@ visibility="public" > </method> <method name="getNextFocusForwardId" return="int" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="getNextFocusLeftId" return="int" abstract="false" Loading Loading @@ -209156,6 +209178,19 @@ <parameter name="nextFocusDownId" type="int"> </parameter> </method> <method name="setNextFocusForwardId" return="void" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="nextFocusForwardId" type="int"> </parameter> </method> <method name="setNextFocusLeftId" return="void" abstract="false" core/java/android/view/FocusFinder.java +102 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.view; import android.graphics.Rect; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; /** * The algorithm used for finding the next focusable view in a given direction Loading @@ -44,6 +46,7 @@ public class FocusFinder { Rect mFocusedRect = new Rect(); Rect mOtherRect = new Rect(); Rect mBestCandidateRect = new Rect(); SequentialFocusComparator mSequentialFocusComparator = new SequentialFocusComparator(); // enforce thread local access private FocusFinder() {} Loading Loading @@ -76,6 +79,7 @@ public class FocusFinder { switch (direction) { case View.FOCUS_RIGHT: case View.FOCUS_DOWN: case View.FOCUS_FORWARD: final int rootTop = root.getScrollY(); final int rootLeft = root.getScrollX(); mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop); Loading @@ -83,6 +87,7 @@ public class FocusFinder { case View.FOCUS_LEFT: case View.FOCUS_UP: case View.FOCUS_BACKWARD: final int rootBottom = root.getScrollY() + root.getHeight(); final int rootRight = root.getScrollX() + root.getWidth(); mFocusedRect.set(rootRight, rootBottom, Loading @@ -107,6 +112,48 @@ public class FocusFinder { private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) { ArrayList<View> focusables = root.getFocusables(direction); if (focusables.isEmpty()) { // The focus cannot change. return null; } if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) { if (focused != null && !focusables.contains(focused)) { // Add the currently focused view to the list to have it sorted // along with the other views. focusables.add(focused); } try { // Note: This sort is stable. mSequentialFocusComparator.setRoot(root); Collections.sort(focusables, mSequentialFocusComparator); } finally { mSequentialFocusComparator.recycle(); } final int count = focusables.size(); switch (direction) { case View.FOCUS_FORWARD: if (focused != null) { int position = focusables.lastIndexOf(focused); if (position >= 0 && position + 1 < count) { return focusables.get(position + 1); } } return focusables.get(0); case View.FOCUS_BACKWARD: if (focused != null) { int position = focusables.indexOf(focused); if (position > 0) { return focusables.get(position - 1); } } return focusables.get(count - 1); } return null; } // initialize the best candidate to something impossible // (so the first plausible view will become the best choice) Loading Loading @@ -477,4 +524,59 @@ public class FocusFinder { throw new IllegalArgumentException("direction must be one of " + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}."); } /** * Sorts views according to their visual layout and geometry for default tab order. * This is used for sequential focus traversal. */ private static final class SequentialFocusComparator implements Comparator<View> { private final Rect mFirstRect = new Rect(); private final Rect mSecondRect = new Rect(); private ViewGroup mRoot; public void recycle() { mRoot = null; } public void setRoot(ViewGroup root) { mRoot = root; } public int compare(View first, View second) { if (first == second) { return 0; } getRect(first, mFirstRect); getRect(second, mSecondRect); if (mFirstRect.top < mSecondRect.top) { return -1; } else if (mFirstRect.top > mSecondRect.top) { return 1; } else if (mFirstRect.left < mSecondRect.left) { return -1; } else if (mFirstRect.left > mSecondRect.left) { return 1; } else if (mFirstRect.bottom < mSecondRect.bottom) { return -1; } else if (mFirstRect.bottom > mSecondRect.bottom) { return 1; } else if (mFirstRect.right < mSecondRect.right) { return -1; } else if (mFirstRect.right > mSecondRect.right) { return 1; } else { // The view are distinct but completely coincident so we consider // them equal for our purposes. Since the sort is stable, this // means that the views will retain their layout order relative to one another. return 0; } } private void getRect(View view, Rect rect) { view.getDrawingRect(rect); mRoot.offsetDescendantRectToMyCoords(view, rect); } } } core/java/android/view/View.java +93 −22 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.ScrollBarDrawable; import com.android.internal.R; import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; import java.lang.ref.WeakReference; Loading Loading @@ -2066,6 +2067,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ private int mNextFocusDownId = View.NO_ID; /** * When this view has focus and the next focus is {@link #FOCUS_FORWARD}, * the user may specify which view to go to next. */ int mNextFocusForwardId = View.NO_ID; private CheckForLongPress mPendingCheckForLongPress; private CheckForTap mPendingCheckForTap = null; private PerformClick mPerformClick; Loading Loading @@ -2424,6 +2431,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility case R.styleable.View_nextFocusDown: mNextFocusDownId = a.getResourceId(attr, View.NO_ID); break; case R.styleable.View_nextFocusForward: mNextFocusForwardId = a.getResourceId(attr, View.NO_ID); break; case R.styleable.View_minWidth: mMinWidth = a.getDimensionPixelSize(attr, 0); break; Loading Loading @@ -3112,9 +3122,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @param gainFocus True if the View has focus; false otherwise. * @param direction The direction focus has moved when requestFocus() * is called to give this view focus. Values are * {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT} or * {@link #FOCUS_RIGHT}. It may not always apply, in which * case use the default. * {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT}, * {@link #FOCUS_RIGHT}, {@link #FOCUS_FORWARD}, or {@link #FOCUS_BACKWARD}. * It may not always apply, in which case use the default. * @param previouslyFocusedRect The rectangle, in this view's coordinate * system, of the previously focused view. If applicable, this will be * passed in as finer grained information about where the focus is coming Loading Loading @@ -3359,7 +3369,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_LEFT}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusLeft */ Loading @@ -3368,9 +3379,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusLeftId * Sets the id of the view to use when the next focus is {@link #FOCUS_LEFT}. * @param nextFocusLeftId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusLeft */ Loading @@ -3379,7 +3390,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusRight */ Loading @@ -3388,9 +3400,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusRightId * Sets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}. * @param nextFocusRightId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusRight */ Loading @@ -3399,7 +3411,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_UP}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusUp */ Loading @@ -3408,9 +3421,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusUpId * Sets the id of the view to use when the next focus is {@link #FOCUS_UP}. * @param nextFocusUpId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusUp */ Loading @@ -3419,7 +3432,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_DOWN}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusDown */ Loading @@ -3428,9 +3442,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusDownId * Sets the id of the view to use when the next focus is {@link #FOCUS_DOWN}. * @param nextFocusDownId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusDown */ Loading @@ -3438,6 +3452,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mNextFocusDownId = nextFocusDownId; } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusForward */ public int getNextFocusForwardId() { return mNextFocusForwardId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}. * @param nextFocusForwardId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusForward */ public void setNextFocusForwardId(int nextFocusForwardId) { mNextFocusForwardId = nextFocusForwardId; } /** * Returns the visibility of this view and all of its ancestors * Loading Loading @@ -3949,10 +3984,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * If a user manually specified the next view id for a particular direction, * use the root to look up the view. Once a view is found, it is cached * for future lookups. * use the root to look up the view. * @param root The root view of the hierarchy containing this view. * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, * or FOCUS_BACKWARD. * @return The user specified next view, or null if there is none. */ View findUserSetNextFocus(View root, int direction) { Loading @@ -3969,6 +4004,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility case FOCUS_DOWN: if (mNextFocusDownId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusDownId); case FOCUS_FORWARD: if (mNextFocusForwardId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusForwardId); case FOCUS_BACKWARD: { final int id = mID; return root.findViewByPredicate(new Predicate<View>() { @Override public boolean apply(View t) { return t.mNextFocusForwardId == id; } }); } } return null; } Loading Loading @@ -9358,6 +9405,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return null; } /** * {@hide} * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. */ protected View findViewByPredicateTraversal(Predicate<View> predicate) { if (predicate.apply(this)) { return this; } return null; } /** * Look for a child view with the given id. If this view has the given * id, return this view. Loading Loading @@ -9386,6 +9445,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return findViewWithTagTraversal(tag); } /** * {@hide} * Look for a child view that matches the specified predicate. * If this view matches the predicate, return this view. * * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. */ public final View findViewByPredicate(Predicate<View> predicate) { return findViewByPredicateTraversal(predicate); } /** * Sets the identifier for this view. The identifier does not have to be * unique in this view's hierarchy. The identifier should be a positive Loading core/java/android/view/ViewGroup.java +28 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import android.animation.LayoutTransition; import com.android.internal.R; import com.android.internal.util.Predicate; import android.content.Context; import android.content.res.Configuration; Loading Loading @@ -2515,6 +2516,33 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return null; } /** * {@hide} */ @Override protected View findViewByPredicateTraversal(Predicate<View> predicate) { if (predicate.apply(this)) { return this; } final View[] where = mChildren; final int len = mChildrenCount; for (int i = 0; i < len; i++) { View v = where[i]; if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { v = v.findViewByPredicate(predicate); if (v != null) { return v; } } } return null; } /** * Adds a child view. If no layout parameters are already set on the child, the * default parameters for this ViewGroup are set on the child. Loading core/java/android/view/ViewRoot.java +58 −46 Original line number Diff line number Diff line Loading @@ -2415,81 +2415,78 @@ public final class ViewRoot extends Handler implements ViewParent, } /** * @param keyCode The key code * @return True if the key is directional. * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. * @return True if the key is used for keyboard navigation. */ static boolean isDirectional(int keyCode) { switch (keyCode) { private static boolean isNavigationKey(KeyEvent keyEvent) { switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_PAGE_UP: case KeyEvent.KEYCODE_PAGE_DOWN: case KeyEvent.KEYCODE_MOVE_HOME: case KeyEvent.KEYCODE_MOVE_END: case KeyEvent.KEYCODE_TAB: case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_ENTER: return true; } return false; } /** * Returns true if this key is a keyboard key. * Returns true if the key is used for typing. * @param keyEvent The key event. * @return whether this key is a keyboard key. * @return True if the key is used for typing. */ private static boolean isKeyboardKey(KeyEvent keyEvent) { final int convertedKey = keyEvent.getUnicodeChar(); return convertedKey > 0; private static boolean isTypingKey(KeyEvent keyEvent) { return keyEvent.getUnicodeChar() > 0; } /** * See if the key event means we should leave touch mode (and leave touch * mode if so). * See if the key event means we should leave touch mode (and leave touch mode if so). * @param event The key event. * @return Whether this key event should be consumed (meaning the act of * leaving touch mode alone is considered the event). */ private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { // Only relevant in touch mode. if (!mAttachInfo.mInTouchMode) { return false; } // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. final int action = event.getAction(); if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { return false; } // Don't leave touch mode if the IME told us not to. if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { return false; } // only relevant if we are in touch mode if (!mAttachInfo.mInTouchMode) { return false; // If the key can be used for keyboard navigation then leave touch mode // and select a focused view if needed (in ensureTouchMode). // When a new focused view is selected, we consume the navigation key because // navigation doesn't make much sense unless a view already has focus so // the key's purpose is to set focus. if (isNavigationKey(event)) { return ensureTouchMode(false); } // if something like an edit text has focus and the user is typing, // leave touch mode // // note: the condition of not being a keyboard key is kind of a hacky // approximation of whether we think the focused view will want the // key; if we knew for sure whether the focused view would consume // the event, that would be better. if (isKeyboardKey(event) && mView != null && mView.hasFocus()) { mFocusedView = mView.findFocus(); if ((mFocusedView instanceof ViewGroup) && ((ViewGroup) mFocusedView).getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS) { // something has focus, but is holding it weakly as a container return false; } if (ensureTouchMode(false)) { throw new IllegalStateException("should not have changed focus " + "when leaving touch mode while a view has focus."); } // If the key can be used for typing then leave touch mode // and select a focused view if needed (in ensureTouchMode). // Always allow the view to process the typing key. if (isTypingKey(event)) { ensureTouchMode(false); return false; } if (isDirectional(event.getKeyCode())) { // no view has focus, so we leave touch mode (and find something // to give focus to). the event is consumed if we were able to // find something to give focus to. return ensureTouchMode(false); } return false; } Loading Loading @@ -2640,16 +2637,31 @@ public final class ViewRoot extends Handler implements ViewParent, int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: if (event.hasNoModifiers()) { direction = View.FOCUS_LEFT; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (event.hasNoModifiers()) { direction = View.FOCUS_RIGHT; } break; case KeyEvent.KEYCODE_DPAD_UP: if (event.hasNoModifiers()) { direction = View.FOCUS_UP; } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (event.hasNoModifiers()) { direction = View.FOCUS_DOWN; } break; case KeyEvent.KEYCODE_TAB: if (event.hasNoModifiers()) { direction = View.FOCUS_FORWARD; } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { direction = View.FOCUS_BACKWARD; } break; } Loading Loading
api/current.xml +35 −0 Original line number Diff line number Diff line Loading @@ -6730,6 +6730,17 @@ visibility="public" > </field> <field name="nextFocusForward" type="int" transient="false" volatile="false" value="16843594" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> <field name="nextFocusLeft" type="int" transient="false" Loading Loading @@ -206743,6 +206754,17 @@ visibility="public" > </method> <method name="getNextFocusForwardId" return="int" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="getNextFocusLeftId" return="int" abstract="false" Loading Loading @@ -209156,6 +209178,19 @@ <parameter name="nextFocusDownId" type="int"> </parameter> </method> <method name="setNextFocusForwardId" return="void" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="nextFocusForwardId" type="int"> </parameter> </method> <method name="setNextFocusLeftId" return="void" abstract="false"
core/java/android/view/FocusFinder.java +102 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.view; import android.graphics.Rect; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; /** * The algorithm used for finding the next focusable view in a given direction Loading @@ -44,6 +46,7 @@ public class FocusFinder { Rect mFocusedRect = new Rect(); Rect mOtherRect = new Rect(); Rect mBestCandidateRect = new Rect(); SequentialFocusComparator mSequentialFocusComparator = new SequentialFocusComparator(); // enforce thread local access private FocusFinder() {} Loading Loading @@ -76,6 +79,7 @@ public class FocusFinder { switch (direction) { case View.FOCUS_RIGHT: case View.FOCUS_DOWN: case View.FOCUS_FORWARD: final int rootTop = root.getScrollY(); final int rootLeft = root.getScrollX(); mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop); Loading @@ -83,6 +87,7 @@ public class FocusFinder { case View.FOCUS_LEFT: case View.FOCUS_UP: case View.FOCUS_BACKWARD: final int rootBottom = root.getScrollY() + root.getHeight(); final int rootRight = root.getScrollX() + root.getWidth(); mFocusedRect.set(rootRight, rootBottom, Loading @@ -107,6 +112,48 @@ public class FocusFinder { private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) { ArrayList<View> focusables = root.getFocusables(direction); if (focusables.isEmpty()) { // The focus cannot change. return null; } if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) { if (focused != null && !focusables.contains(focused)) { // Add the currently focused view to the list to have it sorted // along with the other views. focusables.add(focused); } try { // Note: This sort is stable. mSequentialFocusComparator.setRoot(root); Collections.sort(focusables, mSequentialFocusComparator); } finally { mSequentialFocusComparator.recycle(); } final int count = focusables.size(); switch (direction) { case View.FOCUS_FORWARD: if (focused != null) { int position = focusables.lastIndexOf(focused); if (position >= 0 && position + 1 < count) { return focusables.get(position + 1); } } return focusables.get(0); case View.FOCUS_BACKWARD: if (focused != null) { int position = focusables.indexOf(focused); if (position > 0) { return focusables.get(position - 1); } } return focusables.get(count - 1); } return null; } // initialize the best candidate to something impossible // (so the first plausible view will become the best choice) Loading Loading @@ -477,4 +524,59 @@ public class FocusFinder { throw new IllegalArgumentException("direction must be one of " + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}."); } /** * Sorts views according to their visual layout and geometry for default tab order. * This is used for sequential focus traversal. */ private static final class SequentialFocusComparator implements Comparator<View> { private final Rect mFirstRect = new Rect(); private final Rect mSecondRect = new Rect(); private ViewGroup mRoot; public void recycle() { mRoot = null; } public void setRoot(ViewGroup root) { mRoot = root; } public int compare(View first, View second) { if (first == second) { return 0; } getRect(first, mFirstRect); getRect(second, mSecondRect); if (mFirstRect.top < mSecondRect.top) { return -1; } else if (mFirstRect.top > mSecondRect.top) { return 1; } else if (mFirstRect.left < mSecondRect.left) { return -1; } else if (mFirstRect.left > mSecondRect.left) { return 1; } else if (mFirstRect.bottom < mSecondRect.bottom) { return -1; } else if (mFirstRect.bottom > mSecondRect.bottom) { return 1; } else if (mFirstRect.right < mSecondRect.right) { return -1; } else if (mFirstRect.right > mSecondRect.right) { return 1; } else { // The view are distinct but completely coincident so we consider // them equal for our purposes. Since the sort is stable, this // means that the views will retain their layout order relative to one another. return 0; } } private void getRect(View view, Rect rect) { view.getDrawingRect(rect); mRoot.offsetDescendantRectToMyCoords(view, rect); } } }
core/java/android/view/View.java +93 −22 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.ScrollBarDrawable; import com.android.internal.R; import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; import java.lang.ref.WeakReference; Loading Loading @@ -2066,6 +2067,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ private int mNextFocusDownId = View.NO_ID; /** * When this view has focus and the next focus is {@link #FOCUS_FORWARD}, * the user may specify which view to go to next. */ int mNextFocusForwardId = View.NO_ID; private CheckForLongPress mPendingCheckForLongPress; private CheckForTap mPendingCheckForTap = null; private PerformClick mPerformClick; Loading Loading @@ -2424,6 +2431,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility case R.styleable.View_nextFocusDown: mNextFocusDownId = a.getResourceId(attr, View.NO_ID); break; case R.styleable.View_nextFocusForward: mNextFocusForwardId = a.getResourceId(attr, View.NO_ID); break; case R.styleable.View_minWidth: mMinWidth = a.getDimensionPixelSize(attr, 0); break; Loading Loading @@ -3112,9 +3122,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @param gainFocus True if the View has focus; false otherwise. * @param direction The direction focus has moved when requestFocus() * is called to give this view focus. Values are * {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT} or * {@link #FOCUS_RIGHT}. It may not always apply, in which * case use the default. * {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT}, * {@link #FOCUS_RIGHT}, {@link #FOCUS_FORWARD}, or {@link #FOCUS_BACKWARD}. * It may not always apply, in which case use the default. * @param previouslyFocusedRect The rectangle, in this view's coordinate * system, of the previously focused view. If applicable, this will be * passed in as finer grained information about where the focus is coming Loading Loading @@ -3359,7 +3369,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_LEFT}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusLeft */ Loading @@ -3368,9 +3379,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusLeftId * Sets the id of the view to use when the next focus is {@link #FOCUS_LEFT}. * @param nextFocusLeftId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusLeft */ Loading @@ -3379,7 +3390,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusRight */ Loading @@ -3388,9 +3400,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusRightId * Sets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}. * @param nextFocusRightId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusRight */ Loading @@ -3399,7 +3411,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_UP}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusUp */ Loading @@ -3408,9 +3421,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusUpId * Sets the id of the view to use when the next focus is {@link #FOCUS_UP}. * @param nextFocusUpId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusUp */ Loading @@ -3419,7 +3432,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * @return The user specified next focus ID. * Gets the id of the view to use when the next focus is {@link #FOCUS_DOWN}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusDown */ Loading @@ -3428,9 +3442,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** * Set the id of the view to use for the next focus * * @param nextFocusDownId * Sets the id of the view to use when the next focus is {@link #FOCUS_DOWN}. * @param nextFocusDownId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusDown */ Loading @@ -3438,6 +3452,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mNextFocusDownId = nextFocusDownId; } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusForward */ public int getNextFocusForwardId() { return mNextFocusForwardId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}. * @param nextFocusForwardId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusForward */ public void setNextFocusForwardId(int nextFocusForwardId) { mNextFocusForwardId = nextFocusForwardId; } /** * Returns the visibility of this view and all of its ancestors * Loading Loading @@ -3949,10 +3984,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * If a user manually specified the next view id for a particular direction, * use the root to look up the view. Once a view is found, it is cached * for future lookups. * use the root to look up the view. * @param root The root view of the hierarchy containing this view. * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, * or FOCUS_BACKWARD. * @return The user specified next view, or null if there is none. */ View findUserSetNextFocus(View root, int direction) { Loading @@ -3969,6 +4004,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility case FOCUS_DOWN: if (mNextFocusDownId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusDownId); case FOCUS_FORWARD: if (mNextFocusForwardId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusForwardId); case FOCUS_BACKWARD: { final int id = mID; return root.findViewByPredicate(new Predicate<View>() { @Override public boolean apply(View t) { return t.mNextFocusForwardId == id; } }); } } return null; } Loading Loading @@ -9358,6 +9405,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return null; } /** * {@hide} * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. */ protected View findViewByPredicateTraversal(Predicate<View> predicate) { if (predicate.apply(this)) { return this; } return null; } /** * Look for a child view with the given id. If this view has the given * id, return this view. Loading Loading @@ -9386,6 +9445,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return findViewWithTagTraversal(tag); } /** * {@hide} * Look for a child view that matches the specified predicate. * If this view matches the predicate, return this view. * * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. */ public final View findViewByPredicate(Predicate<View> predicate) { return findViewByPredicateTraversal(predicate); } /** * Sets the identifier for this view. The identifier does not have to be * unique in this view's hierarchy. The identifier should be a positive Loading
core/java/android/view/ViewGroup.java +28 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import android.animation.LayoutTransition; import com.android.internal.R; import com.android.internal.util.Predicate; import android.content.Context; import android.content.res.Configuration; Loading Loading @@ -2515,6 +2516,33 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return null; } /** * {@hide} */ @Override protected View findViewByPredicateTraversal(Predicate<View> predicate) { if (predicate.apply(this)) { return this; } final View[] where = mChildren; final int len = mChildrenCount; for (int i = 0; i < len; i++) { View v = where[i]; if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { v = v.findViewByPredicate(predicate); if (v != null) { return v; } } } return null; } /** * Adds a child view. If no layout parameters are already set on the child, the * default parameters for this ViewGroup are set on the child. Loading
core/java/android/view/ViewRoot.java +58 −46 Original line number Diff line number Diff line Loading @@ -2415,81 +2415,78 @@ public final class ViewRoot extends Handler implements ViewParent, } /** * @param keyCode The key code * @return True if the key is directional. * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. * @return True if the key is used for keyboard navigation. */ static boolean isDirectional(int keyCode) { switch (keyCode) { private static boolean isNavigationKey(KeyEvent keyEvent) { switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_PAGE_UP: case KeyEvent.KEYCODE_PAGE_DOWN: case KeyEvent.KEYCODE_MOVE_HOME: case KeyEvent.KEYCODE_MOVE_END: case KeyEvent.KEYCODE_TAB: case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_ENTER: return true; } return false; } /** * Returns true if this key is a keyboard key. * Returns true if the key is used for typing. * @param keyEvent The key event. * @return whether this key is a keyboard key. * @return True if the key is used for typing. */ private static boolean isKeyboardKey(KeyEvent keyEvent) { final int convertedKey = keyEvent.getUnicodeChar(); return convertedKey > 0; private static boolean isTypingKey(KeyEvent keyEvent) { return keyEvent.getUnicodeChar() > 0; } /** * See if the key event means we should leave touch mode (and leave touch * mode if so). * See if the key event means we should leave touch mode (and leave touch mode if so). * @param event The key event. * @return Whether this key event should be consumed (meaning the act of * leaving touch mode alone is considered the event). */ private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { // Only relevant in touch mode. if (!mAttachInfo.mInTouchMode) { return false; } // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. final int action = event.getAction(); if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { return false; } // Don't leave touch mode if the IME told us not to. if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { return false; } // only relevant if we are in touch mode if (!mAttachInfo.mInTouchMode) { return false; // If the key can be used for keyboard navigation then leave touch mode // and select a focused view if needed (in ensureTouchMode). // When a new focused view is selected, we consume the navigation key because // navigation doesn't make much sense unless a view already has focus so // the key's purpose is to set focus. if (isNavigationKey(event)) { return ensureTouchMode(false); } // if something like an edit text has focus and the user is typing, // leave touch mode // // note: the condition of not being a keyboard key is kind of a hacky // approximation of whether we think the focused view will want the // key; if we knew for sure whether the focused view would consume // the event, that would be better. if (isKeyboardKey(event) && mView != null && mView.hasFocus()) { mFocusedView = mView.findFocus(); if ((mFocusedView instanceof ViewGroup) && ((ViewGroup) mFocusedView).getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS) { // something has focus, but is holding it weakly as a container return false; } if (ensureTouchMode(false)) { throw new IllegalStateException("should not have changed focus " + "when leaving touch mode while a view has focus."); } // If the key can be used for typing then leave touch mode // and select a focused view if needed (in ensureTouchMode). // Always allow the view to process the typing key. if (isTypingKey(event)) { ensureTouchMode(false); return false; } if (isDirectional(event.getKeyCode())) { // no view has focus, so we leave touch mode (and find something // to give focus to). the event is consumed if we were able to // find something to give focus to. return ensureTouchMode(false); } return false; } Loading Loading @@ -2640,16 +2637,31 @@ public final class ViewRoot extends Handler implements ViewParent, int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: if (event.hasNoModifiers()) { direction = View.FOCUS_LEFT; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (event.hasNoModifiers()) { direction = View.FOCUS_RIGHT; } break; case KeyEvent.KEYCODE_DPAD_UP: if (event.hasNoModifiers()) { direction = View.FOCUS_UP; } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (event.hasNoModifiers()) { direction = View.FOCUS_DOWN; } break; case KeyEvent.KEYCODE_TAB: if (event.hasNoModifiers()) { direction = View.FOCUS_FORWARD; } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { direction = View.FOCUS_BACKWARD; } break; } Loading