am dcf21b96: Merge "Enhance computation of click point for accessibility." into lmp-mr1-dev automerge: 74a4a8d
* commit 'dcf21b968d3bd15f7e917ee585cf2a8dbe1babd5': Enhance computation of click point for accessibility.
This commit is contained in:
@@ -5877,6 +5877,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
return true;
|
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) {
|
static void offsetRects(List<RectF> rects, float offsetX, float offsetY) {
|
||||||
final int rectCount = rects.size();
|
final int rectCount = rects.size();
|
||||||
for (int i = 0; i < rectCount; i++) {
|
for (int i = 0; i < rectCount; i++) {
|
||||||
|
|||||||
@@ -51,8 +51,10 @@ import com.android.internal.util.Predicate;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
|
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
|
||||||
|
|
||||||
@@ -468,6 +470,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
@ViewDebug.ExportedProperty(category = "layout")
|
@ViewDebug.ExportedProperty(category = "layout")
|
||||||
private int mChildCountWithTransientState = 0;
|
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
|
* Currently registered axes for nested scrolling. Flag set consisting of
|
||||||
* {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
|
* {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
|
||||||
@@ -817,19 +822,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether any clickable siblings cover the child
|
Iterator<View> iterator = obtainOrderedChildIterator();
|
||||||
// view and if so keep track of the intersections. Also
|
while (iterator.hasNext()) {
|
||||||
// respect Z ordering when iterating over children.
|
View sibling = iterator.next();
|
||||||
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);
|
|
||||||
|
|
||||||
// We care only about siblings over the child.
|
// We care only about siblings over the child.
|
||||||
if (sibling == child) {
|
if (sibling == child) {
|
||||||
@@ -837,12 +832,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ignore invisible views as they are not interactive.
|
// Ignore invisible views as they are not interactive.
|
||||||
if (sibling.getVisibility() != View.VISIBLE) {
|
if (!isVisible(sibling)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If sibling is not interactive we do not care.
|
|
||||||
if (!sibling.isClickable() && !sibling.isLongClickable()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,29 +840,36 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
RectF siblingBounds = mAttachInfo.mTmpTransformRect1;
|
RectF siblingBounds = mAttachInfo.mTmpTransformRect1;
|
||||||
siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight());
|
siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight());
|
||||||
|
|
||||||
// Take into account the sibling transformation matrix.
|
// Translate the sibling bounds to our coordinates.
|
||||||
if (!sibling.hasIdentityMatrix()) {
|
offsetChildRectToMyCoords(siblingBounds, sibling);
|
||||||
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);
|
|
||||||
|
|
||||||
// Compute the intersection between the child and the sibling.
|
// Compute the intersection between the child and the sibling.
|
||||||
if (siblingBounds.intersect(bounds)) {
|
if (siblingBounds.intersect(bounds)) {
|
||||||
// If an interactive sibling completely covers the child, done.
|
List<RectF> clickableRects = new ArrayList<>();
|
||||||
if (siblingBounds.equals(bounds)) {
|
sibling.addClickableRectsForAccessibility(clickableRects);
|
||||||
if (orderedList != null) orderedList.clear();
|
|
||||||
return false;
|
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.
|
||||||
|
intersections.add(clickableRect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Keep track of the intersection rectangle.
|
|
||||||
RectF intersection = new RectF(siblingBounds);
|
|
||||||
intersections.add(intersection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (orderedList != null) orderedList.clear();
|
|
||||||
|
releaseOrderedChildIterator();
|
||||||
|
|
||||||
if (mParent instanceof ViewGroup) {
|
if (mParent instanceof ViewGroup) {
|
||||||
ViewGroup parentGroup = (ViewGroup) mParent;
|
ViewGroup parentGroup = (ViewGroup) mParent;
|
||||||
@@ -883,6 +880,94 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
return true;
|
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.
|
* Called when a child view has changed whether or not it is tracking transient state.
|
||||||
*/
|
*/
|
||||||
@@ -7298,4 +7383,57 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
|||||||
|
|
||||||
canvas.drawLines(sDebugLines, paint);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user