Loading core/java/android/view/View.java +15 −0 Original line number Diff line number Diff line Loading @@ -5877,6 +5877,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return true; } /** * Adds the clickable rectangles withing the bounds of this view. They * may overlap. This method is intended for use only by the accessibility * layer. * * @param outRects List to which to add clickable areas. */ void addClickableRectsForAccessibility(List<RectF> outRects) { if (isClickable() || isLongClickable()) { RectF bounds = new RectF(); bounds.set(0, 0, getWidth(), getHeight()); outRects.add(bounds); } } static void offsetRects(List<RectF> rects, float offsetX, float offsetY) { final int rectCount = rects.size(); for (int i = 0; i < rectCount; i++) { Loading core/java/android/view/ViewGroup.java +174 −36 Original line number Diff line number Diff line Loading @@ -51,8 +51,10 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; Loading Loading @@ -468,6 +470,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @ViewDebug.ExportedProperty(category = "layout") private int mChildCountWithTransientState = 0; // Iterator over the children in decreasing Z order (top children first). private OrderedChildIterator mOrderedChildIterator; /** * Currently registered axes for nested scrolling. Flag set consisting of * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE} Loading Loading @@ -817,19 +822,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return false; } // Check whether any clickable siblings cover the child // view and if so keep track of the intersections. Also // respect Z ordering when iterating over children. ArrayList<View> orderedList = buildOrderedChildList(); final boolean useCustomOrder = orderedList == null && isChildrenDrawingOrderEnabled(); final int childCount = mChildrenCount; for (int i = childCount - 1; i >= 0; i--) { final int childIndex = useCustomOrder ? getChildDrawingOrder(childCount, i) : i; final View sibling = (orderedList == null) ? mChildren[childIndex] : orderedList.get(childIndex); Iterator<View> iterator = obtainOrderedChildIterator(); while (iterator.hasNext()) { View sibling = iterator.next(); // We care only about siblings over the child. if (sibling == child) { Loading @@ -837,12 +832,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } // Ignore invisible views as they are not interactive. if (sibling.getVisibility() != View.VISIBLE) { continue; } // If sibling is not interactive we do not care. if (!sibling.isClickable() && !sibling.isLongClickable()) { if (!isVisible(sibling)) { continue; } Loading @@ -850,29 +840,36 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager RectF siblingBounds = mAttachInfo.mTmpTransformRect1; siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight()); // Take into account the sibling transformation matrix. if (!sibling.hasIdentityMatrix()) { sibling.getMatrix().mapRect(siblingBounds); } // Offset the sibling to our coordinates. final int siblingDx = sibling.mLeft - mScrollX; final int siblingDy = sibling.mTop - mScrollY; siblingBounds.offset(siblingDx, siblingDy); // Translate the sibling bounds to our coordinates. offsetChildRectToMyCoords(siblingBounds, sibling); // Compute the intersection between the child and the sibling. if (siblingBounds.intersect(bounds)) { // If an interactive sibling completely covers the child, done. if (siblingBounds.equals(bounds)) { if (orderedList != null) orderedList.clear(); List<RectF> clickableRects = new ArrayList<>(); sibling.addClickableRectsForAccessibility(clickableRects); final int clickableRectCount = clickableRects.size(); for (int j = 0; j < clickableRectCount; j++) { RectF clickableRect = clickableRects.get(j); // Translate the clickable rect to our coordinates. offsetChildRectToMyCoords(clickableRect, sibling); // Compute the intersection between the child and the clickable rects. if (clickableRect.intersect(bounds)) { // If a clickable rect completely covers the child, done. if (clickableRect.equals(bounds)) { releaseOrderedChildIterator(); return false; } // Keep track of the intersection rectangle. RectF intersection = new RectF(siblingBounds); intersections.add(intersection); intersections.add(clickableRect); } } } if (orderedList != null) orderedList.clear(); } releaseOrderedChildIterator(); if (mParent instanceof ViewGroup) { ViewGroup parentGroup = (ViewGroup) mParent; Loading @@ -883,6 +880,94 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return true; } @Override void addClickableRectsForAccessibility(List<RectF> outRects) { int sizeBefore = outRects.size(); super.addClickableRectsForAccessibility(outRects); // If we added ourselves, then no need to visit children. if (outRects.size() > sizeBefore) { return; } Iterator<View> iterator = obtainOrderedChildIterator(); while (iterator.hasNext()) { View child = iterator.next(); // Cannot click on an invisible view. if (!isVisible(child)) { continue; } sizeBefore = outRects.size(); // Add clickable rects in the child bounds. child.addClickableRectsForAccessibility(outRects); // Offset the clickable rects for out children to our coordinates. final int sizeAfter = outRects.size(); for (int j = sizeBefore; j < sizeAfter; j++) { RectF rect = outRects.get(j); // Translate the clickable rect to our coordinates. offsetChildRectToMyCoords(rect, child); // If a clickable rect fills the parent, done. if ((int) rect.left == 0 && (int) rect.top == 0 && (int) rect.right == mRight && (int) rect.bottom == mBottom) { releaseOrderedChildIterator(); return; } } } releaseOrderedChildIterator(); } private void offsetChildRectToMyCoords(RectF rect, View child) { if (!child.hasIdentityMatrix()) { child.getMatrix().mapRect(rect); } final int childDx = child.mLeft - mScrollX; final int childDy = child.mTop - mScrollY; rect.offset(childDx, childDy); } private static boolean isVisible(View view) { return (view.getAlpha() > 0 && view.getTransitionAlpha() > 0 && view.getVisibility() == VISIBLE); } /** * Obtains the iterator to traverse the children in a descending Z order. * Only one party can use the iterator at any given time and you cannot * modify the children while using this iterator. Acquisition if already * obtained is an error. * * @return The child iterator. */ OrderedChildIterator obtainOrderedChildIterator() { if (mOrderedChildIterator == null) { mOrderedChildIterator = new OrderedChildIterator(); } else if (mOrderedChildIterator.isInitialized()) { throw new IllegalStateException("Already obtained"); } mOrderedChildIterator.initialize(); return mOrderedChildIterator; } /** * Releases the iterator to traverse the children in a descending Z order. * Release if not obtained is an error. */ void releaseOrderedChildIterator() { if (mOrderedChildIterator == null || !mOrderedChildIterator.isInitialized()) { throw new IllegalStateException("Not obtained"); } mOrderedChildIterator.release(); } /** * Called when a child view has changed whether or not it is tracking transient state. */ Loading Loading @@ -7298,4 +7383,57 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.drawLines(sDebugLines, paint); } private final class OrderedChildIterator implements Iterator<View> { private List<View> mOrderedChildList; private boolean mUseCustomOrder; private int mCurrentIndex; private boolean mInitialized; public void initialize() { mOrderedChildList = buildOrderedChildList(); mUseCustomOrder = (mOrderedChildList == null) && isChildrenDrawingOrderEnabled(); mCurrentIndex = mChildrenCount - 1; mInitialized = true; } public void release() { if (mOrderedChildList != null) { mOrderedChildList.clear(); } mUseCustomOrder = false; mCurrentIndex = 0; mInitialized = false; } public boolean isInitialized() { return mInitialized; } @Override public boolean hasNext() { return (mCurrentIndex >= 0); } @Override public View next() { if (!hasNext()) { throw new NoSuchElementException("No such element"); } return getChild(mCurrentIndex--); } private View getChild(int index) { final int childIndex = mUseCustomOrder ? getChildDrawingOrder(mChildrenCount, index) : index; return (mOrderedChildList == null) ? mChildren[childIndex] : mOrderedChildList.get(childIndex); } @Override public void remove() { throw new UnsupportedOperationException(); } } } Loading
core/java/android/view/View.java +15 −0 Original line number Diff line number Diff line Loading @@ -5877,6 +5877,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return true; } /** * Adds the clickable rectangles withing the bounds of this view. They * may overlap. This method is intended for use only by the accessibility * layer. * * @param outRects List to which to add clickable areas. */ void addClickableRectsForAccessibility(List<RectF> outRects) { if (isClickable() || isLongClickable()) { RectF bounds = new RectF(); bounds.set(0, 0, getWidth(), getHeight()); outRects.add(bounds); } } static void offsetRects(List<RectF> rects, float offsetX, float offsetY) { final int rectCount = rects.size(); for (int i = 0; i < rectCount; i++) { Loading
core/java/android/view/ViewGroup.java +174 −36 Original line number Diff line number Diff line Loading @@ -51,8 +51,10 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; Loading Loading @@ -468,6 +470,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @ViewDebug.ExportedProperty(category = "layout") private int mChildCountWithTransientState = 0; // Iterator over the children in decreasing Z order (top children first). private OrderedChildIterator mOrderedChildIterator; /** * Currently registered axes for nested scrolling. Flag set consisting of * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE} Loading Loading @@ -817,19 +822,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return false; } // Check whether any clickable siblings cover the child // view and if so keep track of the intersections. Also // respect Z ordering when iterating over children. ArrayList<View> orderedList = buildOrderedChildList(); final boolean useCustomOrder = orderedList == null && isChildrenDrawingOrderEnabled(); final int childCount = mChildrenCount; for (int i = childCount - 1; i >= 0; i--) { final int childIndex = useCustomOrder ? getChildDrawingOrder(childCount, i) : i; final View sibling = (orderedList == null) ? mChildren[childIndex] : orderedList.get(childIndex); Iterator<View> iterator = obtainOrderedChildIterator(); while (iterator.hasNext()) { View sibling = iterator.next(); // We care only about siblings over the child. if (sibling == child) { Loading @@ -837,12 +832,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } // Ignore invisible views as they are not interactive. if (sibling.getVisibility() != View.VISIBLE) { continue; } // If sibling is not interactive we do not care. if (!sibling.isClickable() && !sibling.isLongClickable()) { if (!isVisible(sibling)) { continue; } Loading @@ -850,29 +840,36 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager RectF siblingBounds = mAttachInfo.mTmpTransformRect1; siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight()); // Take into account the sibling transformation matrix. if (!sibling.hasIdentityMatrix()) { sibling.getMatrix().mapRect(siblingBounds); } // Offset the sibling to our coordinates. final int siblingDx = sibling.mLeft - mScrollX; final int siblingDy = sibling.mTop - mScrollY; siblingBounds.offset(siblingDx, siblingDy); // Translate the sibling bounds to our coordinates. offsetChildRectToMyCoords(siblingBounds, sibling); // Compute the intersection between the child and the sibling. if (siblingBounds.intersect(bounds)) { // If an interactive sibling completely covers the child, done. if (siblingBounds.equals(bounds)) { if (orderedList != null) orderedList.clear(); List<RectF> clickableRects = new ArrayList<>(); sibling.addClickableRectsForAccessibility(clickableRects); final int clickableRectCount = clickableRects.size(); for (int j = 0; j < clickableRectCount; j++) { RectF clickableRect = clickableRects.get(j); // Translate the clickable rect to our coordinates. offsetChildRectToMyCoords(clickableRect, sibling); // Compute the intersection between the child and the clickable rects. if (clickableRect.intersect(bounds)) { // If a clickable rect completely covers the child, done. if (clickableRect.equals(bounds)) { releaseOrderedChildIterator(); return false; } // Keep track of the intersection rectangle. RectF intersection = new RectF(siblingBounds); intersections.add(intersection); intersections.add(clickableRect); } } } if (orderedList != null) orderedList.clear(); } releaseOrderedChildIterator(); if (mParent instanceof ViewGroup) { ViewGroup parentGroup = (ViewGroup) mParent; Loading @@ -883,6 +880,94 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return true; } @Override void addClickableRectsForAccessibility(List<RectF> outRects) { int sizeBefore = outRects.size(); super.addClickableRectsForAccessibility(outRects); // If we added ourselves, then no need to visit children. if (outRects.size() > sizeBefore) { return; } Iterator<View> iterator = obtainOrderedChildIterator(); while (iterator.hasNext()) { View child = iterator.next(); // Cannot click on an invisible view. if (!isVisible(child)) { continue; } sizeBefore = outRects.size(); // Add clickable rects in the child bounds. child.addClickableRectsForAccessibility(outRects); // Offset the clickable rects for out children to our coordinates. final int sizeAfter = outRects.size(); for (int j = sizeBefore; j < sizeAfter; j++) { RectF rect = outRects.get(j); // Translate the clickable rect to our coordinates. offsetChildRectToMyCoords(rect, child); // If a clickable rect fills the parent, done. if ((int) rect.left == 0 && (int) rect.top == 0 && (int) rect.right == mRight && (int) rect.bottom == mBottom) { releaseOrderedChildIterator(); return; } } } releaseOrderedChildIterator(); } private void offsetChildRectToMyCoords(RectF rect, View child) { if (!child.hasIdentityMatrix()) { child.getMatrix().mapRect(rect); } final int childDx = child.mLeft - mScrollX; final int childDy = child.mTop - mScrollY; rect.offset(childDx, childDy); } private static boolean isVisible(View view) { return (view.getAlpha() > 0 && view.getTransitionAlpha() > 0 && view.getVisibility() == VISIBLE); } /** * Obtains the iterator to traverse the children in a descending Z order. * Only one party can use the iterator at any given time and you cannot * modify the children while using this iterator. Acquisition if already * obtained is an error. * * @return The child iterator. */ OrderedChildIterator obtainOrderedChildIterator() { if (mOrderedChildIterator == null) { mOrderedChildIterator = new OrderedChildIterator(); } else if (mOrderedChildIterator.isInitialized()) { throw new IllegalStateException("Already obtained"); } mOrderedChildIterator.initialize(); return mOrderedChildIterator; } /** * Releases the iterator to traverse the children in a descending Z order. * Release if not obtained is an error. */ void releaseOrderedChildIterator() { if (mOrderedChildIterator == null || !mOrderedChildIterator.isInitialized()) { throw new IllegalStateException("Not obtained"); } mOrderedChildIterator.release(); } /** * Called when a child view has changed whether or not it is tracking transient state. */ Loading Loading @@ -7298,4 +7383,57 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.drawLines(sDebugLines, paint); } private final class OrderedChildIterator implements Iterator<View> { private List<View> mOrderedChildList; private boolean mUseCustomOrder; private int mCurrentIndex; private boolean mInitialized; public void initialize() { mOrderedChildList = buildOrderedChildList(); mUseCustomOrder = (mOrderedChildList == null) && isChildrenDrawingOrderEnabled(); mCurrentIndex = mChildrenCount - 1; mInitialized = true; } public void release() { if (mOrderedChildList != null) { mOrderedChildList.clear(); } mUseCustomOrder = false; mCurrentIndex = 0; mInitialized = false; } public boolean isInitialized() { return mInitialized; } @Override public boolean hasNext() { return (mCurrentIndex >= 0); } @Override public View next() { if (!hasNext()) { throw new NoSuchElementException("No such element"); } return getChild(mCurrentIndex--); } private View getChild(int index) { final int childIndex = mUseCustomOrder ? getChildDrawingOrder(mChildrenCount, index) : index; return (mOrderedChildList == null) ? mChildren[childIndex] : mOrderedChildList.get(childIndex); } @Override public void remove() { throw new UnsupportedOperationException(); } } }