2 * Copyright (C) 2006 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 import android.animation.LayoutTransition;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.content.res.Configuration;
23 import android.content.res.TypedArray;
24 import android.graphics.Bitmap;
25 import android.graphics.Canvas;
26 import android.graphics.Color;
27 import android.graphics.Insets;
28 import android.graphics.Matrix;
29 import android.graphics.Paint;
30 import android.graphics.PointF;
31 import android.graphics.Rect;
32 import android.graphics.RectF;
33 import android.graphics.Region;
34 import android.os.Build;
35 import android.os.Parcelable;
36 import android.os.SystemClock;
37 import android.util.AttributeSet;
38 import android.util.Log;
39 import android.util.Pools.SynchronizedPool;
40 import android.util.SparseArray;
41 import android.view.accessibility.AccessibilityEvent;
42 import android.view.accessibility.AccessibilityNodeInfo;
43 import android.view.animation.Animation;
44 import android.view.animation.AnimationUtils;
45 import android.view.animation.LayoutAnimationController;
46 import android.view.animation.Transformation;
48 import com.android.internal.R;
49 import com.android.internal.util.Predicate;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.HashSet;
54 import java.util.Iterator;
55 import java.util.List;
57 import java.util.NoSuchElementException;
59 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
63 * A <code>ViewGroup</code> is a special view that can contain other views
64 * (called children.) The view group is the base class for layouts and views
65 * containers. This class also defines the
66 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
67 * class for layouts parameters.
71 * Also see {@link LayoutParams} for layout attributes.
74 * <div class="special reference">
75 * <h3>Developer Guides</h3>
76 * <p>For more information about creating user interface layouts, read the
77 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
80 * <p>Here is a complete implementation of a custom ViewGroup that implements
81 * a simple {@link android.widget.FrameLayout} along with the ability to stack
82 * children in left and right gutters.</p>
84 * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
87 * <p>If you are implementing XML layout attributes as shown in the example, this is the
88 * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
90 * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
92 * <p>Finally the layout manager can be used in an XML layout like so:</p>
94 * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
96 * @attr ref android.R.styleable#ViewGroup_clipChildren
97 * @attr ref android.R.styleable#ViewGroup_clipToPadding
98 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
99 * @attr ref android.R.styleable#ViewGroup_animationCache
100 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
101 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
102 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
103 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
104 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
105 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
106 * @attr ref android.R.styleable#ViewGroup_layoutMode
108 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
109 private static final String TAG = "ViewGroup";
111 private static final boolean DBG = false;
113 public static boolean DEBUG_DRAW = false;
116 * Views which have been hidden or removed which need to be animated on
118 * This field should be made private, so it is hidden from the SDK.
121 protected ArrayList<View> mDisappearingChildren;
124 * Listener used to propagate events indicating when children are added
125 * and/or removed from a view group.
126 * This field should be made private, so it is hidden from the SDK.
129 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
131 // The view contained within this ViewGroup that has or contains focus.
132 private View mFocused;
135 * A Transformation used when drawing children, to
136 * apply on the child being drawn.
138 private Transformation mChildTransformation;
141 * Used to track the current invalidation region.
143 RectF mInvalidateRegion;
146 * A Transformation used to calculate a correct
147 * invalidation area when the application is autoscaled.
149 Transformation mInvalidationTransformation;
151 // View currently under an ongoing drag
152 private View mCurrentDragView;
154 // Metadata about the ongoing drag
155 private DragEvent mCurrentDrag;
156 private HashSet<View> mDragNotifiedChildren;
158 // Does this group have a child that can accept the current drag payload?
159 private boolean mChildAcceptsDrag;
161 // Used during drag dispatch
162 private PointF mLocalPoint;
165 private LayoutAnimationController mLayoutAnimationController;
166 private Animation.AnimationListener mAnimationListener;
168 // First touch target in the linked list of touch targets.
169 private TouchTarget mFirstTouchTarget;
171 // For debugging only. You can see these in hierarchyviewer.
172 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
173 @ViewDebug.ExportedProperty(category = "events")
174 private long mLastTouchDownTime;
175 @ViewDebug.ExportedProperty(category = "events")
176 private int mLastTouchDownIndex = -1;
177 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
178 @ViewDebug.ExportedProperty(category = "events")
179 private float mLastTouchDownX;
180 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
181 @ViewDebug.ExportedProperty(category = "events")
182 private float mLastTouchDownY;
184 // First hover target in the linked list of hover targets.
185 // The hover targets are children which have received ACTION_HOVER_ENTER.
186 // They might not have actually handled the hover event, but we will
187 // continue sending hover events to them as long as the pointer remains over
188 // their bounds and the view group does not intercept hover.
189 private HoverTarget mFirstHoverTarget;
191 // True if the view group itself received a hover event.
192 // It might not have actually handled the hover event.
193 private boolean mHoveredSelf;
198 * This field should be made private, so it is hidden from the SDK.
201 @ViewDebug.ExportedProperty(flagMapping = {
202 @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN,
203 name = "CLIP_CHILDREN"),
204 @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING,
205 name = "CLIP_TO_PADDING"),
206 @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL,
207 name = "PADDING_NOT_NULL")
208 }, formatToHexString = true)
209 protected int mGroupFlags;
212 * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
214 private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
217 * NOTE: If you change the flags below make sure to reflect the changes
218 * the DisplayList class
221 // When set, ViewGroup invalidates only the child's rectangle
223 static final int FLAG_CLIP_CHILDREN = 0x1;
225 // When set, ViewGroup excludes the padding area from the invalidate rectangle
227 private static final int FLAG_CLIP_TO_PADDING = 0x2;
229 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
230 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
231 static final int FLAG_INVALIDATE_REQUIRED = 0x4;
233 // When set, dispatchDraw() will run the layout animation and unset the flag
234 private static final int FLAG_RUN_ANIMATION = 0x8;
236 // When set, there is either no layout animation on the ViewGroup or the layout
239 static final int FLAG_ANIMATION_DONE = 0x10;
241 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
242 // to clip it, even if FLAG_CLIP_TO_PADDING is set
243 private static final int FLAG_PADDING_NOT_NULL = 0x20;
245 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
247 private static final int FLAG_ANIMATION_CACHE = 0x40;
249 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
250 // layout animation; this avoid clobbering the hierarchy
251 // Automatically set when the layout animation starts, depending on the animation's
253 static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
255 // When set, the next call to drawChild() will clear mChildTransformation's matrix
256 static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
258 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
259 // the children's Bitmap caches if necessary
260 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
261 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
264 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
265 * to get the index of the child to draw for that iteration.
269 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
272 * When set, this ViewGroup supports static transformations on children; this causes
273 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
274 * invoked when a child is drawn.
276 * Any subclass overriding
277 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
278 * set this flags in {@link #mGroupFlags}.
282 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
284 // UNUSED FLAG VALUE: 0x1000;
287 * When set, this ViewGroup's drawable states also include those
290 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
293 * When set, this ViewGroup tries to always draw its children using their drawing cache.
295 static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
298 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
299 * draw its children with their drawing cache.
301 static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
304 * When set, this group will go through its list of children to notify them of
305 * any drawable state change.
307 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
309 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
312 * This view will get focus before any of its descendants.
314 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
317 * This view will get focus only if none of its descendants want it.
319 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
322 * This view will block any of its descendants from getting focus, even
323 * if they are focusable.
325 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
328 * Used to map between enum in attrubutes and flag values.
330 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
331 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
332 FOCUS_BLOCK_DESCENDANTS};
335 * When set, this ViewGroup should not intercept touch events.
338 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
341 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
343 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
346 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
347 * to children when adding new views. This is used to prevent multiple
348 * onAttached calls when a ViewGroup adds children in its own onAttached method.
350 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
353 * When true, indicates that a layoutMode has been explicitly set, either with
354 * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
355 * This distinguishes the situation in which a layout mode was inherited from
356 * one of the ViewGroup's ancestors and cached locally.
358 private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
360 static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
362 static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
365 * When set, focus will not be permitted to enter this group if a touchscreen is present.
367 static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000;
370 * Indicates which types of drawing caches are to be kept in memory.
371 * This field should be made private, so it is hidden from the SDK.
374 protected int mPersistentDrawingCache;
377 * Used to indicate that no drawing cache should be kept in memory.
379 public static final int PERSISTENT_NO_CACHE = 0x0;
382 * Used to indicate that the animation drawing cache should be kept in memory.
384 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
387 * Used to indicate that the scrolling drawing cache should be kept in memory.
389 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
392 * Used to indicate that all drawing caches should be kept in memory.
394 public static final int PERSISTENT_ALL_CACHES = 0x3;
398 private static final int LAYOUT_MODE_UNDEFINED = -1;
401 * This constant is a {@link #setLayoutMode(int) layoutMode}.
402 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
403 * {@link #getRight() right} and {@link #getBottom() bottom}.
405 public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
408 * This constant is a {@link #setLayoutMode(int) layoutMode}.
409 * Optical bounds describe where a widget appears to be. They sit inside the clip
410 * bounds which need to cover a larger area to allow other effects,
411 * such as shadows and glows, to be drawn.
413 public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
416 public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
419 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
420 * are set at the same time.
422 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
424 // Index of the child's left position in the mLocation array
425 private static final int CHILD_LEFT_INDEX = 0;
426 // Index of the child's top position in the mLocation array
427 private static final int CHILD_TOP_INDEX = 1;
429 // Child views of this ViewGroup
430 private View[] mChildren;
431 // Number of valid children in the mChildren array, the rest should be null or not
432 // considered as children
433 private int mChildrenCount;
435 // Whether layout calls are currently being suppressed, controlled by calls to
437 boolean mSuppressLayout = false;
439 // Whether any layout calls have actually been suppressed while mSuppressLayout
440 // has been true. This tracks whether we need to issue a requestLayout() when
441 // layout is later re-enabled.
442 private boolean mLayoutCalledWhileSuppressed = false;
444 private static final int ARRAY_INITIAL_CAPACITY = 12;
445 private static final int ARRAY_CAPACITY_INCREMENT = 12;
447 private static Paint sDebugPaint;
448 private static float[] sDebugLines;
450 // Used to draw cached views
453 // Used to animate add/remove changes in layout
454 private LayoutTransition mTransition;
456 // The set of views that are currently being transitioned. This list is used to track views
457 // being removed that should not actually be removed from the parent yet because they are
459 private ArrayList<View> mTransitioningViews;
461 // List of children changing visibility. This is used to potentially keep rendering
462 // views during a transition when they otherwise would have become gone/invisible
463 private ArrayList<View> mVisibilityChangingChildren;
465 // Temporary holder of presorted children, only used for
466 // input/software draw dispatch for correctly Z ordering.
467 private ArrayList<View> mPreSortedChildren;
469 // Indicates how many of this container's child subtrees contain transient state
470 @ViewDebug.ExportedProperty(category = "layout")
471 private int mChildCountWithTransientState = 0;
473 // Iterator over the children in decreasing Z order (top children first).
474 private OrderedChildIterator mOrderedChildIterator;
477 * Currently registered axes for nested scrolling. Flag set consisting of
478 * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
481 private int mNestedScrollAxes;
483 public ViewGroup(Context context) {
487 public ViewGroup(Context context, AttributeSet attrs) {
488 this(context, attrs, 0);
491 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
492 this(context, attrs, defStyleAttr, 0);
495 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
496 super(context, attrs, defStyleAttr, defStyleRes);
498 initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
501 private boolean debugDraw() {
502 return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
505 private void initViewGroup() {
506 // ViewGroup doesn't draw by default
508 setFlags(WILL_NOT_DRAW, DRAW_MASK);
510 mGroupFlags |= FLAG_CLIP_CHILDREN;
511 mGroupFlags |= FLAG_CLIP_TO_PADDING;
512 mGroupFlags |= FLAG_ANIMATION_DONE;
513 mGroupFlags |= FLAG_ANIMATION_CACHE;
514 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
516 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
517 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
520 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
522 mChildren = new View[ARRAY_INITIAL_CAPACITY];
525 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
528 private void initFromAttributes(
529 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
530 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyleAttr,
533 final int N = a.getIndexCount();
534 for (int i = 0; i < N; i++) {
535 int attr = a.getIndex(i);
537 case R.styleable.ViewGroup_clipChildren:
538 setClipChildren(a.getBoolean(attr, true));
540 case R.styleable.ViewGroup_clipToPadding:
541 setClipToPadding(a.getBoolean(attr, true));
543 case R.styleable.ViewGroup_animationCache:
544 setAnimationCacheEnabled(a.getBoolean(attr, true));
546 case R.styleable.ViewGroup_persistentDrawingCache:
547 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
549 case R.styleable.ViewGroup_addStatesFromChildren:
550 setAddStatesFromChildren(a.getBoolean(attr, false));
552 case R.styleable.ViewGroup_alwaysDrawnWithCache:
553 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
555 case R.styleable.ViewGroup_layoutAnimation:
556 int id = a.getResourceId(attr, -1);
558 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
561 case R.styleable.ViewGroup_descendantFocusability:
562 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
564 case R.styleable.ViewGroup_splitMotionEvents:
565 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
567 case R.styleable.ViewGroup_animateLayoutChanges:
568 boolean animateLayoutChanges = a.getBoolean(attr, false);
569 if (animateLayoutChanges) {
570 setLayoutTransition(new LayoutTransition());
573 case R.styleable.ViewGroup_layoutMode:
574 setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
576 case R.styleable.ViewGroup_transitionGroup:
577 setTransitionGroup(a.getBoolean(attr, false));
579 case R.styleable.ViewGroup_touchscreenBlocksFocus:
580 setTouchscreenBlocksFocus(a.getBoolean(attr, false));
589 * Gets the descendant focusability of this view group. The descendant
590 * focusability defines the relationship between this view group and its
591 * descendants when looking for a view to take focus in
592 * {@link #requestFocus(int, android.graphics.Rect)}.
594 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
595 * {@link #FOCUS_BLOCK_DESCENDANTS}.
597 @ViewDebug.ExportedProperty(category = "focus", mapping = {
598 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
599 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
600 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
602 public int getDescendantFocusability() {
603 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
607 * Set the descendant focusability of this view group. This defines the relationship
608 * between this view group and its descendants when looking for a view to
609 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
611 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
612 * {@link #FOCUS_BLOCK_DESCENDANTS}.
614 public void setDescendantFocusability(int focusability) {
615 switch (focusability) {
616 case FOCUS_BEFORE_DESCENDANTS:
617 case FOCUS_AFTER_DESCENDANTS:
618 case FOCUS_BLOCK_DESCENDANTS:
621 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
622 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
624 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
625 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
632 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
633 if (mFocused != null) {
634 mFocused.unFocus(this);
637 super.handleFocusGainInternal(direction, previouslyFocusedRect);
643 public void requestChildFocus(View child, View focused) {
645 System.out.println(this + " requestChildFocus()");
647 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
651 // Unfocus us, if necessary
652 super.unFocus(focused);
654 // We had a previous notion of who had focus. Clear it.
655 if (mFocused != child) {
656 if (mFocused != null) {
657 mFocused.unFocus(focused);
662 if (mParent != null) {
663 mParent.requestChildFocus(this, focused);
670 public void focusableViewAvailable(View v) {
672 // shortcut: don't report a new focusable view if we block our descendants from
674 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
675 && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
676 // shortcut: don't report a new focusable view if we already are focused
677 // (and we don't prefer our descendants)
679 // note: knowing that mFocused is non-null is not a good enough reason
680 // to break the traversal since in that case we'd actually have to find
681 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
682 // an ancestor of v; this will get checked for at ViewAncestor
683 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
684 mParent.focusableViewAvailable(v);
691 public boolean showContextMenuForChild(View originalView) {
692 return mParent != null && mParent.showContextMenuForChild(originalView);
698 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
699 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
703 * Find the nearest view in the specified direction that wants to take
706 * @param focused The view that currently has focus
707 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
708 * FOCUS_RIGHT, or 0 for not applicable.
710 public View focusSearch(View focused, int direction) {
711 if (isRootNamespace()) {
712 // root namespace means we should consider ourselves the top of the
713 // tree for focus searching; otherwise we could be focus searching
714 // into other tabs. see LocalActivityManager and TabHost for more info
715 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
716 } else if (mParent != null) {
717 return mParent.focusSearch(focused, direction);
725 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
733 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
734 ViewParent parent = mParent;
735 if (parent == null) {
738 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
742 return parent.requestSendAccessibilityEvent(this, event);
746 * Called when a child has requested sending an {@link AccessibilityEvent} and
747 * gives an opportunity to its parent to augment the event.
749 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
750 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
751 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
752 * is responsible for handling this call.
755 * @param child The child which requests sending the event.
756 * @param event The event to be sent.
757 * @return True if the event should be sent.
759 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
761 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
762 if (mAccessibilityDelegate != null) {
763 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
765 return onRequestSendAccessibilityEventInternal(child, event);
770 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
772 * Note: Called from the default {@link View.AccessibilityDelegate}.
774 boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
779 * Translates the given bounds and intersections from child coordinates to
780 * local coordinates. In case any interactive sibling of the calling child
781 * covers the latter, a new intersections is added to the intersection list.
782 * This method is for the exclusive use by the accessibility layer to compute
783 * a point where a sequence of down and up events would click on a view.
785 * @param child The child making the call.
786 * @param bounds The bounds to translate in child coordinates.
787 * @param intersections The intersections of interactive views covering the child.
788 * @return True if the bounds and intersections were computed, false otherwise.
790 boolean translateBoundsAndIntersectionsInWindowCoordinates(View child,
791 RectF bounds, List<RectF> intersections) {
792 // Not attached, done.
793 if (mAttachInfo == null) {
797 if (getAlpha() <= 0 || getTransitionAlpha() <= 0 ||
798 getVisibility() != VISIBLE) {
799 // Cannot click on a view with an invisible predecessor.
803 // Compensate for the child transformation.
804 if (!child.hasIdentityMatrix()) {
805 Matrix matrix = child.getMatrix();
806 matrix.mapRect(bounds);
807 final int intersectionCount = intersections.size();
808 for (int i = 0; i < intersectionCount; i++) {
809 RectF intersection = intersections.get(i);
810 matrix.mapRect(intersection);
814 // Translate the bounds from child to parent coordinates.
815 final int dx = child.mLeft - mScrollX;
816 final int dy = child.mTop - mScrollY;
817 bounds.offset(dx, dy);
818 offsetRects(intersections, dx, dy);
820 // If the bounds do not intersect our bounds, done.
821 if (!bounds.intersects(0, 0, getWidth(), getHeight())) {
825 Iterator<View> iterator = obtainOrderedChildIterator();
826 while (iterator.hasNext()) {
827 View sibling = iterator.next();
829 // We care only about siblings over the child.
830 if (sibling == child) {
834 // Ignore invisible views as they are not interactive.
835 if (!isVisible(sibling)) {
839 // Compute the sibling bounds in its coordinates.
840 RectF siblingBounds = mAttachInfo.mTmpTransformRect1;
841 siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight());
843 // Translate the sibling bounds to our coordinates.
844 offsetChildRectToMyCoords(siblingBounds, sibling);
846 // Compute the intersection between the child and the sibling.
847 if (siblingBounds.intersect(bounds)) {
848 List<RectF> clickableRects = new ArrayList<>();
849 sibling.addClickableRectsForAccessibility(clickableRects);
851 final int clickableRectCount = clickableRects.size();
852 for (int j = 0; j < clickableRectCount; j++) {
853 RectF clickableRect = clickableRects.get(j);
855 // Translate the clickable rect to our coordinates.
856 offsetChildRectToMyCoords(clickableRect, sibling);
858 // Compute the intersection between the child and the clickable rects.
859 if (clickableRect.intersect(bounds)) {
860 // If a clickable rect completely covers the child, done.
861 if (clickableRect.equals(bounds)) {
862 releaseOrderedChildIterator();
865 // Keep track of the intersection rectangle.
866 intersections.add(clickableRect);
872 releaseOrderedChildIterator();
874 if (mParent instanceof ViewGroup) {
875 ViewGroup parentGroup = (ViewGroup) mParent;
876 return parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
877 this, bounds, intersections);
884 void addClickableRectsForAccessibility(List<RectF> outRects) {
885 int sizeBefore = outRects.size();
887 super.addClickableRectsForAccessibility(outRects);
889 // If we added ourselves, then no need to visit children.
890 if (outRects.size() > sizeBefore) {
894 Iterator<View> iterator = obtainOrderedChildIterator();
895 while (iterator.hasNext()) {
896 View child = iterator.next();
898 // Cannot click on an invisible view.
899 if (!isVisible(child)) {
903 sizeBefore = outRects.size();
905 // Add clickable rects in the child bounds.
906 child.addClickableRectsForAccessibility(outRects);
908 // Offset the clickable rects for out children to our coordinates.
909 final int sizeAfter = outRects.size();
910 for (int j = sizeBefore; j < sizeAfter; j++) {
911 RectF rect = outRects.get(j);
913 // Translate the clickable rect to our coordinates.
914 offsetChildRectToMyCoords(rect, child);
916 // If a clickable rect fills the parent, done.
917 if ((int) rect.left == 0 && (int) rect.top == 0
918 && (int) rect.right == mRight && (int) rect.bottom == mBottom) {
919 releaseOrderedChildIterator();
925 releaseOrderedChildIterator();
928 private void offsetChildRectToMyCoords(RectF rect, View child) {
929 if (!child.hasIdentityMatrix()) {
930 child.getMatrix().mapRect(rect);
932 final int childDx = child.mLeft - mScrollX;
933 final int childDy = child.mTop - mScrollY;
934 rect.offset(childDx, childDy);
937 private static boolean isVisible(View view) {
938 return (view.getAlpha() > 0 && view.getTransitionAlpha() > 0 &&
939 view.getVisibility() == VISIBLE);
943 * Obtains the iterator to traverse the children in a descending Z order.
944 * Only one party can use the iterator at any given time and you cannot
945 * modify the children while using this iterator. Acquisition if already
946 * obtained is an error.
948 * @return The child iterator.
950 OrderedChildIterator obtainOrderedChildIterator() {
951 if (mOrderedChildIterator == null) {
952 mOrderedChildIterator = new OrderedChildIterator();
953 } else if (mOrderedChildIterator.isInitialized()) {
954 throw new IllegalStateException("Already obtained");
956 mOrderedChildIterator.initialize();
957 return mOrderedChildIterator;
961 * Releases the iterator to traverse the children in a descending Z order.
962 * Release if not obtained is an error.
964 void releaseOrderedChildIterator() {
965 if (mOrderedChildIterator == null || !mOrderedChildIterator.isInitialized()) {
966 throw new IllegalStateException("Not obtained");
968 mOrderedChildIterator.release();
972 * Called when a child view has changed whether or not it is tracking transient state.
974 public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
975 final boolean oldHasTransientState = hasTransientState();
976 if (childHasTransientState) {
977 mChildCountWithTransientState++;
979 mChildCountWithTransientState--;
982 final boolean newHasTransientState = hasTransientState();
983 if (mParent != null && oldHasTransientState != newHasTransientState) {
985 mParent.childHasTransientStateChanged(this, newHasTransientState);
986 } catch (AbstractMethodError e) {
987 Log.e(TAG, mParent.getClass().getSimpleName() +
988 " does not fully implement ViewParent", e);
994 public boolean hasTransientState() {
995 return mChildCountWithTransientState > 0 || super.hasTransientState();
1002 public boolean dispatchUnhandledMove(View focused, int direction) {
1003 return mFocused != null &&
1004 mFocused.dispatchUnhandledMove(focused, direction);
1010 public void clearChildFocus(View child) {
1012 System.out.println(this + " clearChildFocus()");
1016 if (mParent != null) {
1017 mParent.clearChildFocus(this);
1025 public void clearFocus() {
1027 System.out.println(this + " clearFocus()");
1029 if (mFocused == null) {
1032 View focused = mFocused;
1034 focused.clearFocus();
1042 void unFocus(View focused) {
1044 System.out.println(this + " unFocus()");
1046 if (mFocused == null) {
1047 super.unFocus(focused);
1049 mFocused.unFocus(focused);
1055 * Returns the focused child of this view, if any. The child may have focus
1058 * @return the focused child or null.
1060 public View getFocusedChild() {
1064 View getDeepestFocusedChild() {
1067 if (v.isFocused()) {
1070 v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
1076 * Returns true if this view has or contains focus
1078 * @return true if this view has or contains focus
1081 public boolean hasFocus() {
1082 return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
1088 * @see android.view.View#findFocus()
1091 public View findFocus() {
1093 System.out.println("Find focus in " + this + ": flags="
1094 + isFocused() + ", child=" + mFocused);
1101 if (mFocused != null) {
1102 return mFocused.findFocus();
1111 public boolean hasFocusable() {
1112 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
1116 if (isFocusable()) {
1120 final int descendantFocusability = getDescendantFocusability();
1121 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1122 final int count = mChildrenCount;
1123 final View[] children = mChildren;
1125 for (int i = 0; i < count; i++) {
1126 final View child = children[i];
1127 if (child.hasFocusable()) {
1140 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1141 final int focusableCount = views.size();
1143 final int descendantFocusability = getDescendantFocusability();
1145 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1146 if (shouldBlockFocusForTouchscreen()) {
1147 focusableMode |= FOCUSABLES_TOUCH_MODE;
1150 final int count = mChildrenCount;
1151 final View[] children = mChildren;
1153 for (int i = 0; i < count; i++) {
1154 final View child = children[i];
1155 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1156 child.addFocusables(views, direction, focusableMode);
1161 // we add ourselves (if focusable) in all cases except for when we are
1162 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
1163 // to avoid the focus search finding layouts when a more precise search
1164 // among the focusable children would be more interesting.
1165 if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS
1166 // No focusable descendants
1167 || (focusableCount == views.size())) &&
1168 (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) {
1169 super.addFocusables(views, direction, focusableMode);
1174 * Set whether this ViewGroup should ignore focus requests for itself and its children.
1175 * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
1176 * will proceed forward.
1178 * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
1180 public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
1181 if (touchscreenBlocksFocus) {
1182 mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1184 final View focusedChild = getDeepestFocusedChild();
1185 if (!focusedChild.isFocusableInTouchMode()) {
1186 final View newFocus = focusSearch(FOCUS_FORWARD);
1187 if (newFocus != null) {
1188 newFocus.requestFocus();
1193 mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1198 * Check whether this ViewGroup should ignore focus requests for itself and its children.
1200 public boolean getTouchscreenBlocksFocus() {
1201 return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
1204 boolean shouldBlockFocusForTouchscreen() {
1205 return getTouchscreenBlocksFocus() &&
1206 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
1210 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
1211 super.findViewsWithText(outViews, text, flags);
1212 final int childrenCount = mChildrenCount;
1213 final View[] children = mChildren;
1214 for (int i = 0; i < childrenCount; i++) {
1215 View child = children[i];
1216 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1217 && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
1218 child.findViewsWithText(outViews, text, flags);
1225 public View findViewByAccessibilityIdTraversal(int accessibilityId) {
1226 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
1227 if (foundView != null) {
1230 final int childrenCount = mChildrenCount;
1231 final View[] children = mChildren;
1232 for (int i = 0; i < childrenCount; i++) {
1233 View child = children[i];
1234 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
1235 if (foundView != null) {
1246 public void dispatchWindowFocusChanged(boolean hasFocus) {
1247 super.dispatchWindowFocusChanged(hasFocus);
1248 final int count = mChildrenCount;
1249 final View[] children = mChildren;
1250 for (int i = 0; i < count; i++) {
1251 children[i].dispatchWindowFocusChanged(hasFocus);
1259 public void addTouchables(ArrayList<View> views) {
1260 super.addTouchables(views);
1262 final int count = mChildrenCount;
1263 final View[] children = mChildren;
1265 for (int i = 0; i < count; i++) {
1266 final View child = children[i];
1267 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1268 child.addTouchables(views);
1277 public void makeOptionalFitsSystemWindows() {
1278 super.makeOptionalFitsSystemWindows();
1279 final int count = mChildrenCount;
1280 final View[] children = mChildren;
1281 for (int i = 0; i < count; i++) {
1282 children[i].makeOptionalFitsSystemWindows();
1290 public void dispatchDisplayHint(int hint) {
1291 super.dispatchDisplayHint(hint);
1292 final int count = mChildrenCount;
1293 final View[] children = mChildren;
1294 for (int i = 0; i < count; i++) {
1295 children[i].dispatchDisplayHint(hint);
1300 * Called when a view's visibility has changed. Notify the parent to take any appropriate
1303 * @param child The view whose visibility has changed
1304 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
1305 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
1308 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1309 if (mTransition != null) {
1310 if (newVisibility == VISIBLE) {
1311 mTransition.showChild(this, child, oldVisibility);
1313 mTransition.hideChild(this, child, newVisibility);
1314 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
1315 // Only track this on disappearing views - appearing views are already visible
1316 // and don't need special handling during drawChild()
1317 if (mVisibilityChangingChildren == null) {
1318 mVisibilityChangingChildren = new ArrayList<View>();
1320 mVisibilityChangingChildren.add(child);
1321 addDisappearingView(child);
1326 // in all cases, for drags
1327 if (mCurrentDrag != null) {
1328 if (newVisibility == VISIBLE) {
1329 notifyChildOfDrag(child);
1338 protected void dispatchVisibilityChanged(View changedView, int visibility) {
1339 super.dispatchVisibilityChanged(changedView, visibility);
1340 final int count = mChildrenCount;
1341 final View[] children = mChildren;
1342 for (int i = 0; i < count; i++) {
1343 children[i].dispatchVisibilityChanged(changedView, visibility);
1351 public void dispatchWindowVisibilityChanged(int visibility) {
1352 super.dispatchWindowVisibilityChanged(visibility);
1353 final int count = mChildrenCount;
1354 final View[] children = mChildren;
1355 for (int i = 0; i < count; i++) {
1356 children[i].dispatchWindowVisibilityChanged(visibility);
1364 public void dispatchConfigurationChanged(Configuration newConfig) {
1365 super.dispatchConfigurationChanged(newConfig);
1366 final int count = mChildrenCount;
1367 final View[] children = mChildren;
1368 for (int i = 0; i < count; i++) {
1369 children[i].dispatchConfigurationChanged(newConfig);
1376 public void recomputeViewAttributes(View child) {
1377 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1378 ViewParent parent = mParent;
1379 if (parent != null) parent.recomputeViewAttributes(this);
1384 void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
1385 if ((visibility & VISIBILITY_MASK) == VISIBLE) {
1386 super.dispatchCollectViewAttributes(attachInfo, visibility);
1387 final int count = mChildrenCount;
1388 final View[] children = mChildren;
1389 for (int i = 0; i < count; i++) {
1390 final View child = children[i];
1391 child.dispatchCollectViewAttributes(attachInfo,
1392 visibility | (child.mViewFlags&VISIBILITY_MASK));
1400 public void bringChildToFront(View child) {
1401 int index = indexOfChild(child);
1403 removeFromArray(index);
1404 addInArray(child, mChildrenCount);
1405 child.mParent = this;
1411 private PointF getLocalPoint() {
1412 if (mLocalPoint == null) mLocalPoint = new PointF();
1419 // TODO: Write real docs
1421 public boolean dispatchDragEvent(DragEvent event) {
1422 boolean retval = false;
1423 final float tx = event.mX;
1424 final float ty = event.mY;
1426 ViewRootImpl root = getViewRootImpl();
1428 // Dispatch down the view hierarchy
1429 final PointF localPoint = getLocalPoint();
1431 switch (event.mAction) {
1432 case DragEvent.ACTION_DRAG_STARTED: {
1433 // clear state to recalculate which views we drag over
1434 mCurrentDragView = null;
1436 // Set up our tracking of drag-started notifications
1437 mCurrentDrag = DragEvent.obtain(event);
1438 if (mDragNotifiedChildren == null) {
1439 mDragNotifiedChildren = new HashSet<View>();
1441 mDragNotifiedChildren.clear();
1444 // Now dispatch down to our children, caching the responses
1445 mChildAcceptsDrag = false;
1446 final int count = mChildrenCount;
1447 final View[] children = mChildren;
1448 for (int i = 0; i < count; i++) {
1449 final View child = children[i];
1450 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1451 if (child.getVisibility() == VISIBLE) {
1452 final boolean handled = notifyChildOfDrag(children[i]);
1454 mChildAcceptsDrag = true;
1459 // Return HANDLED if one of our children can accept the drag
1460 if (mChildAcceptsDrag) {
1465 case DragEvent.ACTION_DRAG_ENDED: {
1466 // Release the bookkeeping now that the drag lifecycle has ended
1467 if (mDragNotifiedChildren != null) {
1468 for (View child : mDragNotifiedChildren) {
1469 // If a child was notified about an ongoing drag, it's told that it's over
1470 child.dispatchDragEvent(event);
1471 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1472 child.refreshDrawableState();
1475 mDragNotifiedChildren.clear();
1476 if (mCurrentDrag != null) {
1477 mCurrentDrag.recycle();
1478 mCurrentDrag = null;
1482 // We consider drag-ended to have been handled if one of our children
1483 // had offered to handle the drag.
1484 if (mChildAcceptsDrag) {
1489 case DragEvent.ACTION_DRAG_LOCATION: {
1490 // Find the [possibly new] drag target
1491 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1493 // If we've changed apparent drag target, tell the view root which view
1494 // we're over now [for purposes of the eventual drag-recipient-changed
1495 // notifications to the framework] and tell the new target that the drag
1496 // has entered its bounds. The root will see setDragFocus() calls all
1497 // the way down to the final leaf view that is handling the LOCATION event
1498 // before reporting the new potential recipient to the framework.
1499 if (mCurrentDragView != target) {
1500 root.setDragFocus(target);
1502 final int action = event.mAction;
1503 // If we've dragged off of a child view, send it the EXITED message
1504 if (mCurrentDragView != null) {
1505 final View view = mCurrentDragView;
1506 event.mAction = DragEvent.ACTION_DRAG_EXITED;
1507 view.dispatchDragEvent(event);
1508 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
1509 view.refreshDrawableState();
1511 mCurrentDragView = target;
1513 // If we've dragged over a new child view, send it the ENTERED message
1514 if (target != null) {
1515 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1516 target.dispatchDragEvent(event);
1517 target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
1518 target.refreshDrawableState();
1520 event.mAction = action; // restore the event's original state
1523 // Dispatch the actual drag location notice, localized into its coordinates
1524 if (target != null) {
1525 event.mX = localPoint.x;
1526 event.mY = localPoint.y;
1528 retval = target.dispatchDragEvent(event);
1535 /* Entered / exited dispatch
1537 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1538 * that we're about to get the corresponding LOCATION event, which we will use to
1539 * determine which of our children is the new target; at that point we will
1540 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1542 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1543 * drag has left this ViewGroup, we know by definition that every contained subview
1544 * is also no longer under the drag point.
1547 case DragEvent.ACTION_DRAG_EXITED: {
1548 if (mCurrentDragView != null) {
1549 final View view = mCurrentDragView;
1550 view.dispatchDragEvent(event);
1551 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
1552 view.refreshDrawableState();
1554 mCurrentDragView = null;
1558 case DragEvent.ACTION_DROP: {
1559 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
1560 View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1561 if (target != null) {
1562 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
1563 event.mX = localPoint.x;
1564 event.mY = localPoint.y;
1565 retval = target.dispatchDragEvent(event);
1569 if (ViewDebug.DEBUG_DRAG) {
1570 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1576 // If none of our children could handle the event, try here
1578 // Call up to the View implementation that dispatches to installed listeners
1579 retval = super.dispatchDragEvent(event);
1584 // Find the frontmost child view that lies under the given point, and calculate
1585 // the position within its own local coordinate system.
1586 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
1587 final int count = mChildrenCount;
1588 final View[] children = mChildren;
1589 for (int i = count - 1; i >= 0; i--) {
1590 final View child = children[i];
1591 if (!child.canAcceptDrag()) {
1595 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1602 boolean notifyChildOfDrag(View child) {
1603 if (ViewDebug.DEBUG_DRAG) {
1604 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1607 boolean canAccept = false;
1608 if (! mDragNotifiedChildren.contains(child)) {
1609 mDragNotifiedChildren.add(child);
1610 canAccept = child.dispatchDragEvent(mCurrentDrag);
1611 if (canAccept && !child.canAcceptDrag()) {
1612 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
1613 child.refreshDrawableState();
1620 public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1621 super.dispatchWindowSystemUiVisiblityChanged(visible);
1623 final int count = mChildrenCount;
1624 final View[] children = mChildren;
1625 for (int i=0; i <count; i++) {
1626 final View child = children[i];
1627 child.dispatchWindowSystemUiVisiblityChanged(visible);
1632 public void dispatchSystemUiVisibilityChanged(int visible) {
1633 super.dispatchSystemUiVisibilityChanged(visible);
1635 final int count = mChildrenCount;
1636 final View[] children = mChildren;
1637 for (int i=0; i <count; i++) {
1638 final View child = children[i];
1639 child.dispatchSystemUiVisibilityChanged(visible);
1644 boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1645 boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
1647 final int count = mChildrenCount;
1648 final View[] children = mChildren;
1649 for (int i=0; i <count; i++) {
1650 final View child = children[i];
1651 changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
1660 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1661 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1662 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1663 return super.dispatchKeyEventPreIme(event);
1664 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1665 == PFLAG_HAS_BOUNDS) {
1666 return mFocused.dispatchKeyEventPreIme(event);
1675 public boolean dispatchKeyEvent(KeyEvent event) {
1676 if (mInputEventConsistencyVerifier != null) {
1677 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1680 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1681 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1682 if (super.dispatchKeyEvent(event)) {
1685 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1686 == PFLAG_HAS_BOUNDS) {
1687 if (mFocused.dispatchKeyEvent(event)) {
1692 if (mInputEventConsistencyVerifier != null) {
1693 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1702 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1703 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1704 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1705 return super.dispatchKeyShortcutEvent(event);
1706 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1707 == PFLAG_HAS_BOUNDS) {
1708 return mFocused.dispatchKeyShortcutEvent(event);
1717 public boolean dispatchTrackballEvent(MotionEvent event) {
1718 if (mInputEventConsistencyVerifier != null) {
1719 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1722 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1723 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1724 if (super.dispatchTrackballEvent(event)) {
1727 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1728 == PFLAG_HAS_BOUNDS) {
1729 if (mFocused.dispatchTrackballEvent(event)) {
1734 if (mInputEventConsistencyVerifier != null) {
1735 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1743 @SuppressWarnings({"ConstantConditions"})
1745 protected boolean dispatchHoverEvent(MotionEvent event) {
1746 final int action = event.getAction();
1748 // First check whether the view group wants to intercept the hover event.
1749 final boolean interceptHover = onInterceptHoverEvent(event);
1750 event.setAction(action); // restore action in case it was changed
1752 MotionEvent eventNoHistory = event;
1753 boolean handled = false;
1755 // Send events to the hovered children and build a new list of hover targets until
1756 // one is found that handles the event.
1757 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1758 mFirstHoverTarget = null;
1759 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
1760 final float x = event.getX();
1761 final float y = event.getY();
1762 final int childrenCount = mChildrenCount;
1763 if (childrenCount != 0) {
1764 final ArrayList<View> preorderedList = buildOrderedChildList();
1765 final boolean customOrder = preorderedList == null
1766 && isChildrenDrawingOrderEnabled();
1767 final View[] children = mChildren;
1768 HoverTarget lastHoverTarget = null;
1769 for (int i = childrenCount - 1; i >= 0; i--) {
1770 int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
1771 final View child = (preorderedList == null)
1772 ? children[childIndex] : preorderedList.get(childIndex);
1773 if (!canViewReceivePointerEvents(child)
1774 || !isTransformedTouchPointInView(x, y, child, null)) {
1778 // Obtain a hover target for this child. Dequeue it from the
1779 // old hover target list if the child was previously hovered.
1780 HoverTarget hoverTarget = firstOldHoverTarget;
1781 final boolean wasHovered;
1782 for (HoverTarget predecessor = null; ;) {
1783 if (hoverTarget == null) {
1784 hoverTarget = HoverTarget.obtain(child);
1789 if (hoverTarget.child == child) {
1790 if (predecessor != null) {
1791 predecessor.next = hoverTarget.next;
1793 firstOldHoverTarget = hoverTarget.next;
1795 hoverTarget.next = null;
1800 predecessor = hoverTarget;
1801 hoverTarget = hoverTarget.next;
1804 // Enqueue the hover target onto the new hover target list.
1805 if (lastHoverTarget != null) {
1806 lastHoverTarget.next = hoverTarget;
1808 mFirstHoverTarget = hoverTarget;
1810 lastHoverTarget = hoverTarget;
1812 // Dispatch the event to the child.
1813 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1815 // Send the enter as is.
1816 handled |= dispatchTransformedGenericPointerEvent(
1817 event, child); // enter
1819 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1821 // Synthesize an enter from a move.
1822 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1823 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1824 handled |= dispatchTransformedGenericPointerEvent(
1825 eventNoHistory, child); // enter
1826 eventNoHistory.setAction(action);
1828 handled |= dispatchTransformedGenericPointerEvent(
1829 eventNoHistory, child); // move
1831 // Send the move as is.
1832 handled |= dispatchTransformedGenericPointerEvent(event, child);
1839 if (preorderedList != null) preorderedList.clear();
1843 // Send exit events to all previously hovered children that are no longer hovered.
1844 while (firstOldHoverTarget != null) {
1845 final View child = firstOldHoverTarget.child;
1847 // Exit the old hovered child.
1848 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1849 // Send the exit as is.
1850 handled |= dispatchTransformedGenericPointerEvent(
1851 event, child); // exit
1853 // Synthesize an exit from a move or enter.
1854 // Ignore the result because hover focus has moved to a different view.
1855 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1856 dispatchTransformedGenericPointerEvent(
1857 event, child); // move
1859 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1860 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1861 dispatchTransformedGenericPointerEvent(
1862 eventNoHistory, child); // exit
1863 eventNoHistory.setAction(action);
1866 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1867 firstOldHoverTarget.recycle();
1868 firstOldHoverTarget = nextOldHoverTarget;
1871 // Send events to the view group itself if no children have handled it.
1872 boolean newHoveredSelf = !handled;
1873 if (newHoveredSelf == mHoveredSelf) {
1874 if (newHoveredSelf) {
1875 // Send event to the view group as before.
1876 handled |= super.dispatchHoverEvent(event);
1880 // Exit the view group.
1881 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1882 // Send the exit as is.
1883 handled |= super.dispatchHoverEvent(event); // exit
1885 // Synthesize an exit from a move or enter.
1886 // Ignore the result because hover focus is moving to a different view.
1887 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1888 super.dispatchHoverEvent(event); // move
1890 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1891 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1892 super.dispatchHoverEvent(eventNoHistory); // exit
1893 eventNoHistory.setAction(action);
1895 mHoveredSelf = false;
1898 if (newHoveredSelf) {
1899 // Enter the view group.
1900 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1901 // Send the enter as is.
1902 handled |= super.dispatchHoverEvent(event); // enter
1903 mHoveredSelf = true;
1904 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1905 // Synthesize an enter from a move.
1906 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1907 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1908 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1909 eventNoHistory.setAction(action);
1911 handled |= super.dispatchHoverEvent(eventNoHistory); // move
1912 mHoveredSelf = true;
1917 // Recycle the copy of the event that we made.
1918 if (eventNoHistory != event) {
1919 eventNoHistory.recycle();
1926 private void exitHoverTargets() {
1927 if (mHoveredSelf || mFirstHoverTarget != null) {
1928 final long now = SystemClock.uptimeMillis();
1929 MotionEvent event = MotionEvent.obtain(now, now,
1930 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1931 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1932 dispatchHoverEvent(event);
1937 private void cancelHoverTarget(View view) {
1938 HoverTarget predecessor = null;
1939 HoverTarget target = mFirstHoverTarget;
1940 while (target != null) {
1941 final HoverTarget next = target.next;
1942 if (target.child == view) {
1943 if (predecessor == null) {
1944 mFirstHoverTarget = next;
1946 predecessor.next = next;
1950 final long now = SystemClock.uptimeMillis();
1951 MotionEvent event = MotionEvent.obtain(now, now,
1952 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1953 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1954 view.dispatchHoverEvent(event);
1958 predecessor = target;
1965 protected boolean hasHoveredChild() {
1966 return mFirstHoverTarget != null;
1970 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
1971 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
1973 final int childrenCount = children.getChildCount();
1974 for (int i = 0; i < childrenCount; i++) {
1975 View child = children.getChildAt(i);
1976 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1977 if (child.includeForAccessibility()) {
1978 childrenForAccessibility.add(child);
1980 child.addChildrenForAccessibility(childrenForAccessibility);
1990 * Implement this method to intercept hover events before they are handled
1993 * This method is called before dispatching a hover event to a child of
1994 * the view group or to the view group's own {@link #onHoverEvent} to allow
1995 * the view group a chance to intercept the hover event.
1996 * This method can also be used to watch all pointer motions that occur within
1997 * the bounds of the view group even when the pointer is hovering over
1998 * a child of the view group rather than over the view group itself.
2000 * The view group can prevent its children from receiving hover events by
2001 * implementing this method and returning <code>true</code> to indicate
2002 * that it would like to intercept hover events. The view group must
2003 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
2004 * for as long as it wishes to continue intercepting hover events from
2007 * Interception preserves the invariant that at most one view can be
2008 * hovered at a time by transferring hover focus from the currently hovered
2009 * child to the view group or vice-versa as needed.
2011 * If this method returns <code>true</code> and a child is already hovered, then the
2012 * child view will first receive a hover exit event and then the view group
2013 * itself will receive a hover enter event in {@link #onHoverEvent}.
2014 * Likewise, if this method had previously returned <code>true</code> to intercept hover
2015 * events and instead returns <code>false</code> while the pointer is hovering
2016 * within the bounds of one of a child, then the view group will first receive a
2017 * hover exit event in {@link #onHoverEvent} and then the hovered child will
2018 * receive a hover enter event.
2020 * The default implementation always returns false.
2023 * @param event The motion event that describes the hover.
2024 * @return True if the view group would like to intercept the hover event
2025 * and prevent its children from receiving it.
2027 public boolean onInterceptHoverEvent(MotionEvent event) {
2031 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
2032 if (event.getHistorySize() == 0) {
2035 return MotionEvent.obtainNoHistory(event);
2042 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
2043 // Send the event to the child under the pointer.
2044 final int childrenCount = mChildrenCount;
2045 if (childrenCount != 0) {
2046 final float x = event.getX();
2047 final float y = event.getY();
2049 final ArrayList<View> preorderedList = buildOrderedChildList();
2050 final boolean customOrder = preorderedList == null
2051 && isChildrenDrawingOrderEnabled();
2052 final View[] children = mChildren;
2053 for (int i = childrenCount - 1; i >= 0; i--) {
2054 int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
2055 final View child = (preorderedList == null)
2056 ? children[childIndex] : preorderedList.get(childIndex);
2057 if (!canViewReceivePointerEvents(child)
2058 || !isTransformedTouchPointInView(x, y, child, null)) {
2062 if (dispatchTransformedGenericPointerEvent(event, child)) {
2063 if (preorderedList != null) preorderedList.clear();
2067 if (preorderedList != null) preorderedList.clear();
2070 // No child handled the event. Send it to this view group.
2071 return super.dispatchGenericPointerEvent(event);
2078 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
2079 // Send the event to the focused child or to this view group if it has focus.
2080 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
2081 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
2082 return super.dispatchGenericFocusedEvent(event);
2083 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
2084 == PFLAG_HAS_BOUNDS) {
2085 return mFocused.dispatchGenericMotionEvent(event);
2091 * Dispatches a generic pointer event to a child, taking into account
2092 * transformations that apply to the child.
2094 * @param event The event to send.
2095 * @param child The view to send the event to.
2096 * @return {@code true} if the child handled the event.
2098 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
2099 final float offsetX = mScrollX - child.mLeft;
2100 final float offsetY = mScrollY - child.mTop;
2103 if (!child.hasIdentityMatrix()) {
2104 MotionEvent transformedEvent = MotionEvent.obtain(event);
2105 transformedEvent.offsetLocation(offsetX, offsetY);
2106 transformedEvent.transform(child.getInverseMatrix());
2107 handled = child.dispatchGenericMotionEvent(transformedEvent);
2108 transformedEvent.recycle();
2110 event.offsetLocation(offsetX, offsetY);
2111 handled = child.dispatchGenericMotionEvent(event);
2112 event.offsetLocation(-offsetX, -offsetY);
2121 public boolean dispatchTouchEvent(MotionEvent ev) {
2122 if (mInputEventConsistencyVerifier != null) {
2123 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
2126 boolean handled = false;
2127 if (onFilterTouchEventForSecurity(ev)) {
2128 final int action = ev.getAction();
2129 final int actionMasked = action & MotionEvent.ACTION_MASK;
2131 // Handle an initial down.
2132 if (actionMasked == MotionEvent.ACTION_DOWN) {
2133 // Throw away all previous state when starting a new touch gesture.
2134 // The framework may have dropped the up or cancel event for the previous gesture
2135 // due to an app switch, ANR, or some other state change.
2136 cancelAndClearTouchTargets(ev);
2140 // Check for interception.
2141 final boolean intercepted;
2142 if (actionMasked == MotionEvent.ACTION_DOWN
2143 || mFirstTouchTarget != null) {
2144 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
2145 if (!disallowIntercept) {
2146 intercepted = onInterceptTouchEvent(ev);
2147 ev.setAction(action); // restore action in case it was changed
2149 intercepted = false;
2152 // There are no touch targets and this action is not an initial down
2153 // so this view group continues to intercept touches.
2157 // Check for cancelation.
2158 final boolean canceled = resetCancelNextUpFlag(this)
2159 || actionMasked == MotionEvent.ACTION_CANCEL;
2161 // Update list of touch targets for pointer down, if needed.
2162 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
2163 TouchTarget newTouchTarget = null;
2164 boolean alreadyDispatchedToNewTouchTarget = false;
2165 if (!canceled && !intercepted) {
2166 if (actionMasked == MotionEvent.ACTION_DOWN
2167 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
2168 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2169 final int actionIndex = ev.getActionIndex(); // always 0 for down
2170 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
2171 : TouchTarget.ALL_POINTER_IDS;
2173 // Clean up earlier touch targets for this pointer id in case they
2174 // have become out of sync.
2175 removePointersFromTouchTargets(idBitsToAssign);
2177 final int childrenCount = mChildrenCount;
2178 if (newTouchTarget == null && childrenCount != 0) {
2179 final float x = ev.getX(actionIndex);
2180 final float y = ev.getY(actionIndex);
2181 // Find a child that can receive the event.
2182 // Scan children from front to back.
2183 final ArrayList<View> preorderedList = buildOrderedChildList();
2184 final boolean customOrder = preorderedList == null
2185 && isChildrenDrawingOrderEnabled();
2186 final View[] children = mChildren;
2187 for (int i = childrenCount - 1; i >= 0; i--) {
2188 final int childIndex = customOrder
2189 ? getChildDrawingOrder(childrenCount, i) : i;
2190 final View child = (preorderedList == null)
2191 ? children[childIndex] : preorderedList.get(childIndex);
2192 if (!canViewReceivePointerEvents(child)
2193 || !isTransformedTouchPointInView(x, y, child, null)) {
2197 newTouchTarget = getTouchTarget(child);
2198 if (newTouchTarget != null) {
2199 // Child is already receiving touch within its bounds.
2200 // Give it the new pointer in addition to the ones it is handling.
2201 newTouchTarget.pointerIdBits |= idBitsToAssign;
2205 resetCancelNextUpFlag(child);
2206 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
2207 // Child wants to receive touch within its bounds.
2208 mLastTouchDownTime = ev.getDownTime();
2209 if (preorderedList != null) {
2210 // childIndex points into presorted list, find original index
2211 for (int j = 0; j < childrenCount; j++) {
2212 if (children[childIndex] == mChildren[j]) {
2213 mLastTouchDownIndex = j;
2218 mLastTouchDownIndex = childIndex;
2220 mLastTouchDownX = ev.getX();
2221 mLastTouchDownY = ev.getY();
2222 newTouchTarget = addTouchTarget(child, idBitsToAssign);
2223 alreadyDispatchedToNewTouchTarget = true;
2227 if (preorderedList != null) preorderedList.clear();
2230 if (newTouchTarget == null && mFirstTouchTarget != null) {
2231 // Did not find a child to receive the event.
2232 // Assign the pointer to the least recently added target.
2233 newTouchTarget = mFirstTouchTarget;
2234 while (newTouchTarget.next != null) {
2235 newTouchTarget = newTouchTarget.next;
2237 newTouchTarget.pointerIdBits |= idBitsToAssign;
2242 // Dispatch to touch targets.
2243 if (mFirstTouchTarget == null) {
2244 // No touch targets so treat this as an ordinary view.
2245 handled = dispatchTransformedTouchEvent(ev, canceled, null,
2246 TouchTarget.ALL_POINTER_IDS);
2248 // Dispatch to touch targets, excluding the new touch target if we already
2249 // dispatched to it. Cancel touch targets if necessary.
2250 TouchTarget predecessor = null;
2251 TouchTarget target = mFirstTouchTarget;
2252 while (target != null) {
2253 final TouchTarget next = target.next;
2254 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
2257 final boolean cancelChild = resetCancelNextUpFlag(target.child)
2259 if (dispatchTransformedTouchEvent(ev, cancelChild,
2260 target.child, target.pointerIdBits)) {
2264 if (predecessor == null) {
2265 mFirstTouchTarget = next;
2267 predecessor.next = next;
2274 predecessor = target;
2279 // Update list of touch targets for pointer up or cancel, if needed.
2281 || actionMasked == MotionEvent.ACTION_UP
2282 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2284 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
2285 final int actionIndex = ev.getActionIndex();
2286 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
2287 removePointersFromTouchTargets(idBitsToRemove);
2291 if (!handled && mInputEventConsistencyVerifier != null) {
2292 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
2298 * Resets all touch state in preparation for a new cycle.
2300 private void resetTouchState() {
2301 clearTouchTargets();
2302 resetCancelNextUpFlag(this);
2303 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2304 mNestedScrollAxes = SCROLL_AXIS_NONE;
2308 * Resets the cancel next up flag.
2309 * Returns true if the flag was previously set.
2311 private static boolean resetCancelNextUpFlag(View view) {
2312 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
2313 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
2320 * Clears all touch targets.
2322 private void clearTouchTargets() {
2323 TouchTarget target = mFirstTouchTarget;
2324 if (target != null) {
2326 TouchTarget next = target.next;
2329 } while (target != null);
2330 mFirstTouchTarget = null;
2335 * Cancels and clears all touch targets.
2337 private void cancelAndClearTouchTargets(MotionEvent event) {
2338 if (mFirstTouchTarget != null) {
2339 boolean syntheticEvent = false;
2340 if (event == null) {
2341 final long now = SystemClock.uptimeMillis();
2342 event = MotionEvent.obtain(now, now,
2343 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2344 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2345 syntheticEvent = true;
2348 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2349 resetCancelNextUpFlag(target.child);
2350 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2352 clearTouchTargets();
2354 if (syntheticEvent) {
2361 * Gets the touch target for specified child view.
2362 * Returns null if not found.
2364 private TouchTarget getTouchTarget(View child) {
2365 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2366 if (target.child == child) {
2374 * Adds a touch target for specified child to the beginning of the list.
2375 * Assumes the target child is not already present.
2377 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
2378 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2379 target.next = mFirstTouchTarget;
2380 mFirstTouchTarget = target;
2385 * Removes the pointer ids from consideration.
2387 private void removePointersFromTouchTargets(int pointerIdBits) {
2388 TouchTarget predecessor = null;
2389 TouchTarget target = mFirstTouchTarget;
2390 while (target != null) {
2391 final TouchTarget next = target.next;
2392 if ((target.pointerIdBits & pointerIdBits) != 0) {
2393 target.pointerIdBits &= ~pointerIdBits;
2394 if (target.pointerIdBits == 0) {
2395 if (predecessor == null) {
2396 mFirstTouchTarget = next;
2398 predecessor.next = next;
2405 predecessor = target;
2410 private void cancelTouchTarget(View view) {
2411 TouchTarget predecessor = null;
2412 TouchTarget target = mFirstTouchTarget;
2413 while (target != null) {
2414 final TouchTarget next = target.next;
2415 if (target.child == view) {
2416 if (predecessor == null) {
2417 mFirstTouchTarget = next;
2419 predecessor.next = next;
2423 final long now = SystemClock.uptimeMillis();
2424 MotionEvent event = MotionEvent.obtain(now, now,
2425 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2426 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2427 view.dispatchTouchEvent(event);
2431 predecessor = target;
2437 * Returns true if a child view can receive pointer events.
2440 private static boolean canViewReceivePointerEvents(View child) {
2441 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2442 || child.getAnimation() != null;
2446 * Returns true if a child view contains the specified point when transformed
2447 * into its coordinate space.
2448 * Child must not be null.
2451 protected boolean isTransformedTouchPointInView(float x, float y, View child,
2452 PointF outLocalPoint) {
2453 float localX = x + mScrollX - child.mLeft;
2454 float localY = y + mScrollY - child.mTop;
2455 if (! child.hasIdentityMatrix() && mAttachInfo != null) {
2456 final float[] localXY = mAttachInfo.mTmpTransformLocation;
2457 localXY[0] = localX;
2458 localXY[1] = localY;
2459 child.getInverseMatrix().mapPoints(localXY);
2460 localX = localXY[0];
2461 localY = localXY[1];
2463 final boolean isInView = child.pointInView(localX, localY);
2464 if (isInView && outLocalPoint != null) {
2465 outLocalPoint.set(localX, localY);
2471 * Transforms a motion event into the coordinate space of a particular child view,
2472 * filters out irrelevant pointer ids, and overrides its action if necessary.
2473 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2475 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
2476 View child, int desiredPointerIdBits) {
2477 final boolean handled;
2479 // Canceling motions is a special case. We don't need to perform any transformations
2480 // or filtering. The important part is the action, not the contents.
2481 final int oldAction = event.getAction();
2482 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
2483 event.setAction(MotionEvent.ACTION_CANCEL);
2484 if (child == null) {
2485 handled = super.dispatchTouchEvent(event);
2487 handled = child.dispatchTouchEvent(event);
2489 event.setAction(oldAction);
2493 // Calculate the number of pointers to deliver.
2494 final int oldPointerIdBits = event.getPointerIdBits();
2495 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
2497 // If for some reason we ended up in an inconsistent state where it looks like we
2498 // might produce a motion event with no pointers in it, then drop the event.
2499 if (newPointerIdBits == 0) {
2503 // If the number of pointers is the same and we don't need to perform any fancy
2504 // irreversible transformations, then we can reuse the motion event for this
2505 // dispatch as long as we are careful to revert any changes we make.
2506 // Otherwise we need to make a copy.
2507 final MotionEvent transformedEvent;
2508 if (newPointerIdBits == oldPointerIdBits) {
2509 if (child == null || child.hasIdentityMatrix()) {
2510 if (child == null) {
2511 handled = super.dispatchTouchEvent(event);
2513 final float offsetX = mScrollX - child.mLeft;
2514 final float offsetY = mScrollY - child.mTop;
2515 event.offsetLocation(offsetX, offsetY);
2517 handled = child.dispatchTouchEvent(event);
2519 event.offsetLocation(-offsetX, -offsetY);
2523 transformedEvent = MotionEvent.obtain(event);
2525 transformedEvent = event.split(newPointerIdBits);
2528 // Perform any necessary transformations and dispatch.
2529 if (child == null) {
2530 handled = super.dispatchTouchEvent(transformedEvent);
2532 final float offsetX = mScrollX - child.mLeft;
2533 final float offsetY = mScrollY - child.mTop;
2534 transformedEvent.offsetLocation(offsetX, offsetY);
2535 if (! child.hasIdentityMatrix()) {
2536 transformedEvent.transform(child.getInverseMatrix());
2539 handled = child.dispatchTouchEvent(transformedEvent);
2543 transformedEvent.recycle();
2548 * Enable or disable the splitting of MotionEvents to multiple children during touch event
2549 * dispatch. This behavior is enabled by default for applications that target an
2550 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
2552 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2553 * views depending on where each pointer initially went down. This allows for user interactions
2554 * such as scrolling two panes of content independently, chording of buttons, and performing
2555 * independent gestures on different pieces of content.
2557 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2558 * child views. <code>false</code> to only allow one child view to be the target of
2559 * any MotionEvent received by this ViewGroup.
2560 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
2562 public void setMotionEventSplittingEnabled(boolean split) {
2563 // TODO Applications really shouldn't change this setting mid-touch event,
2564 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2565 // with gestures in progress when this is changed.
2567 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2569 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
2574 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2575 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2577 public boolean isMotionEventSplittingEnabled() {
2578 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2582 * Returns true if this ViewGroup should be considered as a single entity for removal
2583 * when executing an Activity transition. If this is false, child elements will move
2584 * individually during the transition.
2585 * @return True if the ViewGroup should be acted on together during an Activity transition.
2586 * The default value is false when the background is null and true when the background
2587 * is not null or if {@link #getTransitionName()} is not null.
2589 public boolean isTransitionGroup() {
2590 if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
2591 return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
2593 return getBackground() != null || getTransitionName() != null;
2598 * Changes whether or not this ViewGroup should be treated as a single entity during
2599 * Activity Transitions.
2600 * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
2601 * in Activity transitions. If false, the ViewGroup won't transition,
2602 * only its children. If true, the entire ViewGroup will transition
2604 * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
2605 * android.util.Pair[])
2607 public void setTransitionGroup(boolean isTransitionGroup) {
2608 mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
2609 if (isTransitionGroup) {
2610 mGroupFlags |= FLAG_IS_TRANSITION_GROUP;
2612 mGroupFlags &= ~FLAG_IS_TRANSITION_GROUP;
2619 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2621 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2622 // We're already in this state, assume our ancestors are too
2626 if (disallowIntercept) {
2627 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2629 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2632 // Pass it up to our parent
2633 if (mParent != null) {
2634 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2639 * Implement this method to intercept all touch screen motion events. This
2640 * allows you to watch events as they are dispatched to your children, and
2641 * take ownership of the current gesture at any point.
2643 * <p>Using this function takes some care, as it has a fairly complicated
2644 * interaction with {@link View#onTouchEvent(MotionEvent)
2645 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2646 * that method as well as this one in the correct way. Events will be
2647 * received in the following order:
2650 * <li> You will receive the down event here.
2651 * <li> The down event will be handled either by a child of this view
2652 * group, or given to your own onTouchEvent() method to handle; this means
2653 * you should implement onTouchEvent() to return true, so you will
2654 * continue to see the rest of the gesture (instead of looking for
2655 * a parent view to handle it). Also, by returning true from
2656 * onTouchEvent(), you will not receive any following
2657 * events in onInterceptTouchEvent() and all touch processing must
2658 * happen in onTouchEvent() like normal.
2659 * <li> For as long as you return false from this function, each following
2660 * event (up to and including the final up) will be delivered first here
2661 * and then to the target's onTouchEvent().
2662 * <li> If you return true from here, you will not receive any
2663 * following events: the target view will receive the same event but
2664 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2665 * events will be delivered to your onTouchEvent() method and no longer
2669 * @param ev The motion event being dispatched down the hierarchy.
2670 * @return Return true to steal motion events from the children and have
2671 * them dispatched to this ViewGroup through onTouchEvent().
2672 * The current target will receive an ACTION_CANCEL event, and no further
2673 * messages will be delivered here.
2675 public boolean onInterceptTouchEvent(MotionEvent ev) {
2682 * Looks for a view to give focus to respecting the setting specified by
2683 * {@link #getDescendantFocusability()}.
2685 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2686 * find focus within the children of this group when appropriate.
2688 * @see #FOCUS_BEFORE_DESCENDANTS
2689 * @see #FOCUS_AFTER_DESCENDANTS
2690 * @see #FOCUS_BLOCK_DESCENDANTS
2691 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
2694 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2696 System.out.println(this + " ViewGroup.requestFocus direction="
2699 int descendantFocusability = getDescendantFocusability();
2701 switch (descendantFocusability) {
2702 case FOCUS_BLOCK_DESCENDANTS:
2703 return super.requestFocus(direction, previouslyFocusedRect);
2704 case FOCUS_BEFORE_DESCENDANTS: {
2705 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2706 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2708 case FOCUS_AFTER_DESCENDANTS: {
2709 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2710 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2713 throw new IllegalStateException("descendant focusability must be "
2714 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2715 + "but is " + descendantFocusability);
2720 * Look for a descendant to call {@link View#requestFocus} on.
2721 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2722 * when it wants to request focus within its children. Override this to
2723 * customize how your {@link ViewGroup} requests focus within its children.
2724 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2725 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2726 * to give a finer grained hint about where focus is coming from. May be null
2727 * if there is no hint.
2728 * @return Whether focus was taken.
2730 @SuppressWarnings({"ConstantConditions"})
2731 protected boolean onRequestFocusInDescendants(int direction,
2732 Rect previouslyFocusedRect) {
2736 int count = mChildrenCount;
2737 if ((direction & FOCUS_FORWARD) != 0) {
2746 final View[] children = mChildren;
2747 for (int i = index; i != end; i += increment) {
2748 View child = children[i];
2749 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2750 if (child.requestFocus(direction, previouslyFocusedRect)) {
2764 public void dispatchStartTemporaryDetach() {
2765 super.dispatchStartTemporaryDetach();
2766 final int count = mChildrenCount;
2767 final View[] children = mChildren;
2768 for (int i = 0; i < count; i++) {
2769 children[i].dispatchStartTemporaryDetach();
2779 public void dispatchFinishTemporaryDetach() {
2780 super.dispatchFinishTemporaryDetach();
2781 final int count = mChildrenCount;
2782 final View[] children = mChildren;
2783 for (int i = 0; i < count; i++) {
2784 children[i].dispatchFinishTemporaryDetach();
2792 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
2793 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2794 super.dispatchAttachedToWindow(info, visibility);
2795 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2797 final int count = mChildrenCount;
2798 final View[] children = mChildren;
2799 for (int i = 0; i < count; i++) {
2800 final View child = children[i];
2801 child.dispatchAttachedToWindow(info,
2802 visibility | (child.mViewFlags & VISIBILITY_MASK));
2807 void dispatchScreenStateChanged(int screenState) {
2808 super.dispatchScreenStateChanged(screenState);
2810 final int count = mChildrenCount;
2811 final View[] children = mChildren;
2812 for (int i = 0; i < count; i++) {
2813 children[i].dispatchScreenStateChanged(screenState);
2818 boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
2819 boolean handled = false;
2820 if (includeForAccessibility()) {
2821 handled = super.dispatchPopulateAccessibilityEventInternal(event);
2826 // Let our children have a shot in populating the event.
2827 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
2829 final int childCount = children.getChildCount();
2830 for (int i = 0; i < childCount; i++) {
2831 View child = children.getChildAt(i);
2832 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2833 handled = child.dispatchPopulateAccessibilityEvent(event);
2846 void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2847 super.onInitializeAccessibilityNodeInfoInternal(info);
2848 if (mAttachInfo != null) {
2849 final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
2850 childrenForAccessibility.clear();
2851 addChildrenForAccessibility(childrenForAccessibility);
2852 final int childrenForAccessibilityCount = childrenForAccessibility.size();
2853 for (int i = 0; i < childrenForAccessibilityCount; i++) {
2854 final View child = childrenForAccessibility.get(i);
2855 info.addChildUnchecked(child);
2857 childrenForAccessibility.clear();
2862 void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
2863 super.onInitializeAccessibilityEventInternal(event);
2864 event.setClassName(ViewGroup.class.getName());
2868 public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
2869 // If this is a live region, we should send a subtree change event
2870 // from this view. Otherwise, we can let it propagate up.
2871 if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
2872 notifyViewAccessibilityStateChangedIfNeeded(changeType);
2873 } else if (mParent != null) {
2875 mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
2876 } catch (AbstractMethodError e) {
2877 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
2878 " does not fully implement ViewParent", e);
2884 void resetSubtreeAccessibilityStateChanged() {
2885 super.resetSubtreeAccessibilityStateChanged();
2886 View[] children = mChildren;
2887 final int childCount = mChildrenCount;
2888 for (int i = 0; i < childCount; i++) {
2889 children[i].resetSubtreeAccessibilityStateChanged();
2897 void dispatchDetachedFromWindow() {
2898 // If we still have a touch target, we are still in the process of
2899 // dispatching motion events to a child; we need to get rid of that
2900 // child to avoid dispatching events to it after the window is torn
2901 // down. To make sure we keep the child in a consistent state, we
2902 // first send it an ACTION_CANCEL motion event.
2903 cancelAndClearTouchTargets(null);
2905 // Similarly, set ACTION_EXIT to all hover targets and clear them.
2908 // In case view is detached while transition is running
2909 mLayoutCalledWhileSuppressed = false;
2911 // Tear down our drag tracking
2912 mDragNotifiedChildren = null;
2913 if (mCurrentDrag != null) {
2914 mCurrentDrag.recycle();
2915 mCurrentDrag = null;
2918 final int count = mChildrenCount;
2919 final View[] children = mChildren;
2920 for (int i = 0; i < count; i++) {
2921 children[i].dispatchDetachedFromWindow();
2923 clearDisappearingChildren();
2924 super.dispatchDetachedFromWindow();
2931 protected void internalSetPadding(int left, int top, int right, int bottom) {
2932 super.internalSetPadding(left, top, right, bottom);
2934 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
2935 mGroupFlags |= FLAG_PADDING_NOT_NULL;
2937 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2945 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2946 super.dispatchSaveInstanceState(container);
2947 final int count = mChildrenCount;
2948 final View[] children = mChildren;
2949 for (int i = 0; i < count; i++) {
2950 View c = children[i];
2951 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2952 c.dispatchSaveInstanceState(container);
2958 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
2959 * to only this view, not to its children. For use when overriding
2960 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
2961 * subclasses to freeze their own state but not the state of their children.
2963 * @param container the container
2965 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2966 super.dispatchSaveInstanceState(container);
2973 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2974 super.dispatchRestoreInstanceState(container);
2975 final int count = mChildrenCount;
2976 final View[] children = mChildren;
2977 for (int i = 0; i < count; i++) {
2978 View c = children[i];
2979 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2980 c.dispatchRestoreInstanceState(container);
2986 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2987 * to only this view, not to its children. For use when overriding
2988 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2989 * subclasses to thaw their own state but not the state of their children.
2991 * @param container the container
2993 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2994 super.dispatchRestoreInstanceState(container);
2998 * Enables or disables the drawing cache for each child of this view group.
3000 * @param enabled true to enable the cache, false to dispose of it
3002 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
3003 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
3004 final View[] children = mChildren;
3005 final int count = mChildrenCount;
3006 for (int i = 0; i < count; i++) {
3007 children[i].setDrawingCacheEnabled(enabled);
3013 protected void onAnimationStart() {
3014 super.onAnimationStart();
3016 // When this ViewGroup's animation starts, build the cache for the children
3017 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
3018 final int count = mChildrenCount;
3019 final View[] children = mChildren;
3020 final boolean buildCache = !isHardwareAccelerated();
3022 for (int i = 0; i < count; i++) {
3023 final View child = children[i];
3024 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3025 child.setDrawingCacheEnabled(true);
3027 child.buildDrawingCache(true);
3032 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
3037 protected void onAnimationEnd() {
3038 super.onAnimationEnd();
3040 // When this ViewGroup's animation ends, destroy the cache of the children
3041 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
3042 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
3044 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
3045 setChildrenDrawingCacheEnabled(false);
3051 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
3052 int count = mChildrenCount;
3053 int[] visibilities = null;
3056 visibilities = new int[count];
3057 for (int i = 0; i < count; i++) {
3058 View child = getChildAt(i);
3059 visibilities[i] = child.getVisibility();
3060 if (visibilities[i] == View.VISIBLE) {
3061 child.setVisibility(INVISIBLE);
3066 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
3069 for (int i = 0; i < count; i++) {
3070 getChildAt(i).setVisibility(visibilities[i]);
3077 /** Return true if this ViewGroup is laying out using optical bounds. */
3078 boolean isLayoutModeOptical() {
3079 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
3082 Insets computeOpticalInsets() {
3083 if (isLayoutModeOptical()) {
3088 for (int i = 0; i < mChildrenCount; i++) {
3089 View child = getChildAt(i);
3090 if (child.getVisibility() == VISIBLE) {
3091 Insets insets = child.getOpticalInsets();
3092 left = Math.max(left, insets.left);
3093 top = Math.max(top, insets.top);
3094 right = Math.max(right, insets.right);
3095 bottom = Math.max(bottom, insets.bottom);
3098 return Insets.of(left, top, right, bottom);
3104 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
3105 if (x1 != x2 && y1 != y2) {
3107 int tmp = x1; x1 = x2; x2 = tmp;
3110 int tmp = y1; y1 = y2; y2 = tmp;
3112 canvas.drawRect(x1, y1, x2, y2, paint);
3116 private static int sign(int x) {
3117 return (x >= 0) ? 1 : -1;
3120 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
3121 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
3122 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
3125 private int dipsToPixels(int dips) {
3126 float scale = getContext().getResources().getDisplayMetrics().density;
3127 return (int) (dips * scale + 0.5f);
3130 private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
3131 int lineLength, int lineWidth) {
3132 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
3133 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
3134 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
3135 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
3138 private static void fillDifference(Canvas canvas,
3139 int x2, int y2, int x3, int y3,
3140 int dx1, int dy1, int dx2, int dy2, Paint paint) {
3147 fillRect(canvas, paint, x1, y1, x4, y2);
3148 fillRect(canvas, paint, x1, y2, x2, y3);
3149 fillRect(canvas, paint, x3, y2, x4, y3);
3150 fillRect(canvas, paint, x1, y3, x4, y4);
3156 protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
3157 for (int i = 0; i < getChildCount(); i++) {
3158 View c = getChildAt(i);
3159 c.getLayoutParams().onDebugDraw(c, canvas, paint);
3166 protected void onDebugDraw(Canvas canvas) {
3167 Paint paint = getDebugPaint();
3169 // Draw optical bounds
3171 paint.setColor(Color.RED);
3172 paint.setStyle(Paint.Style.STROKE);
3174 for (int i = 0; i < getChildCount(); i++) {
3175 View c = getChildAt(i);
3176 Insets insets = c.getOpticalInsets();
3178 drawRect(canvas, paint,
3179 c.getLeft() + insets.left,
3180 c.getTop() + insets.top,
3181 c.getRight() - insets.right - 1,
3182 c.getBottom() - insets.bottom - 1);
3188 paint.setColor(Color.argb(63, 255, 0, 255));
3189 paint.setStyle(Paint.Style.FILL);
3191 onDebugDrawMargins(canvas, paint);
3196 paint.setColor(Color.rgb(63, 127, 255));
3197 paint.setStyle(Paint.Style.FILL);
3199 int lineLength = dipsToPixels(8);
3200 int lineWidth = dipsToPixels(1);
3201 for (int i = 0; i < getChildCount(); i++) {
3202 View c = getChildAt(i);
3203 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
3204 paint, lineLength, lineWidth);
3213 protected void dispatchDraw(Canvas canvas) {
3214 boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
3215 final int childrenCount = mChildrenCount;
3216 final View[] children = mChildren;
3217 int flags = mGroupFlags;
3219 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
3220 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
3222 final boolean buildCache = !isHardwareAccelerated();
3223 for (int i = 0; i < childrenCount; i++) {
3224 final View child = children[i];
3225 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3226 final LayoutParams params = child.getLayoutParams();
3227 attachLayoutAnimationParameters(child, params, i, childrenCount);
3228 bindLayoutAnimation(child);
3230 child.setDrawingCacheEnabled(true);
3232 child.buildDrawingCache(true);
3238 final LayoutAnimationController controller = mLayoutAnimationController;
3239 if (controller.willOverlap()) {
3240 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
3245 mGroupFlags &= ~FLAG_RUN_ANIMATION;
3246 mGroupFlags &= ~FLAG_ANIMATION_DONE;
3249 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
3252 if (mAnimationListener != null) {
3253 mAnimationListener.onAnimationStart(controller.getAnimation());
3257 int clipSaveCount = 0;
3258 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
3259 if (clipToPadding) {
3260 clipSaveCount = canvas.save();
3261 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
3262 mScrollX + mRight - mLeft - mPaddingRight,
3263 mScrollY + mBottom - mTop - mPaddingBottom);
3266 // We will draw our child's animation, let's reset the flag
3267 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
3268 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
3270 boolean more = false;
3271 final long drawingTime = getDrawingTime();
3273 if (usingRenderNodeProperties) canvas.insertReorderBarrier();
3274 // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
3275 // draw reordering internally
3276 final ArrayList<View> preorderedList = usingRenderNodeProperties
3277 ? null : buildOrderedChildList();
3278 final boolean customOrder = preorderedList == null
3279 && isChildrenDrawingOrderEnabled();
3280 for (int i = 0; i < childrenCount; i++) {
3281 int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
3282 final View child = (preorderedList == null)
3283 ? children[childIndex] : preorderedList.get(childIndex);
3284 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
3285 more |= drawChild(canvas, child, drawingTime);
3288 if (preorderedList != null) preorderedList.clear();
3290 // Draw any disappearing views that have animations
3291 if (mDisappearingChildren != null) {
3292 final ArrayList<View> disappearingChildren = mDisappearingChildren;
3293 final int disappearingCount = disappearingChildren.size() - 1;
3294 // Go backwards -- we may delete as animations finish
3295 for (int i = disappearingCount; i >= 0; i--) {
3296 final View child = disappearingChildren.get(i);
3297 more |= drawChild(canvas, child, drawingTime);
3300 if (usingRenderNodeProperties) canvas.insertInorderBarrier();
3303 onDebugDraw(canvas);
3306 if (clipToPadding) {
3307 canvas.restoreToCount(clipSaveCount);
3310 // mGroupFlags might have been updated by drawChild()
3311 flags = mGroupFlags;
3313 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
3317 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
3318 mLayoutAnimationController.isDone() && !more) {
3319 // We want to erase the drawing cache and notify the listener after the
3320 // next frame is drawn because one extra invalidate() is caused by
3321 // drawChild() after the animation is over
3322 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
3323 final Runnable end = new Runnable() {
3325 notifyAnimationListener();
3333 * Returns the ViewGroupOverlay for this view group, creating it if it does
3334 * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
3335 * {@link ViewGroupOverlay} allows views to be added to the overlay. These
3336 * views, like overlay drawables, are visual-only; they do not receive input
3337 * events and should not be used as anything other than a temporary
3338 * representation of a view in a parent container, such as might be used
3339 * by an animation effect.
3341 * <p>Note: Overlays do not currently work correctly with {@link
3342 * SurfaceView} or {@link TextureView}; contents in overlays for these
3343 * types of views may not display correctly.</p>
3345 * @return The ViewGroupOverlay object for this view.
3346 * @see ViewGroupOverlay
3349 public ViewGroupOverlay getOverlay() {
3350 if (mOverlay == null) {
3351 mOverlay = new ViewGroupOverlay(mContext, this);
3353 return (ViewGroupOverlay) mOverlay;
3357 * Returns the index of the child to draw for this iteration. Override this
3358 * if you want to change the drawing order of children. By default, it
3361 * NOTE: In order for this method to be called, you must enable child ordering
3362 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
3364 * @param i The current iteration.
3365 * @return The index of the child to draw this iteration.
3367 * @see #setChildrenDrawingOrderEnabled(boolean)
3368 * @see #isChildrenDrawingOrderEnabled()
3370 protected int getChildDrawingOrder(int childCount, int i) {
3374 private boolean hasChildWithZ() {
3375 for (int i = 0; i < mChildrenCount; i++) {
3376 if (mChildren[i].getZ() != 0) return true;
3382 * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
3383 * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
3384 * after use to avoid leaking child Views.
3386 * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
3389 ArrayList<View> buildOrderedChildList() {
3390 final int count = mChildrenCount;
3391 if (count <= 1 || !hasChildWithZ()) return null;
3393 if (mPreSortedChildren == null) {
3394 mPreSortedChildren = new ArrayList<View>(count);
3396 mPreSortedChildren.ensureCapacity(count);
3399 final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
3400 for (int i = 0; i < mChildrenCount; i++) {
3401 // add next child (in child order) to end of list
3402 int childIndex = useCustomOrder ? getChildDrawingOrder(mChildrenCount, i) : i;
3403 View nextChild = mChildren[childIndex];
3404 float currentZ = nextChild.getZ();
3406 // insert ahead of any Views with greater Z
3407 int insertIndex = i;
3408 while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
3411 mPreSortedChildren.add(insertIndex, nextChild);
3413 return mPreSortedChildren;
3416 private void notifyAnimationListener() {
3417 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
3418 mGroupFlags |= FLAG_ANIMATION_DONE;
3420 if (mAnimationListener != null) {
3421 final Runnable end = new Runnable() {
3423 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
3429 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
3430 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
3431 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
3432 setChildrenDrawingCacheEnabled(false);
3440 * This method is used to cause children of this ViewGroup to restore or recreate their
3441 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
3442 * to recreate its own display list, which would happen if it went through the normal
3443 * draw/dispatchDraw mechanisms.
3448 protected void dispatchGetDisplayList() {
3449 final int count = mChildrenCount;
3450 final View[] children = mChildren;
3451 for (int i = 0; i < count; i++) {
3452 final View child = children[i];
3453 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
3454 child.hasStaticLayer()) {
3455 recreateChildDisplayList(child);
3458 if (mOverlay != null) {
3459 View overlayView = mOverlay.getOverlayView();
3460 recreateChildDisplayList(overlayView);
3462 if (mDisappearingChildren != null) {
3463 final ArrayList<View> disappearingChildren = mDisappearingChildren;
3464 final int disappearingCount = disappearingChildren.size();
3465 for (int i = 0; i < disappearingCount; ++i) {
3466 final View child = disappearingChildren.get(i);
3467 recreateChildDisplayList(child);
3472 private void recreateChildDisplayList(View child) {
3473 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED)
3474 == PFLAG_INVALIDATED;
3475 child.mPrivateFlags &= ~PFLAG_INVALIDATED;
3476 child.getDisplayList();
3477 child.mRecreateDisplayList = false;
3481 * Draw one child of this View Group. This method is responsible for getting
3482 * the canvas in the right state. This includes clipping, translating so
3483 * that the child's scrolled origin is at 0, 0, and applying any animation
3486 * @param canvas The canvas on which to draw the child
3487 * @param child Who to draw
3488 * @param drawingTime The time at which draw is occurring
3489 * @return True if an invalidate() was issued
3491 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3492 return child.draw(canvas, this, drawingTime);
3496 * Returns whether this group's children are clipped to their bounds before drawing.
3497 * The default value is true.
3498 * @see #setClipChildren(boolean)
3500 * @return True if the group's children will be clipped to their bounds,
3503 @ViewDebug.ExportedProperty(category = "drawing")
3504 public boolean getClipChildren() {
3505 return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
3509 * By default, children are clipped to their bounds before drawing. This
3510 * allows view groups to override this behavior for animations, etc.
3512 * @param clipChildren true to clip children to their bounds,
3514 * @attr ref android.R.styleable#ViewGroup_clipChildren
3516 public void setClipChildren(boolean clipChildren) {
3517 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
3518 if (clipChildren != previousValue) {
3519 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
3520 for (int i = 0; i < mChildrenCount; ++i) {
3521 View child = getChildAt(i);
3522 if (child.mRenderNode != null) {
3523 child.mRenderNode.setClipToBounds(clipChildren);
3531 * By default, children are clipped to the padding of the ViewGroup. This
3532 * allows view groups to override this behavior
3534 * @param clipToPadding true to clip children to the padding of the
3535 * group, false otherwise
3536 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3538 public void setClipToPadding(boolean clipToPadding) {
3539 if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) {
3540 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3546 * Check if this ViewGroup is configured to clip child views to its padding.
3548 * @return true if this ViewGroup clips children to its padding, false otherwise
3550 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3552 @ViewDebug.ExportedProperty(category = "drawing")
3553 public boolean getClipToPadding() {
3554 return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
3561 public void dispatchSetSelected(boolean selected) {
3562 final View[] children = mChildren;
3563 final int count = mChildrenCount;
3564 for (int i = 0; i < count; i++) {
3565 children[i].setSelected(selected);
3573 public void dispatchSetActivated(boolean activated) {
3574 final View[] children = mChildren;
3575 final int count = mChildrenCount;
3576 for (int i = 0; i < count; i++) {
3577 children[i].setActivated(activated);
3582 protected void dispatchSetPressed(boolean pressed) {
3583 final View[] children = mChildren;
3584 final int count = mChildrenCount;
3585 for (int i = 0; i < count; i++) {
3586 final View child = children[i];
3587 // Children that are clickable on their own should not
3588 // show a pressed state when their parent view does.
3589 // Clearing a pressed state always propagates.
3590 if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
3591 child.setPressed(pressed);
3597 void dispatchCancelPendingInputEvents() {
3598 super.dispatchCancelPendingInputEvents();
3600 final View[] children = mChildren;
3601 final int count = mChildrenCount;
3602 for (int i = 0; i < count; i++) {
3603 children[i].dispatchCancelPendingInputEvents();
3608 * When this property is set to true, this ViewGroup supports static transformations on
3609 * children; this causes
3610 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3611 * invoked when a child is drawn.
3613 * Any subclass overriding
3614 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3615 * set this property to true.
3617 * @param enabled True to enable static transformations on children, false otherwise.
3619 * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
3621 protected void setStaticTransformationsEnabled(boolean enabled) {
3622 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3626 * Sets <code>t</code> to be the static transformation of the child, if set, returning a
3627 * boolean to indicate whether a static transform was set. The default implementation
3628 * simply returns <code>false</code>; subclasses may override this method for different
3629 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
3630 * for this method to be called.
3632 * @param child The child view whose static transform is being requested
3633 * @param t The Transformation which will hold the result
3634 * @return true if the transformation was set, false otherwise
3635 * @see #setStaticTransformationsEnabled(boolean)
3637 protected boolean getChildStaticTransformation(View child, Transformation t) {
3641 Transformation getChildTransformation() {
3642 if (mChildTransformation == null) {
3643 mChildTransformation = new Transformation();
3645 return mChildTransformation;
3652 protected View findViewTraversal(int id) {
3657 final View[] where = mChildren;
3658 final int len = mChildrenCount;
3660 for (int i = 0; i < len; i++) {
3663 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3664 v = v.findViewById(id);
3679 protected View findViewWithTagTraversal(Object tag) {
3680 if (tag != null && tag.equals(mTag)) {
3684 final View[] where = mChildren;
3685 final int len = mChildrenCount;
3687 for (int i = 0; i < len; i++) {
3690 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3691 v = v.findViewWithTag(tag);
3706 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
3707 if (predicate.apply(this)) {
3711 final View[] where = mChildren;
3712 final int len = mChildrenCount;
3714 for (int i = 0; i < len; i++) {
3717 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3718 v = v.findViewByPredicate(predicate);
3730 * <p>Adds a child view. If no layout parameters are already set on the child, the
3731 * default parameters for this ViewGroup are set on the child.</p>
3733 * <p><strong>Note:</strong> do not invoke this method from
3734 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3735 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3737 * @param child the child view to add
3739 * @see #generateDefaultLayoutParams()
3741 public void addView(View child) {
3746 * Adds a child view. If no layout parameters are already set on the child, the
3747 * default parameters for this ViewGroup are set on the child.
3749 * <p><strong>Note:</strong> do not invoke this method from
3750 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3751 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3753 * @param child the child view to add
3754 * @param index the position at which to add the child
3756 * @see #generateDefaultLayoutParams()
3758 public void addView(View child, int index) {
3759 if (child == null) {
3760 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
3762 LayoutParams params = child.getLayoutParams();
3763 if (params == null) {
3764 params = generateDefaultLayoutParams();
3765 if (params == null) {
3766 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3769 addView(child, index, params);
3773 * Adds a child view with this ViewGroup's default layout parameters and the
3774 * specified width and height.
3776 * <p><strong>Note:</strong> do not invoke this method from
3777 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3778 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3780 * @param child the child view to add
3782 public void addView(View child, int width, int height) {
3783 final LayoutParams params = generateDefaultLayoutParams();
3784 params.width = width;
3785 params.height = height;
3786 addView(child, -1, params);
3790 * Adds a child view with the specified layout parameters.
3792 * <p><strong>Note:</strong> do not invoke this method from
3793 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3794 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3796 * @param child the child view to add
3797 * @param params the layout parameters to set on the child
3799 public void addView(View child, LayoutParams params) {
3800 addView(child, -1, params);
3804 * Adds a child view with the specified layout parameters.
3806 * <p><strong>Note:</strong> do not invoke this method from
3807 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3808 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3810 * @param child the child view to add
3811 * @param index the position at which to add the child
3812 * @param params the layout parameters to set on the child
3814 public void addView(View child, int index, LayoutParams params) {
3816 System.out.println(this + " addView");
3819 if (child == null) {
3820 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
3823 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3824 // therefore, we call requestLayout() on ourselves before, so that the child's request
3825 // will be blocked at our level
3828 addViewInner(child, index, params, false);
3834 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3835 if (!checkLayoutParams(params)) {
3836 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3838 if (view.mParent != this) {
3839 throw new IllegalArgumentException("Given view not a child of " + this);
3841 view.setLayoutParams(params);
3847 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3852 * Interface definition for a callback to be invoked when the hierarchy
3853 * within this view changed. The hierarchy changes whenever a child is added
3854 * to or removed from this view.
3856 public interface OnHierarchyChangeListener {
3858 * Called when a new child is added to a parent view.
3860 * @param parent the view in which a child was added
3861 * @param child the new child view added in the hierarchy
3863 void onChildViewAdded(View parent, View child);
3866 * Called when a child is removed from a parent view.
3868 * @param parent the view from which the child was removed
3869 * @param child the child removed from the hierarchy
3871 void onChildViewRemoved(View parent, View child);
3875 * Register a callback to be invoked when a child is added to or removed
3878 * @param listener the callback to invoke on hierarchy change
3880 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3881 mOnHierarchyChangeListener = listener;
3887 protected void onViewAdded(View child) {
3888 if (mOnHierarchyChangeListener != null) {
3889 mOnHierarchyChangeListener.onChildViewAdded(this, child);
3896 protected void onViewRemoved(View child) {
3897 if (mOnHierarchyChangeListener != null) {
3898 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3902 private void clearCachedLayoutMode() {
3903 if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
3904 mLayoutMode = LAYOUT_MODE_UNDEFINED;
3909 protected void onAttachedToWindow() {
3910 super.onAttachedToWindow();
3911 clearCachedLayoutMode();
3915 protected void onDetachedFromWindow() {
3916 super.onDetachedFromWindow();
3917 clearCachedLayoutMode();
3921 * Adds a view during layout. This is useful if in your onLayout() method,
3922 * you need to add more views (as does the list view for example).
3924 * If index is negative, it means put it at the end of the list.
3926 * @param child the view to add to the group
3927 * @param index the index at which the child must be added
3928 * @param params the layout parameters to associate with the child
3929 * @return true if the child was added, false otherwise
3931 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3932 return addViewInLayout(child, index, params, false);
3936 * Adds a view during layout. This is useful if in your onLayout() method,
3937 * you need to add more views (as does the list view for example).
3939 * If index is negative, it means put it at the end of the list.
3941 * @param child the view to add to the group
3942 * @param index the index at which the child must be added
3943 * @param params the layout parameters to associate with the child
3944 * @param preventRequestLayout if true, calling this method will not trigger a
3945 * layout request on child
3946 * @return true if the child was added, false otherwise
3948 protected boolean addViewInLayout(View child, int index, LayoutParams params,
3949 boolean preventRequestLayout) {
3950 if (child == null) {
3951 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
3953 child.mParent = null;
3954 addViewInner(child, index, params, preventRequestLayout);
3955 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
3960 * Prevents the specified child to be laid out during the next layout pass.
3962 * @param child the child on which to perform the cleanup
3964 protected void cleanupLayoutState(View child) {
3965 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
3968 private void addViewInner(View child, int index, LayoutParams params,
3969 boolean preventRequestLayout) {
3971 if (mTransition != null) {
3972 // Don't prevent other add transitions from completing, but cancel remove
3973 // transitions to let them complete the process before we add to the container
3974 mTransition.cancel(LayoutTransition.DISAPPEARING);
3977 if (child.getParent() != null) {
3978 throw new IllegalStateException("The specified child already has a parent. " +
3979 "You must call removeView() on the child's parent first.");
3982 if (mTransition != null) {
3983 mTransition.addChild(this, child);
3986 if (!checkLayoutParams(params)) {
3987 params = generateLayoutParams(params);
3990 if (preventRequestLayout) {
3991 child.mLayoutParams = params;
3993 child.setLayoutParams(params);
3997 index = mChildrenCount;
4000 addInArray(child, index);
4002 // tell our children
4003 if (preventRequestLayout) {
4004 child.assignParent(this);
4006 child.mParent = this;
4009 if (child.hasFocus()) {
4010 requestChildFocus(child, child.findFocus());
4013 AttachInfo ai = mAttachInfo;
4014 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
4015 boolean lastKeepOn = ai.mKeepScreenOn;
4016 ai.mKeepScreenOn = false;
4017 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
4018 if (ai.mKeepScreenOn) {
4019 needGlobalAttributesUpdate(true);
4021 ai.mKeepScreenOn = lastKeepOn;
4024 if (child.isLayoutDirectionInherited()) {
4025 child.resetRtlProperties();
4030 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
4031 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
4034 if (child.hasTransientState()) {
4035 childHasTransientStateChanged(child, true);
4038 if (child.getVisibility() != View.GONE) {
4039 notifySubtreeAccessibilityStateChangedIfNeeded();
4043 private void addInArray(View child, int index) {
4044 View[] children = mChildren;
4045 final int count = mChildrenCount;
4046 final int size = children.length;
4047 if (index == count) {
4048 if (size == count) {
4049 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
4050 System.arraycopy(children, 0, mChildren, 0, size);
4051 children = mChildren;
4053 children[mChildrenCount++] = child;
4054 } else if (index < count) {
4055 if (size == count) {
4056 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
4057 System.arraycopy(children, 0, mChildren, 0, index);
4058 System.arraycopy(children, index, mChildren, index + 1, count - index);
4059 children = mChildren;
4061 System.arraycopy(children, index, children, index + 1, count - index);
4063 children[index] = child;
4065 if (mLastTouchDownIndex >= index) {
4066 mLastTouchDownIndex++;
4069 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
4073 // This method also sets the child's mParent to null
4074 private void removeFromArray(int index) {
4075 final View[] children = mChildren;
4076 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
4077 children[index].mParent = null;
4079 final int count = mChildrenCount;
4080 if (index == count - 1) {
4081 children[--mChildrenCount] = null;
4082 } else if (index >= 0 && index < count) {
4083 System.arraycopy(children, index + 1, children, index, count - index - 1);
4084 children[--mChildrenCount] = null;
4086 throw new IndexOutOfBoundsException();
4088 if (mLastTouchDownIndex == index) {
4089 mLastTouchDownTime = 0;
4090 mLastTouchDownIndex = -1;
4091 } else if (mLastTouchDownIndex > index) {
4092 mLastTouchDownIndex--;
4096 // This method also sets the children's mParent to null
4097 private void removeFromArray(int start, int count) {
4098 final View[] children = mChildren;
4099 final int childrenCount = mChildrenCount;
4101 start = Math.max(0, start);
4102 final int end = Math.min(childrenCount, start + count);
4108 if (end == childrenCount) {
4109 for (int i = start; i < end; i++) {
4110 children[i].mParent = null;
4114 for (int i = start; i < end; i++) {
4115 children[i].mParent = null;
4118 // Since we're looping above, we might as well do the copy, but is arraycopy()
4119 // faster than the extra 2 bounds checks we would do in the loop?
4120 System.arraycopy(children, end, children, start, childrenCount - end);
4122 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
4127 mChildrenCount -= (end - start);
4130 private void bindLayoutAnimation(View child) {
4131 Animation a = mLayoutAnimationController.getAnimationForView(child);
4132 child.setAnimation(a);
4136 * Subclasses should override this method to set layout animation
4137 * parameters on the supplied child.
4139 * @param child the child to associate with animation parameters
4140 * @param params the child's layout parameters which hold the animation
4142 * @param index the index of the child in the view group
4143 * @param count the number of children in the view group
4145 protected void attachLayoutAnimationParameters(View child,
4146 LayoutParams params, int index, int count) {
4147 LayoutAnimationController.AnimationParameters animationParams =
4148 params.layoutAnimationParameters;
4149 if (animationParams == null) {
4150 animationParams = new LayoutAnimationController.AnimationParameters();
4151 params.layoutAnimationParameters = animationParams;
4154 animationParams.count = count;
4155 animationParams.index = index;
4161 * <p><strong>Note:</strong> do not invoke this method from
4162 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4163 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4165 public void removeView(View view) {
4166 removeViewInternal(view);
4172 * Removes a view during layout. This is useful if in your onLayout() method,
4173 * you need to remove more views.
4175 * <p><strong>Note:</strong> do not invoke this method from
4176 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4177 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4179 * @param view the view to remove from the group
4181 public void removeViewInLayout(View view) {
4182 removeViewInternal(view);
4186 * Removes a range of views during layout. This is useful if in your onLayout() method,
4187 * you need to remove more views.
4189 * <p><strong>Note:</strong> do not invoke this method from
4190 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4191 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4193 * @param start the index of the first view to remove from the group
4194 * @param count the number of views to remove from the group
4196 public void removeViewsInLayout(int start, int count) {
4197 removeViewsInternal(start, count);
4201 * Removes the view at the specified position in the group.
4203 * <p><strong>Note:</strong> do not invoke this method from
4204 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4205 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4207 * @param index the position in the group of the view to remove
4209 public void removeViewAt(int index) {
4210 removeViewInternal(index, getChildAt(index));
4216 * Removes the specified range of views from the group.
4218 * <p><strong>Note:</strong> do not invoke this method from
4219 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4220 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4222 * @param start the first position in the group of the range of views to remove
4223 * @param count the number of views to remove
4225 public void removeViews(int start, int count) {
4226 removeViewsInternal(start, count);
4231 private void removeViewInternal(View view) {
4232 final int index = indexOfChild(view);
4234 removeViewInternal(index, view);
4238 private void removeViewInternal(int index, View view) {
4240 if (mTransition != null) {
4241 mTransition.removeChild(this, view);
4244 boolean clearChildFocus = false;
4245 if (view == mFocused) {
4247 clearChildFocus = true;
4250 if (view.isAccessibilityFocused()) {
4251 view.clearAccessibilityFocus();
4254 cancelTouchTarget(view);
4255 cancelHoverTarget(view);
4257 if (view.getAnimation() != null ||
4258 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4259 addDisappearingView(view);
4260 } else if (view.mAttachInfo != null) {
4261 view.dispatchDetachedFromWindow();
4264 if (view.hasTransientState()) {
4265 childHasTransientStateChanged(view, false);
4268 needGlobalAttributesUpdate(false);
4270 removeFromArray(index);
4272 if (clearChildFocus) {
4273 clearChildFocus(view);
4274 if (!rootViewRequestFocus()) {
4275 notifyGlobalFocusCleared(this);
4279 onViewRemoved(view);
4281 if (view.getVisibility() != View.GONE) {
4282 notifySubtreeAccessibilityStateChangedIfNeeded();
4287 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4288 * not null, changes in layout which occur because of children being added to or removed from
4289 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4290 * object. By default, the transition object is null (so layout changes are not animated).
4292 * <p>Replacing a non-null transition will cause that previous transition to be
4293 * canceled, if it is currently running, to restore this container to
4294 * its correct post-transition state.</p>
4296 * @param transition The LayoutTransition object that will animated changes in layout. A value
4297 * of <code>null</code> means no transition will run on layout changes.
4298 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
4300 public void setLayoutTransition(LayoutTransition transition) {
4301 if (mTransition != null) {
4302 LayoutTransition previousTransition = mTransition;
4303 previousTransition.cancel();
4304 previousTransition.removeTransitionListener(mLayoutTransitionListener);
4306 mTransition = transition;
4307 if (mTransition != null) {
4308 mTransition.addTransitionListener(mLayoutTransitionListener);
4313 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4314 * not null, changes in layout which occur because of children being added to or removed from
4315 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4316 * object. By default, the transition object is null (so layout changes are not animated).
4318 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
4319 * A value of <code>null</code> means no transition will run on layout changes.
4321 public LayoutTransition getLayoutTransition() {
4325 private void removeViewsInternal(int start, int count) {
4326 final View focused = mFocused;
4327 final boolean detach = mAttachInfo != null;
4328 boolean clearChildFocus = false;
4330 final View[] children = mChildren;
4331 final int end = start + count;
4333 for (int i = start; i < end; i++) {
4334 final View view = children[i];
4336 if (mTransition != null) {
4337 mTransition.removeChild(this, view);
4340 if (view == focused) {
4342 clearChildFocus = true;
4345 if (view.isAccessibilityFocused()) {
4346 view.clearAccessibilityFocus();
4349 cancelTouchTarget(view);
4350 cancelHoverTarget(view);
4352 if (view.getAnimation() != null ||
4353 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4354 addDisappearingView(view);
4355 } else if (detach) {
4356 view.dispatchDetachedFromWindow();
4359 if (view.hasTransientState()) {
4360 childHasTransientStateChanged(view, false);
4363 needGlobalAttributesUpdate(false);
4365 onViewRemoved(view);
4368 removeFromArray(start, count);
4370 if (clearChildFocus) {
4371 clearChildFocus(focused);
4372 if (!rootViewRequestFocus()) {
4373 notifyGlobalFocusCleared(focused);
4379 * Call this method to remove all child views from the
4382 * <p><strong>Note:</strong> do not invoke this method from
4383 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4384 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4386 public void removeAllViews() {
4387 removeAllViewsInLayout();
4393 * Called by a ViewGroup subclass to remove child views from itself,
4394 * when it must first know its size on screen before it can calculate how many
4395 * child views it will render. An example is a Gallery or a ListView, which
4396 * may "have" 50 children, but actually only render the number of children
4397 * that can currently fit inside the object on screen. Do not call
4398 * this method unless you are extending ViewGroup and understand the
4399 * view measuring and layout pipeline.
4401 * <p><strong>Note:</strong> do not invoke this method from
4402 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4403 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4405 public void removeAllViewsInLayout() {
4406 final int count = mChildrenCount;
4411 final View[] children = mChildren;
4414 final View focused = mFocused;
4415 final boolean detach = mAttachInfo != null;
4416 boolean clearChildFocus = false;
4418 needGlobalAttributesUpdate(false);
4420 for (int i = count - 1; i >= 0; i--) {
4421 final View view = children[i];
4423 if (mTransition != null) {
4424 mTransition.removeChild(this, view);
4427 if (view == focused) {
4429 clearChildFocus = true;
4432 if (view.isAccessibilityFocused()) {
4433 view.clearAccessibilityFocus();
4436 cancelTouchTarget(view);
4437 cancelHoverTarget(view);
4439 if (view.getAnimation() != null ||
4440 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4441 addDisappearingView(view);
4442 } else if (detach) {
4443 view.dispatchDetachedFromWindow();
4446 if (view.hasTransientState()) {
4447 childHasTransientStateChanged(view, false);
4450 onViewRemoved(view);
4452 view.mParent = null;
4456 if (clearChildFocus) {
4457 clearChildFocus(focused);
4458 if (!rootViewRequestFocus()) {
4459 notifyGlobalFocusCleared(focused);
4465 * Finishes the removal of a detached view. This method will dispatch the detached from
4466 * window event and notify the hierarchy change listener.
4468 * This method is intended to be lightweight and makes no assumptions about whether the
4469 * parent or child should be redrawn. Proper use of this method will include also making
4470 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4471 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4472 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
4473 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4475 * @param child the child to be definitely removed from the view hierarchy
4476 * @param animate if true and the view has an animation, the view is placed in the
4477 * disappearing views list, otherwise, it is detached from the window
4479 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4480 * @see #detachAllViewsFromParent()
4481 * @see #detachViewFromParent(View)
4482 * @see #detachViewFromParent(int)
4484 protected void removeDetachedView(View child, boolean animate) {
4485 if (mTransition != null) {
4486 mTransition.removeChild(this, child);
4489 if (child == mFocused) {
4493 child.clearAccessibilityFocus();
4495 cancelTouchTarget(child);
4496 cancelHoverTarget(child);
4498 if ((animate && child.getAnimation() != null) ||
4499 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
4500 addDisappearingView(child);
4501 } else if (child.mAttachInfo != null) {
4502 child.dispatchDetachedFromWindow();
4505 if (child.hasTransientState()) {
4506 childHasTransientStateChanged(child, false);
4509 onViewRemoved(child);
4513 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
4514 * sets the layout parameters and puts the view in the list of children so that
4515 * it can be retrieved by calling {@link #getChildAt(int)}.
4517 * This method is intended to be lightweight and makes no assumptions about whether the
4518 * parent or child should be redrawn. Proper use of this method will include also making
4519 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4520 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4521 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
4522 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4524 * This method should be called only for views which were detached from their parent.
4526 * @param child the child to attach
4527 * @param index the index at which the child should be attached
4528 * @param params the layout parameters of the child
4530 * @see #removeDetachedView(View, boolean)
4531 * @see #detachAllViewsFromParent()
4532 * @see #detachViewFromParent(View)
4533 * @see #detachViewFromParent(int)
4535 protected void attachViewToParent(View child, int index, LayoutParams params) {
4536 child.mLayoutParams = params;
4539 index = mChildrenCount;
4542 addInArray(child, index);
4544 child.mParent = this;
4545 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
4546 & ~PFLAG_DRAWING_CACHE_VALID)
4547 | PFLAG_DRAWN | PFLAG_INVALIDATED;
4548 this.mPrivateFlags |= PFLAG_INVALIDATED;
4550 if (child.hasFocus()) {
4551 requestChildFocus(child, child.findFocus());
4556 * Detaches a view from its parent. Detaching a view should be followed
4557 * either by a call to
4558 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4559 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4560 * temporary; reattachment or removal should happen within the same drawing cycle as
4561 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4562 * call to {@link #getChildAt(int)}.
4564 * @param child the child to detach
4566 * @see #detachViewFromParent(int)
4567 * @see #detachViewsFromParent(int, int)
4568 * @see #detachAllViewsFromParent()
4569 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4570 * @see #removeDetachedView(View, boolean)
4572 protected void detachViewFromParent(View child) {
4573 removeFromArray(indexOfChild(child));
4577 * Detaches a view from its parent. Detaching a view should be followed
4578 * either by a call to
4579 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4580 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4581 * temporary; reattachment or removal should happen within the same drawing cycle as
4582 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4583 * call to {@link #getChildAt(int)}.
4585 * @param index the index of the child to detach
4587 * @see #detachViewFromParent(View)
4588 * @see #detachAllViewsFromParent()
4589 * @see #detachViewsFromParent(int, int)
4590 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4591 * @see #removeDetachedView(View, boolean)
4593 protected void detachViewFromParent(int index) {
4594 removeFromArray(index);
4598 * Detaches a range of views from their parents. Detaching a view should be followed
4599 * either by a call to
4600 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4601 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4602 * temporary; reattachment or removal should happen within the same drawing cycle as
4603 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4604 * call to {@link #getChildAt(int)}.
4606 * @param start the first index of the childrend range to detach
4607 * @param count the number of children to detach
4609 * @see #detachViewFromParent(View)
4610 * @see #detachViewFromParent(int)
4611 * @see #detachAllViewsFromParent()
4612 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4613 * @see #removeDetachedView(View, boolean)
4615 protected void detachViewsFromParent(int start, int count) {
4616 removeFromArray(start, count);
4620 * Detaches all views from the parent. Detaching a view should be followed
4621 * either by a call to
4622 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4623 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4624 * temporary; reattachment or removal should happen within the same drawing cycle as
4625 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4626 * call to {@link #getChildAt(int)}.
4628 * @see #detachViewFromParent(View)
4629 * @see #detachViewFromParent(int)
4630 * @see #detachViewsFromParent(int, int)
4631 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4632 * @see #removeDetachedView(View, boolean)
4634 protected void detachAllViewsFromParent() {
4635 final int count = mChildrenCount;
4640 final View[] children = mChildren;
4643 for (int i = count - 1; i >= 0; i--) {
4644 children[i].mParent = null;
4650 * Don't call or override this method. It is used for the implementation of
4651 * the view hierarchy.
4653 public final void invalidateChild(View child, final Rect dirty) {
4654 ViewParent parent = this;
4656 final AttachInfo attachInfo = mAttachInfo;
4657 if (attachInfo != null) {
4658 // If the child is drawing an animation, we want to copy this flag onto
4659 // ourselves and the parent to make sure the invalidate request goes
4661 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
4662 == PFLAG_DRAW_ANIMATION;
4664 // Check whether the child that requests the invalidate is fully opaque
4665 // Views being animated or transformed are not considered opaque because we may
4666 // be invalidating their old position and need the parent to paint behind them.
4667 Matrix childMatrix = child.getMatrix();
4668 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
4669 child.getAnimation() == null && childMatrix.isIdentity();
4670 // Mark the child as dirty, using the appropriate flag
4671 // Make sure we do not set both flags at the same time
4672 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
4674 if (child.mLayerType != LAYER_TYPE_NONE) {
4675 mPrivateFlags |= PFLAG_INVALIDATED;
4676 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
4679 final int[] location = attachInfo.mInvalidateChildLocation;
4680 location[CHILD_LEFT_INDEX] = child.mLeft;
4681 location[CHILD_TOP_INDEX] = child.mTop;
4682 if (!childMatrix.isIdentity() ||
4683 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
4684 RectF boundingRect = attachInfo.mTmpTransformRect;
4685 boundingRect.set(dirty);
4686 Matrix transformMatrix;
4687 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
4688 Transformation t = attachInfo.mTmpTransformation;
4689 boolean transformed = getChildStaticTransformation(child, t);
4691 transformMatrix = attachInfo.mTmpMatrix;
4692 transformMatrix.set(t.getMatrix());
4693 if (!childMatrix.isIdentity()) {
4694 transformMatrix.preConcat(childMatrix);
4697 transformMatrix = childMatrix;
4700 transformMatrix = childMatrix;
4702 transformMatrix.mapRect(boundingRect);
4703 dirty.set((int) (boundingRect.left - 0.5f),
4704 (int) (boundingRect.top - 0.5f),
4705 (int) (boundingRect.right + 0.5f),
4706 (int) (boundingRect.bottom + 0.5f));
4711 if (parent instanceof View) {
4712 view = (View) parent;
4715 if (drawAnimation) {
4717 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
4718 } else if (parent instanceof ViewRootImpl) {
4719 ((ViewRootImpl) parent).mIsAnimating = true;
4723 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4724 // flag coming from the child that initiated the invalidate
4726 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
4727 view.getSolidColor() == 0) {
4728 opaqueFlag = PFLAG_DIRTY;
4730 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
4731 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
4735 parent = parent.invalidateChildInParent(location, dirty);
4737 // Account for transform on current parent
4738 Matrix m = view.getMatrix();
4739 if (!m.isIdentity()) {
4740 RectF boundingRect = attachInfo.mTmpTransformRect;
4741 boundingRect.set(dirty);
4742 m.mapRect(boundingRect);
4743 dirty.set((int) (boundingRect.left - 0.5f),
4744 (int) (boundingRect.top - 0.5f),
4745 (int) (boundingRect.right + 0.5f),
4746 (int) (boundingRect.bottom + 0.5f));
4749 } while (parent != null);
4754 * Don't call or override this method. It is used for the implementation of
4755 * the view hierarchy.
4757 * This implementation returns null if this ViewGroup does not have a parent,
4758 * if this ViewGroup is already fully invalidated or if the dirty rectangle
4759 * does not intersect with this ViewGroup's bounds.
4761 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
4762 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
4763 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
4764 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
4765 FLAG_OPTIMIZE_INVALIDATE) {
4766 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
4767 location[CHILD_TOP_INDEX] - mScrollY);
4768 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
4769 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4772 final int left = mLeft;
4773 final int top = mTop;
4775 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4776 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
4780 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
4782 location[CHILD_LEFT_INDEX] = left;
4783 location[CHILD_TOP_INDEX] = top;
4785 if (mLayerType != LAYER_TYPE_NONE) {
4786 mPrivateFlags |= PFLAG_INVALIDATED;
4792 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
4794 location[CHILD_LEFT_INDEX] = mLeft;
4795 location[CHILD_TOP_INDEX] = mTop;
4796 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4797 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
4799 // in case the dirty rect extends outside the bounds of this container
4800 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4803 if (mLayerType != LAYER_TYPE_NONE) {
4804 mPrivateFlags |= PFLAG_INVALIDATED;
4815 * Native-calculated damage path
4816 * Returns false if this path was unable to complete successfully. This means
4817 * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
4821 public boolean damageChildDeferred(View child) {
4822 ViewParent parent = getParent();
4823 while (parent != null) {
4824 if (parent instanceof ViewGroup) {
4825 parent = parent.getParent();
4826 } else if (parent instanceof ViewRootImpl) {
4827 ((ViewRootImpl) parent).invalidate();
4837 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
4838 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
4839 * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
4843 public void damageChild(View child, final Rect dirty) {
4844 if (damageChildDeferred(child)) {
4848 ViewParent parent = this;
4850 final AttachInfo attachInfo = mAttachInfo;
4851 if (attachInfo != null) {
4852 int left = child.mLeft;
4853 int top = child.mTop;
4854 if (!child.getMatrix().isIdentity()) {
4855 child.transformRect(dirty);
4859 if (parent instanceof ViewGroup) {
4860 ViewGroup parentVG = (ViewGroup) parent;
4861 if (parentVG.mLayerType != LAYER_TYPE_NONE) {
4862 // Layered parents should be recreated, not just re-issued
4863 parentVG.invalidate();
4866 parent = parentVG.damageChildInParent(left, top, dirty);
4867 left = parentVG.mLeft;
4868 top = parentVG.mTop;
4871 // Reached the top; this calls into the usual invalidate method in
4872 // ViewRootImpl, which schedules a traversal
4873 final int[] location = attachInfo.mInvalidateChildLocation;
4876 parent = parent.invalidateChildInParent(location, dirty);
4878 } while (parent != null);
4883 * Quick invalidation method that simply transforms the dirty rect into the parent's
4884 * coordinate system, pruning the invalidation if the parent has already been invalidated.
4888 protected ViewParent damageChildInParent(int left, int top, final Rect dirty) {
4889 if ((mPrivateFlags & PFLAG_DRAWN) != 0
4890 || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) != 0) {
4891 dirty.offset(left - mScrollX, top - mScrollY);
4892 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
4893 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4896 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
4897 dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4899 if (!getMatrix().isIdentity()) {
4900 transformRect(dirty);
4911 * Offset a rectangle that is in a descendant's coordinate
4912 * space into our coordinate space.
4913 * @param descendant A descendant of this view
4914 * @param rect A rectangle defined in descendant's coordinate space.
4916 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4917 offsetRectBetweenParentAndChild(descendant, rect, true, false);
4921 * Offset a rectangle that is in our coordinate space into an ancestor's
4923 * @param descendant A descendant of this view
4924 * @param rect A rectangle defined in descendant's coordinate space.
4926 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4927 offsetRectBetweenParentAndChild(descendant, rect, false, false);
4931 * Helper method that offsets a rect either from parent to descendant or
4932 * descendant to parent.
4934 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4935 boolean offsetFromChildToParent, boolean clipToBounds) {
4937 // already in the same coord system :)
4938 if (descendant == this) {
4942 ViewParent theParent = descendant.mParent;
4944 // search and offset up to the parent
4945 while ((theParent != null)
4946 && (theParent instanceof View)
4947 && (theParent != this)) {
4949 if (offsetFromChildToParent) {
4950 rect.offset(descendant.mLeft - descendant.mScrollX,
4951 descendant.mTop - descendant.mScrollY);
4953 View p = (View) theParent;
4954 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4958 View p = (View) theParent;
4959 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4961 rect.offset(descendant.mScrollX - descendant.mLeft,
4962 descendant.mScrollY - descendant.mTop);
4965 descendant = (View) theParent;
4966 theParent = descendant.mParent;
4969 // now that we are up to this view, need to offset one more time
4970 // to get into our coordinate space
4971 if (theParent == this) {
4972 if (offsetFromChildToParent) {
4973 rect.offset(descendant.mLeft - descendant.mScrollX,
4974 descendant.mTop - descendant.mScrollY);
4976 rect.offset(descendant.mScrollX - descendant.mLeft,
4977 descendant.mScrollY - descendant.mTop);
4980 throw new IllegalArgumentException("parameter must be a descendant of this view");
4985 * Offset the vertical location of all children of this view by the specified number of pixels.
4987 * @param offset the number of pixels to offset
4991 public void offsetChildrenTopAndBottom(int offset) {
4992 final int count = mChildrenCount;
4993 final View[] children = mChildren;
4994 boolean invalidate = false;
4996 for (int i = 0; i < count; i++) {
4997 final View v = children[i];
4999 v.mBottom += offset;
5000 if (v.mRenderNode != null) {
5002 v.mRenderNode.offsetTopAndBottom(offset);
5007 invalidateViewProperty(false, false);
5009 notifySubtreeAccessibilityStateChangedIfNeeded();
5015 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
5016 // It doesn't make a whole lot of sense to call this on a view that isn't attached,
5017 // but for some simple tests it can be useful. If we don't have attach info this
5018 // will allocate memory.
5019 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
5022 if (!child.hasIdentityMatrix()) {
5023 child.getMatrix().mapRect(rect);
5026 int dx = child.mLeft - mScrollX;
5027 int dy = child.mTop - mScrollY;
5029 rect.offset(dx, dy);
5031 if (offset != null) {
5032 if (!child.hasIdentityMatrix()) {
5033 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
5035 position[0] = offset.x;
5036 position[1] = offset.y;
5037 child.getMatrix().mapPoints(position);
5038 offset.x = (int) (position[0] + 0.5f);
5039 offset.y = (int) (position[1] + 0.5f);
5045 if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
5046 if (mParent == null) return true;
5047 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f),
5048 (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f));
5049 return mParent.getChildVisibleRect(this, r, offset);
5059 public final void layout(int l, int t, int r, int b) {
5060 if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
5061 if (mTransition != null) {
5062 mTransition.layoutChange(this);
5064 super.layout(l, t, r, b);
5066 // record the fact that we noop'd it; request layout when transition finishes
5067 mLayoutCalledWhileSuppressed = true;
5075 protected abstract void onLayout(boolean changed,
5076 int l, int t, int r, int b);
5079 * Indicates whether the view group has the ability to animate its children
5080 * after the first layout.
5082 * @return true if the children can be animated, false otherwise
5084 protected boolean canAnimate() {
5085 return mLayoutAnimationController != null;
5089 * Runs the layout animation. Calling this method triggers a relayout of
5092 public void startLayoutAnimation() {
5093 if (mLayoutAnimationController != null) {
5094 mGroupFlags |= FLAG_RUN_ANIMATION;
5100 * Schedules the layout animation to be played after the next layout pass
5101 * of this view group. This can be used to restart the layout animation
5102 * when the content of the view group changes or when the activity is
5103 * paused and resumed.
5105 public void scheduleLayoutAnimation() {
5106 mGroupFlags |= FLAG_RUN_ANIMATION;
5110 * Sets the layout animation controller used to animate the group's
5111 * children after the first layout.
5113 * @param controller the animation controller
5115 public void setLayoutAnimation(LayoutAnimationController controller) {
5116 mLayoutAnimationController = controller;
5117 if (mLayoutAnimationController != null) {
5118 mGroupFlags |= FLAG_RUN_ANIMATION;
5123 * Returns the layout animation controller used to animate the group's
5126 * @return the current animation controller
5128 public LayoutAnimationController getLayoutAnimation() {
5129 return mLayoutAnimationController;
5133 * Indicates whether the children's drawing cache is used during a layout
5134 * animation. By default, the drawing cache is enabled but this will prevent
5135 * nested layout animations from working. To nest animations, you must disable
5138 * @return true if the animation cache is enabled, false otherwise
5140 * @see #setAnimationCacheEnabled(boolean)
5141 * @see View#setDrawingCacheEnabled(boolean)
5143 @ViewDebug.ExportedProperty
5144 public boolean isAnimationCacheEnabled() {
5145 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
5149 * Enables or disables the children's drawing cache during a layout animation.
5150 * By default, the drawing cache is enabled but this will prevent nested
5151 * layout animations from working. To nest animations, you must disable the
5154 * @param enabled true to enable the animation cache, false otherwise
5156 * @see #isAnimationCacheEnabled()
5157 * @see View#setDrawingCacheEnabled(boolean)
5159 public void setAnimationCacheEnabled(boolean enabled) {
5160 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
5164 * Indicates whether this ViewGroup will always try to draw its children using their
5165 * drawing cache. By default this property is enabled.
5167 * @return true if the animation cache is enabled, false otherwise
5169 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5170 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5171 * @see View#setDrawingCacheEnabled(boolean)
5173 @ViewDebug.ExportedProperty(category = "drawing")
5174 public boolean isAlwaysDrawnWithCacheEnabled() {
5175 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
5179 * Indicates whether this ViewGroup will always try to draw its children using their
5180 * drawing cache. This property can be set to true when the cache rendering is
5181 * slightly different from the children's normal rendering. Renderings can be different,
5182 * for instance, when the cache's quality is set to low.
5184 * When this property is disabled, the ViewGroup will use the drawing cache of its
5185 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
5186 * when to start using the drawing cache and when to stop using it.
5188 * @param always true to always draw with the drawing cache, false otherwise
5190 * @see #isAlwaysDrawnWithCacheEnabled()
5191 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5192 * @see View#setDrawingCacheEnabled(boolean)
5193 * @see View#setDrawingCacheQuality(int)
5195 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
5196 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
5200 * Indicates whether the ViewGroup is currently drawing its children using
5201 * their drawing cache.
5203 * @return true if children should be drawn with their cache, false otherwise
5205 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5206 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5208 @ViewDebug.ExportedProperty(category = "drawing")
5209 protected boolean isChildrenDrawnWithCacheEnabled() {
5210 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
5214 * Tells the ViewGroup to draw its children using their drawing cache. This property
5215 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
5216 * will be used only if it has been enabled.
5218 * Subclasses should call this method to start and stop using the drawing cache when
5219 * they perform performance sensitive operations, like scrolling or animating.
5221 * @param enabled true if children should be drawn with their cache, false otherwise
5223 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5224 * @see #isChildrenDrawnWithCacheEnabled()
5226 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
5227 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
5231 * Indicates whether the ViewGroup is drawing its children in the order defined by
5232 * {@link #getChildDrawingOrder(int, int)}.
5234 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
5237 * @see #setChildrenDrawingOrderEnabled(boolean)
5238 * @see #getChildDrawingOrder(int, int)
5240 @ViewDebug.ExportedProperty(category = "drawing")
5241 protected boolean isChildrenDrawingOrderEnabled() {
5242 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
5246 * Tells the ViewGroup whether to draw its children in the order defined by the method
5247 * {@link #getChildDrawingOrder(int, int)}.
5249 * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
5250 * will override custom child ordering done via this method.
5252 * @param enabled true if the order of the children when drawing is determined by
5253 * {@link #getChildDrawingOrder(int, int)}, false otherwise
5255 * @see #isChildrenDrawingOrderEnabled()
5256 * @see #getChildDrawingOrder(int, int)
5258 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
5259 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
5262 private boolean hasBooleanFlag(int flag) {
5263 return (mGroupFlags & flag) == flag;
5266 private void setBooleanFlag(int flag, boolean value) {
5268 mGroupFlags |= flag;
5270 mGroupFlags &= ~flag;
5275 * Returns an integer indicating what types of drawing caches are kept in memory.
5277 * @see #setPersistentDrawingCache(int)
5278 * @see #setAnimationCacheEnabled(boolean)
5280 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
5281 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5282 * and {@link #PERSISTENT_ALL_CACHES}
5284 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
5285 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
5286 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
5287 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
5288 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
5290 public int getPersistentDrawingCache() {
5291 return mPersistentDrawingCache;
5295 * Indicates what types of drawing caches should be kept in memory after
5296 * they have been created.
5298 * @see #getPersistentDrawingCache()
5299 * @see #setAnimationCacheEnabled(boolean)
5301 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
5302 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5303 * and {@link #PERSISTENT_ALL_CACHES}
5305 public void setPersistentDrawingCache(int drawingCacheToKeep) {
5306 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
5309 private void setLayoutMode(int layoutMode, boolean explicitly) {
5310 mLayoutMode = layoutMode;
5311 setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
5315 * Recursively traverse the view hierarchy, resetting the layoutMode of any
5316 * descendants that had inherited a different layoutMode from a previous parent.
5317 * Recursion terminates when a descendant's mode is:
5319 * <li>Undefined</li>
5320 * <li>The same as the root node's</li>
5321 * <li>A mode that had been explicitly set</li>
5323 * The first two clauses are optimizations.
5324 * @param layoutModeOfRoot
5327 void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
5328 if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
5329 mLayoutMode == layoutModeOfRoot ||
5330 hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
5333 setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
5335 // apply recursively
5336 for (int i = 0, N = getChildCount(); i < N; i++) {
5337 getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
5342 * Returns the basis of alignment during layout operations on this ViewGroup:
5343 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5345 * If no layoutMode was explicitly set, either programmatically or in an XML resource,
5346 * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
5347 * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
5349 * @return the layout mode to use during layout operations
5351 * @see #setLayoutMode(int)
5353 public int getLayoutMode() {
5354 if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
5355 int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
5356 ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
5357 setLayoutMode(inheritedLayoutMode, false);
5363 * Sets the basis of alignment during the layout of this ViewGroup.
5364 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
5365 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5367 * @param layoutMode the layout mode to use during layout operations
5369 * @see #getLayoutMode()
5370 * @attr ref android.R.styleable#ViewGroup_layoutMode
5372 public void setLayoutMode(int layoutMode) {
5373 if (mLayoutMode != layoutMode) {
5374 invalidateInheritedLayoutMode(layoutMode);
5375 setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
5381 * Returns a new set of layout parameters based on the supplied attributes set.
5383 * @param attrs the attributes to build the layout parameters from
5385 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5386 * of its descendants
5388 public LayoutParams generateLayoutParams(AttributeSet attrs) {
5389 return new LayoutParams(getContext(), attrs);
5393 * Returns a safe set of layout parameters based on the supplied layout params.
5394 * When a ViewGroup is passed a View whose layout params do not pass the test of
5395 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
5396 * is invoked. This method should return a new set of layout params suitable for
5397 * this ViewGroup, possibly by copying the appropriate attributes from the
5398 * specified set of layout params.
5400 * @param p The layout parameters to convert into a suitable set of layout parameters
5401 * for this ViewGroup.
5403 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5404 * of its descendants
5406 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
5411 * Returns a set of default layout parameters. These parameters are requested
5412 * when the View passed to {@link #addView(View)} has no layout parameters
5413 * already set. If null is returned, an exception is thrown from addView.
5415 * @return a set of default layout parameters or null
5417 protected LayoutParams generateDefaultLayoutParams() {
5418 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
5425 protected void debug(int depth) {
5429 if (mFocused != null) {
5430 output = debugIndent(depth);
5431 output += "mFocused";
5432 Log.d(VIEW_LOG_TAG, output);
5434 if (mChildrenCount != 0) {
5435 output = debugIndent(depth);
5437 Log.d(VIEW_LOG_TAG, output);
5439 int count = mChildrenCount;
5440 for (int i = 0; i < count; i++) {
5441 View child = mChildren[i];
5442 child.debug(depth + 1);
5445 if (mChildrenCount != 0) {
5446 output = debugIndent(depth);
5448 Log.d(VIEW_LOG_TAG, output);
5453 * Returns the position in the group of the specified child view.
5455 * @param child the view for which to get the position
5456 * @return a positive integer representing the position of the view in the
5457 * group, or -1 if the view does not exist in the group
5459 public int indexOfChild(View child) {
5460 final int count = mChildrenCount;
5461 final View[] children = mChildren;
5462 for (int i = 0; i < count; i++) {
5463 if (children[i] == child) {
5471 * Returns the number of children in the group.
5473 * @return a positive integer representing the number of children in
5476 public int getChildCount() {
5477 return mChildrenCount;
5481 * Returns the view at the specified position in the group.
5483 * @param index the position at which to get the view from
5484 * @return the view at the specified position or null if the position
5485 * does not exist within the group
5487 public View getChildAt(int index) {
5488 if (index < 0 || index >= mChildrenCount) {
5491 return mChildren[index];
5495 * Ask all of the children of this view to measure themselves, taking into
5496 * account both the MeasureSpec requirements for this view and its padding.
5497 * We skip children that are in the GONE state The heavy lifting is done in
5498 * getChildMeasureSpec.
5500 * @param widthMeasureSpec The width requirements for this view
5501 * @param heightMeasureSpec The height requirements for this view
5503 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
5504 final int size = mChildrenCount;
5505 final View[] children = mChildren;
5506 for (int i = 0; i < size; ++i) {
5507 final View child = children[i];
5508 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
5509 measureChild(child, widthMeasureSpec, heightMeasureSpec);
5515 * Ask one of the children of this view to measure itself, taking into
5516 * account both the MeasureSpec requirements for this view and its padding.
5517 * The heavy lifting is done in getChildMeasureSpec.
5519 * @param child The child to measure
5520 * @param parentWidthMeasureSpec The width requirements for this view
5521 * @param parentHeightMeasureSpec The height requirements for this view
5523 protected void measureChild(View child, int parentWidthMeasureSpec,
5524 int parentHeightMeasureSpec) {
5525 final LayoutParams lp = child.getLayoutParams();
5527 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5528 mPaddingLeft + mPaddingRight, lp.width);
5529 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5530 mPaddingTop + mPaddingBottom, lp.height);
5532 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5536 * Ask one of the children of this view to measure itself, taking into
5537 * account both the MeasureSpec requirements for this view and its padding
5538 * and margins. The child must have MarginLayoutParams The heavy lifting is
5539 * done in getChildMeasureSpec.
5541 * @param child The child to measure
5542 * @param parentWidthMeasureSpec The width requirements for this view
5543 * @param widthUsed Extra space that has been used up by the parent
5544 * horizontally (possibly by other children of the parent)
5545 * @param parentHeightMeasureSpec The height requirements for this view
5546 * @param heightUsed Extra space that has been used up by the parent
5547 * vertically (possibly by other children of the parent)
5549 protected void measureChildWithMargins(View child,
5550 int parentWidthMeasureSpec, int widthUsed,
5551 int parentHeightMeasureSpec, int heightUsed) {
5552 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
5554 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5555 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
5556 + widthUsed, lp.width);
5557 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5558 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
5559 + heightUsed, lp.height);
5561 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5565 * Does the hard part of measureChildren: figuring out the MeasureSpec to
5566 * pass to a particular child. This method figures out the right MeasureSpec
5567 * for one dimension (height or width) of one child view.
5569 * The goal is to combine information from our MeasureSpec with the
5570 * LayoutParams of the child to get the best possible results. For example,
5571 * if the this view knows its size (because its MeasureSpec has a mode of
5572 * EXACTLY), and the child has indicated in its LayoutParams that it wants
5573 * to be the same size as the parent, the parent should ask the child to
5574 * layout given an exact size.
5576 * @param spec The requirements for this view
5577 * @param padding The padding of this view for the current dimension and
5578 * margins, if applicable
5579 * @param childDimension How big the child wants to be in the current
5581 * @return a MeasureSpec integer for the child
5583 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
5584 int specMode = MeasureSpec.getMode(spec);
5585 int specSize = MeasureSpec.getSize(spec);
5587 int size = Math.max(0, specSize - padding);
5593 // Parent has imposed an exact size on us
5594 case MeasureSpec.EXACTLY:
5595 if (childDimension >= 0) {
5596 resultSize = childDimension;
5597 resultMode = MeasureSpec.EXACTLY;
5598 } else if (childDimension == LayoutParams.MATCH_PARENT) {
5599 // Child wants to be our size. So be it.
5601 resultMode = MeasureSpec.EXACTLY;
5602 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5603 // Child wants to determine its own size. It can't be
5606 resultMode = MeasureSpec.AT_MOST;
5610 // Parent has imposed a maximum size on us
5611 case MeasureSpec.AT_MOST:
5612 if (childDimension >= 0) {
5613 // Child wants a specific size... so be it
5614 resultSize = childDimension;
5615 resultMode = MeasureSpec.EXACTLY;
5616 } else if (childDimension == LayoutParams.MATCH_PARENT) {
5617 // Child wants to be our size, but our size is not fixed.
5618 // Constrain child to not be bigger than us.
5620 resultMode = MeasureSpec.AT_MOST;
5621 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5622 // Child wants to determine its own size. It can't be
5625 resultMode = MeasureSpec.AT_MOST;
5629 // Parent asked to see how big we want to be
5630 case MeasureSpec.UNSPECIFIED:
5631 if (childDimension >= 0) {
5632 // Child wants a specific size... let him have it
5633 resultSize = childDimension;
5634 resultMode = MeasureSpec.EXACTLY;
5635 } else if (childDimension == LayoutParams.MATCH_PARENT) {
5636 // Child wants to be our size... find out how big it should
5639 resultMode = MeasureSpec.UNSPECIFIED;
5640 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5641 // Child wants to determine its own size.... find out how
5644 resultMode = MeasureSpec.UNSPECIFIED;
5648 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
5653 * Removes any pending animations for views that have been removed. Call
5654 * this if you don't want animations for exiting views to stack up.
5656 public void clearDisappearingChildren() {
5657 final ArrayList<View> disappearingChildren = mDisappearingChildren;
5658 if (disappearingChildren != null) {
5659 final int count = disappearingChildren.size();
5660 for (int i = 0; i < count; i++) {
5661 final View view = disappearingChildren.get(i);
5662 if (view.mAttachInfo != null) {
5663 view.dispatchDetachedFromWindow();
5665 view.clearAnimation();
5667 disappearingChildren.clear();
5673 * Add a view which is removed from mChildren but still needs animation
5675 * @param v View to add
5677 private void addDisappearingView(View v) {
5678 ArrayList<View> disappearingChildren = mDisappearingChildren;
5680 if (disappearingChildren == null) {
5681 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
5684 disappearingChildren.add(v);
5688 * Cleanup a view when its animation is done. This may mean removing it from
5689 * the list of disappearing views.
5691 * @param view The view whose animation has finished
5692 * @param animation The animation, cannot be null
5694 void finishAnimatingView(final View view, Animation animation) {
5695 final ArrayList<View> disappearingChildren = mDisappearingChildren;
5696 if (disappearingChildren != null) {
5697 if (disappearingChildren.contains(view)) {
5698 disappearingChildren.remove(view);
5700 if (view.mAttachInfo != null) {
5701 view.dispatchDetachedFromWindow();
5704 view.clearAnimation();
5705 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
5709 if (animation != null && !animation.getFillAfter()) {
5710 view.clearAnimation();
5713 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
5714 view.onAnimationEnd();
5715 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
5716 // so we'd rather be safe than sorry
5717 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
5718 // Draw one more frame after the animation is done
5719 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
5724 * Utility function called by View during invalidation to determine whether a view that
5725 * is invisible or gone should still be invalidated because it is being transitioned (and
5726 * therefore still needs to be drawn).
5728 boolean isViewTransitioning(View view) {
5729 return (mTransitioningViews != null && mTransitioningViews.contains(view));
5733 * This method tells the ViewGroup that the given View object, which should have this
5734 * ViewGroup as its parent,
5735 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
5736 * is removed from its parent. This allows animations, such as those used by
5737 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
5738 * the removal of views. A call to this method should always be accompanied by a later call
5739 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
5740 * so that the View finally gets removed.
5742 * @param view The View object to be kept visible even if it gets removed from its parent.
5744 public void startViewTransition(View view) {
5745 if (view.mParent == this) {
5746 if (mTransitioningViews == null) {
5747 mTransitioningViews = new ArrayList<View>();
5749 mTransitioningViews.add(view);
5754 * This method should always be called following an earlier call to
5755 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
5756 * and will no longer be displayed. Note that this method does not perform the functionality
5757 * of removing a view from its parent; it just discontinues the display of a View that
5758 * has previously been removed.
5760 * @return view The View object that has been removed but is being kept around in the visible
5761 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
5763 public void endViewTransition(View view) {
5764 if (mTransitioningViews != null) {
5765 mTransitioningViews.remove(view);
5766 final ArrayList<View> disappearingChildren = mDisappearingChildren;
5767 if (disappearingChildren != null && disappearingChildren.contains(view)) {
5768 disappearingChildren.remove(view);
5769 if (mVisibilityChangingChildren != null &&
5770 mVisibilityChangingChildren.contains(view)) {
5771 mVisibilityChangingChildren.remove(view);
5773 if (view.mAttachInfo != null) {
5774 view.dispatchDetachedFromWindow();
5776 if (view.mParent != null) {
5777 view.mParent = null;
5785 private LayoutTransition.TransitionListener mLayoutTransitionListener =
5786 new LayoutTransition.TransitionListener() {
5788 public void startTransition(LayoutTransition transition, ViewGroup container,
5789 View view, int transitionType) {
5790 // We only care about disappearing items, since we need special logic to keep
5791 // those items visible after they've been 'removed'
5792 if (transitionType == LayoutTransition.DISAPPEARING) {
5793 startViewTransition(view);
5798 public void endTransition(LayoutTransition transition, ViewGroup container,
5799 View view, int transitionType) {
5800 if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
5802 mLayoutCalledWhileSuppressed = false;
5804 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
5805 endViewTransition(view);
5811 * Tells this ViewGroup to suppress all layout() calls until layout
5812 * suppression is disabled with a later call to suppressLayout(false).
5813 * When layout suppression is disabled, a requestLayout() call is sent
5814 * if layout() was attempted while layout was being suppressed.
5818 public void suppressLayout(boolean suppress) {
5819 mSuppressLayout = suppress;
5821 if (mLayoutCalledWhileSuppressed) {
5823 mLayoutCalledWhileSuppressed = false;
5829 * Returns whether layout calls on this container are currently being
5830 * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
5832 * @return true if layout calls are currently suppressed, false otherwise.
5836 public boolean isLayoutSuppressed() {
5837 return mSuppressLayout;
5844 public boolean gatherTransparentRegion(Region region) {
5845 // If no transparent regions requested, we are always opaque.
5846 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
5847 if (meOpaque && region == null) {
5848 // The caller doesn't care about the region, so stop now.
5851 super.gatherTransparentRegion(region);
5852 final View[] children = mChildren;
5853 final int count = mChildrenCount;
5854 boolean noneOfTheChildrenAreTransparent = true;
5855 for (int i = 0; i < count; i++) {
5856 final View child = children[i];
5857 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
5858 if (!child.gatherTransparentRegion(region)) {
5859 noneOfTheChildrenAreTransparent = false;
5863 return meOpaque || noneOfTheChildrenAreTransparent;
5869 public void requestTransparentRegion(View child) {
5870 if (child != null) {
5871 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
5872 if (mParent != null) {
5873 mParent.requestTransparentRegion(this);
5879 public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
5880 insets = super.dispatchApplyWindowInsets(insets);
5881 if (!insets.isConsumed()) {
5882 final int count = getChildCount();
5883 for (int i = 0; i < count; i++) {
5884 insets = getChildAt(i).dispatchApplyWindowInsets(insets);
5885 if (insets.isConsumed()) {
5894 * Returns the animation listener to which layout animation events are
5897 * @return an {@link android.view.animation.Animation.AnimationListener}
5899 public Animation.AnimationListener getLayoutAnimationListener() {
5900 return mAnimationListener;
5904 protected void drawableStateChanged() {
5905 super.drawableStateChanged();
5907 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
5908 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5909 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
5910 + " child has duplicateParentState set to true");
5913 final View[] children = mChildren;
5914 final int count = mChildrenCount;
5916 for (int i = 0; i < count; i++) {
5917 final View child = children[i];
5918 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
5919 child.refreshDrawableState();
5926 public void jumpDrawablesToCurrentState() {
5927 super.jumpDrawablesToCurrentState();
5928 final View[] children = mChildren;
5929 final int count = mChildrenCount;
5930 for (int i = 0; i < count; i++) {
5931 children[i].jumpDrawablesToCurrentState();
5936 public void drawableHotspotChanged(float x, float y) {
5937 super.drawableHotspotChanged(x, y);
5939 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
5940 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5941 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
5942 + " child has duplicateParentState set to true");
5945 final View[] children = mChildren;
5946 final int count = mChildrenCount;
5948 for (int i = 0; i < count; i++) {
5949 final View child = children[i];
5950 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
5951 child.drawableHotspotChanged(x, y);
5958 protected int[] onCreateDrawableState(int extraSpace) {
5959 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
5960 return super.onCreateDrawableState(extraSpace);
5964 int n = getChildCount();
5965 for (int i = 0; i < n; i++) {
5966 int[] childState = getChildAt(i).getDrawableState();
5968 if (childState != null) {
5969 need += childState.length;
5973 int[] state = super.onCreateDrawableState(extraSpace + need);
5975 for (int i = 0; i < n; i++) {
5976 int[] childState = getChildAt(i).getDrawableState();
5978 if (childState != null) {
5979 state = mergeDrawableStates(state, childState);
5987 * Sets whether this ViewGroup's drawable states also include
5988 * its children's drawable states. This is used, for example, to
5989 * make a group appear to be focused when its child EditText or button
5992 public void setAddStatesFromChildren(boolean addsStates) {
5994 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
5996 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
5999 refreshDrawableState();
6003 * Returns whether this ViewGroup's drawable states also include
6004 * its children's drawable states. This is used, for example, to
6005 * make a group appear to be focused when its child EditText or button
6008 public boolean addStatesFromChildren() {
6009 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
6013 * If {@link #addStatesFromChildren} is true, refreshes this group's
6014 * drawable state (to include the states from its children).
6016 public void childDrawableStateChanged(View child) {
6017 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
6018 refreshDrawableState();
6023 * Specifies the animation listener to which layout animation events must
6025 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
6027 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
6030 * @param animationListener the layout animation listener
6032 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
6033 mAnimationListener = animationListener;
6037 * This method is called by LayoutTransition when there are 'changing' animations that need
6038 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
6039 * starts all pending transitions prior to the drawing phase in the current traversal.
6041 * @param transition The LayoutTransition to be started on the next traversal.
6045 public void requestTransitionStart(LayoutTransition transition) {
6046 ViewRootImpl viewAncestor = getViewRootImpl();
6047 if (viewAncestor != null) {
6048 viewAncestor.requestTransitionStart(transition);
6056 public boolean resolveRtlPropertiesIfNeeded() {
6057 final boolean result = super.resolveRtlPropertiesIfNeeded();
6058 // We dont need to resolve the children RTL properties if nothing has changed for the parent
6060 int count = getChildCount();
6061 for (int i = 0; i < count; i++) {
6062 final View child = getChildAt(i);
6063 if (child.isLayoutDirectionInherited()) {
6064 child.resolveRtlPropertiesIfNeeded();
6075 public boolean resolveLayoutDirection() {
6076 final boolean result = super.resolveLayoutDirection();
6078 int count = getChildCount();
6079 for (int i = 0; i < count; i++) {
6080 final View child = getChildAt(i);
6081 if (child.isLayoutDirectionInherited()) {
6082 child.resolveLayoutDirection();
6093 public boolean resolveTextDirection() {
6094 final boolean result = super.resolveTextDirection();
6096 int count = getChildCount();
6097 for (int i = 0; i < count; i++) {
6098 final View child = getChildAt(i);
6099 if (child.isTextDirectionInherited()) {
6100 child.resolveTextDirection();
6111 public boolean resolveTextAlignment() {
6112 final boolean result = super.resolveTextAlignment();
6114 int count = getChildCount();
6115 for (int i = 0; i < count; i++) {
6116 final View child = getChildAt(i);
6117 if (child.isTextAlignmentInherited()) {
6118 child.resolveTextAlignment();
6129 public void resolvePadding() {
6130 super.resolvePadding();
6131 int count = getChildCount();
6132 for (int i = 0; i < count; i++) {
6133 final View child = getChildAt(i);
6134 if (child.isLayoutDirectionInherited()) {
6135 child.resolvePadding();
6144 protected void resolveDrawables() {
6145 super.resolveDrawables();
6146 int count = getChildCount();
6147 for (int i = 0; i < count; i++) {
6148 final View child = getChildAt(i);
6149 if (child.isLayoutDirectionInherited()) {
6150 child.resolveDrawables();
6159 public void resolveLayoutParams() {
6160 super.resolveLayoutParams();
6161 int count = getChildCount();
6162 for (int i = 0; i < count; i++) {
6163 final View child = getChildAt(i);
6164 child.resolveLayoutParams();
6172 public void resetResolvedLayoutDirection() {
6173 super.resetResolvedLayoutDirection();
6175 int count = getChildCount();
6176 for (int i = 0; i < count; i++) {
6177 final View child = getChildAt(i);
6178 if (child.isLayoutDirectionInherited()) {
6179 child.resetResolvedLayoutDirection();
6188 public void resetResolvedTextDirection() {
6189 super.resetResolvedTextDirection();
6191 int count = getChildCount();
6192 for (int i = 0; i < count; i++) {
6193 final View child = getChildAt(i);
6194 if (child.isTextDirectionInherited()) {
6195 child.resetResolvedTextDirection();
6204 public void resetResolvedTextAlignment() {
6205 super.resetResolvedTextAlignment();
6207 int count = getChildCount();
6208 for (int i = 0; i < count; i++) {
6209 final View child = getChildAt(i);
6210 if (child.isTextAlignmentInherited()) {
6211 child.resetResolvedTextAlignment();
6220 public void resetResolvedPadding() {
6221 super.resetResolvedPadding();
6223 int count = getChildCount();
6224 for (int i = 0; i < count; i++) {
6225 final View child = getChildAt(i);
6226 if (child.isLayoutDirectionInherited()) {
6227 child.resetResolvedPadding();
6236 protected void resetResolvedDrawables() {
6237 super.resetResolvedDrawables();
6239 int count = getChildCount();
6240 for (int i = 0; i < count; i++) {
6241 final View child = getChildAt(i);
6242 if (child.isLayoutDirectionInherited()) {
6243 child.resetResolvedDrawables();
6249 * Return true if the pressed state should be delayed for children or descendants of this
6250 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
6251 * This prevents the pressed state from appearing when the user is actually trying to scroll
6254 * The default implementation returns true for compatibility reasons. Subclasses that do
6255 * not scroll should generally override this method and return false.
6257 public boolean shouldDelayChildPressedState() {
6265 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
6273 public void onNestedScrollAccepted(View child, View target, int axes) {
6274 mNestedScrollAxes = axes;
6280 * <p>The default implementation of onStopNestedScroll calls
6281 * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
6284 public void onStopNestedScroll(View child) {
6285 // Stop any recursive nested scrolling.
6293 public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
6294 int dxUnconsumed, int dyUnconsumed) {
6302 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
6310 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
6318 public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
6323 * Return the current axes of nested scrolling for this ViewGroup.
6325 * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
6326 * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p>
6328 * @return Flags indicating the current axes of nested scrolling
6329 * @see #SCROLL_AXIS_HORIZONTAL
6330 * @see #SCROLL_AXIS_VERTICAL
6331 * @see #SCROLL_AXIS_NONE
6333 public int getNestedScrollAxes() {
6334 return mNestedScrollAxes;
6338 protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
6343 public void captureTransitioningViews(List<View> transitioningViews) {
6344 if (getVisibility() != View.VISIBLE) {
6347 if (isTransitionGroup()) {
6348 transitioningViews.add(this);
6350 int count = getChildCount();
6351 for (int i = 0; i < count; i++) {
6352 View child = getChildAt(i);
6353 child.captureTransitioningViews(transitioningViews);
6360 public void findNamedViews(Map<String, View> namedElements) {
6361 if (getVisibility() != VISIBLE && mGhostView == null) {
6364 super.findNamedViews(namedElements);
6365 int count = getChildCount();
6366 for (int i = 0; i < count; i++) {
6367 View child = getChildAt(i);
6368 child.findNamedViews(namedElements);
6373 * LayoutParams are used by views to tell their parents how they want to be
6375 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
6376 * for a list of all child view attributes that this class supports.
6379 * The base LayoutParams class just describes how big the view wants to be
6380 * for both width and height. For each dimension, it can specify one of:
6382 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
6383 * means that the view wants to be as big as its parent (minus padding)
6384 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
6385 * to enclose its content (plus padding)
6386 * <li> an exact number
6388 * There are subclasses of LayoutParams for different subclasses of
6389 * ViewGroup. For example, AbsoluteLayout has its own subclass of
6390 * LayoutParams which adds an X and Y value.</p>
6392 * <div class="special reference">
6393 * <h3>Developer Guides</h3>
6394 * <p>For more information about creating user interface layouts, read the
6395 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
6398 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
6399 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
6401 public static class LayoutParams {
6403 * Special value for the height or width requested by a View.
6404 * FILL_PARENT means that the view wants to be as big as its parent,
6405 * minus the parent's padding, if any. This value is deprecated
6406 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
6408 @SuppressWarnings({"UnusedDeclaration"})
6410 public static final int FILL_PARENT = -1;
6413 * Special value for the height or width requested by a View.
6414 * MATCH_PARENT means that the view wants to be as big as its parent,
6415 * minus the parent's padding, if any. Introduced in API Level 8.
6417 public static final int MATCH_PARENT = -1;
6420 * Special value for the height or width requested by a View.
6421 * WRAP_CONTENT means that the view wants to be just large enough to fit
6422 * its own internal content, taking its own padding into account.
6424 public static final int WRAP_CONTENT = -2;
6427 * Information about how wide the view wants to be. Can be one of the
6428 * constants FILL_PARENT (replaced by MATCH_PARENT ,
6429 * in API Level 8) or WRAP_CONTENT. or an exact size.
6431 @ViewDebug.ExportedProperty(category = "layout", mapping = {
6432 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6433 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6438 * Information about how tall the view wants to be. Can be one of the
6439 * constants FILL_PARENT (replaced by MATCH_PARENT ,
6440 * in API Level 8) or WRAP_CONTENT. or an exact size.
6442 @ViewDebug.ExportedProperty(category = "layout", mapping = {
6443 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6444 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6449 * Used to animate layouts.
6451 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
6454 * Creates a new set of layout parameters. The values are extracted from
6455 * the supplied attributes set and context. The XML attributes mapped
6456 * to this set of layout parameters are:
6459 * <li><code>layout_width</code>: the width, either an exact value,
6460 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6461 * {@link #MATCH_PARENT} in API Level 8)</li>
6462 * <li><code>layout_height</code>: the height, either an exact value,
6463 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6464 * {@link #MATCH_PARENT} in API Level 8)</li>
6467 * @param c the application environment
6468 * @param attrs the set of attributes from which to extract the layout
6469 * parameters' values
6471 public LayoutParams(Context c, AttributeSet attrs) {
6472 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
6473 setBaseAttributes(a,
6474 R.styleable.ViewGroup_Layout_layout_width,
6475 R.styleable.ViewGroup_Layout_layout_height);
6480 * Creates a new set of layout parameters with the specified width
6483 * @param width the width, either {@link #WRAP_CONTENT},
6484 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6485 * API Level 8), or a fixed size in pixels
6486 * @param height the height, either {@link #WRAP_CONTENT},
6487 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6488 * API Level 8), or a fixed size in pixels
6490 public LayoutParams(int width, int height) {
6492 this.height = height;
6496 * Copy constructor. Clones the width and height values of the source.
6498 * @param source The layout params to copy from.
6500 public LayoutParams(LayoutParams source) {
6501 this.width = source.width;
6502 this.height = source.height;
6506 * Used internally by MarginLayoutParams.
6513 * Extracts the layout parameters from the supplied attributes.
6515 * @param a the style attributes to extract the parameters from
6516 * @param widthAttr the identifier of the width attribute
6517 * @param heightAttr the identifier of the height attribute
6519 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
6520 width = a.getLayoutDimension(widthAttr, "layout_width");
6521 height = a.getLayoutDimension(heightAttr, "layout_height");
6525 * Resolve layout parameters depending on the layout direction. Subclasses that care about
6526 * layoutDirection changes should override this method. The default implementation does
6529 * @param layoutDirection the direction of the layout
6531 * {@link View#LAYOUT_DIRECTION_LTR}
6532 * {@link View#LAYOUT_DIRECTION_RTL}
6534 public void resolveLayoutDirection(int layoutDirection) {
6538 * Returns a String representation of this set of layout parameters.
6540 * @param output the String to prepend to the internal representation
6541 * @return a String with the following format: output +
6542 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
6546 public String debug(String output) {
6547 return output + "ViewGroup.LayoutParams={ width="
6548 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
6552 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
6554 * @param view the view that contains these layout parameters
6555 * @param canvas the canvas on which to draw
6559 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
6563 * Converts the specified size to a readable String.
6565 * @param size the size to convert
6566 * @return a String instance representing the supplied size
6570 protected static String sizeToString(int size) {
6571 if (size == WRAP_CONTENT) {
6572 return "wrap-content";
6574 if (size == MATCH_PARENT) {
6575 return "match-parent";
6577 return String.valueOf(size);
6582 * Per-child layout information for layouts that support margins.
6584 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
6585 * for a list of all child view attributes that this class supports.
6587 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
6589 * The left margin in pixels of the child. Margin values should be positive.
6590 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6593 @ViewDebug.ExportedProperty(category = "layout")
6594 public int leftMargin;
6597 * The top margin in pixels of the child. Margin values should be positive.
6598 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6601 @ViewDebug.ExportedProperty(category = "layout")
6602 public int topMargin;
6605 * The right margin in pixels of the child. Margin values should be positive.
6606 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6609 @ViewDebug.ExportedProperty(category = "layout")
6610 public int rightMargin;
6613 * The bottom margin in pixels of the child. Margin values should be positive.
6614 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6617 @ViewDebug.ExportedProperty(category = "layout")
6618 public int bottomMargin;
6621 * The start margin in pixels of the child. Margin values should be positive.
6622 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6625 @ViewDebug.ExportedProperty(category = "layout")
6626 private int startMargin = DEFAULT_MARGIN_RELATIVE;
6629 * The end margin in pixels of the child. Margin values should be positive.
6630 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6633 @ViewDebug.ExportedProperty(category = "layout")
6634 private int endMargin = DEFAULT_MARGIN_RELATIVE;
6637 * The default start and end margin.
6640 public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
6643 * Bit 0: layout direction
6644 * Bit 1: layout direction
6645 * Bit 2: left margin undefined
6646 * Bit 3: right margin undefined
6647 * Bit 4: is RTL compatibility mode
6648 * Bit 5: need resolution
6650 * Bit 6 to 7 not used
6654 @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
6655 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
6656 equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
6657 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
6658 equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
6659 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
6660 equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
6661 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
6662 equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
6663 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
6664 equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
6665 }, formatToHexString = true)
6668 private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
6669 private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
6670 private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
6671 private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
6672 private static final int NEED_RESOLUTION_MASK = 0x00000020;
6674 private static final int DEFAULT_MARGIN_RESOLVED = 0;
6675 private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
6678 * Creates a new set of layout parameters. The values are extracted from
6679 * the supplied attributes set and context.
6681 * @param c the application environment
6682 * @param attrs the set of attributes from which to extract the layout
6683 * parameters' values
6685 public MarginLayoutParams(Context c, AttributeSet attrs) {
6688 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
6689 setBaseAttributes(a,
6690 R.styleable.ViewGroup_MarginLayout_layout_width,
6691 R.styleable.ViewGroup_MarginLayout_layout_height);
6693 int margin = a.getDimensionPixelSize(
6694 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
6696 leftMargin = margin;
6698 rightMargin= margin;
6699 bottomMargin = margin;
6701 leftMargin = a.getDimensionPixelSize(
6702 R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
6704 if (leftMargin == UNDEFINED_MARGIN) {
6705 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6706 leftMargin = DEFAULT_MARGIN_RESOLVED;
6708 rightMargin = a.getDimensionPixelSize(
6709 R.styleable.ViewGroup_MarginLayout_layout_marginRight,
6711 if (rightMargin == UNDEFINED_MARGIN) {
6712 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
6713 rightMargin = DEFAULT_MARGIN_RESOLVED;
6716 topMargin = a.getDimensionPixelSize(
6717 R.styleable.ViewGroup_MarginLayout_layout_marginTop,
6718 DEFAULT_MARGIN_RESOLVED);
6719 bottomMargin = a.getDimensionPixelSize(
6720 R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
6721 DEFAULT_MARGIN_RESOLVED);
6723 startMargin = a.getDimensionPixelSize(
6724 R.styleable.ViewGroup_MarginLayout_layout_marginStart,
6725 DEFAULT_MARGIN_RELATIVE);
6726 endMargin = a.getDimensionPixelSize(
6727 R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
6728 DEFAULT_MARGIN_RELATIVE);
6730 if (isMarginRelative()) {
6731 mMarginFlags |= NEED_RESOLUTION_MASK;
6735 final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
6736 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
6737 if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
6738 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
6741 // Layout direction is LTR by default
6742 mMarginFlags |= LAYOUT_DIRECTION_LTR;
6750 public MarginLayoutParams(int width, int height) {
6751 super(width, height);
6753 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6754 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
6756 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6757 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
6761 * Copy constructor. Clones the width, height and margin values of the source.
6763 * @param source The layout params to copy from.
6765 public MarginLayoutParams(MarginLayoutParams source) {
6766 this.width = source.width;
6767 this.height = source.height;
6769 this.leftMargin = source.leftMargin;
6770 this.topMargin = source.topMargin;
6771 this.rightMargin = source.rightMargin;
6772 this.bottomMargin = source.bottomMargin;
6773 this.startMargin = source.startMargin;
6774 this.endMargin = source.endMargin;
6776 this.mMarginFlags = source.mMarginFlags;
6782 public MarginLayoutParams(LayoutParams source) {
6785 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6786 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
6788 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6789 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
6793 * @hide Used internally.
6795 public final void copyMarginsFrom(MarginLayoutParams source) {
6796 this.leftMargin = source.leftMargin;
6797 this.topMargin = source.topMargin;
6798 this.rightMargin = source.rightMargin;
6799 this.bottomMargin = source.bottomMargin;
6800 this.startMargin = source.startMargin;
6801 this.endMargin = source.endMargin;
6803 this.mMarginFlags = source.mMarginFlags;
6807 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
6808 * to be done so that the new margins are taken into account. Left and right margins may be
6809 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
6810 * Margin values should be positive.
6812 * @param left the left margin size
6813 * @param top the top margin size
6814 * @param right the right margin size
6815 * @param bottom the bottom margin size
6817 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
6818 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
6819 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
6820 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
6822 public void setMargins(int left, int top, int right, int bottom) {
6825 rightMargin = right;
6826 bottomMargin = bottom;
6827 mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
6828 mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
6829 if (isMarginRelative()) {
6830 mMarginFlags |= NEED_RESOLUTION_MASK;
6832 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6837 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
6838 * needs to be done so that the new relative margins are taken into account. Left and right
6839 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
6840 * direction. Margin values should be positive.
6842 * @param start the start margin size
6843 * @param top the top margin size
6844 * @param end the right margin size
6845 * @param bottom the bottom margin size
6847 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6848 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
6849 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6850 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
6854 public void setMarginsRelative(int start, int top, int end, int bottom) {
6855 startMargin = start;
6858 bottomMargin = bottom;
6859 mMarginFlags |= NEED_RESOLUTION_MASK;
6863 * Sets the relative start margin. Margin values should be positive.
6865 * @param start the start margin size
6867 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6869 public void setMarginStart(int start) {
6870 startMargin = start;
6871 mMarginFlags |= NEED_RESOLUTION_MASK;
6875 * Returns the start margin in pixels.
6877 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6879 * @return the start margin in pixels.
6881 public int getMarginStart() {
6882 if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
6883 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
6886 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
6887 case View.LAYOUT_DIRECTION_RTL:
6889 case View.LAYOUT_DIRECTION_LTR:
6896 * Sets the relative end margin. Margin values should be positive.
6898 * @param end the end margin size
6900 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6902 public void setMarginEnd(int end) {
6904 mMarginFlags |= NEED_RESOLUTION_MASK;
6908 * Returns the end margin in pixels.
6910 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6912 * @return the end margin in pixels.
6914 public int getMarginEnd() {
6915 if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
6916 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
6919 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
6920 case View.LAYOUT_DIRECTION_RTL:
6922 case View.LAYOUT_DIRECTION_LTR:
6929 * Check if margins are relative.
6931 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6932 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6934 * @return true if either marginStart or marginEnd has been set.
6936 public boolean isMarginRelative() {
6937 return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
6941 * Set the layout direction
6942 * @param layoutDirection the layout direction.
6943 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
6944 * or {@link View#LAYOUT_DIRECTION_RTL}.
6946 public void setLayoutDirection(int layoutDirection) {
6947 if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
6948 layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
6949 if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
6950 mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
6951 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
6952 if (isMarginRelative()) {
6953 mMarginFlags |= NEED_RESOLUTION_MASK;
6955 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6961 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
6962 * {@link View#LAYOUT_DIRECTION_RTL}.
6964 * @return the layout direction.
6966 public int getLayoutDirection() {
6967 return (mMarginFlags & LAYOUT_DIRECTION_MASK);
6971 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
6972 * may be overridden depending on layout direction.
6975 public void resolveLayoutDirection(int layoutDirection) {
6976 setLayoutDirection(layoutDirection);
6978 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
6979 // Will use the left and right margins if no relative margin is defined.
6980 if (!isMarginRelative() ||
6981 (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
6983 // Proceed with resolution
6987 private void doResolveMargins() {
6988 if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
6989 // if left or right margins are not defined and if we have some start or end margin
6990 // defined then use those start and end margins.
6991 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
6992 && startMargin > DEFAULT_MARGIN_RELATIVE) {
6993 leftMargin = startMargin;
6995 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
6996 && endMargin > DEFAULT_MARGIN_RELATIVE) {
6997 rightMargin = endMargin;
7000 // We have some relative margins (either the start one or the end one or both). So use
7001 // them and override what has been defined for left and right margins. If either start
7002 // or end margin is not defined, just set it to default "0".
7003 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
7004 case View.LAYOUT_DIRECTION_RTL:
7005 leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
7006 endMargin : DEFAULT_MARGIN_RESOLVED;
7007 rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
7008 startMargin : DEFAULT_MARGIN_RESOLVED;
7010 case View.LAYOUT_DIRECTION_LTR:
7012 leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
7013 startMargin : DEFAULT_MARGIN_RESOLVED;
7014 rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
7015 endMargin : DEFAULT_MARGIN_RESOLVED;
7019 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7025 public boolean isLayoutRtl() {
7026 return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
7033 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
7034 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
7036 fillDifference(canvas,
7037 view.getLeft() + oi.left,
7038 view.getTop() + oi.top,
7039 view.getRight() - oi.right,
7040 view.getBottom() - oi.bottom,
7049 /* Describes a touched view and the ids of the pointers that it has captured.
7051 * This code assumes that pointer ids are always in the range 0..31 such that
7052 * it can use a bitfield to track which pointer ids are present.
7053 * As it happens, the lower layers of the input dispatch pipeline also use the
7054 * same trick so the assumption should be safe here...
7056 private static final class TouchTarget {
7057 private static final int MAX_RECYCLED = 32;
7058 private static final Object sRecycleLock = new Object[0];
7059 private static TouchTarget sRecycleBin;
7060 private static int sRecycledCount;
7062 public static final int ALL_POINTER_IDS = -1; // all ones
7064 // The touched child view.
7067 // The combined bit mask of pointer ids for all pointers captured by the target.
7068 public int pointerIdBits;
7070 // The next target in the target list.
7071 public TouchTarget next;
7073 private TouchTarget() {
7076 public static TouchTarget obtain(View child, int pointerIdBits) {
7077 final TouchTarget target;
7078 synchronized (sRecycleLock) {
7079 if (sRecycleBin == null) {
7080 target = new TouchTarget();
7082 target = sRecycleBin;
7083 sRecycleBin = target.next;
7088 target.child = child;
7089 target.pointerIdBits = pointerIdBits;
7093 public void recycle() {
7094 synchronized (sRecycleLock) {
7095 if (sRecycledCount < MAX_RECYCLED) {
7098 sRecycledCount += 1;
7107 /* Describes a hovered view. */
7108 private static final class HoverTarget {
7109 private static final int MAX_RECYCLED = 32;
7110 private static final Object sRecycleLock = new Object[0];
7111 private static HoverTarget sRecycleBin;
7112 private static int sRecycledCount;
7114 // The hovered child view.
7117 // The next target in the target list.
7118 public HoverTarget next;
7120 private HoverTarget() {
7123 public static HoverTarget obtain(View child) {
7124 final HoverTarget target;
7125 synchronized (sRecycleLock) {
7126 if (sRecycleBin == null) {
7127 target = new HoverTarget();
7129 target = sRecycleBin;
7130 sRecycleBin = target.next;
7135 target.child = child;
7139 public void recycle() {
7140 synchronized (sRecycleLock) {
7141 if (sRecycledCount < MAX_RECYCLED) {
7144 sRecycledCount += 1;
7154 * Pooled class that orderes the children of a ViewGroup from start
7155 * to end based on how they are laid out and the layout direction.
7157 static class ChildListForAccessibility {
7159 private static final int MAX_POOL_SIZE = 32;
7161 private static final SynchronizedPool<ChildListForAccessibility> sPool =
7162 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
7164 private final ArrayList<View> mChildren = new ArrayList<View>();
7166 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
7168 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
7169 ChildListForAccessibility list = sPool.acquire();
7171 list = new ChildListForAccessibility();
7173 list.init(parent, sort);
7177 public void recycle() {
7179 sPool.release(this);
7182 public int getChildCount() {
7183 return mChildren.size();
7186 public View getChildAt(int index) {
7187 return mChildren.get(index);
7190 public int getChildIndex(View child) {
7191 return mChildren.indexOf(child);
7194 private void init(ViewGroup parent, boolean sort) {
7195 ArrayList<View> children = mChildren;
7196 final int childCount = parent.getChildCount();
7197 for (int i = 0; i < childCount; i++) {
7198 View child = parent.getChildAt(i);
7199 children.add(child);
7202 ArrayList<ViewLocationHolder> holders = mHolders;
7203 for (int i = 0; i < childCount; i++) {
7204 View child = children.get(i);
7205 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
7206 holders.add(holder);
7209 for (int i = 0; i < childCount; i++) {
7210 ViewLocationHolder holder = holders.get(i);
7211 children.set(i, holder.mView);
7218 private void sort(ArrayList<ViewLocationHolder> holders) {
7219 // This is gross but the least risky solution. The current comparison
7220 // strategy breaks transitivity but produces very good results. Coming
7221 // up with a new strategy requires time which we do not have, so ...
7223 ViewLocationHolder.setComparisonStrategy(
7224 ViewLocationHolder.COMPARISON_STRATEGY_STRIPE);
7225 Collections.sort(holders);
7226 } catch (IllegalArgumentException iae) {
7227 // Note that in practice this occurs extremely rarely in a couple
7228 // of pathological cases.
7229 ViewLocationHolder.setComparisonStrategy(
7230 ViewLocationHolder.COMPARISON_STRATEGY_LOCATION);
7231 Collections.sort(holders);
7235 private void clear() {
7241 * Pooled class that holds a View and its location with respect to
7242 * a specified root. This enables sorting of views based on their
7243 * coordinates without recomputing the position relative to the root
7244 * on every comparison.
7246 static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
7248 private static final int MAX_POOL_SIZE = 32;
7250 private static final SynchronizedPool<ViewLocationHolder> sPool =
7251 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
7253 public static final int COMPARISON_STRATEGY_STRIPE = 1;
7255 public static final int COMPARISON_STRATEGY_LOCATION = 2;
7257 private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
7259 private final Rect mLocation = new Rect();
7263 private int mLayoutDirection;
7265 public static ViewLocationHolder obtain(ViewGroup root, View view) {
7266 ViewLocationHolder holder = sPool.acquire();
7267 if (holder == null) {
7268 holder = new ViewLocationHolder();
7270 holder.init(root, view);
7274 public static void setComparisonStrategy(int strategy) {
7275 sComparisonStrategy = strategy;
7278 public void recycle() {
7280 sPool.release(this);
7284 public int compareTo(ViewLocationHolder another) {
7285 // This instance is greater than an invalid argument.
7286 if (another == null) {
7290 if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
7291 // First is above second.
7292 if (mLocation.bottom - another.mLocation.top <= 0) {
7295 // First is below second.
7296 if (mLocation.top - another.mLocation.bottom >= 0) {
7301 // We are ordering left-to-right, top-to-bottom.
7302 if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
7303 final int leftDifference = mLocation.left - another.mLocation.left;
7304 if (leftDifference != 0) {
7305 return leftDifference;
7308 final int rightDifference = mLocation.right - another.mLocation.right;
7309 if (rightDifference != 0) {
7310 return -rightDifference;
7313 // We are ordering left-to-right, top-to-bottom.
7314 final int topDifference = mLocation.top - another.mLocation.top;
7315 if (topDifference != 0) {
7316 return topDifference;
7318 // Break tie by height.
7319 final int heightDiference = mLocation.height() - another.mLocation.height();
7320 if (heightDiference != 0) {
7321 return -heightDiference;
7323 // Break tie by width.
7324 final int widthDiference = mLocation.width() - another.mLocation.width();
7325 if (widthDiference != 0) {
7326 return -widthDiference;
7328 // Just break the tie somehow. The accessibliity ids are unique
7329 // and stable, hence this is deterministic tie breaking.
7330 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
7333 private void init(ViewGroup root, View view) {
7334 Rect viewLocation = mLocation;
7335 view.getDrawingRect(viewLocation);
7336 root.offsetDescendantRectToMyCoords(view, viewLocation);
7338 mLayoutDirection = root.getLayoutDirection();
7341 private void clear() {
7343 mLocation.set(0, 0, 0, 0);
7347 private static Paint getDebugPaint() {
7348 if (sDebugPaint == null) {
7349 sDebugPaint = new Paint();
7350 sDebugPaint.setAntiAlias(false);
7355 private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
7356 if (sDebugLines== null) {
7357 // TODO: This won't work with multiple UI threads in a single process
7358 sDebugLines = new float[16];
7361 sDebugLines[0] = x1;
7362 sDebugLines[1] = y1;
7363 sDebugLines[2] = x2;
7364 sDebugLines[3] = y1;
7366 sDebugLines[4] = x2;
7367 sDebugLines[5] = y1;
7368 sDebugLines[6] = x2;
7369 sDebugLines[7] = y2;
7371 sDebugLines[8] = x2;
7372 sDebugLines[9] = y2;
7373 sDebugLines[10] = x1;
7374 sDebugLines[11] = y2;
7376 sDebugLines[12] = x1;
7377 sDebugLines[13] = y2;
7378 sDebugLines[14] = x1;
7379 sDebugLines[15] = y1;
7381 canvas.drawLines(sDebugLines, paint);
7384 private final class OrderedChildIterator implements Iterator<View> {
7385 private List<View> mOrderedChildList;
7386 private boolean mUseCustomOrder;
7387 private int mCurrentIndex;
7388 private boolean mInitialized;
7390 public void initialize() {
7391 mOrderedChildList = buildOrderedChildList();
7392 mUseCustomOrder = (mOrderedChildList == null)
7393 && isChildrenDrawingOrderEnabled();
7394 mCurrentIndex = mChildrenCount - 1;
7395 mInitialized = true;
7398 public void release() {
7399 if (mOrderedChildList != null) {
7400 mOrderedChildList.clear();
7402 mUseCustomOrder = false;
7404 mInitialized = false;
7407 public boolean isInitialized() {
7408 return mInitialized;
7412 public boolean hasNext() {
7413 return (mCurrentIndex >= 0);
7417 public View next() {
7419 throw new NoSuchElementException("No such element");
7421 return getChild(mCurrentIndex--);
7424 private View getChild(int index) {
7425 final int childIndex = mUseCustomOrder
7426 ? getChildDrawingOrder(mChildrenCount, index) : index;
7427 return (mOrderedChildList == null)
7428 ? mChildren[childIndex] : mOrderedChildList.get(childIndex);
7432 public void remove() {
7433 throw new UnsupportedOperationException();