summaryrefslogtreecommitdiff
path: root/core/java/android/view/ViewGroup.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/view/ViewGroup.java')
-rw-r--r--core/java/android/view/ViewGroup.java216
1 files changed, 171 insertions, 45 deletions
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3aa4cfb34374..94142376b263 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -38,6 +38,7 @@ import android.util.Log;
import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -130,7 +131,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* A Transformation used when drawing children, to
* apply on the child being drawn.
*/
- final Transformation mChildTransformation = new Transformation();
+ private Transformation mChildTransformation;
/**
* Used to track the current invalidation region.
@@ -154,7 +155,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private boolean mChildAcceptsDrag;
// Used during drag dispatch
- private final PointF mLocalPoint = new PointF();
+ private PointF mLocalPoint;
// Layout animation
private LayoutAnimationController mLayoutAnimationController;
@@ -206,7 +207,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
*/
- private int mLayoutMode = DEFAULT_LAYOUT_MODE;
+ private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
/**
* NOTE: If you change the flags below make sure to reflect the changes
@@ -347,6 +348,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
/**
+ * When true, indicates that a layoutMode has been explicitly set, either with
+ * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
+ * This distinguishes the situation in which a layout mode was inherited from
+ * one of the ViewGroup's ancestors and cached locally.
+ */
+ private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
+
+ /**
* Indicates which types of drawing caches are to be kept in memory.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -375,6 +384,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// Layout Modes
+ private static final int LAYOUT_MODE_UNDEFINED = -1;
+
/**
* This constant is a {@link #setLayoutMode(int) layoutMode}.
* Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
@@ -391,7 +402,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
/** @hide */
- public static int DEFAULT_LAYOUT_MODE = LAYOUT_MODE_CLIP_BOUNDS;
+ public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
/**
* We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
@@ -533,7 +544,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
break;
case R.styleable.ViewGroup_layoutMode:
- setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE));
+ setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
break;
}
}
@@ -732,8 +743,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Called when a child view has changed whether or not it is tracking transient state.
- *
- * @hide
*/
public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
final boolean oldHasTransientState = hasTransientState();
@@ -754,9 +763,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
- /**
- * @hide
- */
@Override
public boolean hasTransientState() {
return mChildCountWithTransientState > 0 || super.hasTransientState();
@@ -1118,9 +1124,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
removeFromArray(index);
addInArray(child, mChildrenCount);
child.mParent = this;
+ requestLayout();
+ invalidate();
}
}
+ private PointF getLocalPoint() {
+ if (mLocalPoint == null) mLocalPoint = new PointF();
+ return mLocalPoint;
+ }
+
/**
* {@inheritDoc}
*/
@@ -1134,6 +1147,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
ViewRootImpl root = getViewRootImpl();
// Dispatch down the view hierarchy
+ final PointF localPoint = getLocalPoint();
+
switch (event.mAction) {
case DragEvent.ACTION_DRAG_STARTED: {
// clear state to recalculate which views we drag over
@@ -1194,7 +1209,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
case DragEvent.ACTION_DRAG_LOCATION: {
// Find the [possibly new] drag target
- final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+ final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
// If we've changed apparent drag target, tell the view root which view
// we're over now [for purposes of the eventual drag-recipient-changed
@@ -1228,8 +1243,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// Dispatch the actual drag location notice, localized into its coordinates
if (target != null) {
- event.mX = mLocalPoint.x;
- event.mY = mLocalPoint.y;
+ event.mX = localPoint.x;
+ event.mY = localPoint.y;
retval = target.dispatchDragEvent(event);
@@ -1263,11 +1278,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
case DragEvent.ACTION_DROP: {
if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
- View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+ View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
if (target != null) {
if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
- event.mX = mLocalPoint.x;
- event.mY = mLocalPoint.y;
+ event.mX = localPoint.x;
+ event.mY = localPoint.y;
retval = target.dispatchDragEvent(event);
event.mX = tx;
event.mY = ty;
@@ -1690,16 +1705,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * @hide
- */
- @Override
- public void childAccessibilityStateChanged(View child) {
- if (mParent != null) {
- mParent.childAccessibilityStateChanged(child);
- }
- }
-
- /**
* Implement this method to intercept hover events before they are handled
* by child views.
* <p>
@@ -2523,17 +2528,29 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
event.setClassName(ViewGroup.class.getName());
}
- /**
- * @hide
- */
@Override
- public void resetAccessibilityStateChanged() {
- super.resetAccessibilityStateChanged();
+ public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
+ // If this is a live region, we should send a subtree change event
+ // from this view. Otherwise, we can let it propagate up.
+ if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
+ notifyViewAccessibilityStateChangedIfNeeded(changeType);
+ } else if (mParent != null) {
+ try {
+ mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
+ } catch (AbstractMethodError e) {
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+ " does not fully implement ViewParent", e);
+ }
+ }
+ }
+
+ @Override
+ void resetSubtreeAccessibilityStateChanged() {
+ super.resetSubtreeAccessibilityStateChanged();
View[] children = mChildren;
final int childCount = mChildrenCount;
for (int i = 0; i < childCount; i++) {
- View child = children[i];
- child.resetAccessibilityStateChanged();
+ children[i].resetSubtreeAccessibilityStateChanged();
}
}
@@ -2773,8 +2790,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return (int) (dips * scale + 0.5f);
}
- private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
- int lineLength, int lineWidth) {
+ private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
+ int lineLength, int lineWidth) {
drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
@@ -3170,6 +3187,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
+ @Override
+ void dispatchCancelPendingInputEvents() {
+ super.dispatchCancelPendingInputEvents();
+
+ final View[] children = mChildren;
+ final int count = mChildrenCount;
+ for (int i = 0; i < count; i++) {
+ children[i].dispatchCancelPendingInputEvents();
+ }
+ }
+
/**
* When this property is set to true, this ViewGroup supports static transformations on
* children; this causes
@@ -3204,6 +3232,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return false;
}
+ Transformation getChildTransformation() {
+ if (mChildTransformation == null) {
+ mChildTransformation = new Transformation();
+ }
+ return mChildTransformation;
+ }
+
/**
* {@hide}
*/
@@ -3451,6 +3486,24 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
+ private void clearCachedLayoutMode() {
+ if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
+ mLayoutMode = LAYOUT_MODE_UNDEFINED;
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ clearCachedLayoutMode();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ clearCachedLayoutMode();
+ }
+
/**
* Adds a view during layout. This is useful if in your onLayout() method,
* you need to add more views (as does the list view for example).
@@ -3565,6 +3618,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (child.hasTransientState()) {
childHasTransientStateChanged(child, true);
}
+
+ if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
private void addInArray(View child, int index) {
@@ -3804,6 +3861,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
onViewRemoved(view);
+
+ if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
/**
@@ -3812,13 +3873,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* the ViewGroup will be animated according to the animations defined in that LayoutTransition
* object. By default, the transition object is null (so layout changes are not animated).
*
+ * <p>Replacing a non-null transition will cause that previous transition to be
+ * canceled, if it is currently running, to restore this container to
+ * its correct post-transition state.</p>
+ *
* @param transition The LayoutTransition object that will animated changes in layout. A value
* of <code>null</code> means no transition will run on layout changes.
* @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
*/
public void setLayoutTransition(LayoutTransition transition) {
if (mTransition != null) {
- mTransition.removeTransitionListener(mLayoutTransitionListener);
+ LayoutTransition previousTransition = mTransition;
+ previousTransition.cancel();
+ previousTransition.removeTransitionListener(mLayoutTransitionListener);
}
mTransition = transition;
if (mTransition != null) {
@@ -4380,8 +4447,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Quick invalidation method that simply transforms the dirty rect into the parent's
* coordinate system, pruning the invalidation if the parent has already been invalidated.
+ *
+ * @hide
*/
- private ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) {
+ protected ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) {
if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
dirty.offset(left - mScrollX, top - mScrollY);
@@ -4754,6 +4823,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
}
+ private boolean hasBooleanFlag(int flag) {
+ return (mGroupFlags & flag) == flag;
+ }
+
private void setBooleanFlag(int flag, boolean value) {
if (value) {
mGroupFlags |= flag;
@@ -4797,24 +4870,63 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
}
+ private void setLayoutMode(int layoutMode, boolean explicitly) {
+ mLayoutMode = layoutMode;
+ setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
+ }
+
+ /**
+ * Recursively traverse the view hierarchy, resetting the layoutMode of any
+ * descendants that had inherited a different layoutMode from a previous parent.
+ * Recursion terminates when a descendant's mode is:
+ * <ul>
+ * <li>Undefined</li>
+ * <li>The same as the root node's</li>
+ * <li>A mode that had been explicitly set</li>
+ * <ul/>
+ * The first two clauses are optimizations.
+ * @param layoutModeOfRoot
+ */
+ @Override
+ void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
+ if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
+ mLayoutMode == layoutModeOfRoot ||
+ hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
+ return;
+ }
+ setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
+
+ // apply recursively
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
+ }
+ }
+
/**
- * Returns the basis of alignment during layout operations on this view group:
+ * Returns the basis of alignment during layout operations on this ViewGroup:
* either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
+ * <p>
+ * If no layoutMode was explicitly set, either programmatically or in an XML resource,
+ * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
+ * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
*
* @return the layout mode to use during layout operations
*
* @see #setLayoutMode(int)
*/
public int getLayoutMode() {
+ if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
+ int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
+ ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
+ setLayoutMode(inheritedLayoutMode, false);
+ }
return mLayoutMode;
}
/**
- * Sets the basis of alignment during the layout of this view group.
+ * Sets the basis of alignment during the layout of this ViewGroup.
* Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
* {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
- * <p>
- * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}.
*
* @param layoutMode the layout mode to use during layout operations
*
@@ -4823,7 +4935,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
public void setLayoutMode(int layoutMode) {
if (mLayoutMode != layoutMode) {
- mLayoutMode = layoutMode;
+ invalidateInheritedLayoutMode(layoutMode);
+ setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
requestLayout();
}
}
@@ -5268,6 +5381,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Returns whether layout calls on this container are currently being
+ * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
+ *
+ * @return true if layout calls are currently suppressed, false otherwise.
+ *
+ * @hide
+ */
+ public boolean isLayoutSuppressed() {
+ return mSuppressLayout;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -6343,7 +6468,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
private static final class TouchTarget {
private static final int MAX_RECYCLED = 32;
- private static final Object sRecycleLock = new Object();
+ private static final Object sRecycleLock = new Object[0];
private static TouchTarget sRecycleBin;
private static int sRecycledCount;
@@ -6395,7 +6520,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/* Describes a hovered view. */
private static final class HoverTarget {
private static final int MAX_RECYCLED = 32;
- private static final Object sRecycleLock = new Object();
+ private static final Object sRecycleLock = new Object[0];
private static HoverTarget sRecycleBin;
private static int sRecycledCount;
@@ -6614,8 +6739,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return sDebugPaint;
}
- private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
+ private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
if (sDebugLines== null) {
+ // TODO: This won't work with multiple UI threads in a single process
sDebugLines = new float[16];
}