Roll android_tools support library to 25.1.0
[android_tools.git] / sdk / sources / android-23 / android / view / ViewGroup.java
blob52d6cbec4abb33e1139c8370ebc9d8698d1c16a9
1 /*
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.
17 package android.view;
19 import android.animation.LayoutTransition;
20 import android.annotation.IdRes;
21 import android.annotation.NonNull;
22 import android.annotation.UiThread;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.res.Configuration;
27 import android.content.res.TypedArray;
28 import android.graphics.Bitmap;
29 import android.graphics.Canvas;
30 import android.graphics.Color;
31 import android.graphics.Insets;
32 import android.graphics.Matrix;
33 import android.graphics.Paint;
34 import android.graphics.PointF;
35 import android.graphics.Rect;
36 import android.graphics.RectF;
37 import android.graphics.Region;
38 import android.os.Build;
39 import android.os.Bundle;
40 import android.os.Parcelable;
41 import android.os.SystemClock;
42 import android.util.AttributeSet;
43 import android.util.Log;
44 import android.util.Pools.SynchronizedPool;
45 import android.util.SparseArray;
46 import android.util.SparseBooleanArray;
47 import android.view.accessibility.AccessibilityEvent;
48 import android.view.accessibility.AccessibilityNodeInfo;
49 import android.view.animation.Animation;
50 import android.view.animation.AnimationUtils;
51 import android.view.animation.LayoutAnimationController;
52 import android.view.animation.Transformation;
54 import com.android.internal.R;
55 import com.android.internal.util.Predicate;
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.HashSet;
60 import java.util.List;
61 import java.util.Map;
62 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
64 /**
65 * <p>
66 * A <code>ViewGroup</code> is a special view that can contain other views
67 * (called children.) The view group is the base class for layouts and views
68 * containers. This class also defines the
69 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
70 * class for layouts parameters.
71 * </p>
73 * <p>
74 * Also see {@link LayoutParams} for layout attributes.
75 * </p>
77 * <div class="special reference">
78 * <h3>Developer Guides</h3>
79 * <p>For more information about creating user interface layouts, read the
80 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
81 * guide.</p></div>
83 * <p>Here is a complete implementation of a custom ViewGroup that implements
84 * a simple {@link android.widget.FrameLayout} along with the ability to stack
85 * children in left and right gutters.</p>
87 * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
88 * Complete}
90 * <p>If you are implementing XML layout attributes as shown in the example, this is the
91 * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
93 * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
95 * <p>Finally the layout manager can be used in an XML layout like so:</p>
97 * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
99 * @attr ref android.R.styleable#ViewGroup_clipChildren
100 * @attr ref android.R.styleable#ViewGroup_clipToPadding
101 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
102 * @attr ref android.R.styleable#ViewGroup_animationCache
103 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
104 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
105 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
106 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
107 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
108 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
109 * @attr ref android.R.styleable#ViewGroup_layoutMode
111 @UiThread
112 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
113 private static final String TAG = "ViewGroup";
115 private static final boolean DBG = false;
116 /** @hide */
117 public static boolean DEBUG_DRAW = false;
120 * Views which have been hidden or removed which need to be animated on
121 * their way out.
122 * This field should be made private, so it is hidden from the SDK.
123 * {@hide}
125 protected ArrayList<View> mDisappearingChildren;
128 * Listener used to propagate events indicating when children are added
129 * and/or removed from a view group.
130 * This field should be made private, so it is hidden from the SDK.
131 * {@hide}
133 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
135 // The view contained within this ViewGroup that has or contains focus.
136 private View mFocused;
139 * A Transformation used when drawing children, to
140 * apply on the child being drawn.
142 private Transformation mChildTransformation;
145 * Used to track the current invalidation region.
147 RectF mInvalidateRegion;
150 * A Transformation used to calculate a correct
151 * invalidation area when the application is autoscaled.
153 Transformation mInvalidationTransformation;
155 // View currently under an ongoing drag
156 private View mCurrentDragView;
158 // Metadata about the ongoing drag
159 private DragEvent mCurrentDrag;
160 private HashSet<View> mDragNotifiedChildren;
162 // Does this group have a child that can accept the current drag payload?
163 private boolean mChildAcceptsDrag;
165 // Used during drag dispatch
166 private PointF mLocalPoint;
168 // Lazily-created holder for point computations.
169 private float[] mTempPoint;
171 // Layout animation
172 private LayoutAnimationController mLayoutAnimationController;
173 private Animation.AnimationListener mAnimationListener;
175 // First touch target in the linked list of touch targets.
176 private TouchTarget mFirstTouchTarget;
178 // For debugging only. You can see these in hierarchyviewer.
179 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
180 @ViewDebug.ExportedProperty(category = "events")
181 private long mLastTouchDownTime;
182 @ViewDebug.ExportedProperty(category = "events")
183 private int mLastTouchDownIndex = -1;
184 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
185 @ViewDebug.ExportedProperty(category = "events")
186 private float mLastTouchDownX;
187 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
188 @ViewDebug.ExportedProperty(category = "events")
189 private float mLastTouchDownY;
191 // First hover target in the linked list of hover targets.
192 // The hover targets are children which have received ACTION_HOVER_ENTER.
193 // They might not have actually handled the hover event, but we will
194 // continue sending hover events to them as long as the pointer remains over
195 // their bounds and the view group does not intercept hover.
196 private HoverTarget mFirstHoverTarget;
198 // True if the view group itself received a hover event.
199 // It might not have actually handled the hover event.
200 private boolean mHoveredSelf;
203 * Internal flags.
205 * This field should be made private, so it is hidden from the SDK.
206 * {@hide}
208 @ViewDebug.ExportedProperty(flagMapping = {
209 @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN,
210 name = "CLIP_CHILDREN"),
211 @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING,
212 name = "CLIP_TO_PADDING"),
213 @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL,
214 name = "PADDING_NOT_NULL")
215 }, formatToHexString = true)
216 protected int mGroupFlags;
219 * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
221 private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
224 * NOTE: If you change the flags below make sure to reflect the changes
225 * the DisplayList class
228 // When set, ViewGroup invalidates only the child's rectangle
229 // Set by default
230 static final int FLAG_CLIP_CHILDREN = 0x1;
232 // When set, ViewGroup excludes the padding area from the invalidate rectangle
233 // Set by default
234 private static final int FLAG_CLIP_TO_PADDING = 0x2;
236 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
237 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
238 static final int FLAG_INVALIDATE_REQUIRED = 0x4;
240 // When set, dispatchDraw() will run the layout animation and unset the flag
241 private static final int FLAG_RUN_ANIMATION = 0x8;
243 // When set, there is either no layout animation on the ViewGroup or the layout
244 // animation is over
245 // Set by default
246 static final int FLAG_ANIMATION_DONE = 0x10;
248 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
249 // to clip it, even if FLAG_CLIP_TO_PADDING is set
250 private static final int FLAG_PADDING_NOT_NULL = 0x20;
252 /** @deprecated - functionality removed */
253 private static final int FLAG_ANIMATION_CACHE = 0x40;
255 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
256 // layout animation; this avoid clobbering the hierarchy
257 // Automatically set when the layout animation starts, depending on the animation's
258 // characteristics
259 static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
261 // When set, the next call to drawChild() will clear mChildTransformation's matrix
262 static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
264 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
265 // the children's Bitmap caches if necessary
266 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
267 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
270 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
271 * to get the index of the child to draw for that iteration.
273 * @hide
275 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
278 * When set, this ViewGroup supports static transformations on children; this causes
279 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
280 * invoked when a child is drawn.
282 * Any subclass overriding
283 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
284 * set this flags in {@link #mGroupFlags}.
286 * {@hide}
288 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
290 // UNUSED FLAG VALUE: 0x1000;
293 * When set, this ViewGroup's drawable states also include those
294 * of its children.
296 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
298 /** @deprecated functionality removed */
299 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
301 /** @deprecated functionality removed */
302 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
305 * When set, this group will go through its list of children to notify them of
306 * any drawable state change.
308 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
310 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
313 * This view will get focus before any of its descendants.
315 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
318 * This view will get focus only if none of its descendants want it.
320 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
323 * This view will block any of its descendants from getting focus, even
324 * if they are focusable.
326 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
329 * Used to map between enum in attrubutes and flag values.
331 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
332 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
333 FOCUS_BLOCK_DESCENDANTS};
336 * When set, this ViewGroup should not intercept touch events.
337 * {@hide}
339 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
342 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
344 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
347 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
348 * to children when adding new views. This is used to prevent multiple
349 * onAttached calls when a ViewGroup adds children in its own onAttached method.
351 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
354 * When true, indicates that a layoutMode has been explicitly set, either with
355 * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
356 * This distinguishes the situation in which a layout mode was inherited from
357 * one of the ViewGroup's ancestors and cached locally.
359 private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
361 static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
363 static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
366 * When set, focus will not be permitted to enter this group if a touchscreen is present.
368 static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000;
371 * When true, indicates that a call to startActionModeForChild was made with the type parameter
372 * and should not be ignored. This helps in backwards compatibility with the existing method
373 * without a type.
375 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
376 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
378 private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED = 0x8000000;
381 * When true, indicates that a call to startActionModeForChild was made without the type
382 * parameter. This helps in backwards compatibility with the existing method
383 * without a type.
385 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
386 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
388 private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED = 0x10000000;
391 * Indicates which types of drawing caches are to be kept in memory.
392 * This field should be made private, so it is hidden from the SDK.
393 * {@hide}
395 protected int mPersistentDrawingCache;
398 * Used to indicate that no drawing cache should be kept in memory.
400 public static final int PERSISTENT_NO_CACHE = 0x0;
403 * Used to indicate that the animation drawing cache should be kept in memory.
405 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
408 * Used to indicate that the scrolling drawing cache should be kept in memory.
410 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
413 * Used to indicate that all drawing caches should be kept in memory.
415 public static final int PERSISTENT_ALL_CACHES = 0x3;
417 // Layout Modes
419 private static final int LAYOUT_MODE_UNDEFINED = -1;
422 * This constant is a {@link #setLayoutMode(int) layoutMode}.
423 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
424 * {@link #getRight() right} and {@link #getBottom() bottom}.
426 public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
429 * This constant is a {@link #setLayoutMode(int) layoutMode}.
430 * Optical bounds describe where a widget appears to be. They sit inside the clip
431 * bounds which need to cover a larger area to allow other effects,
432 * such as shadows and glows, to be drawn.
434 public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
436 /** @hide */
437 public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
440 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
441 * are set at the same time.
443 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
445 // Index of the child's left position in the mLocation array
446 private static final int CHILD_LEFT_INDEX = 0;
447 // Index of the child's top position in the mLocation array
448 private static final int CHILD_TOP_INDEX = 1;
450 // Child views of this ViewGroup
451 private View[] mChildren;
452 // Number of valid children in the mChildren array, the rest should be null or not
453 // considered as children
454 private int mChildrenCount;
456 // Whether layout calls are currently being suppressed, controlled by calls to
457 // suppressLayout()
458 boolean mSuppressLayout = false;
460 // Whether any layout calls have actually been suppressed while mSuppressLayout
461 // has been true. This tracks whether we need to issue a requestLayout() when
462 // layout is later re-enabled.
463 private boolean mLayoutCalledWhileSuppressed = false;
465 private static final int ARRAY_INITIAL_CAPACITY = 12;
466 private static final int ARRAY_CAPACITY_INCREMENT = 12;
468 private static Paint sDebugPaint;
469 private static float[] sDebugLines;
471 // Used to draw cached views
472 Paint mCachePaint;
474 // Used to animate add/remove changes in layout
475 private LayoutTransition mTransition;
477 // The set of views that are currently being transitioned. This list is used to track views
478 // being removed that should not actually be removed from the parent yet because they are
479 // being animated.
480 private ArrayList<View> mTransitioningViews;
482 // List of children changing visibility. This is used to potentially keep rendering
483 // views during a transition when they otherwise would have become gone/invisible
484 private ArrayList<View> mVisibilityChangingChildren;
486 // Temporary holder of presorted children, only used for
487 // input/software draw dispatch for correctly Z ordering.
488 private ArrayList<View> mPreSortedChildren;
490 // Indicates how many of this container's child subtrees contain transient state
491 @ViewDebug.ExportedProperty(category = "layout")
492 private int mChildCountWithTransientState = 0;
495 * Currently registered axes for nested scrolling. Flag set consisting of
496 * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
497 * for null.
499 private int mNestedScrollAxes;
501 // Used to manage the list of transient views, added by addTransientView()
502 private List<Integer> mTransientIndices = null;
503 private List<View> mTransientViews = null;
507 * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
509 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
510 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
512 private static final ActionMode SENTINEL_ACTION_MODE = new ActionMode() {
513 @Override
514 public void setTitle(CharSequence title) {}
516 @Override
517 public void setTitle(int resId) {}
519 @Override
520 public void setSubtitle(CharSequence subtitle) {}
522 @Override
523 public void setSubtitle(int resId) {}
525 @Override
526 public void setCustomView(View view) {}
528 @Override
529 public void invalidate() {}
531 @Override
532 public void finish() {}
534 @Override
535 public Menu getMenu() {
536 return null;
539 @Override
540 public CharSequence getTitle() {
541 return null;
544 @Override
545 public CharSequence getSubtitle() {
546 return null;
549 @Override
550 public View getCustomView() {
551 return null;
554 @Override
555 public MenuInflater getMenuInflater() {
556 return null;
560 public ViewGroup(Context context) {
561 this(context, null);
564 public ViewGroup(Context context, AttributeSet attrs) {
565 this(context, attrs, 0);
568 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
569 this(context, attrs, defStyleAttr, 0);
572 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
573 super(context, attrs, defStyleAttr, defStyleRes);
574 initViewGroup();
575 initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
578 private boolean debugDraw() {
579 return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
582 private void initViewGroup() {
583 // ViewGroup doesn't draw by default
584 if (!debugDraw()) {
585 setFlags(WILL_NOT_DRAW, DRAW_MASK);
587 mGroupFlags |= FLAG_CLIP_CHILDREN;
588 mGroupFlags |= FLAG_CLIP_TO_PADDING;
589 mGroupFlags |= FLAG_ANIMATION_DONE;
590 mGroupFlags |= FLAG_ANIMATION_CACHE;
591 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
593 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
594 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
597 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
599 mChildren = new View[ARRAY_INITIAL_CAPACITY];
600 mChildrenCount = 0;
602 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
605 private void initFromAttributes(
606 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
607 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyleAttr,
608 defStyleRes);
610 final int N = a.getIndexCount();
611 for (int i = 0; i < N; i++) {
612 int attr = a.getIndex(i);
613 switch (attr) {
614 case R.styleable.ViewGroup_clipChildren:
615 setClipChildren(a.getBoolean(attr, true));
616 break;
617 case R.styleable.ViewGroup_clipToPadding:
618 setClipToPadding(a.getBoolean(attr, true));
619 break;
620 case R.styleable.ViewGroup_animationCache:
621 setAnimationCacheEnabled(a.getBoolean(attr, true));
622 break;
623 case R.styleable.ViewGroup_persistentDrawingCache:
624 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
625 break;
626 case R.styleable.ViewGroup_addStatesFromChildren:
627 setAddStatesFromChildren(a.getBoolean(attr, false));
628 break;
629 case R.styleable.ViewGroup_alwaysDrawnWithCache:
630 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
631 break;
632 case R.styleable.ViewGroup_layoutAnimation:
633 int id = a.getResourceId(attr, -1);
634 if (id > 0) {
635 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
637 break;
638 case R.styleable.ViewGroup_descendantFocusability:
639 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
640 break;
641 case R.styleable.ViewGroup_splitMotionEvents:
642 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
643 break;
644 case R.styleable.ViewGroup_animateLayoutChanges:
645 boolean animateLayoutChanges = a.getBoolean(attr, false);
646 if (animateLayoutChanges) {
647 setLayoutTransition(new LayoutTransition());
649 break;
650 case R.styleable.ViewGroup_layoutMode:
651 setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
652 break;
653 case R.styleable.ViewGroup_transitionGroup:
654 setTransitionGroup(a.getBoolean(attr, false));
655 break;
656 case R.styleable.ViewGroup_touchscreenBlocksFocus:
657 setTouchscreenBlocksFocus(a.getBoolean(attr, false));
658 break;
662 a.recycle();
666 * Gets the descendant focusability of this view group. The descendant
667 * focusability defines the relationship between this view group and its
668 * descendants when looking for a view to take focus in
669 * {@link #requestFocus(int, android.graphics.Rect)}.
671 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
672 * {@link #FOCUS_BLOCK_DESCENDANTS}.
674 @ViewDebug.ExportedProperty(category = "focus", mapping = {
675 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
676 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
677 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
679 public int getDescendantFocusability() {
680 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
684 * Set the descendant focusability of this view group. This defines the relationship
685 * between this view group and its descendants when looking for a view to
686 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
688 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
689 * {@link #FOCUS_BLOCK_DESCENDANTS}.
691 public void setDescendantFocusability(int focusability) {
692 switch (focusability) {
693 case FOCUS_BEFORE_DESCENDANTS:
694 case FOCUS_AFTER_DESCENDANTS:
695 case FOCUS_BLOCK_DESCENDANTS:
696 break;
697 default:
698 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
699 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
701 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
702 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
706 * {@inheritDoc}
708 @Override
709 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
710 if (mFocused != null) {
711 mFocused.unFocus(this);
712 mFocused = null;
714 super.handleFocusGainInternal(direction, previouslyFocusedRect);
718 * {@inheritDoc}
720 public void requestChildFocus(View child, View focused) {
721 if (DBG) {
722 System.out.println(this + " requestChildFocus()");
724 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
725 return;
728 // Unfocus us, if necessary
729 super.unFocus(focused);
731 // We had a previous notion of who had focus. Clear it.
732 if (mFocused != child) {
733 if (mFocused != null) {
734 mFocused.unFocus(focused);
737 mFocused = child;
739 if (mParent != null) {
740 mParent.requestChildFocus(this, focused);
745 * {@inheritDoc}
747 public void focusableViewAvailable(View v) {
748 if (mParent != null
749 // shortcut: don't report a new focusable view if we block our descendants from
750 // getting focus
751 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
752 && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
753 // shortcut: don't report a new focusable view if we already are focused
754 // (and we don't prefer our descendants)
756 // note: knowing that mFocused is non-null is not a good enough reason
757 // to break the traversal since in that case we'd actually have to find
758 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
759 // an ancestor of v; this will get checked for at ViewAncestor
760 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
761 mParent.focusableViewAvailable(v);
766 * {@inheritDoc}
768 public boolean showContextMenuForChild(View originalView) {
769 return mParent != null && mParent.showContextMenuForChild(originalView);
773 * {@inheritDoc}
775 @Override
776 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
777 if ((mGroupFlags & FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED) == 0) {
778 // This is the original call.
779 try {
780 mGroupFlags |= FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED;
781 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
782 } finally {
783 mGroupFlags &= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED;
785 } else {
786 // We are being called from the new method with type.
787 return SENTINEL_ACTION_MODE;
792 * {@inheritDoc}
794 @Override
795 public ActionMode startActionModeForChild(
796 View originalView, ActionMode.Callback callback, int type) {
797 if ((mGroupFlags & FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED) == 0
798 && type == ActionMode.TYPE_PRIMARY) {
799 ActionMode mode;
800 try {
801 mGroupFlags |= FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED;
802 mode = startActionModeForChild(originalView, callback);
803 } finally {
804 mGroupFlags &= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED;
806 if (mode != SENTINEL_ACTION_MODE) {
807 return mode;
810 if (mParent != null) {
811 try {
812 return mParent.startActionModeForChild(originalView, callback, type);
813 } catch (AbstractMethodError ame) {
814 // Custom view parents might not implement this method.
815 return mParent.startActionModeForChild(originalView, callback);
818 return null;
822 * @hide
824 @Override
825 public boolean dispatchActivityResult(
826 String who, int requestCode, int resultCode, Intent data) {
827 if (super.dispatchActivityResult(who, requestCode, resultCode, data)) {
828 return true;
830 int childCount = getChildCount();
831 for (int i = 0; i < childCount; i++) {
832 View child = getChildAt(i);
833 if (child.dispatchActivityResult(who, requestCode, resultCode, data)) {
834 return true;
837 return false;
841 * Find the nearest view in the specified direction that wants to take
842 * focus.
844 * @param focused The view that currently has focus
845 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
846 * FOCUS_RIGHT, or 0 for not applicable.
848 public View focusSearch(View focused, int direction) {
849 if (isRootNamespace()) {
850 // root namespace means we should consider ourselves the top of the
851 // tree for focus searching; otherwise we could be focus searching
852 // into other tabs. see LocalActivityManager and TabHost for more info
853 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
854 } else if (mParent != null) {
855 return mParent.focusSearch(focused, direction);
857 return null;
861 * {@inheritDoc}
863 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
864 return false;
868 * {@inheritDoc}
870 @Override
871 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
872 ViewParent parent = mParent;
873 if (parent == null) {
874 return false;
876 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
877 if (!propagate) {
878 return false;
880 return parent.requestSendAccessibilityEvent(this, event);
884 * Called when a child has requested sending an {@link AccessibilityEvent} and
885 * gives an opportunity to its parent to augment the event.
886 * <p>
887 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
888 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
889 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
890 * is responsible for handling this call.
891 * </p>
893 * @param child The child which requests sending the event.
894 * @param event The event to be sent.
895 * @return True if the event should be sent.
897 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
899 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
900 if (mAccessibilityDelegate != null) {
901 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
902 } else {
903 return onRequestSendAccessibilityEventInternal(child, event);
908 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
910 * Note: Called from the default {@link View.AccessibilityDelegate}.
912 * @hide
914 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
915 return true;
919 * Called when a child view has changed whether or not it is tracking transient state.
921 public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
922 final boolean oldHasTransientState = hasTransientState();
923 if (childHasTransientState) {
924 mChildCountWithTransientState++;
925 } else {
926 mChildCountWithTransientState--;
929 final boolean newHasTransientState = hasTransientState();
930 if (mParent != null && oldHasTransientState != newHasTransientState) {
931 try {
932 mParent.childHasTransientStateChanged(this, newHasTransientState);
933 } catch (AbstractMethodError e) {
934 Log.e(TAG, mParent.getClass().getSimpleName() +
935 " does not fully implement ViewParent", e);
940 @Override
941 public boolean hasTransientState() {
942 return mChildCountWithTransientState > 0 || super.hasTransientState();
946 * {@inheritDoc}
948 @Override
949 public boolean dispatchUnhandledMove(View focused, int direction) {
950 return mFocused != null &&
951 mFocused.dispatchUnhandledMove(focused, direction);
955 * {@inheritDoc}
957 public void clearChildFocus(View child) {
958 if (DBG) {
959 System.out.println(this + " clearChildFocus()");
962 mFocused = null;
963 if (mParent != null) {
964 mParent.clearChildFocus(this);
969 * {@inheritDoc}
971 @Override
972 public void clearFocus() {
973 if (DBG) {
974 System.out.println(this + " clearFocus()");
976 if (mFocused == null) {
977 super.clearFocus();
978 } else {
979 View focused = mFocused;
980 mFocused = null;
981 focused.clearFocus();
986 * {@inheritDoc}
988 @Override
989 void unFocus(View focused) {
990 if (DBG) {
991 System.out.println(this + " unFocus()");
993 if (mFocused == null) {
994 super.unFocus(focused);
995 } else {
996 mFocused.unFocus(focused);
997 mFocused = null;
1002 * Returns the focused child of this view, if any. The child may have focus
1003 * or contain focus.
1005 * @return the focused child or null.
1007 public View getFocusedChild() {
1008 return mFocused;
1011 View getDeepestFocusedChild() {
1012 View v = this;
1013 while (v != null) {
1014 if (v.isFocused()) {
1015 return v;
1017 v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
1019 return null;
1023 * Returns true if this view has or contains focus
1025 * @return true if this view has or contains focus
1027 @Override
1028 public boolean hasFocus() {
1029 return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
1033 * (non-Javadoc)
1035 * @see android.view.View#findFocus()
1037 @Override
1038 public View findFocus() {
1039 if (DBG) {
1040 System.out.println("Find focus in " + this + ": flags="
1041 + isFocused() + ", child=" + mFocused);
1044 if (isFocused()) {
1045 return this;
1048 if (mFocused != null) {
1049 return mFocused.findFocus();
1051 return null;
1055 * {@inheritDoc}
1057 @Override
1058 public boolean hasFocusable() {
1059 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
1060 return false;
1063 if (isFocusable()) {
1064 return true;
1067 final int descendantFocusability = getDescendantFocusability();
1068 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1069 final int count = mChildrenCount;
1070 final View[] children = mChildren;
1072 for (int i = 0; i < count; i++) {
1073 final View child = children[i];
1074 if (child.hasFocusable()) {
1075 return true;
1080 return false;
1084 * {@inheritDoc}
1086 @Override
1087 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1088 final int focusableCount = views.size();
1090 final int descendantFocusability = getDescendantFocusability();
1092 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1093 if (shouldBlockFocusForTouchscreen()) {
1094 focusableMode |= FOCUSABLES_TOUCH_MODE;
1097 final int count = mChildrenCount;
1098 final View[] children = mChildren;
1100 for (int i = 0; i < count; i++) {
1101 final View child = children[i];
1102 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1103 child.addFocusables(views, direction, focusableMode);
1108 // we add ourselves (if focusable) in all cases except for when we are
1109 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
1110 // to avoid the focus search finding layouts when a more precise search
1111 // among the focusable children would be more interesting.
1112 if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS
1113 // No focusable descendants
1114 || (focusableCount == views.size())) &&
1115 (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) {
1116 super.addFocusables(views, direction, focusableMode);
1121 * Set whether this ViewGroup should ignore focus requests for itself and its children.
1122 * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
1123 * will proceed forward.
1125 * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
1127 public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
1128 if (touchscreenBlocksFocus) {
1129 mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1130 if (hasFocus()) {
1131 final View focusedChild = getDeepestFocusedChild();
1132 if (!focusedChild.isFocusableInTouchMode()) {
1133 final View newFocus = focusSearch(FOCUS_FORWARD);
1134 if (newFocus != null) {
1135 newFocus.requestFocus();
1139 } else {
1140 mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1145 * Check whether this ViewGroup should ignore focus requests for itself and its children.
1147 public boolean getTouchscreenBlocksFocus() {
1148 return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
1151 boolean shouldBlockFocusForTouchscreen() {
1152 return getTouchscreenBlocksFocus() &&
1153 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
1156 @Override
1157 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
1158 super.findViewsWithText(outViews, text, flags);
1159 final int childrenCount = mChildrenCount;
1160 final View[] children = mChildren;
1161 for (int i = 0; i < childrenCount; i++) {
1162 View child = children[i];
1163 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1164 && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
1165 child.findViewsWithText(outViews, text, flags);
1170 /** @hide */
1171 @Override
1172 public View findViewByAccessibilityIdTraversal(int accessibilityId) {
1173 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
1174 if (foundView != null) {
1175 return foundView;
1178 if (getAccessibilityNodeProvider() != null) {
1179 return null;
1182 final int childrenCount = mChildrenCount;
1183 final View[] children = mChildren;
1184 for (int i = 0; i < childrenCount; i++) {
1185 View child = children[i];
1186 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
1187 if (foundView != null) {
1188 return foundView;
1192 return null;
1196 * {@inheritDoc}
1198 @Override
1199 public void dispatchWindowFocusChanged(boolean hasFocus) {
1200 super.dispatchWindowFocusChanged(hasFocus);
1201 final int count = mChildrenCount;
1202 final View[] children = mChildren;
1203 for (int i = 0; i < count; i++) {
1204 children[i].dispatchWindowFocusChanged(hasFocus);
1209 * {@inheritDoc}
1211 @Override
1212 public void addTouchables(ArrayList<View> views) {
1213 super.addTouchables(views);
1215 final int count = mChildrenCount;
1216 final View[] children = mChildren;
1218 for (int i = 0; i < count; i++) {
1219 final View child = children[i];
1220 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1221 child.addTouchables(views);
1227 * @hide
1229 @Override
1230 public void makeOptionalFitsSystemWindows() {
1231 super.makeOptionalFitsSystemWindows();
1232 final int count = mChildrenCount;
1233 final View[] children = mChildren;
1234 for (int i = 0; i < count; i++) {
1235 children[i].makeOptionalFitsSystemWindows();
1240 * {@inheritDoc}
1242 @Override
1243 public void dispatchDisplayHint(int hint) {
1244 super.dispatchDisplayHint(hint);
1245 final int count = mChildrenCount;
1246 final View[] children = mChildren;
1247 for (int i = 0; i < count; i++) {
1248 children[i].dispatchDisplayHint(hint);
1253 * Called when a view's visibility has changed. Notify the parent to take any appropriate
1254 * action.
1256 * @param child The view whose visibility has changed
1257 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
1258 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
1259 * @hide
1261 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1262 if (mTransition != null) {
1263 if (newVisibility == VISIBLE) {
1264 mTransition.showChild(this, child, oldVisibility);
1265 } else {
1266 mTransition.hideChild(this, child, newVisibility);
1267 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
1268 // Only track this on disappearing views - appearing views are already visible
1269 // and don't need special handling during drawChild()
1270 if (mVisibilityChangingChildren == null) {
1271 mVisibilityChangingChildren = new ArrayList<View>();
1273 mVisibilityChangingChildren.add(child);
1274 addDisappearingView(child);
1279 // in all cases, for drags
1280 if (mCurrentDrag != null) {
1281 if (newVisibility == VISIBLE) {
1282 notifyChildOfDrag(child);
1288 * {@inheritDoc}
1290 @Override
1291 protected void dispatchVisibilityChanged(View changedView, int visibility) {
1292 super.dispatchVisibilityChanged(changedView, visibility);
1293 final int count = mChildrenCount;
1294 final View[] children = mChildren;
1295 for (int i = 0; i < count; i++) {
1296 children[i].dispatchVisibilityChanged(changedView, visibility);
1301 * {@inheritDoc}
1303 @Override
1304 public void dispatchWindowVisibilityChanged(int visibility) {
1305 super.dispatchWindowVisibilityChanged(visibility);
1306 final int count = mChildrenCount;
1307 final View[] children = mChildren;
1308 for (int i = 0; i < count; i++) {
1309 children[i].dispatchWindowVisibilityChanged(visibility);
1314 * {@inheritDoc}
1316 @Override
1317 public void dispatchConfigurationChanged(Configuration newConfig) {
1318 super.dispatchConfigurationChanged(newConfig);
1319 final int count = mChildrenCount;
1320 final View[] children = mChildren;
1321 for (int i = 0; i < count; i++) {
1322 children[i].dispatchConfigurationChanged(newConfig);
1327 * {@inheritDoc}
1329 public void recomputeViewAttributes(View child) {
1330 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1331 ViewParent parent = mParent;
1332 if (parent != null) parent.recomputeViewAttributes(this);
1336 @Override
1337 void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
1338 if ((visibility & VISIBILITY_MASK) == VISIBLE) {
1339 super.dispatchCollectViewAttributes(attachInfo, visibility);
1340 final int count = mChildrenCount;
1341 final View[] children = mChildren;
1342 for (int i = 0; i < count; i++) {
1343 final View child = children[i];
1344 child.dispatchCollectViewAttributes(attachInfo,
1345 visibility | (child.mViewFlags&VISIBILITY_MASK));
1351 * {@inheritDoc}
1353 public void bringChildToFront(View child) {
1354 final int index = indexOfChild(child);
1355 if (index >= 0) {
1356 removeFromArray(index);
1357 addInArray(child, mChildrenCount);
1358 child.mParent = this;
1359 requestLayout();
1360 invalidate();
1364 private PointF getLocalPoint() {
1365 if (mLocalPoint == null) mLocalPoint = new PointF();
1366 return mLocalPoint;
1370 * {@inheritDoc}
1372 // TODO: Write real docs
1373 @Override
1374 public boolean dispatchDragEvent(DragEvent event) {
1375 boolean retval = false;
1376 final float tx = event.mX;
1377 final float ty = event.mY;
1379 ViewRootImpl root = getViewRootImpl();
1381 // Dispatch down the view hierarchy
1382 final PointF localPoint = getLocalPoint();
1384 switch (event.mAction) {
1385 case DragEvent.ACTION_DRAG_STARTED: {
1386 // clear state to recalculate which views we drag over
1387 mCurrentDragView = null;
1389 // Set up our tracking of drag-started notifications
1390 mCurrentDrag = DragEvent.obtain(event);
1391 if (mDragNotifiedChildren == null) {
1392 mDragNotifiedChildren = new HashSet<View>();
1393 } else {
1394 mDragNotifiedChildren.clear();
1397 // Now dispatch down to our children, caching the responses
1398 mChildAcceptsDrag = false;
1399 final int count = mChildrenCount;
1400 final View[] children = mChildren;
1401 for (int i = 0; i < count; i++) {
1402 final View child = children[i];
1403 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1404 if (child.getVisibility() == VISIBLE) {
1405 final boolean handled = notifyChildOfDrag(children[i]);
1406 if (handled) {
1407 mChildAcceptsDrag = true;
1412 // Return HANDLED if one of our children can accept the drag
1413 if (mChildAcceptsDrag) {
1414 retval = true;
1416 } break;
1418 case DragEvent.ACTION_DRAG_ENDED: {
1419 // Release the bookkeeping now that the drag lifecycle has ended
1420 if (mDragNotifiedChildren != null) {
1421 for (View child : mDragNotifiedChildren) {
1422 // If a child was notified about an ongoing drag, it's told that it's over
1423 child.dispatchDragEvent(event);
1424 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1425 child.refreshDrawableState();
1428 mDragNotifiedChildren.clear();
1429 if (mCurrentDrag != null) {
1430 mCurrentDrag.recycle();
1431 mCurrentDrag = null;
1435 // We consider drag-ended to have been handled if one of our children
1436 // had offered to handle the drag.
1437 if (mChildAcceptsDrag) {
1438 retval = true;
1440 } break;
1442 case DragEvent.ACTION_DRAG_LOCATION: {
1443 // Find the [possibly new] drag target
1444 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1446 // If we've changed apparent drag target, tell the view root which view
1447 // we're over now [for purposes of the eventual drag-recipient-changed
1448 // notifications to the framework] and tell the new target that the drag
1449 // has entered its bounds. The root will see setDragFocus() calls all
1450 // the way down to the final leaf view that is handling the LOCATION event
1451 // before reporting the new potential recipient to the framework.
1452 if (mCurrentDragView != target) {
1453 root.setDragFocus(target);
1455 final int action = event.mAction;
1456 // If we've dragged off of a child view, send it the EXITED message
1457 if (mCurrentDragView != null) {
1458 final View view = mCurrentDragView;
1459 event.mAction = DragEvent.ACTION_DRAG_EXITED;
1460 view.dispatchDragEvent(event);
1461 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
1462 view.refreshDrawableState();
1464 mCurrentDragView = target;
1466 // If we've dragged over a new child view, send it the ENTERED message
1467 if (target != null) {
1468 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1469 target.dispatchDragEvent(event);
1470 target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
1471 target.refreshDrawableState();
1473 event.mAction = action; // restore the event's original state
1476 // Dispatch the actual drag location notice, localized into its coordinates
1477 if (target != null) {
1478 event.mX = localPoint.x;
1479 event.mY = localPoint.y;
1481 retval = target.dispatchDragEvent(event);
1483 event.mX = tx;
1484 event.mY = ty;
1486 } break;
1488 /* Entered / exited dispatch
1490 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1491 * that we're about to get the corresponding LOCATION event, which we will use to
1492 * determine which of our children is the new target; at that point we will
1493 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1495 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1496 * drag has left this ViewGroup, we know by definition that every contained subview
1497 * is also no longer under the drag point.
1500 case DragEvent.ACTION_DRAG_EXITED: {
1501 if (mCurrentDragView != null) {
1502 final View view = mCurrentDragView;
1503 view.dispatchDragEvent(event);
1504 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
1505 view.refreshDrawableState();
1507 mCurrentDragView = null;
1509 } break;
1511 case DragEvent.ACTION_DROP: {
1512 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
1513 View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1514 if (target != null) {
1515 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
1516 event.mX = localPoint.x;
1517 event.mY = localPoint.y;
1518 retval = target.dispatchDragEvent(event);
1519 event.mX = tx;
1520 event.mY = ty;
1521 } else {
1522 if (ViewDebug.DEBUG_DRAG) {
1523 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1526 } break;
1529 // If none of our children could handle the event, try here
1530 if (!retval) {
1531 // Call up to the View implementation that dispatches to installed listeners
1532 retval = super.dispatchDragEvent(event);
1534 return retval;
1537 // Find the frontmost child view that lies under the given point, and calculate
1538 // the position within its own local coordinate system.
1539 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
1540 final int count = mChildrenCount;
1541 final View[] children = mChildren;
1542 for (int i = count - 1; i >= 0; i--) {
1543 final View child = children[i];
1544 if (!child.canAcceptDrag()) {
1545 continue;
1548 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1549 return child;
1552 return null;
1555 boolean notifyChildOfDrag(View child) {
1556 if (ViewDebug.DEBUG_DRAG) {
1557 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1560 boolean canAccept = false;
1561 if (! mDragNotifiedChildren.contains(child)) {
1562 mDragNotifiedChildren.add(child);
1563 canAccept = child.dispatchDragEvent(mCurrentDrag);
1564 if (canAccept && !child.canAcceptDrag()) {
1565 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
1566 child.refreshDrawableState();
1569 return canAccept;
1572 @Override
1573 public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1574 super.dispatchWindowSystemUiVisiblityChanged(visible);
1576 final int count = mChildrenCount;
1577 final View[] children = mChildren;
1578 for (int i=0; i <count; i++) {
1579 final View child = children[i];
1580 child.dispatchWindowSystemUiVisiblityChanged(visible);
1584 @Override
1585 public void dispatchSystemUiVisibilityChanged(int visible) {
1586 super.dispatchSystemUiVisibilityChanged(visible);
1588 final int count = mChildrenCount;
1589 final View[] children = mChildren;
1590 for (int i=0; i <count; i++) {
1591 final View child = children[i];
1592 child.dispatchSystemUiVisibilityChanged(visible);
1596 @Override
1597 boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1598 boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
1600 final int count = mChildrenCount;
1601 final View[] children = mChildren;
1602 for (int i=0; i <count; i++) {
1603 final View child = children[i];
1604 changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
1606 return changed;
1610 * {@inheritDoc}
1612 @Override
1613 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1614 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1615 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1616 return super.dispatchKeyEventPreIme(event);
1617 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1618 == PFLAG_HAS_BOUNDS) {
1619 return mFocused.dispatchKeyEventPreIme(event);
1621 return false;
1625 * {@inheritDoc}
1627 @Override
1628 public boolean dispatchKeyEvent(KeyEvent event) {
1629 if (mInputEventConsistencyVerifier != null) {
1630 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1633 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1634 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1635 if (super.dispatchKeyEvent(event)) {
1636 return true;
1638 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1639 == PFLAG_HAS_BOUNDS) {
1640 if (mFocused.dispatchKeyEvent(event)) {
1641 return true;
1645 if (mInputEventConsistencyVerifier != null) {
1646 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1648 return false;
1652 * {@inheritDoc}
1654 @Override
1655 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1656 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1657 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1658 return super.dispatchKeyShortcutEvent(event);
1659 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1660 == PFLAG_HAS_BOUNDS) {
1661 return mFocused.dispatchKeyShortcutEvent(event);
1663 return false;
1667 * {@inheritDoc}
1669 @Override
1670 public boolean dispatchTrackballEvent(MotionEvent event) {
1671 if (mInputEventConsistencyVerifier != null) {
1672 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1675 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1676 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1677 if (super.dispatchTrackballEvent(event)) {
1678 return true;
1680 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1681 == PFLAG_HAS_BOUNDS) {
1682 if (mFocused.dispatchTrackballEvent(event)) {
1683 return true;
1687 if (mInputEventConsistencyVerifier != null) {
1688 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1690 return false;
1694 * {@inheritDoc}
1696 @SuppressWarnings({"ConstantConditions"})
1697 @Override
1698 protected boolean dispatchHoverEvent(MotionEvent event) {
1699 final int action = event.getAction();
1701 // First check whether the view group wants to intercept the hover event.
1702 final boolean interceptHover = onInterceptHoverEvent(event);
1703 event.setAction(action); // restore action in case it was changed
1705 MotionEvent eventNoHistory = event;
1706 boolean handled = false;
1708 // Send events to the hovered children and build a new list of hover targets until
1709 // one is found that handles the event.
1710 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1711 mFirstHoverTarget = null;
1712 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
1713 final float x = event.getX();
1714 final float y = event.getY();
1715 final int childrenCount = mChildrenCount;
1716 if (childrenCount != 0) {
1717 final ArrayList<View> preorderedList = buildOrderedChildList();
1718 final boolean customOrder = preorderedList == null
1719 && isChildrenDrawingOrderEnabled();
1720 final View[] children = mChildren;
1721 HoverTarget lastHoverTarget = null;
1722 for (int i = childrenCount - 1; i >= 0; i--) {
1723 int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
1724 final View child = (preorderedList == null)
1725 ? children[childIndex] : preorderedList.get(childIndex);
1726 if (!canViewReceivePointerEvents(child)
1727 || !isTransformedTouchPointInView(x, y, child, null)) {
1728 continue;
1731 // Obtain a hover target for this child. Dequeue it from the
1732 // old hover target list if the child was previously hovered.
1733 HoverTarget hoverTarget = firstOldHoverTarget;
1734 final boolean wasHovered;
1735 for (HoverTarget predecessor = null; ;) {
1736 if (hoverTarget == null) {
1737 hoverTarget = HoverTarget.obtain(child);
1738 wasHovered = false;
1739 break;
1742 if (hoverTarget.child == child) {
1743 if (predecessor != null) {
1744 predecessor.next = hoverTarget.next;
1745 } else {
1746 firstOldHoverTarget = hoverTarget.next;
1748 hoverTarget.next = null;
1749 wasHovered = true;
1750 break;
1753 predecessor = hoverTarget;
1754 hoverTarget = hoverTarget.next;
1757 // Enqueue the hover target onto the new hover target list.
1758 if (lastHoverTarget != null) {
1759 lastHoverTarget.next = hoverTarget;
1760 } else {
1761 mFirstHoverTarget = hoverTarget;
1763 lastHoverTarget = hoverTarget;
1765 // Dispatch the event to the child.
1766 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1767 if (!wasHovered) {
1768 // Send the enter as is.
1769 handled |= dispatchTransformedGenericPointerEvent(
1770 event, child); // enter
1772 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1773 if (!wasHovered) {
1774 // Synthesize an enter from a move.
1775 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1776 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1777 handled |= dispatchTransformedGenericPointerEvent(
1778 eventNoHistory, child); // enter
1779 eventNoHistory.setAction(action);
1781 handled |= dispatchTransformedGenericPointerEvent(
1782 eventNoHistory, child); // move
1783 } else {
1784 // Send the move as is.
1785 handled |= dispatchTransformedGenericPointerEvent(event, child);
1788 if (handled) {
1789 break;
1792 if (preorderedList != null) preorderedList.clear();
1796 // Send exit events to all previously hovered children that are no longer hovered.
1797 while (firstOldHoverTarget != null) {
1798 final View child = firstOldHoverTarget.child;
1800 // Exit the old hovered child.
1801 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1802 // Send the exit as is.
1803 handled |= dispatchTransformedGenericPointerEvent(
1804 event, child); // exit
1805 } else {
1806 // Synthesize an exit from a move or enter.
1807 // Ignore the result because hover focus has moved to a different view.
1808 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1809 dispatchTransformedGenericPointerEvent(
1810 event, child); // move
1812 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1813 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1814 dispatchTransformedGenericPointerEvent(
1815 eventNoHistory, child); // exit
1816 eventNoHistory.setAction(action);
1819 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1820 firstOldHoverTarget.recycle();
1821 firstOldHoverTarget = nextOldHoverTarget;
1824 // Send events to the view group itself if no children have handled it.
1825 boolean newHoveredSelf = !handled;
1826 if (newHoveredSelf == mHoveredSelf) {
1827 if (newHoveredSelf) {
1828 // Send event to the view group as before.
1829 handled |= super.dispatchHoverEvent(event);
1831 } else {
1832 if (mHoveredSelf) {
1833 // Exit the view group.
1834 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1835 // Send the exit as is.
1836 handled |= super.dispatchHoverEvent(event); // exit
1837 } else {
1838 // Synthesize an exit from a move or enter.
1839 // Ignore the result because hover focus is moving to a different view.
1840 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1841 super.dispatchHoverEvent(event); // move
1843 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1844 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1845 super.dispatchHoverEvent(eventNoHistory); // exit
1846 eventNoHistory.setAction(action);
1848 mHoveredSelf = false;
1851 if (newHoveredSelf) {
1852 // Enter the view group.
1853 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1854 // Send the enter as is.
1855 handled |= super.dispatchHoverEvent(event); // enter
1856 mHoveredSelf = true;
1857 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1858 // Synthesize an enter from a move.
1859 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1860 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1861 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1862 eventNoHistory.setAction(action);
1864 handled |= super.dispatchHoverEvent(eventNoHistory); // move
1865 mHoveredSelf = true;
1870 // Recycle the copy of the event that we made.
1871 if (eventNoHistory != event) {
1872 eventNoHistory.recycle();
1875 // Done.
1876 return handled;
1879 private void exitHoverTargets() {
1880 if (mHoveredSelf || mFirstHoverTarget != null) {
1881 final long now = SystemClock.uptimeMillis();
1882 MotionEvent event = MotionEvent.obtain(now, now,
1883 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1884 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1885 dispatchHoverEvent(event);
1886 event.recycle();
1890 private void cancelHoverTarget(View view) {
1891 HoverTarget predecessor = null;
1892 HoverTarget target = mFirstHoverTarget;
1893 while (target != null) {
1894 final HoverTarget next = target.next;
1895 if (target.child == view) {
1896 if (predecessor == null) {
1897 mFirstHoverTarget = next;
1898 } else {
1899 predecessor.next = next;
1901 target.recycle();
1903 final long now = SystemClock.uptimeMillis();
1904 MotionEvent event = MotionEvent.obtain(now, now,
1905 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1906 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1907 view.dispatchHoverEvent(event);
1908 event.recycle();
1909 return;
1911 predecessor = target;
1912 target = next;
1916 /** @hide */
1917 @Override
1918 protected boolean hasHoveredChild() {
1919 return mFirstHoverTarget != null;
1922 @Override
1923 public void addChildrenForAccessibility(ArrayList<View> outChildren) {
1924 if (getAccessibilityNodeProvider() != null) {
1925 return;
1927 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
1928 try {
1929 final int childrenCount = children.getChildCount();
1930 for (int i = 0; i < childrenCount; i++) {
1931 View child = children.getChildAt(i);
1932 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1933 if (child.includeForAccessibility()) {
1934 outChildren.add(child);
1935 } else {
1936 child.addChildrenForAccessibility(outChildren);
1940 } finally {
1941 children.recycle();
1946 * Implement this method to intercept hover events before they are handled
1947 * by child views.
1948 * <p>
1949 * This method is called before dispatching a hover event to a child of
1950 * the view group or to the view group's own {@link #onHoverEvent} to allow
1951 * the view group a chance to intercept the hover event.
1952 * This method can also be used to watch all pointer motions that occur within
1953 * the bounds of the view group even when the pointer is hovering over
1954 * a child of the view group rather than over the view group itself.
1955 * </p><p>
1956 * The view group can prevent its children from receiving hover events by
1957 * implementing this method and returning <code>true</code> to indicate
1958 * that it would like to intercept hover events. The view group must
1959 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1960 * for as long as it wishes to continue intercepting hover events from
1961 * its children.
1962 * </p><p>
1963 * Interception preserves the invariant that at most one view can be
1964 * hovered at a time by transferring hover focus from the currently hovered
1965 * child to the view group or vice-versa as needed.
1966 * </p><p>
1967 * If this method returns <code>true</code> and a child is already hovered, then the
1968 * child view will first receive a hover exit event and then the view group
1969 * itself will receive a hover enter event in {@link #onHoverEvent}.
1970 * Likewise, if this method had previously returned <code>true</code> to intercept hover
1971 * events and instead returns <code>false</code> while the pointer is hovering
1972 * within the bounds of one of a child, then the view group will first receive a
1973 * hover exit event in {@link #onHoverEvent} and then the hovered child will
1974 * receive a hover enter event.
1975 * </p><p>
1976 * The default implementation always returns false.
1977 * </p>
1979 * @param event The motion event that describes the hover.
1980 * @return True if the view group would like to intercept the hover event
1981 * and prevent its children from receiving it.
1983 public boolean onInterceptHoverEvent(MotionEvent event) {
1984 return false;
1987 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1988 if (event.getHistorySize() == 0) {
1989 return event;
1991 return MotionEvent.obtainNoHistory(event);
1995 * {@inheritDoc}
1997 @Override
1998 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1999 // Send the event to the child under the pointer.
2000 final int childrenCount = mChildrenCount;
2001 if (childrenCount != 0) {
2002 final float x = event.getX();
2003 final float y = event.getY();
2005 final ArrayList<View> preorderedList = buildOrderedChildList();
2006 final boolean customOrder = preorderedList == null
2007 && isChildrenDrawingOrderEnabled();
2008 final View[] children = mChildren;
2009 for (int i = childrenCount - 1; i >= 0; i--) {
2010 int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
2011 final View child = (preorderedList == null)
2012 ? children[childIndex] : preorderedList.get(childIndex);
2013 if (!canViewReceivePointerEvents(child)
2014 || !isTransformedTouchPointInView(x, y, child, null)) {
2015 continue;
2018 if (dispatchTransformedGenericPointerEvent(event, child)) {
2019 if (preorderedList != null) preorderedList.clear();
2020 return true;
2023 if (preorderedList != null) preorderedList.clear();
2026 // No child handled the event. Send it to this view group.
2027 return super.dispatchGenericPointerEvent(event);
2031 * {@inheritDoc}
2033 @Override
2034 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
2035 // Send the event to the focused child or to this view group if it has focus.
2036 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
2037 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
2038 return super.dispatchGenericFocusedEvent(event);
2039 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
2040 == PFLAG_HAS_BOUNDS) {
2041 return mFocused.dispatchGenericMotionEvent(event);
2043 return false;
2047 * Dispatches a generic pointer event to a child, taking into account
2048 * transformations that apply to the child.
2050 * @param event The event to send.
2051 * @param child The view to send the event to.
2052 * @return {@code true} if the child handled the event.
2054 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
2055 final float offsetX = mScrollX - child.mLeft;
2056 final float offsetY = mScrollY - child.mTop;
2058 boolean handled;
2059 if (!child.hasIdentityMatrix()) {
2060 MotionEvent transformedEvent = MotionEvent.obtain(event);
2061 transformedEvent.offsetLocation(offsetX, offsetY);
2062 transformedEvent.transform(child.getInverseMatrix());
2063 handled = child.dispatchGenericMotionEvent(transformedEvent);
2064 transformedEvent.recycle();
2065 } else {
2066 event.offsetLocation(offsetX, offsetY);
2067 handled = child.dispatchGenericMotionEvent(event);
2068 event.offsetLocation(-offsetX, -offsetY);
2070 return handled;
2074 * {@inheritDoc}
2076 @Override
2077 public boolean dispatchTouchEvent(MotionEvent ev) {
2078 if (mInputEventConsistencyVerifier != null) {
2079 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
2082 // If the event targets the accessibility focused view and this is it, start
2083 // normal event dispatch. Maybe a descendant is what will handle the click.
2084 if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
2085 ev.setTargetAccessibilityFocus(false);
2088 boolean handled = false;
2089 if (onFilterTouchEventForSecurity(ev)) {
2090 final int action = ev.getAction();
2091 final int actionMasked = action & MotionEvent.ACTION_MASK;
2093 // Handle an initial down.
2094 if (actionMasked == MotionEvent.ACTION_DOWN) {
2095 // Throw away all previous state when starting a new touch gesture.
2096 // The framework may have dropped the up or cancel event for the previous gesture
2097 // due to an app switch, ANR, or some other state change.
2098 cancelAndClearTouchTargets(ev);
2099 resetTouchState();
2102 // Check for interception.
2103 final boolean intercepted;
2104 if (actionMasked == MotionEvent.ACTION_DOWN
2105 || mFirstTouchTarget != null) {
2106 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
2107 if (!disallowIntercept) {
2108 intercepted = onInterceptTouchEvent(ev);
2109 ev.setAction(action); // restore action in case it was changed
2110 } else {
2111 intercepted = false;
2113 } else {
2114 // There are no touch targets and this action is not an initial down
2115 // so this view group continues to intercept touches.
2116 intercepted = true;
2119 // If intercepted, start normal event dispatch. Also if there is already
2120 // a view that is handling the gesture, do normal event dispatch.
2121 if (intercepted || mFirstTouchTarget != null) {
2122 ev.setTargetAccessibilityFocus(false);
2125 // Check for cancelation.
2126 final boolean canceled = resetCancelNextUpFlag(this)
2127 || actionMasked == MotionEvent.ACTION_CANCEL;
2129 // Update list of touch targets for pointer down, if needed.
2130 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
2131 TouchTarget newTouchTarget = null;
2132 boolean alreadyDispatchedToNewTouchTarget = false;
2133 if (!canceled && !intercepted) {
2135 // If the event is targeting accessiiblity focus we give it to the
2136 // view that has accessibility focus and if it does not handle it
2137 // we clear the flag and dispatch the event to all children as usual.
2138 // We are looking up the accessibility focused host to avoid keeping
2139 // state since these events are very rare.
2140 View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
2141 ? findChildWithAccessibilityFocus() : null;
2143 if (actionMasked == MotionEvent.ACTION_DOWN
2144 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
2145 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2146 final int actionIndex = ev.getActionIndex(); // always 0 for down
2147 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
2148 : TouchTarget.ALL_POINTER_IDS;
2150 // Clean up earlier touch targets for this pointer id in case they
2151 // have become out of sync.
2152 removePointersFromTouchTargets(idBitsToAssign);
2154 final int childrenCount = mChildrenCount;
2155 if (newTouchTarget == null && childrenCount != 0) {
2156 final float x = ev.getX(actionIndex);
2157 final float y = ev.getY(actionIndex);
2158 // Find a child that can receive the event.
2159 // Scan children from front to back.
2160 final ArrayList<View> preorderedList = buildOrderedChildList();
2161 final boolean customOrder = preorderedList == null
2162 && isChildrenDrawingOrderEnabled();
2163 final View[] children = mChildren;
2164 for (int i = childrenCount - 1; i >= 0; i--) {
2165 final int childIndex = customOrder
2166 ? getChildDrawingOrder(childrenCount, i) : i;
2167 final View child = (preorderedList == null)
2168 ? children[childIndex] : preorderedList.get(childIndex);
2170 // If there is a view that has accessibility focus we want it
2171 // to get the event first and if not handled we will perform a
2172 // normal dispatch. We may do a double iteration but this is
2173 // safer given the timeframe.
2174 if (childWithAccessibilityFocus != null) {
2175 if (childWithAccessibilityFocus != child) {
2176 continue;
2178 childWithAccessibilityFocus = null;
2179 i = childrenCount - 1;
2182 if (!canViewReceivePointerEvents(child)
2183 || !isTransformedTouchPointInView(x, y, child, null)) {
2184 ev.setTargetAccessibilityFocus(false);
2185 continue;
2188 newTouchTarget = getTouchTarget(child);
2189 if (newTouchTarget != null) {
2190 // Child is already receiving touch within its bounds.
2191 // Give it the new pointer in addition to the ones it is handling.
2192 newTouchTarget.pointerIdBits |= idBitsToAssign;
2193 break;
2196 resetCancelNextUpFlag(child);
2197 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
2198 // Child wants to receive touch within its bounds.
2199 mLastTouchDownTime = ev.getDownTime();
2200 if (preorderedList != null) {
2201 // childIndex points into presorted list, find original index
2202 for (int j = 0; j < childrenCount; j++) {
2203 if (children[childIndex] == mChildren[j]) {
2204 mLastTouchDownIndex = j;
2205 break;
2208 } else {
2209 mLastTouchDownIndex = childIndex;
2211 mLastTouchDownX = ev.getX();
2212 mLastTouchDownY = ev.getY();
2213 newTouchTarget = addTouchTarget(child, idBitsToAssign);
2214 alreadyDispatchedToNewTouchTarget = true;
2215 break;
2218 // The accessibility focus didn't handle the event, so clear
2219 // the flag and do a normal dispatch to all children.
2220 ev.setTargetAccessibilityFocus(false);
2222 if (preorderedList != null) preorderedList.clear();
2225 if (newTouchTarget == null && mFirstTouchTarget != null) {
2226 // Did not find a child to receive the event.
2227 // Assign the pointer to the least recently added target.
2228 newTouchTarget = mFirstTouchTarget;
2229 while (newTouchTarget.next != null) {
2230 newTouchTarget = newTouchTarget.next;
2232 newTouchTarget.pointerIdBits |= idBitsToAssign;
2237 // Dispatch to touch targets.
2238 if (mFirstTouchTarget == null) {
2239 // No touch targets so treat this as an ordinary view.
2240 handled = dispatchTransformedTouchEvent(ev, canceled, null,
2241 TouchTarget.ALL_POINTER_IDS);
2242 } else {
2243 // Dispatch to touch targets, excluding the new touch target if we already
2244 // dispatched to it. Cancel touch targets if necessary.
2245 TouchTarget predecessor = null;
2246 TouchTarget target = mFirstTouchTarget;
2247 while (target != null) {
2248 final TouchTarget next = target.next;
2249 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
2250 handled = true;
2251 } else {
2252 final boolean cancelChild = resetCancelNextUpFlag(target.child)
2253 || intercepted;
2254 if (dispatchTransformedTouchEvent(ev, cancelChild,
2255 target.child, target.pointerIdBits)) {
2256 handled = true;
2258 if (cancelChild) {
2259 if (predecessor == null) {
2260 mFirstTouchTarget = next;
2261 } else {
2262 predecessor.next = next;
2264 target.recycle();
2265 target = next;
2266 continue;
2269 predecessor = target;
2270 target = next;
2274 // Update list of touch targets for pointer up or cancel, if needed.
2275 if (canceled
2276 || actionMasked == MotionEvent.ACTION_UP
2277 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2278 resetTouchState();
2279 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
2280 final int actionIndex = ev.getActionIndex();
2281 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
2282 removePointersFromTouchTargets(idBitsToRemove);
2286 if (!handled && mInputEventConsistencyVerifier != null) {
2287 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
2289 return handled;
2293 * Finds the child which has accessibility focus.
2295 * @return The child that has focus.
2297 private View findChildWithAccessibilityFocus() {
2298 ViewRootImpl viewRoot = getViewRootImpl();
2299 if (viewRoot == null) {
2300 return null;
2303 View current = viewRoot.getAccessibilityFocusedHost();
2304 if (current == null) {
2305 return null;
2308 ViewParent parent = current.getParent();
2309 while (parent instanceof View) {
2310 if (parent == this) {
2311 return current;
2313 current = (View) parent;
2314 parent = current.getParent();
2317 return null;
2321 * Resets all touch state in preparation for a new cycle.
2323 private void resetTouchState() {
2324 clearTouchTargets();
2325 resetCancelNextUpFlag(this);
2326 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2327 mNestedScrollAxes = SCROLL_AXIS_NONE;
2331 * Resets the cancel next up flag.
2332 * Returns true if the flag was previously set.
2334 private static boolean resetCancelNextUpFlag(View view) {
2335 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
2336 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
2337 return true;
2339 return false;
2343 * Clears all touch targets.
2345 private void clearTouchTargets() {
2346 TouchTarget target = mFirstTouchTarget;
2347 if (target != null) {
2348 do {
2349 TouchTarget next = target.next;
2350 target.recycle();
2351 target = next;
2352 } while (target != null);
2353 mFirstTouchTarget = null;
2358 * Cancels and clears all touch targets.
2360 private void cancelAndClearTouchTargets(MotionEvent event) {
2361 if (mFirstTouchTarget != null) {
2362 boolean syntheticEvent = false;
2363 if (event == null) {
2364 final long now = SystemClock.uptimeMillis();
2365 event = MotionEvent.obtain(now, now,
2366 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2367 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2368 syntheticEvent = true;
2371 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2372 resetCancelNextUpFlag(target.child);
2373 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2375 clearTouchTargets();
2377 if (syntheticEvent) {
2378 event.recycle();
2384 * Gets the touch target for specified child view.
2385 * Returns null if not found.
2387 private TouchTarget getTouchTarget(View child) {
2388 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2389 if (target.child == child) {
2390 return target;
2393 return null;
2397 * Adds a touch target for specified child to the beginning of the list.
2398 * Assumes the target child is not already present.
2400 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
2401 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2402 target.next = mFirstTouchTarget;
2403 mFirstTouchTarget = target;
2404 return target;
2408 * Removes the pointer ids from consideration.
2410 private void removePointersFromTouchTargets(int pointerIdBits) {
2411 TouchTarget predecessor = null;
2412 TouchTarget target = mFirstTouchTarget;
2413 while (target != null) {
2414 final TouchTarget next = target.next;
2415 if ((target.pointerIdBits & pointerIdBits) != 0) {
2416 target.pointerIdBits &= ~pointerIdBits;
2417 if (target.pointerIdBits == 0) {
2418 if (predecessor == null) {
2419 mFirstTouchTarget = next;
2420 } else {
2421 predecessor.next = next;
2423 target.recycle();
2424 target = next;
2425 continue;
2428 predecessor = target;
2429 target = next;
2433 private void cancelTouchTarget(View view) {
2434 TouchTarget predecessor = null;
2435 TouchTarget target = mFirstTouchTarget;
2436 while (target != null) {
2437 final TouchTarget next = target.next;
2438 if (target.child == view) {
2439 if (predecessor == null) {
2440 mFirstTouchTarget = next;
2441 } else {
2442 predecessor.next = next;
2444 target.recycle();
2446 final long now = SystemClock.uptimeMillis();
2447 MotionEvent event = MotionEvent.obtain(now, now,
2448 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2449 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2450 view.dispatchTouchEvent(event);
2451 event.recycle();
2452 return;
2454 predecessor = target;
2455 target = next;
2460 * Returns true if a child view can receive pointer events.
2461 * @hide
2463 private static boolean canViewReceivePointerEvents(View child) {
2464 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2465 || child.getAnimation() != null;
2468 private float[] getTempPoint() {
2469 if (mTempPoint == null) {
2470 mTempPoint = new float[2];
2472 return mTempPoint;
2476 * Returns true if a child view contains the specified point when transformed
2477 * into its coordinate space.
2478 * Child must not be null.
2479 * @hide
2481 protected boolean isTransformedTouchPointInView(float x, float y, View child,
2482 PointF outLocalPoint) {
2483 final float[] point = getTempPoint();
2484 point[0] = x;
2485 point[1] = y;
2486 transformPointToViewLocal(point, child);
2487 final boolean isInView = child.pointInView(point[0], point[1]);
2488 if (isInView && outLocalPoint != null) {
2489 outLocalPoint.set(point[0], point[1]);
2491 return isInView;
2495 * @hide
2497 public void transformPointToViewLocal(float[] point, View child) {
2498 point[0] += mScrollX - child.mLeft;
2499 point[1] += mScrollY - child.mTop;
2501 if (!child.hasIdentityMatrix()) {
2502 child.getInverseMatrix().mapPoints(point);
2507 * Transforms a motion event into the coordinate space of a particular child view,
2508 * filters out irrelevant pointer ids, and overrides its action if necessary.
2509 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2511 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
2512 View child, int desiredPointerIdBits) {
2513 final boolean handled;
2515 // Canceling motions is a special case. We don't need to perform any transformations
2516 // or filtering. The important part is the action, not the contents.
2517 final int oldAction = event.getAction();
2518 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
2519 event.setAction(MotionEvent.ACTION_CANCEL);
2520 if (child == null) {
2521 handled = super.dispatchTouchEvent(event);
2522 } else {
2523 handled = child.dispatchTouchEvent(event);
2525 event.setAction(oldAction);
2526 return handled;
2529 // Calculate the number of pointers to deliver.
2530 final int oldPointerIdBits = event.getPointerIdBits();
2531 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
2533 // If for some reason we ended up in an inconsistent state where it looks like we
2534 // might produce a motion event with no pointers in it, then drop the event.
2535 if (newPointerIdBits == 0) {
2536 return false;
2539 // If the number of pointers is the same and we don't need to perform any fancy
2540 // irreversible transformations, then we can reuse the motion event for this
2541 // dispatch as long as we are careful to revert any changes we make.
2542 // Otherwise we need to make a copy.
2543 final MotionEvent transformedEvent;
2544 if (newPointerIdBits == oldPointerIdBits) {
2545 if (child == null || child.hasIdentityMatrix()) {
2546 if (child == null) {
2547 handled = super.dispatchTouchEvent(event);
2548 } else {
2549 final float offsetX = mScrollX - child.mLeft;
2550 final float offsetY = mScrollY - child.mTop;
2551 event.offsetLocation(offsetX, offsetY);
2553 handled = child.dispatchTouchEvent(event);
2555 event.offsetLocation(-offsetX, -offsetY);
2557 return handled;
2559 transformedEvent = MotionEvent.obtain(event);
2560 } else {
2561 transformedEvent = event.split(newPointerIdBits);
2564 // Perform any necessary transformations and dispatch.
2565 if (child == null) {
2566 handled = super.dispatchTouchEvent(transformedEvent);
2567 } else {
2568 final float offsetX = mScrollX - child.mLeft;
2569 final float offsetY = mScrollY - child.mTop;
2570 transformedEvent.offsetLocation(offsetX, offsetY);
2571 if (! child.hasIdentityMatrix()) {
2572 transformedEvent.transform(child.getInverseMatrix());
2575 handled = child.dispatchTouchEvent(transformedEvent);
2578 // Done.
2579 transformedEvent.recycle();
2580 return handled;
2584 * Enable or disable the splitting of MotionEvents to multiple children during touch event
2585 * dispatch. This behavior is enabled by default for applications that target an
2586 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
2588 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2589 * views depending on where each pointer initially went down. This allows for user interactions
2590 * such as scrolling two panes of content independently, chording of buttons, and performing
2591 * independent gestures on different pieces of content.
2593 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2594 * child views. <code>false</code> to only allow one child view to be the target of
2595 * any MotionEvent received by this ViewGroup.
2596 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
2598 public void setMotionEventSplittingEnabled(boolean split) {
2599 // TODO Applications really shouldn't change this setting mid-touch event,
2600 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2601 // with gestures in progress when this is changed.
2602 if (split) {
2603 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2604 } else {
2605 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
2610 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2611 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2613 public boolean isMotionEventSplittingEnabled() {
2614 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2618 * Returns true if this ViewGroup should be considered as a single entity for removal
2619 * when executing an Activity transition. If this is false, child elements will move
2620 * individually during the transition.
2622 * @return True if the ViewGroup should be acted on together during an Activity transition.
2623 * The default value is true when there is a non-null background or if
2624 * {@link #getTransitionName()} is not null or if a
2625 * non-null {@link android.view.ViewOutlineProvider} other than
2626 * {@link android.view.ViewOutlineProvider#BACKGROUND} was given to
2627 * {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise.
2629 public boolean isTransitionGroup() {
2630 if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
2631 return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
2632 } else {
2633 final ViewOutlineProvider outlineProvider = getOutlineProvider();
2634 return getBackground() != null || getTransitionName() != null ||
2635 (outlineProvider != null && outlineProvider != ViewOutlineProvider.BACKGROUND);
2640 * Changes whether or not this ViewGroup should be treated as a single entity during
2641 * Activity Transitions.
2642 * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
2643 * in Activity transitions. If false, the ViewGroup won't transition,
2644 * only its children. If true, the entire ViewGroup will transition
2645 * together.
2646 * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
2647 * android.util.Pair[])
2649 public void setTransitionGroup(boolean isTransitionGroup) {
2650 mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
2651 if (isTransitionGroup) {
2652 mGroupFlags |= FLAG_IS_TRANSITION_GROUP;
2653 } else {
2654 mGroupFlags &= ~FLAG_IS_TRANSITION_GROUP;
2659 * {@inheritDoc}
2661 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2663 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2664 // We're already in this state, assume our ancestors are too
2665 return;
2668 if (disallowIntercept) {
2669 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2670 } else {
2671 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2674 // Pass it up to our parent
2675 if (mParent != null) {
2676 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2681 * Implement this method to intercept all touch screen motion events. This
2682 * allows you to watch events as they are dispatched to your children, and
2683 * take ownership of the current gesture at any point.
2685 * <p>Using this function takes some care, as it has a fairly complicated
2686 * interaction with {@link View#onTouchEvent(MotionEvent)
2687 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2688 * that method as well as this one in the correct way. Events will be
2689 * received in the following order:
2691 * <ol>
2692 * <li> You will receive the down event here.
2693 * <li> The down event will be handled either by a child of this view
2694 * group, or given to your own onTouchEvent() method to handle; this means
2695 * you should implement onTouchEvent() to return true, so you will
2696 * continue to see the rest of the gesture (instead of looking for
2697 * a parent view to handle it). Also, by returning true from
2698 * onTouchEvent(), you will not receive any following
2699 * events in onInterceptTouchEvent() and all touch processing must
2700 * happen in onTouchEvent() like normal.
2701 * <li> For as long as you return false from this function, each following
2702 * event (up to and including the final up) will be delivered first here
2703 * and then to the target's onTouchEvent().
2704 * <li> If you return true from here, you will not receive any
2705 * following events: the target view will receive the same event but
2706 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2707 * events will be delivered to your onTouchEvent() method and no longer
2708 * appear here.
2709 * </ol>
2711 * @param ev The motion event being dispatched down the hierarchy.
2712 * @return Return true to steal motion events from the children and have
2713 * them dispatched to this ViewGroup through onTouchEvent().
2714 * The current target will receive an ACTION_CANCEL event, and no further
2715 * messages will be delivered here.
2717 public boolean onInterceptTouchEvent(MotionEvent ev) {
2718 return false;
2722 * {@inheritDoc}
2724 * Looks for a view to give focus to respecting the setting specified by
2725 * {@link #getDescendantFocusability()}.
2727 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2728 * find focus within the children of this group when appropriate.
2730 * @see #FOCUS_BEFORE_DESCENDANTS
2731 * @see #FOCUS_AFTER_DESCENDANTS
2732 * @see #FOCUS_BLOCK_DESCENDANTS
2733 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
2735 @Override
2736 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2737 if (DBG) {
2738 System.out.println(this + " ViewGroup.requestFocus direction="
2739 + direction);
2741 int descendantFocusability = getDescendantFocusability();
2743 switch (descendantFocusability) {
2744 case FOCUS_BLOCK_DESCENDANTS:
2745 return super.requestFocus(direction, previouslyFocusedRect);
2746 case FOCUS_BEFORE_DESCENDANTS: {
2747 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2748 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2750 case FOCUS_AFTER_DESCENDANTS: {
2751 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2752 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2754 default:
2755 throw new IllegalStateException("descendant focusability must be "
2756 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2757 + "but is " + descendantFocusability);
2762 * Look for a descendant to call {@link View#requestFocus} on.
2763 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2764 * when it wants to request focus within its children. Override this to
2765 * customize how your {@link ViewGroup} requests focus within its children.
2766 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2767 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2768 * to give a finer grained hint about where focus is coming from. May be null
2769 * if there is no hint.
2770 * @return Whether focus was taken.
2772 @SuppressWarnings({"ConstantConditions"})
2773 protected boolean onRequestFocusInDescendants(int direction,
2774 Rect previouslyFocusedRect) {
2775 int index;
2776 int increment;
2777 int end;
2778 int count = mChildrenCount;
2779 if ((direction & FOCUS_FORWARD) != 0) {
2780 index = 0;
2781 increment = 1;
2782 end = count;
2783 } else {
2784 index = count - 1;
2785 increment = -1;
2786 end = -1;
2788 final View[] children = mChildren;
2789 for (int i = index; i != end; i += increment) {
2790 View child = children[i];
2791 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2792 if (child.requestFocus(direction, previouslyFocusedRect)) {
2793 return true;
2797 return false;
2801 * {@inheritDoc}
2803 * @hide
2805 @Override
2806 public void dispatchStartTemporaryDetach() {
2807 super.dispatchStartTemporaryDetach();
2808 final int count = mChildrenCount;
2809 final View[] children = mChildren;
2810 for (int i = 0; i < count; i++) {
2811 children[i].dispatchStartTemporaryDetach();
2816 * {@inheritDoc}
2818 * @hide
2820 @Override
2821 public void dispatchFinishTemporaryDetach() {
2822 super.dispatchFinishTemporaryDetach();
2823 final int count = mChildrenCount;
2824 final View[] children = mChildren;
2825 for (int i = 0; i < count; i++) {
2826 children[i].dispatchFinishTemporaryDetach();
2831 * {@inheritDoc}
2833 @Override
2834 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
2835 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2836 super.dispatchAttachedToWindow(info, visibility);
2837 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2839 final int count = mChildrenCount;
2840 final View[] children = mChildren;
2841 for (int i = 0; i < count; i++) {
2842 final View child = children[i];
2843 child.dispatchAttachedToWindow(info,
2844 combineVisibility(visibility, child.getVisibility()));
2846 final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
2847 for (int i = 0; i < transientCount; ++i) {
2848 View view = mTransientViews.get(i);
2849 view.dispatchAttachedToWindow(info,
2850 combineVisibility(visibility, view.getVisibility()));
2854 @Override
2855 void dispatchScreenStateChanged(int screenState) {
2856 super.dispatchScreenStateChanged(screenState);
2858 final int count = mChildrenCount;
2859 final View[] children = mChildren;
2860 for (int i = 0; i < count; i++) {
2861 children[i].dispatchScreenStateChanged(screenState);
2865 /** @hide */
2866 @Override
2867 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
2868 boolean handled = false;
2869 if (includeForAccessibility()) {
2870 handled = super.dispatchPopulateAccessibilityEventInternal(event);
2871 if (handled) {
2872 return handled;
2875 // Let our children have a shot in populating the event.
2876 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
2877 try {
2878 final int childCount = children.getChildCount();
2879 for (int i = 0; i < childCount; i++) {
2880 View child = children.getChildAt(i);
2881 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2882 handled = child.dispatchPopulateAccessibilityEvent(event);
2883 if (handled) {
2884 return handled;
2888 } finally {
2889 children.recycle();
2891 return false;
2895 * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation
2896 * adds in all child views of the view group, in addition to calling the default View
2897 * implementation.
2899 public void dispatchProvideStructure(ViewStructure structure) {
2900 super.dispatchProvideStructure(structure);
2901 if (!isAssistBlocked()) {
2902 if (structure.getChildCount() == 0) {
2903 final int childrenCount = getChildCount();
2904 if (childrenCount > 0) {
2905 structure.setChildCount(childrenCount);
2906 ArrayList<View> preorderedList = buildOrderedChildList();
2907 boolean customOrder = preorderedList == null
2908 && isChildrenDrawingOrderEnabled();
2909 final View[] children = mChildren;
2910 for (int i=0; i<childrenCount; i++) {
2911 int childIndex;
2912 try {
2913 childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
2914 } catch (IndexOutOfBoundsException e) {
2915 childIndex = i;
2916 if (mContext.getApplicationInfo().targetSdkVersion
2917 < Build.VERSION_CODES.M) {
2918 Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
2919 + i + " of " + childrenCount, e);
2920 // At least one app is failing when we call getChildDrawingOrder
2921 // at this point, so deal semi-gracefully with it by falling back
2922 // on the basic order.
2923 customOrder = false;
2924 if (i > 0) {
2925 // If we failed at the first index, there really isn't
2926 // anything to do -- we will just proceed with the simple
2927 // sequence order.
2928 // Otherwise, we failed in the middle, so need to come up
2929 // with an order for the remaining indices and use that.
2930 // Failed at the first one, easy peasy.
2931 int[] permutation = new int[childrenCount];
2932 SparseBooleanArray usedIndices = new SparseBooleanArray();
2933 // Go back and collected the indices we have done so far.
2934 for (int j=0; j<i; j++) {
2935 permutation[j] = getChildDrawingOrder(childrenCount, j);
2936 usedIndices.put(permutation[j], true);
2938 // Fill in the remaining indices with indices that have not
2939 // yet been used.
2940 int nextIndex = 0;
2941 for (int j=i; j<childrenCount; j++) {
2942 while (usedIndices.get(nextIndex, false)) {
2943 nextIndex++;
2945 permutation[j] = nextIndex;
2946 nextIndex++;
2948 // Build the final view list.
2949 preorderedList = new ArrayList<>(childrenCount);
2950 for (int j=0; j<childrenCount; j++) {
2951 preorderedList.add(children[permutation[j]]);
2954 } else {
2955 throw e;
2958 final View child = (preorderedList == null)
2959 ? children[childIndex] : preorderedList.get(childIndex);
2960 ViewStructure cstructure = structure.newChild(i);
2961 child.dispatchProvideStructure(cstructure);
2968 /** @hide */
2969 @Override
2970 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2971 super.onInitializeAccessibilityNodeInfoInternal(info);
2972 if (getAccessibilityNodeProvider() != null) {
2973 return;
2975 if (mAttachInfo != null) {
2976 final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
2977 childrenForAccessibility.clear();
2978 addChildrenForAccessibility(childrenForAccessibility);
2979 final int childrenForAccessibilityCount = childrenForAccessibility.size();
2980 for (int i = 0; i < childrenForAccessibilityCount; i++) {
2981 final View child = childrenForAccessibility.get(i);
2982 info.addChildUnchecked(child);
2984 childrenForAccessibility.clear();
2988 @Override
2989 public CharSequence getAccessibilityClassName() {
2990 return ViewGroup.class.getName();
2993 @Override
2994 public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
2995 // If this is a live region, we should send a subtree change event
2996 // from this view. Otherwise, we can let it propagate up.
2997 if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
2998 notifyViewAccessibilityStateChangedIfNeeded(changeType);
2999 } else if (mParent != null) {
3000 try {
3001 mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
3002 } catch (AbstractMethodError e) {
3003 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
3004 " does not fully implement ViewParent", e);
3009 @Override
3010 void resetSubtreeAccessibilityStateChanged() {
3011 super.resetSubtreeAccessibilityStateChanged();
3012 View[] children = mChildren;
3013 final int childCount = mChildrenCount;
3014 for (int i = 0; i < childCount; i++) {
3015 children[i].resetSubtreeAccessibilityStateChanged();
3020 * {@inheritDoc}
3022 * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p>
3024 * @param target The target view dispatching this action
3025 * @param action Action being performed; see
3026 * {@link android.view.accessibility.AccessibilityNodeInfo}
3027 * @param args Optional action arguments
3028 * @return false by default. Subclasses should return true if they handle the event.
3030 @Override
3031 public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
3032 return false;
3036 * {@inheritDoc}
3038 @Override
3039 void dispatchDetachedFromWindow() {
3040 // If we still have a touch target, we are still in the process of
3041 // dispatching motion events to a child; we need to get rid of that
3042 // child to avoid dispatching events to it after the window is torn
3043 // down. To make sure we keep the child in a consistent state, we
3044 // first send it an ACTION_CANCEL motion event.
3045 cancelAndClearTouchTargets(null);
3047 // Similarly, set ACTION_EXIT to all hover targets and clear them.
3048 exitHoverTargets();
3050 // In case view is detached while transition is running
3051 mLayoutCalledWhileSuppressed = false;
3053 // Tear down our drag tracking
3054 mDragNotifiedChildren = null;
3055 if (mCurrentDrag != null) {
3056 mCurrentDrag.recycle();
3057 mCurrentDrag = null;
3060 final int count = mChildrenCount;
3061 final View[] children = mChildren;
3062 for (int i = 0; i < count; i++) {
3063 children[i].dispatchDetachedFromWindow();
3065 clearDisappearingChildren();
3066 final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
3067 for (int i = 0; i < transientCount; ++i) {
3068 View view = mTransientViews.get(i);
3069 view.dispatchDetachedFromWindow();
3071 super.dispatchDetachedFromWindow();
3075 * @hide
3077 @Override
3078 protected void internalSetPadding(int left, int top, int right, int bottom) {
3079 super.internalSetPadding(left, top, right, bottom);
3081 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
3082 mGroupFlags |= FLAG_PADDING_NOT_NULL;
3083 } else {
3084 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
3089 * {@inheritDoc}
3091 @Override
3092 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
3093 super.dispatchSaveInstanceState(container);
3094 final int count = mChildrenCount;
3095 final View[] children = mChildren;
3096 for (int i = 0; i < count; i++) {
3097 View c = children[i];
3098 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
3099 c.dispatchSaveInstanceState(container);
3105 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
3106 * to only this view, not to its children. For use when overriding
3107 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
3108 * subclasses to freeze their own state but not the state of their children.
3110 * @param container the container
3112 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
3113 super.dispatchSaveInstanceState(container);
3117 * {@inheritDoc}
3119 @Override
3120 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
3121 super.dispatchRestoreInstanceState(container);
3122 final int count = mChildrenCount;
3123 final View[] children = mChildren;
3124 for (int i = 0; i < count; i++) {
3125 View c = children[i];
3126 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
3127 c.dispatchRestoreInstanceState(container);
3133 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
3134 * to only this view, not to its children. For use when overriding
3135 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
3136 * subclasses to thaw their own state but not the state of their children.
3138 * @param container the container
3140 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
3141 super.dispatchRestoreInstanceState(container);
3145 * Enables or disables the drawing cache for each child of this view group.
3147 * @param enabled true to enable the cache, false to dispose of it
3149 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
3150 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
3151 final View[] children = mChildren;
3152 final int count = mChildrenCount;
3153 for (int i = 0; i < count; i++) {
3154 children[i].setDrawingCacheEnabled(enabled);
3159 @Override
3160 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
3161 int count = mChildrenCount;
3162 int[] visibilities = null;
3164 if (skipChildren) {
3165 visibilities = new int[count];
3166 for (int i = 0; i < count; i++) {
3167 View child = getChildAt(i);
3168 visibilities[i] = child.getVisibility();
3169 if (visibilities[i] == View.VISIBLE) {
3170 child.setVisibility(INVISIBLE);
3175 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
3177 if (skipChildren) {
3178 for (int i = 0; i < count; i++) {
3179 getChildAt(i).setVisibility(visibilities[i]);
3183 return b;
3186 /** Return true if this ViewGroup is laying out using optical bounds. */
3187 boolean isLayoutModeOptical() {
3188 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
3191 Insets computeOpticalInsets() {
3192 if (isLayoutModeOptical()) {
3193 int left = 0;
3194 int top = 0;
3195 int right = 0;
3196 int bottom = 0;
3197 for (int i = 0; i < mChildrenCount; i++) {
3198 View child = getChildAt(i);
3199 if (child.getVisibility() == VISIBLE) {
3200 Insets insets = child.getOpticalInsets();
3201 left = Math.max(left, insets.left);
3202 top = Math.max(top, insets.top);
3203 right = Math.max(right, insets.right);
3204 bottom = Math.max(bottom, insets.bottom);
3207 return Insets.of(left, top, right, bottom);
3208 } else {
3209 return Insets.NONE;
3213 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
3214 if (x1 != x2 && y1 != y2) {
3215 if (x1 > x2) {
3216 int tmp = x1; x1 = x2; x2 = tmp;
3218 if (y1 > y2) {
3219 int tmp = y1; y1 = y2; y2 = tmp;
3221 canvas.drawRect(x1, y1, x2, y2, paint);
3225 private static int sign(int x) {
3226 return (x >= 0) ? 1 : -1;
3229 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
3230 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
3231 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
3234 private int dipsToPixels(int dips) {
3235 float scale = getContext().getResources().getDisplayMetrics().density;
3236 return (int) (dips * scale + 0.5f);
3239 private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
3240 int lineLength, int lineWidth) {
3241 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
3242 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
3243 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
3244 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
3247 private static void fillDifference(Canvas canvas,
3248 int x2, int y2, int x3, int y3,
3249 int dx1, int dy1, int dx2, int dy2, Paint paint) {
3250 int x1 = x2 - dx1;
3251 int y1 = y2 - dy1;
3253 int x4 = x3 + dx2;
3254 int y4 = y3 + dy2;
3256 fillRect(canvas, paint, x1, y1, x4, y2);
3257 fillRect(canvas, paint, x1, y2, x2, y3);
3258 fillRect(canvas, paint, x3, y2, x4, y3);
3259 fillRect(canvas, paint, x1, y3, x4, y4);
3263 * @hide
3265 protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
3266 for (int i = 0; i < getChildCount(); i++) {
3267 View c = getChildAt(i);
3268 c.getLayoutParams().onDebugDraw(c, canvas, paint);
3273 * @hide
3275 protected void onDebugDraw(Canvas canvas) {
3276 Paint paint = getDebugPaint();
3278 // Draw optical bounds
3280 paint.setColor(Color.RED);
3281 paint.setStyle(Paint.Style.STROKE);
3283 for (int i = 0; i < getChildCount(); i++) {
3284 View c = getChildAt(i);
3285 if (c.getVisibility() != View.GONE) {
3286 Insets insets = c.getOpticalInsets();
3288 drawRect(canvas, paint,
3289 c.getLeft() + insets.left,
3290 c.getTop() + insets.top,
3291 c.getRight() - insets.right - 1,
3292 c.getBottom() - insets.bottom - 1);
3297 // Draw margins
3299 paint.setColor(Color.argb(63, 255, 0, 255));
3300 paint.setStyle(Paint.Style.FILL);
3302 onDebugDrawMargins(canvas, paint);
3305 // Draw clip bounds
3307 paint.setColor(Color.rgb(63, 127, 255));
3308 paint.setStyle(Paint.Style.FILL);
3310 int lineLength = dipsToPixels(8);
3311 int lineWidth = dipsToPixels(1);
3312 for (int i = 0; i < getChildCount(); i++) {
3313 View c = getChildAt(i);
3314 if (c.getVisibility() != View.GONE) {
3315 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
3316 paint, lineLength, lineWidth);
3323 * {@inheritDoc}
3325 @Override
3326 protected void dispatchDraw(Canvas canvas) {
3327 boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
3328 final int childrenCount = mChildrenCount;
3329 final View[] children = mChildren;
3330 int flags = mGroupFlags;
3332 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
3333 final boolean buildCache = !isHardwareAccelerated();
3334 for (int i = 0; i < childrenCount; i++) {
3335 final View child = children[i];
3336 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3337 final LayoutParams params = child.getLayoutParams();
3338 attachLayoutAnimationParameters(child, params, i, childrenCount);
3339 bindLayoutAnimation(child);
3343 final LayoutAnimationController controller = mLayoutAnimationController;
3344 if (controller.willOverlap()) {
3345 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
3348 controller.start();
3350 mGroupFlags &= ~FLAG_RUN_ANIMATION;
3351 mGroupFlags &= ~FLAG_ANIMATION_DONE;
3353 if (mAnimationListener != null) {
3354 mAnimationListener.onAnimationStart(controller.getAnimation());
3358 int clipSaveCount = 0;
3359 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
3360 if (clipToPadding) {
3361 clipSaveCount = canvas.save();
3362 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
3363 mScrollX + mRight - mLeft - mPaddingRight,
3364 mScrollY + mBottom - mTop - mPaddingBottom);
3367 // We will draw our child's animation, let's reset the flag
3368 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
3369 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
3371 boolean more = false;
3372 final long drawingTime = getDrawingTime();
3374 if (usingRenderNodeProperties) canvas.insertReorderBarrier();
3375 final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
3376 int transientIndex = transientCount != 0 ? 0 : -1;
3377 // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
3378 // draw reordering internally
3379 final ArrayList<View> preorderedList = usingRenderNodeProperties
3380 ? null : buildOrderedChildList();
3381 final boolean customOrder = preorderedList == null
3382 && isChildrenDrawingOrderEnabled();
3383 for (int i = 0; i < childrenCount; i++) {
3384 while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
3385 final View transientChild = mTransientViews.get(transientIndex);
3386 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
3387 transientChild.getAnimation() != null) {
3388 more |= drawChild(canvas, transientChild, drawingTime);
3390 transientIndex++;
3391 if (transientIndex >= transientCount) {
3392 transientIndex = -1;
3395 int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
3396 final View child = (preorderedList == null)
3397 ? children[childIndex] : preorderedList.get(childIndex);
3398 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
3399 more |= drawChild(canvas, child, drawingTime);
3402 while (transientIndex >= 0) {
3403 // there may be additional transient views after the normal views
3404 final View transientChild = mTransientViews.get(transientIndex);
3405 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
3406 transientChild.getAnimation() != null) {
3407 more |= drawChild(canvas, transientChild, drawingTime);
3409 transientIndex++;
3410 if (transientIndex >= transientCount) {
3411 break;
3414 if (preorderedList != null) preorderedList.clear();
3416 // Draw any disappearing views that have animations
3417 if (mDisappearingChildren != null) {
3418 final ArrayList<View> disappearingChildren = mDisappearingChildren;
3419 final int disappearingCount = disappearingChildren.size() - 1;
3420 // Go backwards -- we may delete as animations finish
3421 for (int i = disappearingCount; i >= 0; i--) {
3422 final View child = disappearingChildren.get(i);
3423 more |= drawChild(canvas, child, drawingTime);
3426 if (usingRenderNodeProperties) canvas.insertInorderBarrier();
3428 if (debugDraw()) {
3429 onDebugDraw(canvas);
3432 if (clipToPadding) {
3433 canvas.restoreToCount(clipSaveCount);
3436 // mGroupFlags might have been updated by drawChild()
3437 flags = mGroupFlags;
3439 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
3440 invalidate(true);
3443 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
3444 mLayoutAnimationController.isDone() && !more) {
3445 // We want to erase the drawing cache and notify the listener after the
3446 // next frame is drawn because one extra invalidate() is caused by
3447 // drawChild() after the animation is over
3448 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
3449 final Runnable end = new Runnable() {
3450 public void run() {
3451 notifyAnimationListener();
3454 post(end);
3459 * Returns the ViewGroupOverlay for this view group, creating it if it does
3460 * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
3461 * {@link ViewGroupOverlay} allows views to be added to the overlay. These
3462 * views, like overlay drawables, are visual-only; they do not receive input
3463 * events and should not be used as anything other than a temporary
3464 * representation of a view in a parent container, such as might be used
3465 * by an animation effect.
3467 * <p>Note: Overlays do not currently work correctly with {@link
3468 * SurfaceView} or {@link TextureView}; contents in overlays for these
3469 * types of views may not display correctly.</p>
3471 * @return The ViewGroupOverlay object for this view.
3472 * @see ViewGroupOverlay
3474 @Override
3475 public ViewGroupOverlay getOverlay() {
3476 if (mOverlay == null) {
3477 mOverlay = new ViewGroupOverlay(mContext, this);
3479 return (ViewGroupOverlay) mOverlay;
3483 * Returns the index of the child to draw for this iteration. Override this
3484 * if you want to change the drawing order of children. By default, it
3485 * returns i.
3486 * <p>
3487 * NOTE: In order for this method to be called, you must enable child ordering
3488 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
3490 * @param i The current iteration.
3491 * @return The index of the child to draw this iteration.
3493 * @see #setChildrenDrawingOrderEnabled(boolean)
3494 * @see #isChildrenDrawingOrderEnabled()
3496 protected int getChildDrawingOrder(int childCount, int i) {
3497 return i;
3500 private boolean hasChildWithZ() {
3501 for (int i = 0; i < mChildrenCount; i++) {
3502 if (mChildren[i].getZ() != 0) return true;
3504 return false;
3508 * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
3509 * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
3510 * after use to avoid leaking child Views.
3512 * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
3513 * children.
3515 ArrayList<View> buildOrderedChildList() {
3516 final int count = mChildrenCount;
3517 if (count <= 1 || !hasChildWithZ()) return null;
3519 if (mPreSortedChildren == null) {
3520 mPreSortedChildren = new ArrayList<View>(count);
3521 } else {
3522 mPreSortedChildren.ensureCapacity(count);
3525 final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
3526 for (int i = 0; i < mChildrenCount; i++) {
3527 // add next child (in child order) to end of list
3528 int childIndex = useCustomOrder ? getChildDrawingOrder(mChildrenCount, i) : i;
3529 View nextChild = mChildren[childIndex];
3530 float currentZ = nextChild.getZ();
3532 // insert ahead of any Views with greater Z
3533 int insertIndex = i;
3534 while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
3535 insertIndex--;
3537 mPreSortedChildren.add(insertIndex, nextChild);
3539 return mPreSortedChildren;
3542 private void notifyAnimationListener() {
3543 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
3544 mGroupFlags |= FLAG_ANIMATION_DONE;
3546 if (mAnimationListener != null) {
3547 final Runnable end = new Runnable() {
3548 public void run() {
3549 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
3552 post(end);
3555 invalidate(true);
3559 * This method is used to cause children of this ViewGroup to restore or recreate their
3560 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
3561 * to recreate its own display list, which would happen if it went through the normal
3562 * draw/dispatchDraw mechanisms.
3564 * @hide
3566 @Override
3567 protected void dispatchGetDisplayList() {
3568 final int count = mChildrenCount;
3569 final View[] children = mChildren;
3570 for (int i = 0; i < count; i++) {
3571 final View child = children[i];
3572 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
3573 recreateChildDisplayList(child);
3576 if (mOverlay != null) {
3577 View overlayView = mOverlay.getOverlayView();
3578 recreateChildDisplayList(overlayView);
3580 if (mDisappearingChildren != null) {
3581 final ArrayList<View> disappearingChildren = mDisappearingChildren;
3582 final int disappearingCount = disappearingChildren.size();
3583 for (int i = 0; i < disappearingCount; ++i) {
3584 final View child = disappearingChildren.get(i);
3585 recreateChildDisplayList(child);
3590 private void recreateChildDisplayList(View child) {
3591 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
3592 child.mPrivateFlags &= ~PFLAG_INVALIDATED;
3593 child.updateDisplayListIfDirty();
3594 child.mRecreateDisplayList = false;
3598 * Draw one child of this View Group. This method is responsible for getting
3599 * the canvas in the right state. This includes clipping, translating so
3600 * that the child's scrolled origin is at 0, 0, and applying any animation
3601 * transformations.
3603 * @param canvas The canvas on which to draw the child
3604 * @param child Who to draw
3605 * @param drawingTime The time at which draw is occurring
3606 * @return True if an invalidate() was issued
3608 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3609 return child.draw(canvas, this, drawingTime);
3612 @Override
3613 void getScrollIndicatorBounds(@NonNull Rect out) {
3614 super.getScrollIndicatorBounds(out);
3616 // If we have padding and we're supposed to clip children to that
3617 // padding, offset the scroll indicators to match our clip bounds.
3618 final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
3619 if (clipToPadding) {
3620 out.left += mPaddingLeft;
3621 out.right -= mPaddingRight;
3622 out.top += mPaddingTop;
3623 out.bottom -= mPaddingBottom;
3628 * Returns whether this group's children are clipped to their bounds before drawing.
3629 * The default value is true.
3630 * @see #setClipChildren(boolean)
3632 * @return True if the group's children will be clipped to their bounds,
3633 * false otherwise.
3635 @ViewDebug.ExportedProperty(category = "drawing")
3636 public boolean getClipChildren() {
3637 return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
3641 * By default, children are clipped to their bounds before drawing. This
3642 * allows view groups to override this behavior for animations, etc.
3644 * @param clipChildren true to clip children to their bounds,
3645 * false otherwise
3646 * @attr ref android.R.styleable#ViewGroup_clipChildren
3648 public void setClipChildren(boolean clipChildren) {
3649 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
3650 if (clipChildren != previousValue) {
3651 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
3652 for (int i = 0; i < mChildrenCount; ++i) {
3653 View child = getChildAt(i);
3654 if (child.mRenderNode != null) {
3655 child.mRenderNode.setClipToBounds(clipChildren);
3658 invalidate(true);
3663 * Sets whether this ViewGroup will clip its children to its padding and resize (but not
3664 * clip) any EdgeEffect to the padded region, if padding is present.
3665 * <p>
3666 * By default, children are clipped to the padding of their parent
3667 * ViewGroup. This clipping behavior is only enabled if padding is non-zero.
3669 * @param clipToPadding true to clip children to the padding of the group, and resize (but
3670 * not clip) any EdgeEffect to the padded region. False otherwise.
3671 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3673 public void setClipToPadding(boolean clipToPadding) {
3674 if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) {
3675 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3676 invalidate(true);
3681 * Returns whether this ViewGroup will clip its children to its padding, and resize (but
3682 * not clip) any EdgeEffect to the padded region, if padding is present.
3683 * <p>
3684 * By default, children are clipped to the padding of their parent
3685 * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
3687 * @return true if this ViewGroup clips children to its padding and resizes (but doesn't
3688 * clip) any EdgeEffect to the padded region, false otherwise.
3690 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3692 @ViewDebug.ExportedProperty(category = "drawing")
3693 public boolean getClipToPadding() {
3694 return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
3698 * {@inheritDoc}
3700 @Override
3701 public void dispatchSetSelected(boolean selected) {
3702 final View[] children = mChildren;
3703 final int count = mChildrenCount;
3704 for (int i = 0; i < count; i++) {
3705 children[i].setSelected(selected);
3710 * {@inheritDoc}
3712 @Override
3713 public void dispatchSetActivated(boolean activated) {
3714 final View[] children = mChildren;
3715 final int count = mChildrenCount;
3716 for (int i = 0; i < count; i++) {
3717 children[i].setActivated(activated);
3721 @Override
3722 protected void dispatchSetPressed(boolean pressed) {
3723 final View[] children = mChildren;
3724 final int count = mChildrenCount;
3725 for (int i = 0; i < count; i++) {
3726 final View child = children[i];
3727 // Children that are clickable on their own should not
3728 // show a pressed state when their parent view does.
3729 // Clearing a pressed state always propagates.
3730 if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
3731 child.setPressed(pressed);
3737 * Dispatches drawable hotspot changes to child views that meet at least
3738 * one of the following criteria:
3739 * <ul>
3740 * <li>Returns {@code false} from both {@link View#isClickable()} and
3741 * {@link View#isLongClickable()}</li>
3742 * <li>Requests duplication of parent state via
3743 * {@link View#setDuplicateParentStateEnabled(boolean)}</li>
3744 * </ul>
3746 * @param x hotspot x coordinate
3747 * @param y hotspot y coordinate
3748 * @see #drawableHotspotChanged(float, float)
3750 @Override
3751 public void dispatchDrawableHotspotChanged(float x, float y) {
3752 final int count = mChildrenCount;
3753 if (count == 0) {
3754 return;
3757 final View[] children = mChildren;
3758 for (int i = 0; i < count; i++) {
3759 final View child = children[i];
3760 // Children that are clickable on their own should not
3761 // receive hotspots when their parent view does.
3762 final boolean nonActionable = !child.isClickable() && !child.isLongClickable();
3763 final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0;
3764 if (nonActionable || duplicatesState) {
3765 final float[] point = getTempPoint();
3766 point[0] = x;
3767 point[1] = y;
3768 transformPointToViewLocal(point, child);
3769 child.drawableHotspotChanged(point[0], point[1]);
3774 @Override
3775 void dispatchCancelPendingInputEvents() {
3776 super.dispatchCancelPendingInputEvents();
3778 final View[] children = mChildren;
3779 final int count = mChildrenCount;
3780 for (int i = 0; i < count; i++) {
3781 children[i].dispatchCancelPendingInputEvents();
3786 * When this property is set to true, this ViewGroup supports static transformations on
3787 * children; this causes
3788 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3789 * invoked when a child is drawn.
3791 * Any subclass overriding
3792 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3793 * set this property to true.
3795 * @param enabled True to enable static transformations on children, false otherwise.
3797 * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
3799 protected void setStaticTransformationsEnabled(boolean enabled) {
3800 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3804 * Sets <code>t</code> to be the static transformation of the child, if set, returning a
3805 * boolean to indicate whether a static transform was set. The default implementation
3806 * simply returns <code>false</code>; subclasses may override this method for different
3807 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
3808 * for this method to be called.
3810 * @param child The child view whose static transform is being requested
3811 * @param t The Transformation which will hold the result
3812 * @return true if the transformation was set, false otherwise
3813 * @see #setStaticTransformationsEnabled(boolean)
3815 protected boolean getChildStaticTransformation(View child, Transformation t) {
3816 return false;
3819 Transformation getChildTransformation() {
3820 if (mChildTransformation == null) {
3821 mChildTransformation = new Transformation();
3823 return mChildTransformation;
3827 * {@hide}
3829 @Override
3830 protected View findViewTraversal(@IdRes int id) {
3831 if (id == mID) {
3832 return this;
3835 final View[] where = mChildren;
3836 final int len = mChildrenCount;
3838 for (int i = 0; i < len; i++) {
3839 View v = where[i];
3841 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3842 v = v.findViewById(id);
3844 if (v != null) {
3845 return v;
3850 return null;
3854 * {@hide}
3856 @Override
3857 protected View findViewWithTagTraversal(Object tag) {
3858 if (tag != null && tag.equals(mTag)) {
3859 return this;
3862 final View[] where = mChildren;
3863 final int len = mChildrenCount;
3865 for (int i = 0; i < len; i++) {
3866 View v = where[i];
3868 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3869 v = v.findViewWithTag(tag);
3871 if (v != null) {
3872 return v;
3877 return null;
3881 * {@hide}
3883 @Override
3884 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
3885 if (predicate.apply(this)) {
3886 return this;
3889 final View[] where = mChildren;
3890 final int len = mChildrenCount;
3892 for (int i = 0; i < len; i++) {
3893 View v = where[i];
3895 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3896 v = v.findViewByPredicate(predicate);
3898 if (v != null) {
3899 return v;
3904 return null;
3908 * This method adds a view to this container at the specified index purely for the
3909 * purposes of allowing that view to draw even though it is not a normal child of
3910 * the container. That is, the view does not participate in layout, focus, accessibility,
3911 * input, or other normal view operations; it is purely an item to be drawn during the normal
3912 * rendering operation of this container. The index that it is added at is the order
3913 * in which it will be drawn, with respect to the other views in the container.
3914 * For example, a transient view added at index 0 will be drawn before all other views
3915 * in the container because it will be drawn first (including before any real view
3916 * at index 0). There can be more than one transient view at any particular index;
3917 * these views will be drawn in the order in which they were added to the list of
3918 * transient views. The index of transient views can also be greater than the number
3919 * of normal views in the container; that just means that they will be drawn after all
3920 * other views are drawn.
3922 * <p>Note that since transient views do not participate in layout, they must be sized
3923 * manually or, more typically, they should just use the size that they had before they
3924 * were removed from their container.</p>
3926 * <p>Transient views are useful for handling animations of views that have been removed
3927 * from the container, but which should be animated out after the removal. Adding these
3928 * views as transient views allows them to participate in drawing without side-effecting
3929 * the layout of the container.</p>
3931 * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed}
3932 * from the container when they are no longer needed. For example, a transient view
3933 * which is added in order to fade it out in its old location should be removed
3934 * once the animation is complete.</p>
3936 * @param view The view to be added
3937 * @param index The index at which this view should be drawn, must be >= 0.
3938 * This value is relative to the {@link #getChildAt(int) index} values in the normal
3939 * child list of this container, where any transient view at a particular index will
3940 * be drawn before any normal child at that same index.
3942 * @hide
3944 public void addTransientView(View view, int index) {
3945 if (index < 0) {
3946 return;
3948 if (mTransientIndices == null) {
3949 mTransientIndices = new ArrayList<Integer>();
3950 mTransientViews = new ArrayList<View>();
3952 final int oldSize = mTransientIndices.size();
3953 if (oldSize > 0) {
3954 int insertionIndex;
3955 for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) {
3956 if (index < mTransientIndices.get(insertionIndex)) {
3957 break;
3960 mTransientIndices.add(insertionIndex, index);
3961 mTransientViews.add(insertionIndex, view);
3962 } else {
3963 mTransientIndices.add(index);
3964 mTransientViews.add(view);
3966 view.mParent = this;
3967 view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3968 invalidate(true);
3972 * Removes a view from the list of transient views in this container. If there is no
3973 * such transient view, this method does nothing.
3975 * @param view The transient view to be removed
3977 * @hide
3979 public void removeTransientView(View view) {
3980 if (mTransientViews == null) {
3981 return;
3983 final int size = mTransientViews.size();
3984 for (int i = 0; i < size; ++i) {
3985 if (view == mTransientViews.get(i)) {
3986 mTransientViews.remove(i);
3987 mTransientIndices.remove(i);
3988 view.mParent = null;
3989 view.dispatchDetachedFromWindow();
3990 invalidate(true);
3991 return;
3997 * Returns the number of transient views in this container. Specific transient
3998 * views and the index at which they were added can be retrieved via
3999 * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}.
4001 * @see #addTransientView(View, int)
4002 * @return The number of transient views in this container
4004 * @hide
4006 public int getTransientViewCount() {
4007 return mTransientIndices == null ? 0 : mTransientIndices.size();
4011 * Given a valid position within the list of transient views, returns the index of
4012 * the transient view at that position.
4014 * @param position The position of the index being queried. Must be at least 0
4015 * and less than the value returned by {@link #getTransientViewCount()}.
4016 * @return The index of the transient view stored in the given position if the
4017 * position is valid, otherwise -1
4019 * @hide
4021 public int getTransientViewIndex(int position) {
4022 if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) {
4023 return -1;
4025 return mTransientIndices.get(position);
4029 * Given a valid position within the list of transient views, returns the
4030 * transient view at that position.
4032 * @param position The position of the view being queried. Must be at least 0
4033 * and less than the value returned by {@link #getTransientViewCount()}.
4034 * @return The transient view stored in the given position if the
4035 * position is valid, otherwise null
4037 * @hide
4039 public View getTransientView(int position) {
4040 if (mTransientViews == null || position >= mTransientViews.size()) {
4041 return null;
4043 return mTransientViews.get(position);
4047 * <p>Adds a child view. If no layout parameters are already set on the child, the
4048 * default parameters for this ViewGroup are set on the child.</p>
4050 * <p><strong>Note:</strong> do not invoke this method from
4051 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4052 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4054 * @param child the child view to add
4056 * @see #generateDefaultLayoutParams()
4058 public void addView(View child) {
4059 addView(child, -1);
4063 * Adds a child view. If no layout parameters are already set on the child, the
4064 * default parameters for this ViewGroup are set on the child.
4066 * <p><strong>Note:</strong> do not invoke this method from
4067 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4068 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4070 * @param child the child view to add
4071 * @param index the position at which to add the child
4073 * @see #generateDefaultLayoutParams()
4075 public void addView(View child, int index) {
4076 if (child == null) {
4077 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4079 LayoutParams params = child.getLayoutParams();
4080 if (params == null) {
4081 params = generateDefaultLayoutParams();
4082 if (params == null) {
4083 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
4086 addView(child, index, params);
4090 * Adds a child view with this ViewGroup's default layout parameters and the
4091 * specified width and height.
4093 * <p><strong>Note:</strong> do not invoke this method from
4094 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4095 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4097 * @param child the child view to add
4099 public void addView(View child, int width, int height) {
4100 final LayoutParams params = generateDefaultLayoutParams();
4101 params.width = width;
4102 params.height = height;
4103 addView(child, -1, params);
4107 * Adds a child view with the specified layout parameters.
4109 * <p><strong>Note:</strong> do not invoke this method from
4110 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4111 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4113 * @param child the child view to add
4114 * @param params the layout parameters to set on the child
4116 public void addView(View child, LayoutParams params) {
4117 addView(child, -1, params);
4121 * Adds a child view with the specified layout parameters.
4123 * <p><strong>Note:</strong> do not invoke this method from
4124 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4125 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4127 * @param child the child view to add
4128 * @param index the position at which to add the child or -1 to add last
4129 * @param params the layout parameters to set on the child
4131 public void addView(View child, int index, LayoutParams params) {
4132 if (DBG) {
4133 System.out.println(this + " addView");
4136 if (child == null) {
4137 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4140 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
4141 // therefore, we call requestLayout() on ourselves before, so that the child's request
4142 // will be blocked at our level
4143 requestLayout();
4144 invalidate(true);
4145 addViewInner(child, index, params, false);
4149 * {@inheritDoc}
4151 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
4152 if (!checkLayoutParams(params)) {
4153 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
4155 if (view.mParent != this) {
4156 throw new IllegalArgumentException("Given view not a child of " + this);
4158 view.setLayoutParams(params);
4162 * {@inheritDoc}
4164 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4165 return p != null;
4169 * Interface definition for a callback to be invoked when the hierarchy
4170 * within this view changed. The hierarchy changes whenever a child is added
4171 * to or removed from this view.
4173 public interface OnHierarchyChangeListener {
4175 * Called when a new child is added to a parent view.
4177 * @param parent the view in which a child was added
4178 * @param child the new child view added in the hierarchy
4180 void onChildViewAdded(View parent, View child);
4183 * Called when a child is removed from a parent view.
4185 * @param parent the view from which the child was removed
4186 * @param child the child removed from the hierarchy
4188 void onChildViewRemoved(View parent, View child);
4192 * Register a callback to be invoked when a child is added to or removed
4193 * from this view.
4195 * @param listener the callback to invoke on hierarchy change
4197 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
4198 mOnHierarchyChangeListener = listener;
4201 void dispatchViewAdded(View child) {
4202 onViewAdded(child);
4203 if (mOnHierarchyChangeListener != null) {
4204 mOnHierarchyChangeListener.onChildViewAdded(this, child);
4209 * Called when a new child is added to this ViewGroup. Overrides should always
4210 * call super.onViewAdded.
4212 * @param child the added child view
4214 public void onViewAdded(View child) {
4217 void dispatchViewRemoved(View child) {
4218 onViewRemoved(child);
4219 if (mOnHierarchyChangeListener != null) {
4220 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
4225 * Called when a child view is removed from this ViewGroup. Overrides should always
4226 * call super.onViewRemoved.
4228 * @param child the removed child view
4230 public void onViewRemoved(View child) {
4233 private void clearCachedLayoutMode() {
4234 if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
4235 mLayoutMode = LAYOUT_MODE_UNDEFINED;
4239 @Override
4240 protected void onAttachedToWindow() {
4241 super.onAttachedToWindow();
4242 clearCachedLayoutMode();
4245 @Override
4246 protected void onDetachedFromWindow() {
4247 super.onDetachedFromWindow();
4248 clearCachedLayoutMode();
4252 * Adds a view during layout. This is useful if in your onLayout() method,
4253 * you need to add more views (as does the list view for example).
4255 * If index is negative, it means put it at the end of the list.
4257 * @param child the view to add to the group
4258 * @param index the index at which the child must be added or -1 to add last
4259 * @param params the layout parameters to associate with the child
4260 * @return true if the child was added, false otherwise
4262 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
4263 return addViewInLayout(child, index, params, false);
4267 * Adds a view during layout. This is useful if in your onLayout() method,
4268 * you need to add more views (as does the list view for example).
4270 * If index is negative, it means put it at the end of the list.
4272 * @param child the view to add to the group
4273 * @param index the index at which the child must be added or -1 to add last
4274 * @param params the layout parameters to associate with the child
4275 * @param preventRequestLayout if true, calling this method will not trigger a
4276 * layout request on child
4277 * @return true if the child was added, false otherwise
4279 protected boolean addViewInLayout(View child, int index, LayoutParams params,
4280 boolean preventRequestLayout) {
4281 if (child == null) {
4282 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4284 child.mParent = null;
4285 addViewInner(child, index, params, preventRequestLayout);
4286 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
4287 return true;
4291 * Prevents the specified child to be laid out during the next layout pass.
4293 * @param child the child on which to perform the cleanup
4295 protected void cleanupLayoutState(View child) {
4296 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
4299 private void addViewInner(View child, int index, LayoutParams params,
4300 boolean preventRequestLayout) {
4302 if (mTransition != null) {
4303 // Don't prevent other add transitions from completing, but cancel remove
4304 // transitions to let them complete the process before we add to the container
4305 mTransition.cancel(LayoutTransition.DISAPPEARING);
4308 if (child.getParent() != null) {
4309 throw new IllegalStateException("The specified child already has a parent. " +
4310 "You must call removeView() on the child's parent first.");
4313 if (mTransition != null) {
4314 mTransition.addChild(this, child);
4317 if (!checkLayoutParams(params)) {
4318 params = generateLayoutParams(params);
4321 if (preventRequestLayout) {
4322 child.mLayoutParams = params;
4323 } else {
4324 child.setLayoutParams(params);
4327 if (index < 0) {
4328 index = mChildrenCount;
4331 addInArray(child, index);
4333 // tell our children
4334 if (preventRequestLayout) {
4335 child.assignParent(this);
4336 } else {
4337 child.mParent = this;
4340 if (child.hasFocus()) {
4341 requestChildFocus(child, child.findFocus());
4344 AttachInfo ai = mAttachInfo;
4345 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
4346 boolean lastKeepOn = ai.mKeepScreenOn;
4347 ai.mKeepScreenOn = false;
4348 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
4349 if (ai.mKeepScreenOn) {
4350 needGlobalAttributesUpdate(true);
4352 ai.mKeepScreenOn = lastKeepOn;
4355 if (child.isLayoutDirectionInherited()) {
4356 child.resetRtlProperties();
4359 dispatchViewAdded(child);
4361 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
4362 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
4365 if (child.hasTransientState()) {
4366 childHasTransientStateChanged(child, true);
4369 if (child.getVisibility() != View.GONE) {
4370 notifySubtreeAccessibilityStateChangedIfNeeded();
4373 if (mTransientIndices != null) {
4374 final int transientCount = mTransientIndices.size();
4375 for (int i = 0; i < transientCount; ++i) {
4376 final int oldIndex = mTransientIndices.get(i);
4377 if (index <= oldIndex) {
4378 mTransientIndices.set(i, oldIndex + 1);
4384 private void addInArray(View child, int index) {
4385 View[] children = mChildren;
4386 final int count = mChildrenCount;
4387 final int size = children.length;
4388 if (index == count) {
4389 if (size == count) {
4390 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
4391 System.arraycopy(children, 0, mChildren, 0, size);
4392 children = mChildren;
4394 children[mChildrenCount++] = child;
4395 } else if (index < count) {
4396 if (size == count) {
4397 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
4398 System.arraycopy(children, 0, mChildren, 0, index);
4399 System.arraycopy(children, index, mChildren, index + 1, count - index);
4400 children = mChildren;
4401 } else {
4402 System.arraycopy(children, index, children, index + 1, count - index);
4404 children[index] = child;
4405 mChildrenCount++;
4406 if (mLastTouchDownIndex >= index) {
4407 mLastTouchDownIndex++;
4409 } else {
4410 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
4414 // This method also sets the child's mParent to null
4415 private void removeFromArray(int index) {
4416 final View[] children = mChildren;
4417 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
4418 children[index].mParent = null;
4420 final int count = mChildrenCount;
4421 if (index == count - 1) {
4422 children[--mChildrenCount] = null;
4423 } else if (index >= 0 && index < count) {
4424 System.arraycopy(children, index + 1, children, index, count - index - 1);
4425 children[--mChildrenCount] = null;
4426 } else {
4427 throw new IndexOutOfBoundsException();
4429 if (mLastTouchDownIndex == index) {
4430 mLastTouchDownTime = 0;
4431 mLastTouchDownIndex = -1;
4432 } else if (mLastTouchDownIndex > index) {
4433 mLastTouchDownIndex--;
4437 // This method also sets the children's mParent to null
4438 private void removeFromArray(int start, int count) {
4439 final View[] children = mChildren;
4440 final int childrenCount = mChildrenCount;
4442 start = Math.max(0, start);
4443 final int end = Math.min(childrenCount, start + count);
4445 if (start == end) {
4446 return;
4449 if (end == childrenCount) {
4450 for (int i = start; i < end; i++) {
4451 children[i].mParent = null;
4452 children[i] = null;
4454 } else {
4455 for (int i = start; i < end; i++) {
4456 children[i].mParent = null;
4459 // Since we're looping above, we might as well do the copy, but is arraycopy()
4460 // faster than the extra 2 bounds checks we would do in the loop?
4461 System.arraycopy(children, end, children, start, childrenCount - end);
4463 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
4464 children[i] = null;
4468 mChildrenCount -= (end - start);
4471 private void bindLayoutAnimation(View child) {
4472 Animation a = mLayoutAnimationController.getAnimationForView(child);
4473 child.setAnimation(a);
4477 * Subclasses should override this method to set layout animation
4478 * parameters on the supplied child.
4480 * @param child the child to associate with animation parameters
4481 * @param params the child's layout parameters which hold the animation
4482 * parameters
4483 * @param index the index of the child in the view group
4484 * @param count the number of children in the view group
4486 protected void attachLayoutAnimationParameters(View child,
4487 LayoutParams params, int index, int count) {
4488 LayoutAnimationController.AnimationParameters animationParams =
4489 params.layoutAnimationParameters;
4490 if (animationParams == null) {
4491 animationParams = new LayoutAnimationController.AnimationParameters();
4492 params.layoutAnimationParameters = animationParams;
4495 animationParams.count = count;
4496 animationParams.index = index;
4500 * {@inheritDoc}
4502 * <p><strong>Note:</strong> do not invoke this method from
4503 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4504 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4506 public void removeView(View view) {
4507 if (removeViewInternal(view)) {
4508 requestLayout();
4509 invalidate(true);
4514 * Removes a view during layout. This is useful if in your onLayout() method,
4515 * you need to remove more views.
4517 * <p><strong>Note:</strong> do not invoke this method from
4518 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4519 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4521 * @param view the view to remove from the group
4523 public void removeViewInLayout(View view) {
4524 removeViewInternal(view);
4528 * Removes a range of views during layout. This is useful if in your onLayout() method,
4529 * you need to remove more views.
4531 * <p><strong>Note:</strong> do not invoke this method from
4532 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4533 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4535 * @param start the index of the first view to remove from the group
4536 * @param count the number of views to remove from the group
4538 public void removeViewsInLayout(int start, int count) {
4539 removeViewsInternal(start, count);
4543 * Removes the view at the specified position in the group.
4545 * <p><strong>Note:</strong> do not invoke this method from
4546 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4547 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4549 * @param index the position in the group of the view to remove
4551 public void removeViewAt(int index) {
4552 removeViewInternal(index, getChildAt(index));
4553 requestLayout();
4554 invalidate(true);
4558 * Removes the specified range of views from the group.
4560 * <p><strong>Note:</strong> do not invoke this method from
4561 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4562 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4564 * @param start the first position in the group of the range of views to remove
4565 * @param count the number of views to remove
4567 public void removeViews(int start, int count) {
4568 removeViewsInternal(start, count);
4569 requestLayout();
4570 invalidate(true);
4573 private boolean removeViewInternal(View view) {
4574 final int index = indexOfChild(view);
4575 if (index >= 0) {
4576 removeViewInternal(index, view);
4577 return true;
4579 return false;
4582 private void removeViewInternal(int index, View view) {
4584 if (mTransition != null) {
4585 mTransition.removeChild(this, view);
4588 boolean clearChildFocus = false;
4589 if (view == mFocused) {
4590 view.unFocus(null);
4591 clearChildFocus = true;
4594 view.clearAccessibilityFocus();
4596 cancelTouchTarget(view);
4597 cancelHoverTarget(view);
4599 if (view.getAnimation() != null ||
4600 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4601 addDisappearingView(view);
4602 } else if (view.mAttachInfo != null) {
4603 view.dispatchDetachedFromWindow();
4606 if (view.hasTransientState()) {
4607 childHasTransientStateChanged(view, false);
4610 needGlobalAttributesUpdate(false);
4612 removeFromArray(index);
4614 if (clearChildFocus) {
4615 clearChildFocus(view);
4616 if (!rootViewRequestFocus()) {
4617 notifyGlobalFocusCleared(this);
4621 dispatchViewRemoved(view);
4623 if (view.getVisibility() != View.GONE) {
4624 notifySubtreeAccessibilityStateChangedIfNeeded();
4627 int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
4628 for (int i = 0; i < transientCount; ++i) {
4629 final int oldIndex = mTransientIndices.get(i);
4630 if (index < oldIndex) {
4631 mTransientIndices.set(i, oldIndex - 1);
4637 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4638 * not null, changes in layout which occur because of children being added to or removed from
4639 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4640 * object. By default, the transition object is null (so layout changes are not animated).
4642 * <p>Replacing a non-null transition will cause that previous transition to be
4643 * canceled, if it is currently running, to restore this container to
4644 * its correct post-transition state.</p>
4646 * @param transition The LayoutTransition object that will animated changes in layout. A value
4647 * of <code>null</code> means no transition will run on layout changes.
4648 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
4650 public void setLayoutTransition(LayoutTransition transition) {
4651 if (mTransition != null) {
4652 LayoutTransition previousTransition = mTransition;
4653 previousTransition.cancel();
4654 previousTransition.removeTransitionListener(mLayoutTransitionListener);
4656 mTransition = transition;
4657 if (mTransition != null) {
4658 mTransition.addTransitionListener(mLayoutTransitionListener);
4663 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4664 * not null, changes in layout which occur because of children being added to or removed from
4665 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4666 * object. By default, the transition object is null (so layout changes are not animated).
4668 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
4669 * A value of <code>null</code> means no transition will run on layout changes.
4671 public LayoutTransition getLayoutTransition() {
4672 return mTransition;
4675 private void removeViewsInternal(int start, int count) {
4676 final View focused = mFocused;
4677 final boolean detach = mAttachInfo != null;
4678 boolean clearChildFocus = false;
4680 final View[] children = mChildren;
4681 final int end = start + count;
4683 for (int i = start; i < end; i++) {
4684 final View view = children[i];
4686 if (mTransition != null) {
4687 mTransition.removeChild(this, view);
4690 if (view == focused) {
4691 view.unFocus(null);
4692 clearChildFocus = true;
4695 view.clearAccessibilityFocus();
4697 cancelTouchTarget(view);
4698 cancelHoverTarget(view);
4700 if (view.getAnimation() != null ||
4701 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4702 addDisappearingView(view);
4703 } else if (detach) {
4704 view.dispatchDetachedFromWindow();
4707 if (view.hasTransientState()) {
4708 childHasTransientStateChanged(view, false);
4711 needGlobalAttributesUpdate(false);
4713 dispatchViewRemoved(view);
4716 removeFromArray(start, count);
4718 if (clearChildFocus) {
4719 clearChildFocus(focused);
4720 if (!rootViewRequestFocus()) {
4721 notifyGlobalFocusCleared(focused);
4727 * Call this method to remove all child views from the
4728 * ViewGroup.
4730 * <p><strong>Note:</strong> do not invoke this method from
4731 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4732 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4734 public void removeAllViews() {
4735 removeAllViewsInLayout();
4736 requestLayout();
4737 invalidate(true);
4741 * Called by a ViewGroup subclass to remove child views from itself,
4742 * when it must first know its size on screen before it can calculate how many
4743 * child views it will render. An example is a Gallery or a ListView, which
4744 * may "have" 50 children, but actually only render the number of children
4745 * that can currently fit inside the object on screen. Do not call
4746 * this method unless you are extending ViewGroup and understand the
4747 * view measuring and layout pipeline.
4749 * <p><strong>Note:</strong> do not invoke this method from
4750 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4751 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4753 public void removeAllViewsInLayout() {
4754 final int count = mChildrenCount;
4755 if (count <= 0) {
4756 return;
4759 final View[] children = mChildren;
4760 mChildrenCount = 0;
4762 final View focused = mFocused;
4763 final boolean detach = mAttachInfo != null;
4764 boolean clearChildFocus = false;
4766 needGlobalAttributesUpdate(false);
4768 for (int i = count - 1; i >= 0; i--) {
4769 final View view = children[i];
4771 if (mTransition != null) {
4772 mTransition.removeChild(this, view);
4775 if (view == focused) {
4776 view.unFocus(null);
4777 clearChildFocus = true;
4780 view.clearAccessibilityFocus();
4782 cancelTouchTarget(view);
4783 cancelHoverTarget(view);
4785 if (view.getAnimation() != null ||
4786 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4787 addDisappearingView(view);
4788 } else if (detach) {
4789 view.dispatchDetachedFromWindow();
4792 if (view.hasTransientState()) {
4793 childHasTransientStateChanged(view, false);
4796 dispatchViewRemoved(view);
4798 view.mParent = null;
4799 children[i] = null;
4802 if (clearChildFocus) {
4803 clearChildFocus(focused);
4804 if (!rootViewRequestFocus()) {
4805 notifyGlobalFocusCleared(focused);
4811 * Finishes the removal of a detached view. This method will dispatch the detached from
4812 * window event and notify the hierarchy change listener.
4813 * <p>
4814 * This method is intended to be lightweight and makes no assumptions about whether the
4815 * parent or child should be redrawn. Proper use of this method will include also making
4816 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4817 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4818 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
4819 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4821 * @param child the child to be definitely removed from the view hierarchy
4822 * @param animate if true and the view has an animation, the view is placed in the
4823 * disappearing views list, otherwise, it is detached from the window
4825 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4826 * @see #detachAllViewsFromParent()
4827 * @see #detachViewFromParent(View)
4828 * @see #detachViewFromParent(int)
4830 protected void removeDetachedView(View child, boolean animate) {
4831 if (mTransition != null) {
4832 mTransition.removeChild(this, child);
4835 if (child == mFocused) {
4836 child.clearFocus();
4839 child.clearAccessibilityFocus();
4841 cancelTouchTarget(child);
4842 cancelHoverTarget(child);
4844 if ((animate && child.getAnimation() != null) ||
4845 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
4846 addDisappearingView(child);
4847 } else if (child.mAttachInfo != null) {
4848 child.dispatchDetachedFromWindow();
4851 if (child.hasTransientState()) {
4852 childHasTransientStateChanged(child, false);
4855 dispatchViewRemoved(child);
4859 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
4860 * sets the layout parameters and puts the view in the list of children so that
4861 * it can be retrieved by calling {@link #getChildAt(int)}.
4862 * <p>
4863 * This method is intended to be lightweight and makes no assumptions about whether the
4864 * parent or child should be redrawn. Proper use of this method will include also making
4865 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4866 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4867 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
4868 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4869 * <p>
4870 * This method should be called only for views which were detached from their parent.
4872 * @param child the child to attach
4873 * @param index the index at which the child should be attached
4874 * @param params the layout parameters of the child
4876 * @see #removeDetachedView(View, boolean)
4877 * @see #detachAllViewsFromParent()
4878 * @see #detachViewFromParent(View)
4879 * @see #detachViewFromParent(int)
4881 protected void attachViewToParent(View child, int index, LayoutParams params) {
4882 child.mLayoutParams = params;
4884 if (index < 0) {
4885 index = mChildrenCount;
4888 addInArray(child, index);
4890 child.mParent = this;
4891 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
4892 & ~PFLAG_DRAWING_CACHE_VALID)
4893 | PFLAG_DRAWN | PFLAG_INVALIDATED;
4894 this.mPrivateFlags |= PFLAG_INVALIDATED;
4896 if (child.hasFocus()) {
4897 requestChildFocus(child, child.findFocus());
4902 * Detaches a view from its parent. Detaching a view should be followed
4903 * either by a call to
4904 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4905 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4906 * temporary; reattachment or removal should happen within the same drawing cycle as
4907 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4908 * call to {@link #getChildAt(int)}.
4910 * @param child the child to detach
4912 * @see #detachViewFromParent(int)
4913 * @see #detachViewsFromParent(int, int)
4914 * @see #detachAllViewsFromParent()
4915 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4916 * @see #removeDetachedView(View, boolean)
4918 protected void detachViewFromParent(View child) {
4919 removeFromArray(indexOfChild(child));
4923 * Detaches a view from its parent. Detaching a view should be followed
4924 * either by a call to
4925 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4926 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4927 * temporary; reattachment or removal should happen within the same drawing cycle as
4928 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4929 * call to {@link #getChildAt(int)}.
4931 * @param index the index of the child to detach
4933 * @see #detachViewFromParent(View)
4934 * @see #detachAllViewsFromParent()
4935 * @see #detachViewsFromParent(int, int)
4936 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4937 * @see #removeDetachedView(View, boolean)
4939 protected void detachViewFromParent(int index) {
4940 removeFromArray(index);
4944 * Detaches a range of views from their parents. Detaching a view should be followed
4945 * either by a call to
4946 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4947 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4948 * temporary; reattachment or removal should happen within the same drawing cycle as
4949 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4950 * call to {@link #getChildAt(int)}.
4952 * @param start the first index of the childrend range to detach
4953 * @param count the number of children to detach
4955 * @see #detachViewFromParent(View)
4956 * @see #detachViewFromParent(int)
4957 * @see #detachAllViewsFromParent()
4958 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4959 * @see #removeDetachedView(View, boolean)
4961 protected void detachViewsFromParent(int start, int count) {
4962 removeFromArray(start, count);
4966 * Detaches all views from the parent. Detaching a view should be followed
4967 * either by a call to
4968 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4969 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4970 * temporary; reattachment or removal should happen within the same drawing cycle as
4971 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4972 * call to {@link #getChildAt(int)}.
4974 * @see #detachViewFromParent(View)
4975 * @see #detachViewFromParent(int)
4976 * @see #detachViewsFromParent(int, int)
4977 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4978 * @see #removeDetachedView(View, boolean)
4980 protected void detachAllViewsFromParent() {
4981 final int count = mChildrenCount;
4982 if (count <= 0) {
4983 return;
4986 final View[] children = mChildren;
4987 mChildrenCount = 0;
4989 for (int i = count - 1; i >= 0; i--) {
4990 children[i].mParent = null;
4991 children[i] = null;
4996 * Don't call or override this method. It is used for the implementation of
4997 * the view hierarchy.
4999 public final void invalidateChild(View child, final Rect dirty) {
5000 ViewParent parent = this;
5002 final AttachInfo attachInfo = mAttachInfo;
5003 if (attachInfo != null) {
5004 // If the child is drawing an animation, we want to copy this flag onto
5005 // ourselves and the parent to make sure the invalidate request goes
5006 // through
5007 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
5008 == PFLAG_DRAW_ANIMATION;
5010 // Check whether the child that requests the invalidate is fully opaque
5011 // Views being animated or transformed are not considered opaque because we may
5012 // be invalidating their old position and need the parent to paint behind them.
5013 Matrix childMatrix = child.getMatrix();
5014 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
5015 child.getAnimation() == null && childMatrix.isIdentity();
5016 // Mark the child as dirty, using the appropriate flag
5017 // Make sure we do not set both flags at the same time
5018 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
5020 if (child.mLayerType != LAYER_TYPE_NONE) {
5021 mPrivateFlags |= PFLAG_INVALIDATED;
5022 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5025 final int[] location = attachInfo.mInvalidateChildLocation;
5026 location[CHILD_LEFT_INDEX] = child.mLeft;
5027 location[CHILD_TOP_INDEX] = child.mTop;
5028 if (!childMatrix.isIdentity() ||
5029 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
5030 RectF boundingRect = attachInfo.mTmpTransformRect;
5031 boundingRect.set(dirty);
5032 Matrix transformMatrix;
5033 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
5034 Transformation t = attachInfo.mTmpTransformation;
5035 boolean transformed = getChildStaticTransformation(child, t);
5036 if (transformed) {
5037 transformMatrix = attachInfo.mTmpMatrix;
5038 transformMatrix.set(t.getMatrix());
5039 if (!childMatrix.isIdentity()) {
5040 transformMatrix.preConcat(childMatrix);
5042 } else {
5043 transformMatrix = childMatrix;
5045 } else {
5046 transformMatrix = childMatrix;
5048 transformMatrix.mapRect(boundingRect);
5049 dirty.set((int) (boundingRect.left - 0.5f),
5050 (int) (boundingRect.top - 0.5f),
5051 (int) (boundingRect.right + 0.5f),
5052 (int) (boundingRect.bottom + 0.5f));
5055 do {
5056 View view = null;
5057 if (parent instanceof View) {
5058 view = (View) parent;
5061 if (drawAnimation) {
5062 if (view != null) {
5063 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
5064 } else if (parent instanceof ViewRootImpl) {
5065 ((ViewRootImpl) parent).mIsAnimating = true;
5069 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
5070 // flag coming from the child that initiated the invalidate
5071 if (view != null) {
5072 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
5073 view.getSolidColor() == 0) {
5074 opaqueFlag = PFLAG_DIRTY;
5076 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
5077 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
5081 parent = parent.invalidateChildInParent(location, dirty);
5082 if (view != null) {
5083 // Account for transform on current parent
5084 Matrix m = view.getMatrix();
5085 if (!m.isIdentity()) {
5086 RectF boundingRect = attachInfo.mTmpTransformRect;
5087 boundingRect.set(dirty);
5088 m.mapRect(boundingRect);
5089 dirty.set((int) (boundingRect.left - 0.5f),
5090 (int) (boundingRect.top - 0.5f),
5091 (int) (boundingRect.right + 0.5f),
5092 (int) (boundingRect.bottom + 0.5f));
5095 } while (parent != null);
5100 * Don't call or override this method. It is used for the implementation of
5101 * the view hierarchy.
5103 * This implementation returns null if this ViewGroup does not have a parent,
5104 * if this ViewGroup is already fully invalidated or if the dirty rectangle
5105 * does not intersect with this ViewGroup's bounds.
5107 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
5108 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
5109 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
5110 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
5111 FLAG_OPTIMIZE_INVALIDATE) {
5112 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
5113 location[CHILD_TOP_INDEX] - mScrollY);
5114 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
5115 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
5118 final int left = mLeft;
5119 final int top = mTop;
5121 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
5122 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
5123 dirty.setEmpty();
5126 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5128 location[CHILD_LEFT_INDEX] = left;
5129 location[CHILD_TOP_INDEX] = top;
5131 if (mLayerType != LAYER_TYPE_NONE) {
5132 mPrivateFlags |= PFLAG_INVALIDATED;
5135 return mParent;
5137 } else {
5138 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
5140 location[CHILD_LEFT_INDEX] = mLeft;
5141 location[CHILD_TOP_INDEX] = mTop;
5142 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
5143 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
5144 } else {
5145 // in case the dirty rect extends outside the bounds of this container
5146 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
5149 if (mLayerType != LAYER_TYPE_NONE) {
5150 mPrivateFlags |= PFLAG_INVALIDATED;
5153 return mParent;
5157 return null;
5161 * Native-calculated damage path
5162 * Returns false if this path was unable to complete successfully. This means
5163 * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
5164 * damage area
5165 * @hide
5167 public boolean damageChildDeferred(View child) {
5168 ViewParent parent = getParent();
5169 while (parent != null) {
5170 if (parent instanceof ViewGroup) {
5171 parent = parent.getParent();
5172 } else if (parent instanceof ViewRootImpl) {
5173 ((ViewRootImpl) parent).invalidate();
5174 return true;
5175 } else {
5176 parent = null;
5179 return false;
5183 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
5184 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
5185 * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
5187 * @hide
5189 public void damageChild(View child, final Rect dirty) {
5190 if (damageChildDeferred(child)) {
5191 return;
5194 ViewParent parent = this;
5196 final AttachInfo attachInfo = mAttachInfo;
5197 if (attachInfo != null) {
5198 int left = child.mLeft;
5199 int top = child.mTop;
5200 if (!child.getMatrix().isIdentity()) {
5201 child.transformRect(dirty);
5204 do {
5205 if (parent instanceof ViewGroup) {
5206 ViewGroup parentVG = (ViewGroup) parent;
5207 if (parentVG.mLayerType != LAYER_TYPE_NONE) {
5208 // Layered parents should be recreated, not just re-issued
5209 parentVG.invalidate();
5210 parent = null;
5211 } else {
5212 parent = parentVG.damageChildInParent(left, top, dirty);
5213 left = parentVG.mLeft;
5214 top = parentVG.mTop;
5216 } else {
5217 // Reached the top; this calls into the usual invalidate method in
5218 // ViewRootImpl, which schedules a traversal
5219 final int[] location = attachInfo.mInvalidateChildLocation;
5220 location[0] = left;
5221 location[1] = top;
5222 parent = parent.invalidateChildInParent(location, dirty);
5224 } while (parent != null);
5229 * Quick invalidation method that simply transforms the dirty rect into the parent's
5230 * coordinate system, pruning the invalidation if the parent has already been invalidated.
5232 * @hide
5234 protected ViewParent damageChildInParent(int left, int top, final Rect dirty) {
5235 if ((mPrivateFlags & PFLAG_DRAWN) != 0
5236 || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) != 0) {
5237 dirty.offset(left - mScrollX, top - mScrollY);
5238 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
5239 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
5242 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
5243 dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
5245 if (!getMatrix().isIdentity()) {
5246 transformRect(dirty);
5249 return mParent;
5253 return null;
5257 * Offset a rectangle that is in a descendant's coordinate
5258 * space into our coordinate space.
5259 * @param descendant A descendant of this view
5260 * @param rect A rectangle defined in descendant's coordinate space.
5262 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
5263 offsetRectBetweenParentAndChild(descendant, rect, true, false);
5267 * Offset a rectangle that is in our coordinate space into an ancestor's
5268 * coordinate space.
5269 * @param descendant A descendant of this view
5270 * @param rect A rectangle defined in descendant's coordinate space.
5272 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
5273 offsetRectBetweenParentAndChild(descendant, rect, false, false);
5277 * Helper method that offsets a rect either from parent to descendant or
5278 * descendant to parent.
5280 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
5281 boolean offsetFromChildToParent, boolean clipToBounds) {
5283 // already in the same coord system :)
5284 if (descendant == this) {
5285 return;
5288 ViewParent theParent = descendant.mParent;
5290 // search and offset up to the parent
5291 while ((theParent != null)
5292 && (theParent instanceof View)
5293 && (theParent != this)) {
5295 if (offsetFromChildToParent) {
5296 rect.offset(descendant.mLeft - descendant.mScrollX,
5297 descendant.mTop - descendant.mScrollY);
5298 if (clipToBounds) {
5299 View p = (View) theParent;
5300 boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
5301 p.mBottom - p.mTop);
5302 if (!intersected) {
5303 rect.setEmpty();
5306 } else {
5307 if (clipToBounds) {
5308 View p = (View) theParent;
5309 boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
5310 p.mBottom - p.mTop);
5311 if (!intersected) {
5312 rect.setEmpty();
5315 rect.offset(descendant.mScrollX - descendant.mLeft,
5316 descendant.mScrollY - descendant.mTop);
5319 descendant = (View) theParent;
5320 theParent = descendant.mParent;
5323 // now that we are up to this view, need to offset one more time
5324 // to get into our coordinate space
5325 if (theParent == this) {
5326 if (offsetFromChildToParent) {
5327 rect.offset(descendant.mLeft - descendant.mScrollX,
5328 descendant.mTop - descendant.mScrollY);
5329 } else {
5330 rect.offset(descendant.mScrollX - descendant.mLeft,
5331 descendant.mScrollY - descendant.mTop);
5333 } else {
5334 throw new IllegalArgumentException("parameter must be a descendant of this view");
5339 * Offset the vertical location of all children of this view by the specified number of pixels.
5341 * @param offset the number of pixels to offset
5343 * @hide
5345 public void offsetChildrenTopAndBottom(int offset) {
5346 final int count = mChildrenCount;
5347 final View[] children = mChildren;
5348 boolean invalidate = false;
5350 for (int i = 0; i < count; i++) {
5351 final View v = children[i];
5352 v.mTop += offset;
5353 v.mBottom += offset;
5354 if (v.mRenderNode != null) {
5355 invalidate = true;
5356 v.mRenderNode.offsetTopAndBottom(offset);
5360 if (invalidate) {
5361 invalidateViewProperty(false, false);
5363 notifySubtreeAccessibilityStateChangedIfNeeded();
5367 * {@inheritDoc}
5369 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
5370 // It doesn't make a whole lot of sense to call this on a view that isn't attached,
5371 // but for some simple tests it can be useful. If we don't have attach info this
5372 // will allocate memory.
5373 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
5374 rect.set(r);
5376 if (!child.hasIdentityMatrix()) {
5377 child.getMatrix().mapRect(rect);
5380 final int dx = child.mLeft - mScrollX;
5381 final int dy = child.mTop - mScrollY;
5383 rect.offset(dx, dy);
5385 if (offset != null) {
5386 if (!child.hasIdentityMatrix()) {
5387 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
5388 : new float[2];
5389 position[0] = offset.x;
5390 position[1] = offset.y;
5391 child.getMatrix().mapPoints(position);
5392 offset.x = (int) (position[0] + 0.5f);
5393 offset.y = (int) (position[1] + 0.5f);
5395 offset.x += dx;
5396 offset.y += dy;
5399 final int width = mRight - mLeft;
5400 final int height = mBottom - mTop;
5402 boolean rectIsVisible = true;
5403 if (mParent == null ||
5404 (mParent instanceof ViewGroup && ((ViewGroup) mParent).getClipChildren())) {
5405 // Clip to bounds.
5406 rectIsVisible = rect.intersect(0, 0, width, height);
5409 if (rectIsVisible && (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
5410 // Clip to padding.
5411 rectIsVisible = rect.intersect(mPaddingLeft, mPaddingTop,
5412 width - mPaddingRight, height - mPaddingBottom);
5415 if (rectIsVisible && mClipBounds != null) {
5416 // Clip to clipBounds.
5417 rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
5418 mClipBounds.bottom);
5420 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f), (int) (rect.right + 0.5f),
5421 (int) (rect.bottom + 0.5f));
5422 if (rectIsVisible && mParent != null) {
5423 rectIsVisible = mParent.getChildVisibleRect(this, r, offset);
5425 return rectIsVisible;
5429 * {@inheritDoc}
5431 @Override
5432 public final void layout(int l, int t, int r, int b) {
5433 if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
5434 if (mTransition != null) {
5435 mTransition.layoutChange(this);
5437 super.layout(l, t, r, b);
5438 } else {
5439 // record the fact that we noop'd it; request layout when transition finishes
5440 mLayoutCalledWhileSuppressed = true;
5445 * {@inheritDoc}
5447 @Override
5448 protected abstract void onLayout(boolean changed,
5449 int l, int t, int r, int b);
5452 * Indicates whether the view group has the ability to animate its children
5453 * after the first layout.
5455 * @return true if the children can be animated, false otherwise
5457 protected boolean canAnimate() {
5458 return mLayoutAnimationController != null;
5462 * Runs the layout animation. Calling this method triggers a relayout of
5463 * this view group.
5465 public void startLayoutAnimation() {
5466 if (mLayoutAnimationController != null) {
5467 mGroupFlags |= FLAG_RUN_ANIMATION;
5468 requestLayout();
5473 * Schedules the layout animation to be played after the next layout pass
5474 * of this view group. This can be used to restart the layout animation
5475 * when the content of the view group changes or when the activity is
5476 * paused and resumed.
5478 public void scheduleLayoutAnimation() {
5479 mGroupFlags |= FLAG_RUN_ANIMATION;
5483 * Sets the layout animation controller used to animate the group's
5484 * children after the first layout.
5486 * @param controller the animation controller
5488 public void setLayoutAnimation(LayoutAnimationController controller) {
5489 mLayoutAnimationController = controller;
5490 if (mLayoutAnimationController != null) {
5491 mGroupFlags |= FLAG_RUN_ANIMATION;
5496 * Returns the layout animation controller used to animate the group's
5497 * children.
5499 * @return the current animation controller
5501 public LayoutAnimationController getLayoutAnimation() {
5502 return mLayoutAnimationController;
5506 * Indicates whether the children's drawing cache is used during a layout
5507 * animation. By default, the drawing cache is enabled but this will prevent
5508 * nested layout animations from working. To nest animations, you must disable
5509 * the cache.
5511 * @return true if the animation cache is enabled, false otherwise
5513 * @see #setAnimationCacheEnabled(boolean)
5514 * @see View#setDrawingCacheEnabled(boolean)
5516 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5517 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
5519 public boolean isAnimationCacheEnabled() {
5520 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
5524 * Enables or disables the children's drawing cache during a layout animation.
5525 * By default, the drawing cache is enabled but this will prevent nested
5526 * layout animations from working. To nest animations, you must disable the
5527 * cache.
5529 * @param enabled true to enable the animation cache, false otherwise
5531 * @see #isAnimationCacheEnabled()
5532 * @see View#setDrawingCacheEnabled(boolean)
5534 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5535 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
5537 public void setAnimationCacheEnabled(boolean enabled) {
5538 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
5542 * Indicates whether this ViewGroup will always try to draw its children using their
5543 * drawing cache. By default this property is enabled.
5545 * @return true if the animation cache is enabled, false otherwise
5547 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5548 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5549 * @see View#setDrawingCacheEnabled(boolean)
5551 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5552 * Child views may no longer have their caching behavior disabled by parents.
5554 public boolean isAlwaysDrawnWithCacheEnabled() {
5555 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
5559 * Indicates whether this ViewGroup will always try to draw its children using their
5560 * drawing cache. This property can be set to true when the cache rendering is
5561 * slightly different from the children's normal rendering. Renderings can be different,
5562 * for instance, when the cache's quality is set to low.
5564 * When this property is disabled, the ViewGroup will use the drawing cache of its
5565 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
5566 * when to start using the drawing cache and when to stop using it.
5568 * @param always true to always draw with the drawing cache, false otherwise
5570 * @see #isAlwaysDrawnWithCacheEnabled()
5571 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5572 * @see View#setDrawingCacheEnabled(boolean)
5573 * @see View#setDrawingCacheQuality(int)
5575 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5576 * Child views may no longer have their caching behavior disabled by parents.
5578 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
5579 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
5583 * Indicates whether the ViewGroup is currently drawing its children using
5584 * their drawing cache.
5586 * @return true if children should be drawn with their cache, false otherwise
5588 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5589 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5591 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5592 * Child views may no longer be forced to cache their rendering state by their parents.
5593 * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
5595 protected boolean isChildrenDrawnWithCacheEnabled() {
5596 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
5600 * Tells the ViewGroup to draw its children using their drawing cache. This property
5601 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
5602 * will be used only if it has been enabled.
5604 * Subclasses should call this method to start and stop using the drawing cache when
5605 * they perform performance sensitive operations, like scrolling or animating.
5607 * @param enabled true if children should be drawn with their cache, false otherwise
5609 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5610 * @see #isChildrenDrawnWithCacheEnabled()
5612 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5613 * Child views may no longer be forced to cache their rendering state by their parents.
5614 * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
5616 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
5617 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
5621 * Indicates whether the ViewGroup is drawing its children in the order defined by
5622 * {@link #getChildDrawingOrder(int, int)}.
5624 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
5625 * false otherwise
5627 * @see #setChildrenDrawingOrderEnabled(boolean)
5628 * @see #getChildDrawingOrder(int, int)
5630 @ViewDebug.ExportedProperty(category = "drawing")
5631 protected boolean isChildrenDrawingOrderEnabled() {
5632 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
5636 * Tells the ViewGroup whether to draw its children in the order defined by the method
5637 * {@link #getChildDrawingOrder(int, int)}.
5638 * <p>
5639 * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
5640 * will override custom child ordering done via this method.
5642 * @param enabled true if the order of the children when drawing is determined by
5643 * {@link #getChildDrawingOrder(int, int)}, false otherwise
5645 * @see #isChildrenDrawingOrderEnabled()
5646 * @see #getChildDrawingOrder(int, int)
5648 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
5649 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
5652 private boolean hasBooleanFlag(int flag) {
5653 return (mGroupFlags & flag) == flag;
5656 private void setBooleanFlag(int flag, boolean value) {
5657 if (value) {
5658 mGroupFlags |= flag;
5659 } else {
5660 mGroupFlags &= ~flag;
5665 * Returns an integer indicating what types of drawing caches are kept in memory.
5667 * @see #setPersistentDrawingCache(int)
5668 * @see #setAnimationCacheEnabled(boolean)
5670 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
5671 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5672 * and {@link #PERSISTENT_ALL_CACHES}
5674 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
5675 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
5676 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
5677 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
5678 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
5680 public int getPersistentDrawingCache() {
5681 return mPersistentDrawingCache;
5685 * Indicates what types of drawing caches should be kept in memory after
5686 * they have been created.
5688 * @see #getPersistentDrawingCache()
5689 * @see #setAnimationCacheEnabled(boolean)
5691 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
5692 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5693 * and {@link #PERSISTENT_ALL_CACHES}
5695 public void setPersistentDrawingCache(int drawingCacheToKeep) {
5696 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
5699 private void setLayoutMode(int layoutMode, boolean explicitly) {
5700 mLayoutMode = layoutMode;
5701 setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
5705 * Recursively traverse the view hierarchy, resetting the layoutMode of any
5706 * descendants that had inherited a different layoutMode from a previous parent.
5707 * Recursion terminates when a descendant's mode is:
5708 * <ul>
5709 * <li>Undefined</li>
5710 * <li>The same as the root node's</li>
5711 * <li>A mode that had been explicitly set</li>
5712 * <ul/>
5713 * The first two clauses are optimizations.
5714 * @param layoutModeOfRoot
5716 @Override
5717 void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
5718 if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
5719 mLayoutMode == layoutModeOfRoot ||
5720 hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
5721 return;
5723 setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
5725 // apply recursively
5726 for (int i = 0, N = getChildCount(); i < N; i++) {
5727 getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
5732 * Returns the basis of alignment during layout operations on this ViewGroup:
5733 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5734 * <p>
5735 * If no layoutMode was explicitly set, either programmatically or in an XML resource,
5736 * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
5737 * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
5739 * @return the layout mode to use during layout operations
5741 * @see #setLayoutMode(int)
5743 public int getLayoutMode() {
5744 if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
5745 int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
5746 ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
5747 setLayoutMode(inheritedLayoutMode, false);
5749 return mLayoutMode;
5753 * Sets the basis of alignment during the layout of this ViewGroup.
5754 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
5755 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5757 * @param layoutMode the layout mode to use during layout operations
5759 * @see #getLayoutMode()
5760 * @attr ref android.R.styleable#ViewGroup_layoutMode
5762 public void setLayoutMode(int layoutMode) {
5763 if (mLayoutMode != layoutMode) {
5764 invalidateInheritedLayoutMode(layoutMode);
5765 setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
5766 requestLayout();
5771 * Returns a new set of layout parameters based on the supplied attributes set.
5773 * @param attrs the attributes to build the layout parameters from
5775 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5776 * of its descendants
5778 public LayoutParams generateLayoutParams(AttributeSet attrs) {
5779 return new LayoutParams(getContext(), attrs);
5783 * Returns a safe set of layout parameters based on the supplied layout params.
5784 * When a ViewGroup is passed a View whose layout params do not pass the test of
5785 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
5786 * is invoked. This method should return a new set of layout params suitable for
5787 * this ViewGroup, possibly by copying the appropriate attributes from the
5788 * specified set of layout params.
5790 * @param p The layout parameters to convert into a suitable set of layout parameters
5791 * for this ViewGroup.
5793 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5794 * of its descendants
5796 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
5797 return p;
5801 * Returns a set of default layout parameters. These parameters are requested
5802 * when the View passed to {@link #addView(View)} has no layout parameters
5803 * already set. If null is returned, an exception is thrown from addView.
5805 * @return a set of default layout parameters or null
5807 protected LayoutParams generateDefaultLayoutParams() {
5808 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
5812 * {@inheritDoc}
5814 @Override
5815 protected void debug(int depth) {
5816 super.debug(depth);
5817 String output;
5819 if (mFocused != null) {
5820 output = debugIndent(depth);
5821 output += "mFocused";
5822 Log.d(VIEW_LOG_TAG, output);
5824 if (mChildrenCount != 0) {
5825 output = debugIndent(depth);
5826 output += "{";
5827 Log.d(VIEW_LOG_TAG, output);
5829 int count = mChildrenCount;
5830 for (int i = 0; i < count; i++) {
5831 View child = mChildren[i];
5832 child.debug(depth + 1);
5835 if (mChildrenCount != 0) {
5836 output = debugIndent(depth);
5837 output += "}";
5838 Log.d(VIEW_LOG_TAG, output);
5843 * Returns the position in the group of the specified child view.
5845 * @param child the view for which to get the position
5846 * @return a positive integer representing the position of the view in the
5847 * group, or -1 if the view does not exist in the group
5849 public int indexOfChild(View child) {
5850 final int count = mChildrenCount;
5851 final View[] children = mChildren;
5852 for (int i = 0; i < count; i++) {
5853 if (children[i] == child) {
5854 return i;
5857 return -1;
5861 * Returns the number of children in the group.
5863 * @return a positive integer representing the number of children in
5864 * the group
5866 public int getChildCount() {
5867 return mChildrenCount;
5871 * Returns the view at the specified position in the group.
5873 * @param index the position at which to get the view from
5874 * @return the view at the specified position or null if the position
5875 * does not exist within the group
5877 public View getChildAt(int index) {
5878 if (index < 0 || index >= mChildrenCount) {
5879 return null;
5881 return mChildren[index];
5885 * Ask all of the children of this view to measure themselves, taking into
5886 * account both the MeasureSpec requirements for this view and its padding.
5887 * We skip children that are in the GONE state The heavy lifting is done in
5888 * getChildMeasureSpec.
5890 * @param widthMeasureSpec The width requirements for this view
5891 * @param heightMeasureSpec The height requirements for this view
5893 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
5894 final int size = mChildrenCount;
5895 final View[] children = mChildren;
5896 for (int i = 0; i < size; ++i) {
5897 final View child = children[i];
5898 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
5899 measureChild(child, widthMeasureSpec, heightMeasureSpec);
5905 * Ask one of the children of this view to measure itself, taking into
5906 * account both the MeasureSpec requirements for this view and its padding.
5907 * The heavy lifting is done in getChildMeasureSpec.
5909 * @param child The child to measure
5910 * @param parentWidthMeasureSpec The width requirements for this view
5911 * @param parentHeightMeasureSpec The height requirements for this view
5913 protected void measureChild(View child, int parentWidthMeasureSpec,
5914 int parentHeightMeasureSpec) {
5915 final LayoutParams lp = child.getLayoutParams();
5917 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5918 mPaddingLeft + mPaddingRight, lp.width);
5919 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5920 mPaddingTop + mPaddingBottom, lp.height);
5922 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5926 * Ask one of the children of this view to measure itself, taking into
5927 * account both the MeasureSpec requirements for this view and its padding
5928 * and margins. The child must have MarginLayoutParams The heavy lifting is
5929 * done in getChildMeasureSpec.
5931 * @param child The child to measure
5932 * @param parentWidthMeasureSpec The width requirements for this view
5933 * @param widthUsed Extra space that has been used up by the parent
5934 * horizontally (possibly by other children of the parent)
5935 * @param parentHeightMeasureSpec The height requirements for this view
5936 * @param heightUsed Extra space that has been used up by the parent
5937 * vertically (possibly by other children of the parent)
5939 protected void measureChildWithMargins(View child,
5940 int parentWidthMeasureSpec, int widthUsed,
5941 int parentHeightMeasureSpec, int heightUsed) {
5942 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
5944 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5945 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
5946 + widthUsed, lp.width);
5947 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5948 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
5949 + heightUsed, lp.height);
5951 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5955 * Does the hard part of measureChildren: figuring out the MeasureSpec to
5956 * pass to a particular child. This method figures out the right MeasureSpec
5957 * for one dimension (height or width) of one child view.
5959 * The goal is to combine information from our MeasureSpec with the
5960 * LayoutParams of the child to get the best possible results. For example,
5961 * if the this view knows its size (because its MeasureSpec has a mode of
5962 * EXACTLY), and the child has indicated in its LayoutParams that it wants
5963 * to be the same size as the parent, the parent should ask the child to
5964 * layout given an exact size.
5966 * @param spec The requirements for this view
5967 * @param padding The padding of this view for the current dimension and
5968 * margins, if applicable
5969 * @param childDimension How big the child wants to be in the current
5970 * dimension
5971 * @return a MeasureSpec integer for the child
5973 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
5974 int specMode = MeasureSpec.getMode(spec);
5975 int specSize = MeasureSpec.getSize(spec);
5977 int size = Math.max(0, specSize - padding);
5979 int resultSize = 0;
5980 int resultMode = 0;
5982 switch (specMode) {
5983 // Parent has imposed an exact size on us
5984 case MeasureSpec.EXACTLY:
5985 if (childDimension >= 0) {
5986 resultSize = childDimension;
5987 resultMode = MeasureSpec.EXACTLY;
5988 } else if (childDimension == LayoutParams.MATCH_PARENT) {
5989 // Child wants to be our size. So be it.
5990 resultSize = size;
5991 resultMode = MeasureSpec.EXACTLY;
5992 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5993 // Child wants to determine its own size. It can't be
5994 // bigger than us.
5995 resultSize = size;
5996 resultMode = MeasureSpec.AT_MOST;
5998 break;
6000 // Parent has imposed a maximum size on us
6001 case MeasureSpec.AT_MOST:
6002 if (childDimension >= 0) {
6003 // Child wants a specific size... so be it
6004 resultSize = childDimension;
6005 resultMode = MeasureSpec.EXACTLY;
6006 } else if (childDimension == LayoutParams.MATCH_PARENT) {
6007 // Child wants to be our size, but our size is not fixed.
6008 // Constrain child to not be bigger than us.
6009 resultSize = size;
6010 resultMode = MeasureSpec.AT_MOST;
6011 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6012 // Child wants to determine its own size. It can't be
6013 // bigger than us.
6014 resultSize = size;
6015 resultMode = MeasureSpec.AT_MOST;
6017 break;
6019 // Parent asked to see how big we want to be
6020 case MeasureSpec.UNSPECIFIED:
6021 if (childDimension >= 0) {
6022 // Child wants a specific size... let him have it
6023 resultSize = childDimension;
6024 resultMode = MeasureSpec.EXACTLY;
6025 } else if (childDimension == LayoutParams.MATCH_PARENT) {
6026 // Child wants to be our size... find out how big it should
6027 // be
6028 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
6029 resultMode = MeasureSpec.UNSPECIFIED;
6030 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6031 // Child wants to determine its own size.... find out how
6032 // big it should be
6033 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
6034 resultMode = MeasureSpec.UNSPECIFIED;
6036 break;
6038 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
6043 * Removes any pending animations for views that have been removed. Call
6044 * this if you don't want animations for exiting views to stack up.
6046 public void clearDisappearingChildren() {
6047 final ArrayList<View> disappearingChildren = mDisappearingChildren;
6048 if (disappearingChildren != null) {
6049 final int count = disappearingChildren.size();
6050 for (int i = 0; i < count; i++) {
6051 final View view = disappearingChildren.get(i);
6052 if (view.mAttachInfo != null) {
6053 view.dispatchDetachedFromWindow();
6055 view.clearAnimation();
6057 disappearingChildren.clear();
6058 invalidate();
6063 * Add a view which is removed from mChildren but still needs animation
6065 * @param v View to add
6067 private void addDisappearingView(View v) {
6068 ArrayList<View> disappearingChildren = mDisappearingChildren;
6070 if (disappearingChildren == null) {
6071 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
6074 disappearingChildren.add(v);
6078 * Cleanup a view when its animation is done. This may mean removing it from
6079 * the list of disappearing views.
6081 * @param view The view whose animation has finished
6082 * @param animation The animation, cannot be null
6084 void finishAnimatingView(final View view, Animation animation) {
6085 final ArrayList<View> disappearingChildren = mDisappearingChildren;
6086 if (disappearingChildren != null) {
6087 if (disappearingChildren.contains(view)) {
6088 disappearingChildren.remove(view);
6090 if (view.mAttachInfo != null) {
6091 view.dispatchDetachedFromWindow();
6094 view.clearAnimation();
6095 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
6099 if (animation != null && !animation.getFillAfter()) {
6100 view.clearAnimation();
6103 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
6104 view.onAnimationEnd();
6105 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
6106 // so we'd rather be safe than sorry
6107 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
6108 // Draw one more frame after the animation is done
6109 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
6114 * Utility function called by View during invalidation to determine whether a view that
6115 * is invisible or gone should still be invalidated because it is being transitioned (and
6116 * therefore still needs to be drawn).
6118 boolean isViewTransitioning(View view) {
6119 return (mTransitioningViews != null && mTransitioningViews.contains(view));
6123 * This method tells the ViewGroup that the given View object, which should have this
6124 * ViewGroup as its parent,
6125 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
6126 * is removed from its parent. This allows animations, such as those used by
6127 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
6128 * the removal of views. A call to this method should always be accompanied by a later call
6129 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
6130 * so that the View finally gets removed.
6132 * @param view The View object to be kept visible even if it gets removed from its parent.
6134 public void startViewTransition(View view) {
6135 if (view.mParent == this) {
6136 if (mTransitioningViews == null) {
6137 mTransitioningViews = new ArrayList<View>();
6139 mTransitioningViews.add(view);
6144 * This method should always be called following an earlier call to
6145 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
6146 * and will no longer be displayed. Note that this method does not perform the functionality
6147 * of removing a view from its parent; it just discontinues the display of a View that
6148 * has previously been removed.
6150 * @return view The View object that has been removed but is being kept around in the visible
6151 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
6153 public void endViewTransition(View view) {
6154 if (mTransitioningViews != null) {
6155 mTransitioningViews.remove(view);
6156 final ArrayList<View> disappearingChildren = mDisappearingChildren;
6157 if (disappearingChildren != null && disappearingChildren.contains(view)) {
6158 disappearingChildren.remove(view);
6159 if (mVisibilityChangingChildren != null &&
6160 mVisibilityChangingChildren.contains(view)) {
6161 mVisibilityChangingChildren.remove(view);
6162 } else {
6163 if (view.mAttachInfo != null) {
6164 view.dispatchDetachedFromWindow();
6166 if (view.mParent != null) {
6167 view.mParent = null;
6170 invalidate();
6175 private LayoutTransition.TransitionListener mLayoutTransitionListener =
6176 new LayoutTransition.TransitionListener() {
6177 @Override
6178 public void startTransition(LayoutTransition transition, ViewGroup container,
6179 View view, int transitionType) {
6180 // We only care about disappearing items, since we need special logic to keep
6181 // those items visible after they've been 'removed'
6182 if (transitionType == LayoutTransition.DISAPPEARING) {
6183 startViewTransition(view);
6187 @Override
6188 public void endTransition(LayoutTransition transition, ViewGroup container,
6189 View view, int transitionType) {
6190 if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
6191 requestLayout();
6192 mLayoutCalledWhileSuppressed = false;
6194 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
6195 endViewTransition(view);
6201 * Tells this ViewGroup to suppress all layout() calls until layout
6202 * suppression is disabled with a later call to suppressLayout(false).
6203 * When layout suppression is disabled, a requestLayout() call is sent
6204 * if layout() was attempted while layout was being suppressed.
6206 * @hide
6208 public void suppressLayout(boolean suppress) {
6209 mSuppressLayout = suppress;
6210 if (!suppress) {
6211 if (mLayoutCalledWhileSuppressed) {
6212 requestLayout();
6213 mLayoutCalledWhileSuppressed = false;
6219 * Returns whether layout calls on this container are currently being
6220 * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
6222 * @return true if layout calls are currently suppressed, false otherwise.
6224 * @hide
6226 public boolean isLayoutSuppressed() {
6227 return mSuppressLayout;
6231 * {@inheritDoc}
6233 @Override
6234 public boolean gatherTransparentRegion(Region region) {
6235 // If no transparent regions requested, we are always opaque.
6236 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
6237 if (meOpaque && region == null) {
6238 // The caller doesn't care about the region, so stop now.
6239 return true;
6241 super.gatherTransparentRegion(region);
6242 final View[] children = mChildren;
6243 final int count = mChildrenCount;
6244 boolean noneOfTheChildrenAreTransparent = true;
6245 for (int i = 0; i < count; i++) {
6246 final View child = children[i];
6247 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
6248 if (!child.gatherTransparentRegion(region)) {
6249 noneOfTheChildrenAreTransparent = false;
6253 return meOpaque || noneOfTheChildrenAreTransparent;
6257 * {@inheritDoc}
6259 public void requestTransparentRegion(View child) {
6260 if (child != null) {
6261 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
6262 if (mParent != null) {
6263 mParent.requestTransparentRegion(this);
6268 @Override
6269 public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
6270 insets = super.dispatchApplyWindowInsets(insets);
6271 if (!insets.isConsumed()) {
6272 final int count = getChildCount();
6273 for (int i = 0; i < count; i++) {
6274 insets = getChildAt(i).dispatchApplyWindowInsets(insets);
6275 if (insets.isConsumed()) {
6276 break;
6280 return insets;
6284 * Returns the animation listener to which layout animation events are
6285 * sent.
6287 * @return an {@link android.view.animation.Animation.AnimationListener}
6289 public Animation.AnimationListener getLayoutAnimationListener() {
6290 return mAnimationListener;
6293 @Override
6294 protected void drawableStateChanged() {
6295 super.drawableStateChanged();
6297 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
6298 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
6299 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
6300 + " child has duplicateParentState set to true");
6303 final View[] children = mChildren;
6304 final int count = mChildrenCount;
6306 for (int i = 0; i < count; i++) {
6307 final View child = children[i];
6308 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
6309 child.refreshDrawableState();
6315 @Override
6316 public void jumpDrawablesToCurrentState() {
6317 super.jumpDrawablesToCurrentState();
6318 final View[] children = mChildren;
6319 final int count = mChildrenCount;
6320 for (int i = 0; i < count; i++) {
6321 children[i].jumpDrawablesToCurrentState();
6325 @Override
6326 protected int[] onCreateDrawableState(int extraSpace) {
6327 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
6328 return super.onCreateDrawableState(extraSpace);
6331 int need = 0;
6332 int n = getChildCount();
6333 for (int i = 0; i < n; i++) {
6334 int[] childState = getChildAt(i).getDrawableState();
6336 if (childState != null) {
6337 need += childState.length;
6341 int[] state = super.onCreateDrawableState(extraSpace + need);
6343 for (int i = 0; i < n; i++) {
6344 int[] childState = getChildAt(i).getDrawableState();
6346 if (childState != null) {
6347 state = mergeDrawableStates(state, childState);
6351 return state;
6355 * Sets whether this ViewGroup's drawable states also include
6356 * its children's drawable states. This is used, for example, to
6357 * make a group appear to be focused when its child EditText or button
6358 * is focused.
6360 public void setAddStatesFromChildren(boolean addsStates) {
6361 if (addsStates) {
6362 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
6363 } else {
6364 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
6367 refreshDrawableState();
6371 * Returns whether this ViewGroup's drawable states also include
6372 * its children's drawable states. This is used, for example, to
6373 * make a group appear to be focused when its child EditText or button
6374 * is focused.
6376 public boolean addStatesFromChildren() {
6377 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
6381 * If {@link #addStatesFromChildren} is true, refreshes this group's
6382 * drawable state (to include the states from its children).
6384 public void childDrawableStateChanged(View child) {
6385 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
6386 refreshDrawableState();
6391 * Specifies the animation listener to which layout animation events must
6392 * be sent. Only
6393 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
6394 * and
6395 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
6396 * are invoked.
6398 * @param animationListener the layout animation listener
6400 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
6401 mAnimationListener = animationListener;
6405 * This method is called by LayoutTransition when there are 'changing' animations that need
6406 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
6407 * starts all pending transitions prior to the drawing phase in the current traversal.
6409 * @param transition The LayoutTransition to be started on the next traversal.
6411 * @hide
6413 public void requestTransitionStart(LayoutTransition transition) {
6414 ViewRootImpl viewAncestor = getViewRootImpl();
6415 if (viewAncestor != null) {
6416 viewAncestor.requestTransitionStart(transition);
6421 * @hide
6423 @Override
6424 public boolean resolveRtlPropertiesIfNeeded() {
6425 final boolean result = super.resolveRtlPropertiesIfNeeded();
6426 // We dont need to resolve the children RTL properties if nothing has changed for the parent
6427 if (result) {
6428 int count = getChildCount();
6429 for (int i = 0; i < count; i++) {
6430 final View child = getChildAt(i);
6431 if (child.isLayoutDirectionInherited()) {
6432 child.resolveRtlPropertiesIfNeeded();
6436 return result;
6440 * @hide
6442 @Override
6443 public boolean resolveLayoutDirection() {
6444 final boolean result = super.resolveLayoutDirection();
6445 if (result) {
6446 int count = getChildCount();
6447 for (int i = 0; i < count; i++) {
6448 final View child = getChildAt(i);
6449 if (child.isLayoutDirectionInherited()) {
6450 child.resolveLayoutDirection();
6454 return result;
6458 * @hide
6460 @Override
6461 public boolean resolveTextDirection() {
6462 final boolean result = super.resolveTextDirection();
6463 if (result) {
6464 int count = getChildCount();
6465 for (int i = 0; i < count; i++) {
6466 final View child = getChildAt(i);
6467 if (child.isTextDirectionInherited()) {
6468 child.resolveTextDirection();
6472 return result;
6476 * @hide
6478 @Override
6479 public boolean resolveTextAlignment() {
6480 final boolean result = super.resolveTextAlignment();
6481 if (result) {
6482 int count = getChildCount();
6483 for (int i = 0; i < count; i++) {
6484 final View child = getChildAt(i);
6485 if (child.isTextAlignmentInherited()) {
6486 child.resolveTextAlignment();
6490 return result;
6494 * @hide
6496 @Override
6497 public void resolvePadding() {
6498 super.resolvePadding();
6499 int count = getChildCount();
6500 for (int i = 0; i < count; i++) {
6501 final View child = getChildAt(i);
6502 if (child.isLayoutDirectionInherited() && !child.isPaddingResolved()) {
6503 child.resolvePadding();
6509 * @hide
6511 @Override
6512 protected void resolveDrawables() {
6513 super.resolveDrawables();
6514 int count = getChildCount();
6515 for (int i = 0; i < count; i++) {
6516 final View child = getChildAt(i);
6517 if (child.isLayoutDirectionInherited() && !child.areDrawablesResolved()) {
6518 child.resolveDrawables();
6524 * @hide
6526 @Override
6527 public void resolveLayoutParams() {
6528 super.resolveLayoutParams();
6529 int count = getChildCount();
6530 for (int i = 0; i < count; i++) {
6531 final View child = getChildAt(i);
6532 child.resolveLayoutParams();
6537 * @hide
6539 @Override
6540 public void resetResolvedLayoutDirection() {
6541 super.resetResolvedLayoutDirection();
6543 int count = getChildCount();
6544 for (int i = 0; i < count; i++) {
6545 final View child = getChildAt(i);
6546 if (child.isLayoutDirectionInherited()) {
6547 child.resetResolvedLayoutDirection();
6553 * @hide
6555 @Override
6556 public void resetResolvedTextDirection() {
6557 super.resetResolvedTextDirection();
6559 int count = getChildCount();
6560 for (int i = 0; i < count; i++) {
6561 final View child = getChildAt(i);
6562 if (child.isTextDirectionInherited()) {
6563 child.resetResolvedTextDirection();
6569 * @hide
6571 @Override
6572 public void resetResolvedTextAlignment() {
6573 super.resetResolvedTextAlignment();
6575 int count = getChildCount();
6576 for (int i = 0; i < count; i++) {
6577 final View child = getChildAt(i);
6578 if (child.isTextAlignmentInherited()) {
6579 child.resetResolvedTextAlignment();
6585 * @hide
6587 @Override
6588 public void resetResolvedPadding() {
6589 super.resetResolvedPadding();
6591 int count = getChildCount();
6592 for (int i = 0; i < count; i++) {
6593 final View child = getChildAt(i);
6594 if (child.isLayoutDirectionInherited()) {
6595 child.resetResolvedPadding();
6601 * @hide
6603 @Override
6604 protected void resetResolvedDrawables() {
6605 super.resetResolvedDrawables();
6607 int count = getChildCount();
6608 for (int i = 0; i < count; i++) {
6609 final View child = getChildAt(i);
6610 if (child.isLayoutDirectionInherited()) {
6611 child.resetResolvedDrawables();
6617 * Return true if the pressed state should be delayed for children or descendants of this
6618 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
6619 * This prevents the pressed state from appearing when the user is actually trying to scroll
6620 * the content.
6622 * The default implementation returns true for compatibility reasons. Subclasses that do
6623 * not scroll should generally override this method and return false.
6625 public boolean shouldDelayChildPressedState() {
6626 return true;
6630 * @inheritDoc
6632 @Override
6633 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
6634 return false;
6638 * @inheritDoc
6640 @Override
6641 public void onNestedScrollAccepted(View child, View target, int axes) {
6642 mNestedScrollAxes = axes;
6646 * @inheritDoc
6648 * <p>The default implementation of onStopNestedScroll calls
6649 * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
6651 @Override
6652 public void onStopNestedScroll(View child) {
6653 // Stop any recursive nested scrolling.
6654 stopNestedScroll();
6655 mNestedScrollAxes = 0;
6659 * @inheritDoc
6661 @Override
6662 public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
6663 int dxUnconsumed, int dyUnconsumed) {
6664 // Do nothing
6668 * @inheritDoc
6670 @Override
6671 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
6672 // Do nothing
6676 * @inheritDoc
6678 @Override
6679 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
6680 return false;
6684 * @inheritDoc
6686 @Override
6687 public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
6688 return false;
6692 * Return the current axes of nested scrolling for this ViewGroup.
6694 * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
6695 * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p>
6697 * @return Flags indicating the current axes of nested scrolling
6698 * @see #SCROLL_AXIS_HORIZONTAL
6699 * @see #SCROLL_AXIS_VERTICAL
6700 * @see #SCROLL_AXIS_NONE
6702 public int getNestedScrollAxes() {
6703 return mNestedScrollAxes;
6706 /** @hide */
6707 protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
6710 /** @hide */
6711 @Override
6712 public void captureTransitioningViews(List<View> transitioningViews) {
6713 if (getVisibility() != View.VISIBLE) {
6714 return;
6716 if (isTransitionGroup()) {
6717 transitioningViews.add(this);
6718 } else {
6719 int count = getChildCount();
6720 for (int i = 0; i < count; i++) {
6721 View child = getChildAt(i);
6722 child.captureTransitioningViews(transitioningViews);
6727 /** @hide */
6728 @Override
6729 public void findNamedViews(Map<String, View> namedElements) {
6730 if (getVisibility() != VISIBLE && mGhostView == null) {
6731 return;
6733 super.findNamedViews(namedElements);
6734 int count = getChildCount();
6735 for (int i = 0; i < count; i++) {
6736 View child = getChildAt(i);
6737 child.findNamedViews(namedElements);
6742 * LayoutParams are used by views to tell their parents how they want to be
6743 * laid out. See
6744 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
6745 * for a list of all child view attributes that this class supports.
6747 * <p>
6748 * The base LayoutParams class just describes how big the view wants to be
6749 * for both width and height. For each dimension, it can specify one of:
6750 * <ul>
6751 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
6752 * means that the view wants to be as big as its parent (minus padding)
6753 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
6754 * to enclose its content (plus padding)
6755 * <li> an exact number
6756 * </ul>
6757 * There are subclasses of LayoutParams for different subclasses of
6758 * ViewGroup. For example, AbsoluteLayout has its own subclass of
6759 * LayoutParams which adds an X and Y value.</p>
6761 * <div class="special reference">
6762 * <h3>Developer Guides</h3>
6763 * <p>For more information about creating user interface layouts, read the
6764 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
6765 * guide.</p></div>
6767 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
6768 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
6770 public static class LayoutParams {
6772 * Special value for the height or width requested by a View.
6773 * FILL_PARENT means that the view wants to be as big as its parent,
6774 * minus the parent's padding, if any. This value is deprecated
6775 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
6777 @SuppressWarnings({"UnusedDeclaration"})
6778 @Deprecated
6779 public static final int FILL_PARENT = -1;
6782 * Special value for the height or width requested by a View.
6783 * MATCH_PARENT means that the view wants to be as big as its parent,
6784 * minus the parent's padding, if any. Introduced in API Level 8.
6786 public static final int MATCH_PARENT = -1;
6789 * Special value for the height or width requested by a View.
6790 * WRAP_CONTENT means that the view wants to be just large enough to fit
6791 * its own internal content, taking its own padding into account.
6793 public static final int WRAP_CONTENT = -2;
6796 * Information about how wide the view wants to be. Can be one of the
6797 * constants FILL_PARENT (replaced by MATCH_PARENT
6798 * in API Level 8) or WRAP_CONTENT, or an exact size.
6800 @ViewDebug.ExportedProperty(category = "layout", mapping = {
6801 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6802 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6804 public int width;
6807 * Information about how tall the view wants to be. Can be one of the
6808 * constants FILL_PARENT (replaced by MATCH_PARENT
6809 * in API Level 8) or WRAP_CONTENT, or an exact size.
6811 @ViewDebug.ExportedProperty(category = "layout", mapping = {
6812 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6813 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6815 public int height;
6818 * Used to animate layouts.
6820 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
6823 * Creates a new set of layout parameters. The values are extracted from
6824 * the supplied attributes set and context. The XML attributes mapped
6825 * to this set of layout parameters are:
6827 * <ul>
6828 * <li><code>layout_width</code>: the width, either an exact value,
6829 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6830 * {@link #MATCH_PARENT} in API Level 8)</li>
6831 * <li><code>layout_height</code>: the height, either an exact value,
6832 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6833 * {@link #MATCH_PARENT} in API Level 8)</li>
6834 * </ul>
6836 * @param c the application environment
6837 * @param attrs the set of attributes from which to extract the layout
6838 * parameters' values
6840 public LayoutParams(Context c, AttributeSet attrs) {
6841 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
6842 setBaseAttributes(a,
6843 R.styleable.ViewGroup_Layout_layout_width,
6844 R.styleable.ViewGroup_Layout_layout_height);
6845 a.recycle();
6849 * Creates a new set of layout parameters with the specified width
6850 * and height.
6852 * @param width the width, either {@link #WRAP_CONTENT},
6853 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6854 * API Level 8), or a fixed size in pixels
6855 * @param height the height, either {@link #WRAP_CONTENT},
6856 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6857 * API Level 8), or a fixed size in pixels
6859 public LayoutParams(int width, int height) {
6860 this.width = width;
6861 this.height = height;
6865 * Copy constructor. Clones the width and height values of the source.
6867 * @param source The layout params to copy from.
6869 public LayoutParams(LayoutParams source) {
6870 this.width = source.width;
6871 this.height = source.height;
6875 * Used internally by MarginLayoutParams.
6876 * @hide
6878 LayoutParams() {
6882 * Extracts the layout parameters from the supplied attributes.
6884 * @param a the style attributes to extract the parameters from
6885 * @param widthAttr the identifier of the width attribute
6886 * @param heightAttr the identifier of the height attribute
6888 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
6889 width = a.getLayoutDimension(widthAttr, "layout_width");
6890 height = a.getLayoutDimension(heightAttr, "layout_height");
6894 * Resolve layout parameters depending on the layout direction. Subclasses that care about
6895 * layoutDirection changes should override this method. The default implementation does
6896 * nothing.
6898 * @param layoutDirection the direction of the layout
6900 * {@link View#LAYOUT_DIRECTION_LTR}
6901 * {@link View#LAYOUT_DIRECTION_RTL}
6903 public void resolveLayoutDirection(int layoutDirection) {
6907 * Returns a String representation of this set of layout parameters.
6909 * @param output the String to prepend to the internal representation
6910 * @return a String with the following format: output +
6911 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
6913 * @hide
6915 public String debug(String output) {
6916 return output + "ViewGroup.LayoutParams={ width="
6917 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
6921 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
6923 * @param view the view that contains these layout parameters
6924 * @param canvas the canvas on which to draw
6926 * @hide
6928 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
6932 * Converts the specified size to a readable String.
6934 * @param size the size to convert
6935 * @return a String instance representing the supplied size
6937 * @hide
6939 protected static String sizeToString(int size) {
6940 if (size == WRAP_CONTENT) {
6941 return "wrap-content";
6943 if (size == MATCH_PARENT) {
6944 return "match-parent";
6946 return String.valueOf(size);
6949 /** @hide */
6950 void encode(@NonNull ViewHierarchyEncoder encoder) {
6951 encoder.beginObject(this);
6952 encodeProperties(encoder);
6953 encoder.endObject();
6956 /** @hide */
6957 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
6958 encoder.addProperty("width", width);
6959 encoder.addProperty("height", height);
6964 * Per-child layout information for layouts that support margins.
6965 * See
6966 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
6967 * for a list of all child view attributes that this class supports.
6969 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
6971 * The left margin in pixels of the child. Margin values should be positive.
6972 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6973 * to this field.
6975 @ViewDebug.ExportedProperty(category = "layout")
6976 public int leftMargin;
6979 * The top margin in pixels of the child. Margin values should be positive.
6980 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6981 * to this field.
6983 @ViewDebug.ExportedProperty(category = "layout")
6984 public int topMargin;
6987 * The right margin in pixels of the child. Margin values should be positive.
6988 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6989 * to this field.
6991 @ViewDebug.ExportedProperty(category = "layout")
6992 public int rightMargin;
6995 * The bottom margin in pixels of the child. Margin values should be positive.
6996 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6997 * to this field.
6999 @ViewDebug.ExportedProperty(category = "layout")
7000 public int bottomMargin;
7003 * The start margin in pixels of the child. Margin values should be positive.
7004 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7005 * to this field.
7007 @ViewDebug.ExportedProperty(category = "layout")
7008 private int startMargin = DEFAULT_MARGIN_RELATIVE;
7011 * The end margin in pixels of the child. Margin values should be positive.
7012 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7013 * to this field.
7015 @ViewDebug.ExportedProperty(category = "layout")
7016 private int endMargin = DEFAULT_MARGIN_RELATIVE;
7019 * The default start and end margin.
7020 * @hide
7022 public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
7025 * Bit 0: layout direction
7026 * Bit 1: layout direction
7027 * Bit 2: left margin undefined
7028 * Bit 3: right margin undefined
7029 * Bit 4: is RTL compatibility mode
7030 * Bit 5: need resolution
7032 * Bit 6 to 7 not used
7034 * @hide
7036 @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
7037 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
7038 equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
7039 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
7040 equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
7041 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
7042 equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
7043 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
7044 equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
7045 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
7046 equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
7047 }, formatToHexString = true)
7048 byte mMarginFlags;
7050 private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
7051 private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
7052 private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
7053 private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
7054 private static final int NEED_RESOLUTION_MASK = 0x00000020;
7056 private static final int DEFAULT_MARGIN_RESOLVED = 0;
7057 private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
7060 * Creates a new set of layout parameters. The values are extracted from
7061 * the supplied attributes set and context.
7063 * @param c the application environment
7064 * @param attrs the set of attributes from which to extract the layout
7065 * parameters' values
7067 public MarginLayoutParams(Context c, AttributeSet attrs) {
7068 super();
7070 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
7071 setBaseAttributes(a,
7072 R.styleable.ViewGroup_MarginLayout_layout_width,
7073 R.styleable.ViewGroup_MarginLayout_layout_height);
7075 int margin = a.getDimensionPixelSize(
7076 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
7077 if (margin >= 0) {
7078 leftMargin = margin;
7079 topMargin = margin;
7080 rightMargin= margin;
7081 bottomMargin = margin;
7082 } else {
7083 leftMargin = a.getDimensionPixelSize(
7084 R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
7085 UNDEFINED_MARGIN);
7086 if (leftMargin == UNDEFINED_MARGIN) {
7087 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7088 leftMargin = DEFAULT_MARGIN_RESOLVED;
7090 rightMargin = a.getDimensionPixelSize(
7091 R.styleable.ViewGroup_MarginLayout_layout_marginRight,
7092 UNDEFINED_MARGIN);
7093 if (rightMargin == UNDEFINED_MARGIN) {
7094 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7095 rightMargin = DEFAULT_MARGIN_RESOLVED;
7098 topMargin = a.getDimensionPixelSize(
7099 R.styleable.ViewGroup_MarginLayout_layout_marginTop,
7100 DEFAULT_MARGIN_RESOLVED);
7101 bottomMargin = a.getDimensionPixelSize(
7102 R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
7103 DEFAULT_MARGIN_RESOLVED);
7105 startMargin = a.getDimensionPixelSize(
7106 R.styleable.ViewGroup_MarginLayout_layout_marginStart,
7107 DEFAULT_MARGIN_RELATIVE);
7108 endMargin = a.getDimensionPixelSize(
7109 R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
7110 DEFAULT_MARGIN_RELATIVE);
7112 if (isMarginRelative()) {
7113 mMarginFlags |= NEED_RESOLUTION_MASK;
7117 final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
7118 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
7119 if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
7120 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
7123 // Layout direction is LTR by default
7124 mMarginFlags |= LAYOUT_DIRECTION_LTR;
7126 a.recycle();
7130 * {@inheritDoc}
7132 public MarginLayoutParams(int width, int height) {
7133 super(width, height);
7135 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7136 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7138 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7139 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
7143 * Copy constructor. Clones the width, height and margin values of the source.
7145 * @param source The layout params to copy from.
7147 public MarginLayoutParams(MarginLayoutParams source) {
7148 this.width = source.width;
7149 this.height = source.height;
7151 this.leftMargin = source.leftMargin;
7152 this.topMargin = source.topMargin;
7153 this.rightMargin = source.rightMargin;
7154 this.bottomMargin = source.bottomMargin;
7155 this.startMargin = source.startMargin;
7156 this.endMargin = source.endMargin;
7158 this.mMarginFlags = source.mMarginFlags;
7162 * {@inheritDoc}
7164 public MarginLayoutParams(LayoutParams source) {
7165 super(source);
7167 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7168 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7170 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7171 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
7175 * @hide Used internally.
7177 public final void copyMarginsFrom(MarginLayoutParams source) {
7178 this.leftMargin = source.leftMargin;
7179 this.topMargin = source.topMargin;
7180 this.rightMargin = source.rightMargin;
7181 this.bottomMargin = source.bottomMargin;
7182 this.startMargin = source.startMargin;
7183 this.endMargin = source.endMargin;
7185 this.mMarginFlags = source.mMarginFlags;
7189 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
7190 * to be done so that the new margins are taken into account. Left and right margins may be
7191 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
7192 * Margin values should be positive.
7194 * @param left the left margin size
7195 * @param top the top margin size
7196 * @param right the right margin size
7197 * @param bottom the bottom margin size
7199 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
7200 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7201 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
7202 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7204 public void setMargins(int left, int top, int right, int bottom) {
7205 leftMargin = left;
7206 topMargin = top;
7207 rightMargin = right;
7208 bottomMargin = bottom;
7209 mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
7210 mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
7211 if (isMarginRelative()) {
7212 mMarginFlags |= NEED_RESOLUTION_MASK;
7213 } else {
7214 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7219 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
7220 * needs to be done so that the new relative margins are taken into account. Left and right
7221 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
7222 * direction. Margin values should be positive.
7224 * @param start the start margin size
7225 * @param top the top margin size
7226 * @param end the right margin size
7227 * @param bottom the bottom margin size
7229 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7230 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7231 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7232 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7234 * @hide
7236 public void setMarginsRelative(int start, int top, int end, int bottom) {
7237 startMargin = start;
7238 topMargin = top;
7239 endMargin = end;
7240 bottomMargin = bottom;
7241 mMarginFlags |= NEED_RESOLUTION_MASK;
7245 * Sets the relative start margin. Margin values should be positive.
7247 * @param start the start margin size
7249 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7251 public void setMarginStart(int start) {
7252 startMargin = start;
7253 mMarginFlags |= NEED_RESOLUTION_MASK;
7257 * Returns the start margin in pixels.
7259 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7261 * @return the start margin in pixels.
7263 public int getMarginStart() {
7264 if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
7265 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
7266 doResolveMargins();
7268 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
7269 case View.LAYOUT_DIRECTION_RTL:
7270 return rightMargin;
7271 case View.LAYOUT_DIRECTION_LTR:
7272 default:
7273 return leftMargin;
7278 * Sets the relative end margin. Margin values should be positive.
7280 * @param end the end margin size
7282 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7284 public void setMarginEnd(int end) {
7285 endMargin = end;
7286 mMarginFlags |= NEED_RESOLUTION_MASK;
7290 * Returns the end margin in pixels.
7292 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7294 * @return the end margin in pixels.
7296 public int getMarginEnd() {
7297 if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
7298 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
7299 doResolveMargins();
7301 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
7302 case View.LAYOUT_DIRECTION_RTL:
7303 return leftMargin;
7304 case View.LAYOUT_DIRECTION_LTR:
7305 default:
7306 return rightMargin;
7311 * Check if margins are relative.
7313 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7314 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7316 * @return true if either marginStart or marginEnd has been set.
7318 public boolean isMarginRelative() {
7319 return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
7323 * Set the layout direction
7324 * @param layoutDirection the layout direction.
7325 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
7326 * or {@link View#LAYOUT_DIRECTION_RTL}.
7328 public void setLayoutDirection(int layoutDirection) {
7329 if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
7330 layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
7331 if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
7332 mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
7333 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
7334 if (isMarginRelative()) {
7335 mMarginFlags |= NEED_RESOLUTION_MASK;
7336 } else {
7337 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7343 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
7344 * {@link View#LAYOUT_DIRECTION_RTL}.
7346 * @return the layout direction.
7348 public int getLayoutDirection() {
7349 return (mMarginFlags & LAYOUT_DIRECTION_MASK);
7353 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
7354 * may be overridden depending on layout direction.
7356 @Override
7357 public void resolveLayoutDirection(int layoutDirection) {
7358 setLayoutDirection(layoutDirection);
7360 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
7361 // Will use the left and right margins if no relative margin is defined.
7362 if (!isMarginRelative() ||
7363 (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
7365 // Proceed with resolution
7366 doResolveMargins();
7369 private void doResolveMargins() {
7370 if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
7371 // if left or right margins are not defined and if we have some start or end margin
7372 // defined then use those start and end margins.
7373 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
7374 && startMargin > DEFAULT_MARGIN_RELATIVE) {
7375 leftMargin = startMargin;
7377 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
7378 && endMargin > DEFAULT_MARGIN_RELATIVE) {
7379 rightMargin = endMargin;
7381 } else {
7382 // We have some relative margins (either the start one or the end one or both). So use
7383 // them and override what has been defined for left and right margins. If either start
7384 // or end margin is not defined, just set it to default "0".
7385 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
7386 case View.LAYOUT_DIRECTION_RTL:
7387 leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
7388 endMargin : DEFAULT_MARGIN_RESOLVED;
7389 rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
7390 startMargin : DEFAULT_MARGIN_RESOLVED;
7391 break;
7392 case View.LAYOUT_DIRECTION_LTR:
7393 default:
7394 leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
7395 startMargin : DEFAULT_MARGIN_RESOLVED;
7396 rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
7397 endMargin : DEFAULT_MARGIN_RESOLVED;
7398 break;
7401 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7405 * @hide
7407 public boolean isLayoutRtl() {
7408 return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
7412 * @hide
7414 @Override
7415 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
7416 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
7418 fillDifference(canvas,
7419 view.getLeft() + oi.left,
7420 view.getTop() + oi.top,
7421 view.getRight() - oi.right,
7422 view.getBottom() - oi.bottom,
7423 leftMargin,
7424 topMargin,
7425 rightMargin,
7426 bottomMargin,
7427 paint);
7430 /** @hide */
7431 @Override
7432 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
7433 super.encodeProperties(encoder);
7434 encoder.addProperty("leftMargin", leftMargin);
7435 encoder.addProperty("topMargin", topMargin);
7436 encoder.addProperty("rightMargin", rightMargin);
7437 encoder.addProperty("bottomMargin", bottomMargin);
7438 encoder.addProperty("startMargin", startMargin);
7439 encoder.addProperty("endMargin", endMargin);
7443 /* Describes a touched view and the ids of the pointers that it has captured.
7445 * This code assumes that pointer ids are always in the range 0..31 such that
7446 * it can use a bitfield to track which pointer ids are present.
7447 * As it happens, the lower layers of the input dispatch pipeline also use the
7448 * same trick so the assumption should be safe here...
7450 private static final class TouchTarget {
7451 private static final int MAX_RECYCLED = 32;
7452 private static final Object sRecycleLock = new Object[0];
7453 private static TouchTarget sRecycleBin;
7454 private static int sRecycledCount;
7456 public static final int ALL_POINTER_IDS = -1; // all ones
7458 // The touched child view.
7459 public View child;
7461 // The combined bit mask of pointer ids for all pointers captured by the target.
7462 public int pointerIdBits;
7464 // The next target in the target list.
7465 public TouchTarget next;
7467 private TouchTarget() {
7470 public static TouchTarget obtain(View child, int pointerIdBits) {
7471 final TouchTarget target;
7472 synchronized (sRecycleLock) {
7473 if (sRecycleBin == null) {
7474 target = new TouchTarget();
7475 } else {
7476 target = sRecycleBin;
7477 sRecycleBin = target.next;
7478 sRecycledCount--;
7479 target.next = null;
7482 target.child = child;
7483 target.pointerIdBits = pointerIdBits;
7484 return target;
7487 public void recycle() {
7488 synchronized (sRecycleLock) {
7489 if (sRecycledCount < MAX_RECYCLED) {
7490 next = sRecycleBin;
7491 sRecycleBin = this;
7492 sRecycledCount += 1;
7493 } else {
7494 next = null;
7496 child = null;
7501 /* Describes a hovered view. */
7502 private static final class HoverTarget {
7503 private static final int MAX_RECYCLED = 32;
7504 private static final Object sRecycleLock = new Object[0];
7505 private static HoverTarget sRecycleBin;
7506 private static int sRecycledCount;
7508 // The hovered child view.
7509 public View child;
7511 // The next target in the target list.
7512 public HoverTarget next;
7514 private HoverTarget() {
7517 public static HoverTarget obtain(View child) {
7518 final HoverTarget target;
7519 synchronized (sRecycleLock) {
7520 if (sRecycleBin == null) {
7521 target = new HoverTarget();
7522 } else {
7523 target = sRecycleBin;
7524 sRecycleBin = target.next;
7525 sRecycledCount--;
7526 target.next = null;
7529 target.child = child;
7530 return target;
7533 public void recycle() {
7534 synchronized (sRecycleLock) {
7535 if (sRecycledCount < MAX_RECYCLED) {
7536 next = sRecycleBin;
7537 sRecycleBin = this;
7538 sRecycledCount += 1;
7539 } else {
7540 next = null;
7542 child = null;
7548 * Pooled class that orderes the children of a ViewGroup from start
7549 * to end based on how they are laid out and the layout direction.
7551 static class ChildListForAccessibility {
7553 private static final int MAX_POOL_SIZE = 32;
7555 private static final SynchronizedPool<ChildListForAccessibility> sPool =
7556 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
7558 private final ArrayList<View> mChildren = new ArrayList<View>();
7560 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
7562 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
7563 ChildListForAccessibility list = sPool.acquire();
7564 if (list == null) {
7565 list = new ChildListForAccessibility();
7567 list.init(parent, sort);
7568 return list;
7571 public void recycle() {
7572 clear();
7573 sPool.release(this);
7576 public int getChildCount() {
7577 return mChildren.size();
7580 public View getChildAt(int index) {
7581 return mChildren.get(index);
7584 public int getChildIndex(View child) {
7585 return mChildren.indexOf(child);
7588 private void init(ViewGroup parent, boolean sort) {
7589 ArrayList<View> children = mChildren;
7590 final int childCount = parent.getChildCount();
7591 for (int i = 0; i < childCount; i++) {
7592 View child = parent.getChildAt(i);
7593 children.add(child);
7595 if (sort) {
7596 ArrayList<ViewLocationHolder> holders = mHolders;
7597 for (int i = 0; i < childCount; i++) {
7598 View child = children.get(i);
7599 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
7600 holders.add(holder);
7602 sort(holders);
7603 for (int i = 0; i < childCount; i++) {
7604 ViewLocationHolder holder = holders.get(i);
7605 children.set(i, holder.mView);
7606 holder.recycle();
7608 holders.clear();
7612 private void sort(ArrayList<ViewLocationHolder> holders) {
7613 // This is gross but the least risky solution. The current comparison
7614 // strategy breaks transitivity but produces very good results. Coming
7615 // up with a new strategy requires time which we do not have, so ...
7616 try {
7617 ViewLocationHolder.setComparisonStrategy(
7618 ViewLocationHolder.COMPARISON_STRATEGY_STRIPE);
7619 Collections.sort(holders);
7620 } catch (IllegalArgumentException iae) {
7621 // Note that in practice this occurs extremely rarely in a couple
7622 // of pathological cases.
7623 ViewLocationHolder.setComparisonStrategy(
7624 ViewLocationHolder.COMPARISON_STRATEGY_LOCATION);
7625 Collections.sort(holders);
7629 private void clear() {
7630 mChildren.clear();
7635 * Pooled class that holds a View and its location with respect to
7636 * a specified root. This enables sorting of views based on their
7637 * coordinates without recomputing the position relative to the root
7638 * on every comparison.
7640 static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
7642 private static final int MAX_POOL_SIZE = 32;
7644 private static final SynchronizedPool<ViewLocationHolder> sPool =
7645 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
7647 public static final int COMPARISON_STRATEGY_STRIPE = 1;
7649 public static final int COMPARISON_STRATEGY_LOCATION = 2;
7651 private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
7653 private final Rect mLocation = new Rect();
7655 public View mView;
7657 private int mLayoutDirection;
7659 public static ViewLocationHolder obtain(ViewGroup root, View view) {
7660 ViewLocationHolder holder = sPool.acquire();
7661 if (holder == null) {
7662 holder = new ViewLocationHolder();
7664 holder.init(root, view);
7665 return holder;
7668 public static void setComparisonStrategy(int strategy) {
7669 sComparisonStrategy = strategy;
7672 public void recycle() {
7673 clear();
7674 sPool.release(this);
7677 @Override
7678 public int compareTo(ViewLocationHolder another) {
7679 // This instance is greater than an invalid argument.
7680 if (another == null) {
7681 return 1;
7684 if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
7685 // First is above second.
7686 if (mLocation.bottom - another.mLocation.top <= 0) {
7687 return -1;
7689 // First is below second.
7690 if (mLocation.top - another.mLocation.bottom >= 0) {
7691 return 1;
7695 // We are ordering left-to-right, top-to-bottom.
7696 if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
7697 final int leftDifference = mLocation.left - another.mLocation.left;
7698 if (leftDifference != 0) {
7699 return leftDifference;
7701 } else { // RTL
7702 final int rightDifference = mLocation.right - another.mLocation.right;
7703 if (rightDifference != 0) {
7704 return -rightDifference;
7707 // We are ordering left-to-right, top-to-bottom.
7708 final int topDifference = mLocation.top - another.mLocation.top;
7709 if (topDifference != 0) {
7710 return topDifference;
7712 // Break tie by height.
7713 final int heightDiference = mLocation.height() - another.mLocation.height();
7714 if (heightDiference != 0) {
7715 return -heightDiference;
7717 // Break tie by width.
7718 final int widthDiference = mLocation.width() - another.mLocation.width();
7719 if (widthDiference != 0) {
7720 return -widthDiference;
7722 // Just break the tie somehow. The accessibliity ids are unique
7723 // and stable, hence this is deterministic tie breaking.
7724 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
7727 private void init(ViewGroup root, View view) {
7728 Rect viewLocation = mLocation;
7729 view.getDrawingRect(viewLocation);
7730 root.offsetDescendantRectToMyCoords(view, viewLocation);
7731 mView = view;
7732 mLayoutDirection = root.getLayoutDirection();
7735 private void clear() {
7736 mView = null;
7737 mLocation.set(0, 0, 0, 0);
7741 private static Paint getDebugPaint() {
7742 if (sDebugPaint == null) {
7743 sDebugPaint = new Paint();
7744 sDebugPaint.setAntiAlias(false);
7746 return sDebugPaint;
7749 private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
7750 if (sDebugLines== null) {
7751 // TODO: This won't work with multiple UI threads in a single process
7752 sDebugLines = new float[16];
7755 sDebugLines[0] = x1;
7756 sDebugLines[1] = y1;
7757 sDebugLines[2] = x2;
7758 sDebugLines[3] = y1;
7760 sDebugLines[4] = x2;
7761 sDebugLines[5] = y1;
7762 sDebugLines[6] = x2;
7763 sDebugLines[7] = y2;
7765 sDebugLines[8] = x2;
7766 sDebugLines[9] = y2;
7767 sDebugLines[10] = x1;
7768 sDebugLines[11] = y2;
7770 sDebugLines[12] = x1;
7771 sDebugLines[13] = y2;
7772 sDebugLines[14] = x1;
7773 sDebugLines[15] = y1;
7775 canvas.drawLines(sDebugLines, paint);
7778 /** @hide */
7779 @Override
7780 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
7781 super.encodeProperties(encoder);
7783 encoder.addProperty("focus:descendantFocusability", getDescendantFocusability());
7784 encoder.addProperty("drawing:clipChildren", getClipChildren());
7785 encoder.addProperty("drawing:clipToPadding", getClipToPadding());
7786 encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled());
7787 encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache());
7789 int n = getChildCount();
7790 encoder.addProperty("meta:__childCount__", (short)n);
7791 for (int i = 0; i < n; i++) {
7792 encoder.addPropertyKey("meta:__child__" + i);
7793 getChildAt(i).encode(encoder);