1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package android.view;
18 
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;
53 
54 import com.android.internal.R;
55 import com.android.internal.util.Predicate;
56 
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;
63 
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>
72  *
73  * <p>
74  * Also see {@link LayoutParams} for layout attributes.
75  * </p>
76  *
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>
82  *
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>
86  *
87  * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
88  *      Complete}
89  *
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>
92  *
93  * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
94  *
95  * <p>Finally the layout manager can be used in an XML layout like so:</p>
96  *
97  * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
98  *
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
110  */
111 @UiThread
112 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
113     private static final String TAG = "ViewGroup";
114 
115     private static final boolean DBG = false;
116     /** @hide */
117     public static boolean DEBUG_DRAW = false;
118 
119     /**
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}
124      */
125     protected ArrayList<View> mDisappearingChildren;
126 
127     /**
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}
132      */
133     protected OnHierarchyChangeListener mOnHierarchyChangeListener;
134 
135     // The view contained within this ViewGroup that has or contains focus.
136     private View mFocused;
137 
138     /**
139      * A Transformation used when drawing children, to
140      * apply on the child being drawn.
141      */
142     private Transformation mChildTransformation;
143 
144     /**
145      * Used to track the current invalidation region.
146      */
147     RectF mInvalidateRegion;
148 
149     /**
150      * A Transformation used to calculate a correct
151      * invalidation area when the application is autoscaled.
152      */
153     Transformation mInvalidationTransformation;
154 
155     // View currently under an ongoing drag
156     private View mCurrentDragView;
157 
158     // Metadata about the ongoing drag
159     private DragEvent mCurrentDrag;
160     private HashSet<View> mDragNotifiedChildren;
161 
162     // Does this group have a child that can accept the current drag payload?
163     private boolean mChildAcceptsDrag;
164 
165     // Used during drag dispatch
166     private PointF mLocalPoint;
167 
168     // Lazily-created holder for point computations.
169     private float[] mTempPoint;
170 
171     // Layout animation
172     private LayoutAnimationController mLayoutAnimationController;
173     private Animation.AnimationListener mAnimationListener;
174 
175     // First touch target in the linked list of touch targets.
176     private TouchTarget mFirstTouchTarget;
177 
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;
190 
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;
197 
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;
201 
202     /**
203      * Internal flags.
204      *
205      * This field should be made private, so it is hidden from the SDK.
206      * {@hide}
207      */
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;
217 
218     /**
219      * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
220      */
221     private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
222 
223     /**
224      * NOTE: If you change the flags below make sure to reflect the changes
225      *       the DisplayList class
226      */
227 
228     // When set, ViewGroup invalidates only the child's rectangle
229     // Set by default
230     static final int FLAG_CLIP_CHILDREN = 0x1;
231 
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;
235 
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;
239 
240     // When set, dispatchDraw() will run the layout animation and unset the flag
241     private static final int FLAG_RUN_ANIMATION = 0x8;
242 
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;
247 
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;
251 
252     /** @deprecated - functionality removed */
253     private static final int FLAG_ANIMATION_CACHE = 0x40;
254 
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;
260 
261     // When set, the next call to drawChild() will clear mChildTransformation's matrix
262     static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
263 
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;
268 
269     /**
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.
272      *
273      * @hide
274      */
275     protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
276 
277     /**
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.
281      *
282      * Any subclass overriding
283      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
284      * set this flags in {@link #mGroupFlags}.
285      *
286      * {@hide}
287      */
288     protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
289 
290     // UNUSED FLAG VALUE: 0x1000;
291 
292     /**
293      * When set, this ViewGroup's drawable states also include those
294      * of its children.
295      */
296     private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
297 
298     /** @deprecated functionality removed */
299     private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
300 
301     /** @deprecated functionality removed */
302     private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
303 
304     /**
305      * When set, this group will go through its list of children to notify them of
306      * any drawable state change.
307      */
308     private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
309 
310     private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
311 
312     /**
313      * This view will get focus before any of its descendants.
314      */
315     public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
316 
317     /**
318      * This view will get focus only if none of its descendants want it.
319      */
320     public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
321 
322     /**
323      * This view will block any of its descendants from getting focus, even
324      * if they are focusable.
325      */
326     public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
327 
328     /**
329      * Used to map between enum in attrubutes and flag values.
330      */
331     private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
332             {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
333                     FOCUS_BLOCK_DESCENDANTS};
334 
335     /**
336      * When set, this ViewGroup should not intercept touch events.
337      * {@hide}
338      */
339     protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
340 
341     /**
342      * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
343      */
344     private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
345 
346     /**
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.
350      */
351     private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
352 
353     /**
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.
358      */
359     private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
360 
361     static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
362 
363     static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
364 
365     /**
366      * When set, focus will not be permitted to enter this group if a touchscreen is present.
367      */
368     static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000;
369 
370     /**
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.
374      *
375      * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
376      * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
377      */
378     private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED = 0x8000000;
379 
380     /**
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.
384      *
385      * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
386      * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
387      */
388     private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED = 0x10000000;
389 
390     /**
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}
394      */
395     protected int mPersistentDrawingCache;
396 
397     /**
398      * Used to indicate that no drawing cache should be kept in memory.
399      */
400     public static final int PERSISTENT_NO_CACHE = 0x0;
401 
402     /**
403      * Used to indicate that the animation drawing cache should be kept in memory.
404      */
405     public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
406 
407     /**
408      * Used to indicate that the scrolling drawing cache should be kept in memory.
409      */
410     public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
411 
412     /**
413      * Used to indicate that all drawing caches should be kept in memory.
414      */
415     public static final int PERSISTENT_ALL_CACHES = 0x3;
416 
417     // Layout Modes
418 
419     private static final int LAYOUT_MODE_UNDEFINED = -1;
420 
421     /**
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}.
425      */
426     public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
427 
428     /**
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.
433      */
434     public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
435 
436     /** @hide */
437     public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
438 
439     /**
440      * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
441      * are set at the same time.
442      */
443     protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
444 
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;
449 
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;
455 
456     // Whether layout calls are currently being suppressed, controlled by calls to
457     // suppressLayout()
458     boolean mSuppressLayout = false;
459 
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;
464 
465     private static final int ARRAY_INITIAL_CAPACITY = 12;
466     private static final int ARRAY_CAPACITY_INCREMENT = 12;
467 
468     private static Paint sDebugPaint;
469     private static float[] sDebugLines;
470 
471     // Used to draw cached views
472     Paint mCachePaint;
473 
474     // Used to animate add/remove changes in layout
475     private LayoutTransition mTransition;
476 
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;
481 
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;
485 
486     // Temporary holder of presorted children, only used for
487     // input/software draw dispatch for correctly Z ordering.
488     private ArrayList<View> mPreSortedChildren;
489 
490     // Indicates how many of this container's child subtrees contain transient state
491     @ViewDebug.ExportedProperty(category = "layout")
492     private int mChildCountWithTransientState = 0;
493 
494     /**
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.
498      */
499     private int mNestedScrollAxes;
500 
501     // Used to manage the list of transient views, added by addTransientView()
502     private List<Integer> mTransientIndices = null;
503     private List<View> mTransientViews = null;
504 
505 
506     /**
507      * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
508      *
509      * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
510      * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
511      */
512     private static final ActionMode SENTINEL_ACTION_MODE = new ActionMode() {
513         @Override
514         public void setTitle(CharSequence title) {}
515 
516         @Override
517         public void setTitle(int resId) {}
518 
519         @Override
520         public void setSubtitle(CharSequence subtitle) {}
521 
522         @Override
523         public void setSubtitle(int resId) {}
524 
525         @Override
526         public void setCustomView(View view) {}
527 
528         @Override
529         public void invalidate() {}
530 
531         @Override
532         public void finish() {}
533 
534         @Override
535         public Menu getMenu() {
536             return null;
537         }
538 
539         @Override
540         public CharSequence getTitle() {
541             return null;
542         }
543 
544         @Override
545         public CharSequence getSubtitle() {
546             return null;
547         }
548 
549         @Override
550         public View getCustomView() {
551             return null;
552         }
553 
554         @Override
555         public MenuInflater getMenuInflater() {
556             return null;
557         }
558     };
559 
ViewGroup(Context context)560     public ViewGroup(Context context) {
561         this(context, null);
562     }
563 
ViewGroup(Context context, AttributeSet attrs)564     public ViewGroup(Context context, AttributeSet attrs) {
565         this(context, attrs, 0);
566     }
567 
ViewGroup(Context context, AttributeSet attrs, int defStyleAttr)568     public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
569         this(context, attrs, defStyleAttr, 0);
570     }
571 
ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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);
576     }
577 
debugDraw()578     private boolean debugDraw() {
579         return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
580     }
581 
initViewGroup()582     private void initViewGroup() {
583         // ViewGroup doesn't draw by default
584         if (!debugDraw()) {
585             setFlags(WILL_NOT_DRAW, DRAW_MASK);
586         }
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;
592 
593         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
594             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
595         }
596 
597         setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
598 
599         mChildren = new View[ARRAY_INITIAL_CAPACITY];
600         mChildrenCount = 0;
601 
602         mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
603     }
604 
initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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);
609 
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));
636                     }
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());
648                     }
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;
659             }
660         }
661 
662         a.recycle();
663     }
664 
665     /**
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)}.
670      *
671      * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
672      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
673      */
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")
678     })
getDescendantFocusability()679     public int getDescendantFocusability() {
680         return mGroupFlags & FLAG_MASK_FOCUSABILITY;
681     }
682 
683     /**
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)}.
687      *
688      * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
689      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
690      */
setDescendantFocusability(int focusability)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");
700         }
701         mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
702         mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
703     }
704 
705     /**
706      * {@inheritDoc}
707      */
708     @Override
handleFocusGainInternal(int direction, Rect previouslyFocusedRect)709     void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
710         if (mFocused != null) {
711             mFocused.unFocus(this);
712             mFocused = null;
713         }
714         super.handleFocusGainInternal(direction, previouslyFocusedRect);
715     }
716 
717     /**
718      * {@inheritDoc}
719      */
requestChildFocus(View child, View focused)720     public void requestChildFocus(View child, View focused) {
721         if (DBG) {
722             System.out.println(this + " requestChildFocus()");
723         }
724         if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
725             return;
726         }
727 
728         // Unfocus us, if necessary
729         super.unFocus(focused);
730 
731         // We had a previous notion of who had focus. Clear it.
732         if (mFocused != child) {
733             if (mFocused != null) {
734                 mFocused.unFocus(focused);
735             }
736 
737             mFocused = child;
738         }
739         if (mParent != null) {
740             mParent.requestChildFocus(this, focused);
741         }
742     }
743 
744     /**
745      * {@inheritDoc}
746      */
focusableViewAvailable(View v)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)
755                 //
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);
762         }
763     }
764 
765     /**
766      * {@inheritDoc}
767      */
showContextMenuForChild(View originalView)768     public boolean showContextMenuForChild(View originalView) {
769         return mParent != null && mParent.showContextMenuForChild(originalView);
770     }
771 
772     /**
773      * {@inheritDoc}
774      */
775     @Override
startActionModeForChild(View originalView, ActionMode.Callback callback)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;
784             }
785         } else {
786             // We are being called from the new method with type.
787             return SENTINEL_ACTION_MODE;
788         }
789     }
790 
791     /**
792      * {@inheritDoc}
793      */
794     @Override
startActionModeForChild( View originalView, ActionMode.Callback callback, int type)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;
805             }
806             if (mode != SENTINEL_ACTION_MODE) {
807                 return mode;
808             }
809         }
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);
816             }
817         }
818         return null;
819     }
820 
821     /**
822      * @hide
823      */
824     @Override
dispatchActivityResult( String who, int requestCode, int resultCode, Intent data)825     public boolean dispatchActivityResult(
826             String who, int requestCode, int resultCode, Intent data) {
827         if (super.dispatchActivityResult(who, requestCode, resultCode, data)) {
828             return true;
829         }
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;
835             }
836         }
837         return false;
838     }
839 
840     /**
841      * Find the nearest view in the specified direction that wants to take
842      * focus.
843      *
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.
847      */
focusSearch(View focused, int direction)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);
856         }
857         return null;
858     }
859 
860     /**
861      * {@inheritDoc}
862      */
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)863     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
864         return false;
865     }
866 
867     /**
868      * {@inheritDoc}
869      */
870     @Override
requestSendAccessibilityEvent(View child, AccessibilityEvent event)871     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
872         ViewParent parent = mParent;
873         if (parent == null) {
874             return false;
875         }
876         final boolean propagate = onRequestSendAccessibilityEvent(child, event);
877         if (!propagate) {
878             return false;
879         }
880         return parent.requestSendAccessibilityEvent(this, event);
881     }
882 
883     /**
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>
892      *
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.
896      *
897      * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
898      */
onRequestSendAccessibilityEvent(View child, AccessibilityEvent event)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);
904         }
905     }
906 
907     /**
908      * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
909      *
910      * Note: Called from the default {@link View.AccessibilityDelegate}.
911      *
912      * @hide
913      */
onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event)914     public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
915         return true;
916     }
917 
918     /**
919      * Called when a child view has changed whether or not it is tracking transient state.
920      */
childHasTransientStateChanged(View child, boolean childHasTransientState)921     public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
922         final boolean oldHasTransientState = hasTransientState();
923         if (childHasTransientState) {
924             mChildCountWithTransientState++;
925         } else {
926             mChildCountWithTransientState--;
927         }
928 
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);
936             }
937         }
938     }
939 
940     @Override
hasTransientState()941     public boolean hasTransientState() {
942         return mChildCountWithTransientState > 0 || super.hasTransientState();
943     }
944 
945     /**
946      * {@inheritDoc}
947      */
948     @Override
dispatchUnhandledMove(View focused, int direction)949     public boolean dispatchUnhandledMove(View focused, int direction) {
950         return mFocused != null &&
951                 mFocused.dispatchUnhandledMove(focused, direction);
952     }
953 
954     /**
955      * {@inheritDoc}
956      */
clearChildFocus(View child)957     public void clearChildFocus(View child) {
958         if (DBG) {
959             System.out.println(this + " clearChildFocus()");
960         }
961 
962         mFocused = null;
963         if (mParent != null) {
964             mParent.clearChildFocus(this);
965         }
966     }
967 
968     /**
969      * {@inheritDoc}
970      */
971     @Override
clearFocus()972     public void clearFocus() {
973         if (DBG) {
974             System.out.println(this + " clearFocus()");
975         }
976         if (mFocused == null) {
977             super.clearFocus();
978         } else {
979             View focused = mFocused;
980             mFocused = null;
981             focused.clearFocus();
982         }
983     }
984 
985     /**
986      * {@inheritDoc}
987      */
988     @Override
unFocus(View focused)989     void unFocus(View focused) {
990         if (DBG) {
991             System.out.println(this + " unFocus()");
992         }
993         if (mFocused == null) {
994             super.unFocus(focused);
995         } else {
996             mFocused.unFocus(focused);
997             mFocused = null;
998         }
999     }
1000 
1001     /**
1002      * Returns the focused child of this view, if any. The child may have focus
1003      * or contain focus.
1004      *
1005      * @return the focused child or null.
1006      */
getFocusedChild()1007     public View getFocusedChild() {
1008         return mFocused;
1009     }
1010 
getDeepestFocusedChild()1011     View getDeepestFocusedChild() {
1012         View v = this;
1013         while (v != null) {
1014             if (v.isFocused()) {
1015                 return v;
1016             }
1017             v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
1018         }
1019         return null;
1020     }
1021 
1022     /**
1023      * Returns true if this view has or contains focus
1024      *
1025      * @return true if this view has or contains focus
1026      */
1027     @Override
hasFocus()1028     public boolean hasFocus() {
1029         return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
1030     }
1031 
1032     /*
1033      * (non-Javadoc)
1034      *
1035      * @see android.view.View#findFocus()
1036      */
1037     @Override
findFocus()1038     public View findFocus() {
1039         if (DBG) {
1040             System.out.println("Find focus in " + this + ": flags="
1041                     + isFocused() + ", child=" + mFocused);
1042         }
1043 
1044         if (isFocused()) {
1045             return this;
1046         }
1047 
1048         if (mFocused != null) {
1049             return mFocused.findFocus();
1050         }
1051         return null;
1052     }
1053 
1054     /**
1055      * {@inheritDoc}
1056      */
1057     @Override
hasFocusable()1058     public boolean hasFocusable() {
1059         if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
1060             return false;
1061         }
1062 
1063         if (isFocusable()) {
1064             return true;
1065         }
1066 
1067         final int descendantFocusability = getDescendantFocusability();
1068         if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1069             final int count = mChildrenCount;
1070             final View[] children = mChildren;
1071 
1072             for (int i = 0; i < count; i++) {
1073                 final View child = children[i];
1074                 if (child.hasFocusable()) {
1075                     return true;
1076                 }
1077             }
1078         }
1079 
1080         return false;
1081     }
1082 
1083     /**
1084      * {@inheritDoc}
1085      */
1086     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)1087     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1088         final int focusableCount = views.size();
1089 
1090         final int descendantFocusability = getDescendantFocusability();
1091 
1092         if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1093             if (shouldBlockFocusForTouchscreen()) {
1094                 focusableMode |= FOCUSABLES_TOUCH_MODE;
1095             }
1096 
1097             final int count = mChildrenCount;
1098             final View[] children = mChildren;
1099 
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);
1104                 }
1105             }
1106         }
1107 
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);
1117         }
1118     }
1119 
1120     /**
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.
1124      *
1125      * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
1126      */
setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus)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();
1136                     }
1137                 }
1138             }
1139         } else {
1140             mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1141         }
1142     }
1143 
1144     /**
1145      * Check whether this ViewGroup should ignore focus requests for itself and its children.
1146      */
getTouchscreenBlocksFocus()1147     public boolean getTouchscreenBlocksFocus() {
1148         return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
1149     }
1150 
shouldBlockFocusForTouchscreen()1151     boolean shouldBlockFocusForTouchscreen() {
1152         return getTouchscreenBlocksFocus() &&
1153                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
1154     }
1155 
1156     @Override
findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags)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);
1166             }
1167         }
1168     }
1169 
1170     /** @hide */
1171     @Override
findViewByAccessibilityIdTraversal(int accessibilityId)1172     public View findViewByAccessibilityIdTraversal(int accessibilityId) {
1173         View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
1174         if (foundView != null) {
1175             return foundView;
1176         }
1177 
1178         if (getAccessibilityNodeProvider() != null) {
1179             return null;
1180         }
1181 
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;
1189             }
1190         }
1191 
1192         return null;
1193     }
1194 
1195     /**
1196      * {@inheritDoc}
1197      */
1198     @Override
dispatchWindowFocusChanged(boolean hasFocus)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);
1205         }
1206     }
1207 
1208     /**
1209      * {@inheritDoc}
1210      */
1211     @Override
addTouchables(ArrayList<View> views)1212     public void addTouchables(ArrayList<View> views) {
1213         super.addTouchables(views);
1214 
1215         final int count = mChildrenCount;
1216         final View[] children = mChildren;
1217 
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);
1222             }
1223         }
1224     }
1225 
1226     /**
1227      * @hide
1228      */
1229     @Override
makeOptionalFitsSystemWindows()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();
1236         }
1237     }
1238 
1239     /**
1240      * {@inheritDoc}
1241      */
1242     @Override
dispatchDisplayHint(int hint)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);
1249         }
1250     }
1251 
1252     /**
1253      * Called when a view's visibility has changed. Notify the parent to take any appropriate
1254      * action.
1255      *
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
1260      */
onChildVisibilityChanged(View child, int oldVisibility, int newVisibility)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>();
1272                     }
1273                     mVisibilityChangingChildren.add(child);
1274                     addDisappearingView(child);
1275                 }
1276             }
1277         }
1278 
1279         // in all cases, for drags
1280         if (mCurrentDrag != null) {
1281             if (newVisibility == VISIBLE) {
1282                 notifyChildOfDrag(child);
1283             }
1284         }
1285     }
1286 
1287     /**
1288      * {@inheritDoc}
1289      */
1290     @Override
dispatchVisibilityChanged(View changedView, int visibility)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);
1297         }
1298     }
1299 
1300     /**
1301      * {@inheritDoc}
1302      */
1303     @Override
dispatchWindowVisibilityChanged(int visibility)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);
1310         }
1311     }
1312 
1313     /**
1314      * {@inheritDoc}
1315      */
1316     @Override
dispatchConfigurationChanged(Configuration newConfig)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);
1323         }
1324     }
1325 
1326     /**
1327      * {@inheritDoc}
1328      */
recomputeViewAttributes(View child)1329     public void recomputeViewAttributes(View child) {
1330         if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1331             ViewParent parent = mParent;
1332             if (parent != null) parent.recomputeViewAttributes(this);
1333         }
1334     }
1335 
1336     @Override
dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility)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));
1346             }
1347         }
1348     }
1349 
1350     /**
1351      * {@inheritDoc}
1352      */
bringChildToFront(View child)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();
1361         }
1362     }
1363 
getLocalPoint()1364     private PointF getLocalPoint() {
1365         if (mLocalPoint == null) mLocalPoint = new PointF();
1366         return mLocalPoint;
1367     }
1368 
1369     /**
1370      * {@inheritDoc}
1371      */
1372     // TODO: Write real docs
1373     @Override
dispatchDragEvent(DragEvent event)1374     public boolean dispatchDragEvent(DragEvent event) {
1375         boolean retval = false;
1376         final float tx = event.mX;
1377         final float ty = event.mY;
1378 
1379         ViewRootImpl root = getViewRootImpl();
1380 
1381         // Dispatch down the view hierarchy
1382         final PointF localPoint = getLocalPoint();
1383 
1384         switch (event.mAction) {
1385         case DragEvent.ACTION_DRAG_STARTED: {
1386             // clear state to recalculate which views we drag over
1387             mCurrentDragView = null;
1388 
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();
1395             }
1396 
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;
1408                     }
1409                 }
1410             }
1411 
1412             // Return HANDLED if one of our children can accept the drag
1413             if (mChildAcceptsDrag) {
1414                 retval = true;
1415             }
1416         } break;
1417 
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();
1426                 }
1427 
1428                 mDragNotifiedChildren.clear();
1429                 if (mCurrentDrag != null) {
1430                     mCurrentDrag.recycle();
1431                     mCurrentDrag = null;
1432                 }
1433             }
1434 
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;
1439             }
1440         } break;
1441 
1442         case DragEvent.ACTION_DRAG_LOCATION: {
1443             // Find the [possibly new] drag target
1444             final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1445 
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);
1454 
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();
1463                 }
1464                 mCurrentDragView = target;
1465 
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();
1472                 }
1473                 event.mAction = action;  // restore the event's original state
1474             }
1475 
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;
1480 
1481                 retval = target.dispatchDragEvent(event);
1482 
1483                 event.mX = tx;
1484                 event.mY = ty;
1485             }
1486         } break;
1487 
1488         /* Entered / exited dispatch
1489          *
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].
1494          *
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.
1498          */
1499 
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();
1506 
1507                 mCurrentDragView = null;
1508             }
1509         } break;
1510 
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");
1524                 }
1525             }
1526         } break;
1527         }
1528 
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);
1533         }
1534         return retval;
1535     }
1536 
1537     // Find the frontmost child view that lies under the given point, and calculate
1538     // the position within its own local coordinate system.
findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint)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;
1546             }
1547 
1548             if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1549                 return child;
1550             }
1551         }
1552         return null;
1553     }
1554 
notifyChildOfDrag(View child)1555     boolean notifyChildOfDrag(View child) {
1556         if (ViewDebug.DEBUG_DRAG) {
1557             Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1558         }
1559 
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();
1567             }
1568         }
1569         return canAccept;
1570     }
1571 
1572     @Override
dispatchWindowSystemUiVisiblityChanged(int visible)1573     public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1574         super.dispatchWindowSystemUiVisiblityChanged(visible);
1575 
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);
1581         }
1582     }
1583 
1584     @Override
dispatchSystemUiVisibilityChanged(int visible)1585     public void dispatchSystemUiVisibilityChanged(int visible) {
1586         super.dispatchSystemUiVisibilityChanged(visible);
1587 
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);
1593         }
1594     }
1595 
1596     @Override
updateLocalSystemUiVisibility(int localValue, int localChanges)1597     boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1598         boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
1599 
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);
1605         }
1606         return changed;
1607     }
1608 
1609     /**
1610      * {@inheritDoc}
1611      */
1612     @Override
dispatchKeyEventPreIme(KeyEvent event)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);
1620         }
1621         return false;
1622     }
1623 
1624     /**
1625      * {@inheritDoc}
1626      */
1627     @Override
dispatchKeyEvent(KeyEvent event)1628     public boolean dispatchKeyEvent(KeyEvent event) {
1629         if (mInputEventConsistencyVerifier != null) {
1630             mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1631         }
1632 
1633         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1634                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1635             if (super.dispatchKeyEvent(event)) {
1636                 return true;
1637             }
1638         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1639                 == PFLAG_HAS_BOUNDS) {
1640             if (mFocused.dispatchKeyEvent(event)) {
1641                 return true;
1642             }
1643         }
1644 
1645         if (mInputEventConsistencyVerifier != null) {
1646             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1647         }
1648         return false;
1649     }
1650 
1651     /**
1652      * {@inheritDoc}
1653      */
1654     @Override
dispatchKeyShortcutEvent(KeyEvent event)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);
1662         }
1663         return false;
1664     }
1665 
1666     /**
1667      * {@inheritDoc}
1668      */
1669     @Override
dispatchTrackballEvent(MotionEvent event)1670     public boolean dispatchTrackballEvent(MotionEvent event) {
1671         if (mInputEventConsistencyVerifier != null) {
1672             mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1673         }
1674 
1675         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1676                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1677             if (super.dispatchTrackballEvent(event)) {
1678                 return true;
1679             }
1680         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1681                 == PFLAG_HAS_BOUNDS) {
1682             if (mFocused.dispatchTrackballEvent(event)) {
1683                 return true;
1684             }
1685         }
1686 
1687         if (mInputEventConsistencyVerifier != null) {
1688             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1689         }
1690         return false;
1691     }
1692 
1693     /**
1694      * {@inheritDoc}
1695      */
1696     @SuppressWarnings({"ConstantConditions"})
1697     @Override
dispatchHoverEvent(MotionEvent event)1698     protected boolean dispatchHoverEvent(MotionEvent event) {
1699         final int action = event.getAction();
1700 
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
1704 
1705         MotionEvent eventNoHistory = event;
1706         boolean handled = false;
1707 
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;
1729                     }
1730 
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;
1740                         }
1741 
1742                         if (hoverTarget.child == child) {
1743                             if (predecessor != null) {
1744                                 predecessor.next = hoverTarget.next;
1745                             } else {
1746                                 firstOldHoverTarget = hoverTarget.next;
1747                             }
1748                             hoverTarget.next = null;
1749                             wasHovered = true;
1750                             break;
1751                         }
1752 
1753                         predecessor = hoverTarget;
1754                         hoverTarget = hoverTarget.next;
1755                     }
1756 
1757                     // Enqueue the hover target onto the new hover target list.
1758                     if (lastHoverTarget != null) {
1759                         lastHoverTarget.next = hoverTarget;
1760                     } else {
1761                         mFirstHoverTarget = hoverTarget;
1762                     }
1763                     lastHoverTarget = hoverTarget;
1764 
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
1771                         }
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);
1780 
1781                             handled |= dispatchTransformedGenericPointerEvent(
1782                                     eventNoHistory, child); // move
1783                         } else {
1784                             // Send the move as is.
1785                             handled |= dispatchTransformedGenericPointerEvent(event, child);
1786                         }
1787                     }
1788                     if (handled) {
1789                         break;
1790                     }
1791                 }
1792                 if (preorderedList != null) preorderedList.clear();
1793             }
1794         }
1795 
1796         // Send exit events to all previously hovered children that are no longer hovered.
1797         while (firstOldHoverTarget != null) {
1798             final View child = firstOldHoverTarget.child;
1799 
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
1811                 }
1812                 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1813                 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1814                 dispatchTransformedGenericPointerEvent(
1815                         eventNoHistory, child); // exit
1816                 eventNoHistory.setAction(action);
1817             }
1818 
1819             final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1820             firstOldHoverTarget.recycle();
1821             firstOldHoverTarget = nextOldHoverTarget;
1822         }
1823 
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);
1830             }
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
1842                     }
1843                     eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1844                     eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1845                     super.dispatchHoverEvent(eventNoHistory); // exit
1846                     eventNoHistory.setAction(action);
1847                 }
1848                 mHoveredSelf = false;
1849             }
1850 
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);
1863 
1864                     handled |= super.dispatchHoverEvent(eventNoHistory); // move
1865                     mHoveredSelf = true;
1866                 }
1867             }
1868         }
1869 
1870         // Recycle the copy of the event that we made.
1871         if (eventNoHistory != event) {
1872             eventNoHistory.recycle();
1873         }
1874 
1875         // Done.
1876         return handled;
1877     }
1878 
exitHoverTargets()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();
1887         }
1888     }
1889 
cancelHoverTarget(View view)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;
1900                 }
1901                 target.recycle();
1902 
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;
1910             }
1911             predecessor = target;
1912             target = next;
1913         }
1914     }
1915 
1916     /** @hide */
1917     @Override
hasHoveredChild()1918     protected boolean hasHoveredChild() {
1919         return mFirstHoverTarget != null;
1920     }
1921 
1922     @Override
addChildrenForAccessibility(ArrayList<View> outChildren)1923     public void addChildrenForAccessibility(ArrayList<View> outChildren) {
1924         if (getAccessibilityNodeProvider() != null) {
1925             return;
1926         }
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);
1937                     }
1938                 }
1939             }
1940         } finally {
1941             children.recycle();
1942         }
1943     }
1944 
1945     /**
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>
1978      *
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.
1982      */
onInterceptHoverEvent(MotionEvent event)1983     public boolean onInterceptHoverEvent(MotionEvent event) {
1984         return false;
1985     }
1986 
obtainMotionEventNoHistoryOrSelf(MotionEvent event)1987     private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1988         if (event.getHistorySize() == 0) {
1989             return event;
1990         }
1991         return MotionEvent.obtainNoHistory(event);
1992     }
1993 
1994     /**
1995      * {@inheritDoc}
1996      */
1997     @Override
dispatchGenericPointerEvent(MotionEvent event)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();
2004 
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;
2016                 }
2017 
2018                 if (dispatchTransformedGenericPointerEvent(event, child)) {
2019                     if (preorderedList != null) preorderedList.clear();
2020                     return true;
2021                 }
2022             }
2023             if (preorderedList != null) preorderedList.clear();
2024         }
2025 
2026         // No child handled the event.  Send it to this view group.
2027         return super.dispatchGenericPointerEvent(event);
2028     }
2029 
2030     /**
2031      * {@inheritDoc}
2032      */
2033     @Override
dispatchGenericFocusedEvent(MotionEvent event)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);
2042         }
2043         return false;
2044     }
2045 
2046     /**
2047      * Dispatches a generic pointer event to a child, taking into account
2048      * transformations that apply to the child.
2049      *
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.
2053      */
dispatchTransformedGenericPointerEvent(MotionEvent event, View child)2054     private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
2055         final float offsetX = mScrollX - child.mLeft;
2056         final float offsetY = mScrollY - child.mTop;
2057 
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);
2069         }
2070         return handled;
2071     }
2072 
2073     /**
2074      * {@inheritDoc}
2075      */
2076     @Override
dispatchTouchEvent(MotionEvent ev)2077     public boolean dispatchTouchEvent(MotionEvent ev) {
2078         if (mInputEventConsistencyVerifier != null) {
2079             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
2080         }
2081 
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);
2086         }
2087 
2088         boolean handled = false;
2089         if (onFilterTouchEventForSecurity(ev)) {
2090             final int action = ev.getAction();
2091             final int actionMasked = action & MotionEvent.ACTION_MASK;
2092 
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();
2100             }
2101 
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;
2112                 }
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;
2117             }
2118 
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);
2123             }
2124 
2125             // Check for cancelation.
2126             final boolean canceled = resetCancelNextUpFlag(this)
2127                     || actionMasked == MotionEvent.ACTION_CANCEL;
2128 
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) {
2134 
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;
2142 
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;
2149 
2150                     // Clean up earlier touch targets for this pointer id in case they
2151                     // have become out of sync.
2152                     removePointersFromTouchTargets(idBitsToAssign);
2153 
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);
2169 
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;
2177                                 }
2178                                 childWithAccessibilityFocus = null;
2179                                 i = childrenCount - 1;
2180                             }
2181 
2182                             if (!canViewReceivePointerEvents(child)
2183                                     || !isTransformedTouchPointInView(x, y, child, null)) {
2184                                 ev.setTargetAccessibilityFocus(false);
2185                                 continue;
2186                             }
2187 
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;
2194                             }
2195 
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;
2206                                         }
2207                                     }
2208                                 } else {
2209                                     mLastTouchDownIndex = childIndex;
2210                                 }
2211                                 mLastTouchDownX = ev.getX();
2212                                 mLastTouchDownY = ev.getY();
2213                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
2214                                 alreadyDispatchedToNewTouchTarget = true;
2215                                 break;
2216                             }
2217 
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);
2221                         }
2222                         if (preorderedList != null) preorderedList.clear();
2223                     }
2224 
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;
2231                         }
2232                         newTouchTarget.pointerIdBits |= idBitsToAssign;
2233                     }
2234                 }
2235             }
2236 
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;
2257                         }
2258                         if (cancelChild) {
2259                             if (predecessor == null) {
2260                                 mFirstTouchTarget = next;
2261                             } else {
2262                                 predecessor.next = next;
2263                             }
2264                             target.recycle();
2265                             target = next;
2266                             continue;
2267                         }
2268                     }
2269                     predecessor = target;
2270                     target = next;
2271                 }
2272             }
2273 
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);
2283             }
2284         }
2285 
2286         if (!handled && mInputEventConsistencyVerifier != null) {
2287             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
2288         }
2289         return handled;
2290     }
2291 
2292     /**
2293      * Finds the child which has accessibility focus.
2294      *
2295      * @return The child that has focus.
2296      */
findChildWithAccessibilityFocus()2297     private View findChildWithAccessibilityFocus() {
2298         ViewRootImpl viewRoot = getViewRootImpl();
2299         if (viewRoot == null) {
2300             return null;
2301         }
2302 
2303         View current = viewRoot.getAccessibilityFocusedHost();
2304         if (current == null) {
2305             return null;
2306         }
2307 
2308         ViewParent parent = current.getParent();
2309         while (parent instanceof View) {
2310             if (parent == this) {
2311                 return current;
2312             }
2313             current = (View) parent;
2314             parent = current.getParent();
2315         }
2316 
2317         return null;
2318     }
2319 
2320     /**
2321      * Resets all touch state in preparation for a new cycle.
2322      */
resetTouchState()2323     private void resetTouchState() {
2324         clearTouchTargets();
2325         resetCancelNextUpFlag(this);
2326         mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2327         mNestedScrollAxes = SCROLL_AXIS_NONE;
2328     }
2329 
2330     /**
2331      * Resets the cancel next up flag.
2332      * Returns true if the flag was previously set.
2333      */
resetCancelNextUpFlag(View view)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;
2338         }
2339         return false;
2340     }
2341 
2342     /**
2343      * Clears all touch targets.
2344      */
clearTouchTargets()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;
2354         }
2355     }
2356 
2357     /**
2358      * Cancels and clears all touch targets.
2359      */
cancelAndClearTouchTargets(MotionEvent event)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;
2369             }
2370 
2371             for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2372                 resetCancelNextUpFlag(target.child);
2373                 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2374             }
2375             clearTouchTargets();
2376 
2377             if (syntheticEvent) {
2378                 event.recycle();
2379             }
2380         }
2381     }
2382 
2383     /**
2384      * Gets the touch target for specified child view.
2385      * Returns null if not found.
2386      */
getTouchTarget(View child)2387     private TouchTarget getTouchTarget(View child) {
2388         for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2389             if (target.child == child) {
2390                 return target;
2391             }
2392         }
2393         return null;
2394     }
2395 
2396     /**
2397      * Adds a touch target for specified child to the beginning of the list.
2398      * Assumes the target child is not already present.
2399      */
addTouchTarget(View child, int pointerIdBits)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;
2405     }
2406 
2407     /**
2408      * Removes the pointer ids from consideration.
2409      */
removePointersFromTouchTargets(int pointerIdBits)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;
2422                     }
2423                     target.recycle();
2424                     target = next;
2425                     continue;
2426                 }
2427             }
2428             predecessor = target;
2429             target = next;
2430         }
2431     }
2432 
cancelTouchTarget(View view)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;
2443                 }
2444                 target.recycle();
2445 
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;
2453             }
2454             predecessor = target;
2455             target = next;
2456         }
2457     }
2458 
2459     /**
2460      * Returns true if a child view can receive pointer events.
2461      * @hide
2462      */
canViewReceivePointerEvents(View child)2463     private static boolean canViewReceivePointerEvents(View child) {
2464         return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2465                 || child.getAnimation() != null;
2466     }
2467 
getTempPoint()2468     private float[] getTempPoint() {
2469         if (mTempPoint == null) {
2470             mTempPoint = new float[2];
2471         }
2472         return mTempPoint;
2473     }
2474 
2475     /**
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
2480      */
isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint)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]);
2490         }
2491         return isInView;
2492     }
2493 
2494     /**
2495      * @hide
2496      */
transformPointToViewLocal(float[] point, View child)2497     public void transformPointToViewLocal(float[] point, View child) {
2498         point[0] += mScrollX - child.mLeft;
2499         point[1] += mScrollY - child.mTop;
2500 
2501         if (!child.hasIdentityMatrix()) {
2502             child.getInverseMatrix().mapPoints(point);
2503         }
2504     }
2505 
2506     /**
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.
2510      */
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)2511     private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
2512             View child, int desiredPointerIdBits) {
2513         final boolean handled;
2514 
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);
2524             }
2525             event.setAction(oldAction);
2526             return handled;
2527         }
2528 
2529         // Calculate the number of pointers to deliver.
2530         final int oldPointerIdBits = event.getPointerIdBits();
2531         final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
2532 
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;
2537         }
2538 
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);
2552 
2553                     handled = child.dispatchTouchEvent(event);
2554 
2555                     event.offsetLocation(-offsetX, -offsetY);
2556                 }
2557                 return handled;
2558             }
2559             transformedEvent = MotionEvent.obtain(event);
2560         } else {
2561             transformedEvent = event.split(newPointerIdBits);
2562         }
2563 
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());
2573             }
2574 
2575             handled = child.dispatchTouchEvent(transformedEvent);
2576         }
2577 
2578         // Done.
2579         transformedEvent.recycle();
2580         return handled;
2581     }
2582 
2583     /**
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.
2587      *
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.
2592      *
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
2597      */
setMotionEventSplittingEnabled(boolean split)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;
2606         }
2607     }
2608 
2609     /**
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.
2612      */
isMotionEventSplittingEnabled()2613     public boolean isMotionEventSplittingEnabled() {
2614         return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2615     }
2616 
2617     /**
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.
2621      *
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.
2628      */
isTransitionGroup()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);
2636         }
2637     }
2638 
2639     /**
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[])
2648      */
setTransitionGroup(boolean isTransitionGroup)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;
2655         }
2656     }
2657 
2658     /**
2659      * {@inheritDoc}
2660      */
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2661     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2662 
2663         if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2664             // We're already in this state, assume our ancestors are too
2665             return;
2666         }
2667 
2668         if (disallowIntercept) {
2669             mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2670         } else {
2671             mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2672         }
2673 
2674         // Pass it up to our parent
2675         if (mParent != null) {
2676             mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2677         }
2678     }
2679 
2680     /**
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.
2684      *
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:
2690      *
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>
2710      *
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.
2716      */
onInterceptTouchEvent(MotionEvent ev)2717     public boolean onInterceptTouchEvent(MotionEvent ev) {
2718         return false;
2719     }
2720 
2721     /**
2722      * {@inheritDoc}
2723      *
2724      * Looks for a view to give focus to respecting the setting specified by
2725      * {@link #getDescendantFocusability()}.
2726      *
2727      * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2728      * find focus within the children of this group when appropriate.
2729      *
2730      * @see #FOCUS_BEFORE_DESCENDANTS
2731      * @see #FOCUS_AFTER_DESCENDANTS
2732      * @see #FOCUS_BLOCK_DESCENDANTS
2733      * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
2734      */
2735     @Override
requestFocus(int direction, Rect previouslyFocusedRect)2736     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2737         if (DBG) {
2738             System.out.println(this + " ViewGroup.requestFocus direction="
2739                     + direction);
2740         }
2741         int descendantFocusability = getDescendantFocusability();
2742 
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);
2749             }
2750             case FOCUS_AFTER_DESCENDANTS: {
2751                 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2752                 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2753             }
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);
2758         }
2759     }
2760 
2761     /**
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.
2771      */
2772     @SuppressWarnings({"ConstantConditions"})
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)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;
2787         }
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;
2794                 }
2795             }
2796         }
2797         return false;
2798     }
2799 
2800     /**
2801      * {@inheritDoc}
2802      *
2803      * @hide
2804      */
2805     @Override
dispatchStartTemporaryDetach()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();
2812         }
2813     }
2814 
2815     /**
2816      * {@inheritDoc}
2817      *
2818      * @hide
2819      */
2820     @Override
dispatchFinishTemporaryDetach()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();
2827         }
2828     }
2829 
2830     /**
2831      * {@inheritDoc}
2832      */
2833     @Override
dispatchAttachedToWindow(AttachInfo info, int visibility)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;
2838 
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()));
2845         }
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()));
2851         }
2852     }
2853 
2854     @Override
dispatchScreenStateChanged(int screenState)2855     void dispatchScreenStateChanged(int screenState) {
2856         super.dispatchScreenStateChanged(screenState);
2857 
2858         final int count = mChildrenCount;
2859         final View[] children = mChildren;
2860         for (int i = 0; i < count; i++) {
2861             children[i].dispatchScreenStateChanged(screenState);
2862         }
2863     }
2864 
2865     /** @hide */
2866     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)2867     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
2868         boolean handled = false;
2869         if (includeForAccessibility()) {
2870             handled = super.dispatchPopulateAccessibilityEventInternal(event);
2871             if (handled) {
2872                 return handled;
2873             }
2874         }
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;
2885                     }
2886                 }
2887             }
2888         } finally {
2889             children.recycle();
2890         }
2891         return false;
2892     }
2893 
2894     /**
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.
2898      */
dispatchProvideStructure(ViewStructure structure)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);
2937                                     }
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++;
2944                                         }
2945                                         permutation[j] = nextIndex;
2946                                         nextIndex++;
2947                                     }
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]]);
2952                                     }
2953                                 }
2954                             } else {
2955                                 throw e;
2956                             }
2957                         }
2958                         final View child = (preorderedList == null)
2959                                 ? children[childIndex] : preorderedList.get(childIndex);
2960                         ViewStructure cstructure = structure.newChild(i);
2961                         child.dispatchProvideStructure(cstructure);
2962                     }
2963                 }
2964             }
2965         }
2966     }
2967 
2968     /** @hide */
2969     @Override
onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)2970     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2971         super.onInitializeAccessibilityNodeInfoInternal(info);
2972         if (getAccessibilityNodeProvider() != null) {
2973             return;
2974         }
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);
2983             }
2984             childrenForAccessibility.clear();
2985         }
2986     }
2987 
2988     @Override
getAccessibilityClassName()2989     public CharSequence getAccessibilityClassName() {
2990         return ViewGroup.class.getName();
2991     }
2992 
2993     @Override
notifySubtreeAccessibilityStateChanged(View child, View source, int changeType)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);
3005             }
3006         }
3007     }
3008 
3009     @Override
resetSubtreeAccessibilityStateChanged()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();
3016         }
3017     }
3018 
3019     /**
3020      * {@inheritDoc}
3021      *
3022      * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p>
3023      *
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.
3029      */
3030     @Override
onNestedPrePerformAccessibilityAction(View target, int action, Bundle args)3031     public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
3032         return false;
3033     }
3034 
3035     /**
3036      * {@inheritDoc}
3037      */
3038     @Override
dispatchDetachedFromWindow()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);
3046 
3047         // Similarly, set ACTION_EXIT to all hover targets and clear them.
3048         exitHoverTargets();
3049 
3050         // In case view is detached while transition is running
3051         mLayoutCalledWhileSuppressed = false;
3052 
3053         // Tear down our drag tracking
3054         mDragNotifiedChildren = null;
3055         if (mCurrentDrag != null) {
3056             mCurrentDrag.recycle();
3057             mCurrentDrag = null;
3058         }
3059 
3060         final int count = mChildrenCount;
3061         final View[] children = mChildren;
3062         for (int i = 0; i < count; i++) {
3063             children[i].dispatchDetachedFromWindow();
3064         }
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();
3070         }
3071         super.dispatchDetachedFromWindow();
3072     }
3073 
3074     /**
3075      * @hide
3076      */
3077     @Override
internalSetPadding(int left, int top, int right, int bottom)3078     protected void internalSetPadding(int left, int top, int right, int bottom) {
3079         super.internalSetPadding(left, top, right, bottom);
3080 
3081         if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
3082             mGroupFlags |= FLAG_PADDING_NOT_NULL;
3083         } else {
3084             mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
3085         }
3086     }
3087 
3088     /**
3089      * {@inheritDoc}
3090      */
3091     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)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);
3100             }
3101         }
3102     }
3103 
3104     /**
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.
3109      *
3110      * @param container the container
3111      */
dispatchFreezeSelfOnly(SparseArray<Parcelable> container)3112     protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
3113         super.dispatchSaveInstanceState(container);
3114     }
3115 
3116     /**
3117      * {@inheritDoc}
3118      */
3119     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)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);
3128             }
3129         }
3130     }
3131 
3132     /**
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.
3137      *
3138      * @param container the container
3139      */
dispatchThawSelfOnly(SparseArray<Parcelable> container)3140     protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
3141         super.dispatchRestoreInstanceState(container);
3142     }
3143 
3144     /**
3145      * Enables or disables the drawing cache for each child of this view group.
3146      *
3147      * @param enabled true to enable the cache, false to dispose of it
3148      */
setChildrenDrawingCacheEnabled(boolean enabled)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);
3155             }
3156         }
3157     }
3158 
3159     @Override
createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren)3160     Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
3161         int count = mChildrenCount;
3162         int[] visibilities = null;
3163 
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);
3171                 }
3172             }
3173         }
3174 
3175         Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
3176 
3177         if (skipChildren) {
3178             for (int i = 0; i < count; i++) {
3179                 getChildAt(i).setVisibility(visibilities[i]);
3180             }
3181         }
3182 
3183         return b;
3184     }
3185 
3186     /** Return true if this ViewGroup is laying out using optical bounds. */
isLayoutModeOptical()3187     boolean isLayoutModeOptical() {
3188         return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
3189     }
3190 
computeOpticalInsets()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);
3205                 }
3206             }
3207             return Insets.of(left, top, right, bottom);
3208         } else {
3209             return Insets.NONE;
3210         }
3211     }
3212 
fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)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;
3217             }
3218             if (y1 > y2) {
3219                 int tmp = y1; y1 = y2; y2 = tmp;
3220             }
3221             canvas.drawRect(x1, y1, x2, y2, paint);
3222         }
3223     }
3224 
sign(int x)3225     private static int sign(int x) {
3226         return (x >= 0) ? 1 : -1;
3227     }
3228 
drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw)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);
3232     }
3233 
dipsToPixels(int dips)3234     private int dipsToPixels(int dips) {
3235         float scale = getContext().getResources().getDisplayMetrics().density;
3236         return (int) (dips * scale + 0.5f);
3237     }
3238 
drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, int lineLength, int lineWidth)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);
3245     }
3246 
fillDifference(Canvas canvas, int x2, int y2, int x3, int y3, int dx1, int dy1, int dx2, int dy2, Paint paint)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;
3252 
3253         int x4 = x3 + dx2;
3254         int y4 = y3 + dy2;
3255 
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);
3260     }
3261 
3262     /**
3263      * @hide
3264      */
onDebugDrawMargins(Canvas canvas, Paint paint)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);
3269         }
3270     }
3271 
3272     /**
3273      * @hide
3274      */
onDebugDraw(Canvas canvas)3275     protected void onDebugDraw(Canvas canvas) {
3276         Paint paint = getDebugPaint();
3277 
3278         // Draw optical bounds
3279         {
3280             paint.setColor(Color.RED);
3281             paint.setStyle(Paint.Style.STROKE);
3282 
3283             for (int i = 0; i < getChildCount(); i++) {
3284                 View c = getChildAt(i);
3285                 if (c.getVisibility() != View.GONE) {
3286                     Insets insets = c.getOpticalInsets();
3287 
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);
3293                 }
3294             }
3295         }
3296 
3297         // Draw margins
3298         {
3299             paint.setColor(Color.argb(63, 255, 0, 255));
3300             paint.setStyle(Paint.Style.FILL);
3301 
3302             onDebugDrawMargins(canvas, paint);
3303         }
3304 
3305         // Draw clip bounds
3306         {
3307             paint.setColor(Color.rgb(63, 127, 255));
3308             paint.setStyle(Paint.Style.FILL);
3309 
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);
3317                 }
3318             }
3319         }
3320     }
3321 
3322     /**
3323      * {@inheritDoc}
3324      */
3325     @Override
dispatchDraw(Canvas canvas)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;
3331 
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);
3340                 }
3341             }
3342 
3343             final LayoutAnimationController controller = mLayoutAnimationController;
3344             if (controller.willOverlap()) {
3345                 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
3346             }
3347 
3348             controller.start();
3349 
3350             mGroupFlags &= ~FLAG_RUN_ANIMATION;
3351             mGroupFlags &= ~FLAG_ANIMATION_DONE;
3352 
3353             if (mAnimationListener != null) {
3354                 mAnimationListener.onAnimationStart(controller.getAnimation());
3355             }
3356         }
3357 
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);
3365         }
3366 
3367         // We will draw our child's animation, let's reset the flag
3368         mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
3369         mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
3370 
3371         boolean more = false;
3372         final long drawingTime = getDrawingTime();
3373 
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);
3389                 }
3390                 transientIndex++;
3391                 if (transientIndex >= transientCount) {
3392                     transientIndex = -1;
3393                 }
3394             }
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);
3400             }
3401         }
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);
3408             }
3409             transientIndex++;
3410             if (transientIndex >= transientCount) {
3411                 break;
3412             }
3413         }
3414         if (preorderedList != null) preorderedList.clear();
3415 
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);
3424             }
3425         }
3426         if (usingRenderNodeProperties) canvas.insertInorderBarrier();
3427 
3428         if (debugDraw()) {
3429             onDebugDraw(canvas);
3430         }
3431 
3432         if (clipToPadding) {
3433             canvas.restoreToCount(clipSaveCount);
3434         }
3435 
3436         // mGroupFlags might have been updated by drawChild()
3437         flags = mGroupFlags;
3438 
3439         if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
3440             invalidate(true);
3441         }
3442 
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();
3452                }
3453             };
3454             post(end);
3455         }
3456     }
3457 
3458     /**
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.
3466      *
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>
3470      *
3471      * @return The ViewGroupOverlay object for this view.
3472      * @see ViewGroupOverlay
3473      */
3474     @Override
getOverlay()3475     public ViewGroupOverlay getOverlay() {
3476         if (mOverlay == null) {
3477             mOverlay = new ViewGroupOverlay(mContext, this);
3478         }
3479         return (ViewGroupOverlay) mOverlay;
3480     }
3481 
3482     /**
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)}.
3489      *
3490      * @param i The current iteration.
3491      * @return The index of the child to draw this iteration.
3492      *
3493      * @see #setChildrenDrawingOrderEnabled(boolean)
3494      * @see #isChildrenDrawingOrderEnabled()
3495      */
getChildDrawingOrder(int childCount, int i)3496     protected int getChildDrawingOrder(int childCount, int i) {
3497         return i;
3498     }
3499 
hasChildWithZ()3500     private boolean hasChildWithZ() {
3501         for (int i = 0; i < mChildrenCount; i++) {
3502             if (mChildren[i].getZ() != 0) return true;
3503         }
3504         return false;
3505     }
3506 
3507     /**
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.
3511      *
3512      * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
3513      * children.
3514      */
buildOrderedChildList()3515     ArrayList<View> buildOrderedChildList() {
3516         final int count = mChildrenCount;
3517         if (count <= 1 || !hasChildWithZ()) return null;
3518 
3519         if (mPreSortedChildren == null) {
3520             mPreSortedChildren = new ArrayList<View>(count);
3521         } else {
3522             mPreSortedChildren.ensureCapacity(count);
3523         }
3524 
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();
3531 
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--;
3536             }
3537             mPreSortedChildren.add(insertIndex, nextChild);
3538         }
3539         return mPreSortedChildren;
3540     }
3541 
notifyAnimationListener()3542     private void notifyAnimationListener() {
3543         mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
3544         mGroupFlags |= FLAG_ANIMATION_DONE;
3545 
3546         if (mAnimationListener != null) {
3547            final Runnable end = new Runnable() {
3548                public void run() {
3549                    mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
3550                }
3551            };
3552            post(end);
3553         }
3554 
3555         invalidate(true);
3556     }
3557 
3558     /**
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.
3563      *
3564      * @hide
3565      */
3566     @Override
dispatchGetDisplayList()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);
3574             }
3575         }
3576         if (mOverlay != null) {
3577             View overlayView = mOverlay.getOverlayView();
3578             recreateChildDisplayList(overlayView);
3579         }
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);
3586             }
3587         }
3588     }
3589 
recreateChildDisplayList(View 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;
3595     }
3596 
3597     /**
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.
3602      *
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
3607      */
drawChild(Canvas canvas, View child, long drawingTime)3608     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3609         return child.draw(canvas, this, drawingTime);
3610     }
3611 
3612     @Override
getScrollIndicatorBounds(@onNull Rect out)3613     void getScrollIndicatorBounds(@NonNull Rect out) {
3614         super.getScrollIndicatorBounds(out);
3615 
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;
3624         }
3625     }
3626 
3627     /**
3628      * Returns whether this group's children are clipped to their bounds before drawing.
3629      * The default value is true.
3630      * @see #setClipChildren(boolean)
3631      *
3632      * @return True if the group's children will be clipped to their bounds,
3633      * false otherwise.
3634      */
3635     @ViewDebug.ExportedProperty(category = "drawing")
getClipChildren()3636     public boolean getClipChildren() {
3637         return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
3638     }
3639 
3640     /**
3641      * By default, children are clipped to their bounds before drawing. This
3642      * allows view groups to override this behavior for animations, etc.
3643      *
3644      * @param clipChildren true to clip children to their bounds,
3645      *        false otherwise
3646      * @attr ref android.R.styleable#ViewGroup_clipChildren
3647      */
setClipChildren(boolean 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);
3656                 }
3657             }
3658             invalidate(true);
3659         }
3660     }
3661 
3662     /**
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.
3668      *
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
3672      */
setClipToPadding(boolean 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);
3677         }
3678     }
3679 
3680     /**
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.
3686      *
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.
3689      *
3690      * @attr ref android.R.styleable#ViewGroup_clipToPadding
3691      */
3692     @ViewDebug.ExportedProperty(category = "drawing")
getClipToPadding()3693     public boolean getClipToPadding() {
3694         return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
3695     }
3696 
3697     /**
3698      * {@inheritDoc}
3699      */
3700     @Override
dispatchSetSelected(boolean selected)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);
3706         }
3707     }
3708 
3709     /**
3710      * {@inheritDoc}
3711      */
3712     @Override
dispatchSetActivated(boolean activated)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);
3718         }
3719     }
3720 
3721     @Override
dispatchSetPressed(boolean pressed)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);
3732             }
3733         }
3734     }
3735 
3736     /**
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>
3745      *
3746      * @param x hotspot x coordinate
3747      * @param y hotspot y coordinate
3748      * @see #drawableHotspotChanged(float, float)
3749      */
3750     @Override
dispatchDrawableHotspotChanged(float x, float y)3751     public void dispatchDrawableHotspotChanged(float x, float y) {
3752         final int count = mChildrenCount;
3753         if (count == 0) {
3754             return;
3755         }
3756 
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]);
3770             }
3771         }
3772     }
3773 
3774     @Override
dispatchCancelPendingInputEvents()3775     void dispatchCancelPendingInputEvents() {
3776         super.dispatchCancelPendingInputEvents();
3777 
3778         final View[] children = mChildren;
3779         final int count = mChildrenCount;
3780         for (int i = 0; i < count; i++) {
3781             children[i].dispatchCancelPendingInputEvents();
3782         }
3783     }
3784 
3785     /**
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.
3790      *
3791      * Any subclass overriding
3792      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3793      * set this property to true.
3794      *
3795      * @param enabled True to enable static transformations on children, false otherwise.
3796      *
3797      * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
3798      */
setStaticTransformationsEnabled(boolean enabled)3799     protected void setStaticTransformationsEnabled(boolean enabled) {
3800         setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3801     }
3802 
3803     /**
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.
3809      *
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)
3814      */
getChildStaticTransformation(View child, Transformation t)3815     protected boolean getChildStaticTransformation(View child, Transformation t) {
3816         return false;
3817     }
3818 
getChildTransformation()3819     Transformation getChildTransformation() {
3820         if (mChildTransformation == null) {
3821             mChildTransformation = new Transformation();
3822         }
3823         return mChildTransformation;
3824     }
3825 
3826     /**
3827      * {@hide}
3828      */
3829     @Override
findViewTraversal(@dRes int id)3830     protected View findViewTraversal(@IdRes int id) {
3831         if (id == mID) {
3832             return this;
3833         }
3834 
3835         final View[] where = mChildren;
3836         final int len = mChildrenCount;
3837 
3838         for (int i = 0; i < len; i++) {
3839             View v = where[i];
3840 
3841             if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3842                 v = v.findViewById(id);
3843 
3844                 if (v != null) {
3845                     return v;
3846                 }
3847             }
3848         }
3849 
3850         return null;
3851     }
3852 
3853     /**
3854      * {@hide}
3855      */
3856     @Override
findViewWithTagTraversal(Object tag)3857     protected View findViewWithTagTraversal(Object tag) {
3858         if (tag != null && tag.equals(mTag)) {
3859             return this;
3860         }
3861 
3862         final View[] where = mChildren;
3863         final int len = mChildrenCount;
3864 
3865         for (int i = 0; i < len; i++) {
3866             View v = where[i];
3867 
3868             if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3869                 v = v.findViewWithTag(tag);
3870 
3871                 if (v != null) {
3872                     return v;
3873                 }
3874             }
3875         }
3876 
3877         return null;
3878     }
3879 
3880     /**
3881      * {@hide}
3882      */
3883     @Override
findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip)3884     protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
3885         if (predicate.apply(this)) {
3886             return this;
3887         }
3888 
3889         final View[] where = mChildren;
3890         final int len = mChildrenCount;
3891 
3892         for (int i = 0; i < len; i++) {
3893             View v = where[i];
3894 
3895             if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3896                 v = v.findViewByPredicate(predicate);
3897 
3898                 if (v != null) {
3899                     return v;
3900                 }
3901             }
3902         }
3903 
3904         return null;
3905     }
3906 
3907     /**
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.
3921      *
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>
3925      *
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>
3930      *
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>
3935      *
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.
3941      *
3942      * @hide
3943      */
addTransientView(View view, int index)3944     public void addTransientView(View view, int index) {
3945         if (index < 0) {
3946             return;
3947         }
3948         if (mTransientIndices == null) {
3949             mTransientIndices = new ArrayList<Integer>();
3950             mTransientViews = new ArrayList<View>();
3951         }
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;
3958                 }
3959             }
3960             mTransientIndices.add(insertionIndex, index);
3961             mTransientViews.add(insertionIndex, view);
3962         } else {
3963             mTransientIndices.add(index);
3964             mTransientViews.add(view);
3965         }
3966         view.mParent = this;
3967         view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3968         invalidate(true);
3969     }
3970 
3971     /**
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.
3974      *
3975      * @param view The transient view to be removed
3976      *
3977      * @hide
3978      */
removeTransientView(View view)3979     public void removeTransientView(View view) {
3980         if (mTransientViews == null) {
3981             return;
3982         }
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;
3992             }
3993         }
3994     }
3995 
3996     /**
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)}.
4000      *
4001      * @see #addTransientView(View, int)
4002      * @return The number of transient views in this container
4003      *
4004      * @hide
4005      */
getTransientViewCount()4006     public int getTransientViewCount() {
4007         return mTransientIndices == null ? 0 : mTransientIndices.size();
4008     }
4009 
4010     /**
4011      * Given a valid position within the list of transient views, returns the index of
4012      * the transient view at that position.
4013      *
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
4018      *
4019      * @hide
4020      */
getTransientViewIndex(int position)4021     public int getTransientViewIndex(int position) {
4022         if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) {
4023             return -1;
4024         }
4025         return mTransientIndices.get(position);
4026     }
4027 
4028     /**
4029      * Given a valid position within the list of transient views, returns the
4030      * transient view at that position.
4031      *
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
4036      *
4037      * @hide
4038      */
getTransientView(int position)4039     public View getTransientView(int position) {
4040         if (mTransientViews == null || position >= mTransientViews.size()) {
4041             return null;
4042         }
4043         return mTransientViews.get(position);
4044     }
4045 
4046     /**
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>
4049      *
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>
4053      *
4054      * @param child the child view to add
4055      *
4056      * @see #generateDefaultLayoutParams()
4057      */
addView(View child)4058     public void addView(View child) {
4059         addView(child, -1);
4060     }
4061 
4062     /**
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.
4065      *
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>
4069      *
4070      * @param child the child view to add
4071      * @param index the position at which to add the child
4072      *
4073      * @see #generateDefaultLayoutParams()
4074      */
addView(View child, int index)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");
4078         }
4079         LayoutParams params = child.getLayoutParams();
4080         if (params == null) {
4081             params = generateDefaultLayoutParams();
4082             if (params == null) {
4083                 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
4084             }
4085         }
4086         addView(child, index, params);
4087     }
4088 
4089     /**
4090      * Adds a child view with this ViewGroup's default layout parameters and the
4091      * specified width and height.
4092      *
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>
4096      *
4097      * @param child the child view to add
4098      */
addView(View child, int width, int height)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);
4104     }
4105 
4106     /**
4107      * Adds a child view with the specified layout parameters.
4108      *
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>
4112      *
4113      * @param child the child view to add
4114      * @param params the layout parameters to set on the child
4115      */
addView(View child, LayoutParams params)4116     public void addView(View child, LayoutParams params) {
4117         addView(child, -1, params);
4118     }
4119 
4120     /**
4121      * Adds a child view with the specified layout parameters.
4122      *
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>
4126      *
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
4130      */
addView(View child, int index, LayoutParams params)4131     public void addView(View child, int index, LayoutParams params) {
4132         if (DBG) {
4133             System.out.println(this + " addView");
4134         }
4135 
4136         if (child == null) {
4137             throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4138         }
4139 
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);
4146     }
4147 
4148     /**
4149      * {@inheritDoc}
4150      */
updateViewLayout(View view, ViewGroup.LayoutParams params)4151     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
4152         if (!checkLayoutParams(params)) {
4153             throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
4154         }
4155         if (view.mParent != this) {
4156             throw new IllegalArgumentException("Given view not a child of " + this);
4157         }
4158         view.setLayoutParams(params);
4159     }
4160 
4161     /**
4162      * {@inheritDoc}
4163      */
checkLayoutParams(ViewGroup.LayoutParams p)4164     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4165         return  p != null;
4166     }
4167 
4168     /**
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.
4172      */
4173     public interface OnHierarchyChangeListener {
4174         /**
4175          * Called when a new child is added to a parent view.
4176          *
4177          * @param parent the view in which a child was added
4178          * @param child the new child view added in the hierarchy
4179          */
onChildViewAdded(View parent, View child)4180         void onChildViewAdded(View parent, View child);
4181 
4182         /**
4183          * Called when a child is removed from a parent view.
4184          *
4185          * @param parent the view from which the child was removed
4186          * @param child the child removed from the hierarchy
4187          */
onChildViewRemoved(View parent, View child)4188         void onChildViewRemoved(View parent, View child);
4189     }
4190 
4191     /**
4192      * Register a callback to be invoked when a child is added to or removed
4193      * from this view.
4194      *
4195      * @param listener the callback to invoke on hierarchy change
4196      */
setOnHierarchyChangeListener(OnHierarchyChangeListener listener)4197     public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
4198         mOnHierarchyChangeListener = listener;
4199     }
4200 
dispatchViewAdded(View child)4201     void dispatchViewAdded(View child) {
4202         onViewAdded(child);
4203         if (mOnHierarchyChangeListener != null) {
4204             mOnHierarchyChangeListener.onChildViewAdded(this, child);
4205         }
4206     }
4207 
4208     /**
4209      * Called when a new child is added to this ViewGroup. Overrides should always
4210      * call super.onViewAdded.
4211      *
4212      * @param child the added child view
4213      */
onViewAdded(View child)4214     public void onViewAdded(View child) {
4215     }
4216 
dispatchViewRemoved(View child)4217     void dispatchViewRemoved(View child) {
4218         onViewRemoved(child);
4219         if (mOnHierarchyChangeListener != null) {
4220             mOnHierarchyChangeListener.onChildViewRemoved(this, child);
4221         }
4222     }
4223 
4224     /**
4225      * Called when a child view is removed from this ViewGroup. Overrides should always
4226      * call super.onViewRemoved.
4227      *
4228      * @param child the removed child view
4229      */
onViewRemoved(View child)4230     public void onViewRemoved(View child) {
4231     }
4232 
clearCachedLayoutMode()4233     private void clearCachedLayoutMode() {
4234         if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
4235            mLayoutMode = LAYOUT_MODE_UNDEFINED;
4236         }
4237     }
4238 
4239     @Override
onAttachedToWindow()4240     protected void onAttachedToWindow() {
4241         super.onAttachedToWindow();
4242         clearCachedLayoutMode();
4243     }
4244 
4245     @Override
onDetachedFromWindow()4246     protected void onDetachedFromWindow() {
4247         super.onDetachedFromWindow();
4248         clearCachedLayoutMode();
4249     }
4250 
4251     /**
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).
4254      *
4255      * If index is negative, it means put it at the end of the list.
4256      *
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
4261      */
addViewInLayout(View child, int index, LayoutParams params)4262     protected boolean addViewInLayout(View child, int index, LayoutParams params) {
4263         return addViewInLayout(child, index, params, false);
4264     }
4265 
4266     /**
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).
4269      *
4270      * If index is negative, it means put it at the end of the list.
4271      *
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
4278      */
addViewInLayout(View child, int index, LayoutParams params, boolean preventRequestLayout)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");
4283         }
4284         child.mParent = null;
4285         addViewInner(child, index, params, preventRequestLayout);
4286         child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
4287         return true;
4288     }
4289 
4290     /**
4291      * Prevents the specified child to be laid out during the next layout pass.
4292      *
4293      * @param child the child on which to perform the cleanup
4294      */
cleanupLayoutState(View child)4295     protected void cleanupLayoutState(View child) {
4296         child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
4297     }
4298 
addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout)4299     private void addViewInner(View child, int index, LayoutParams params,
4300             boolean preventRequestLayout) {
4301 
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);
4306         }
4307 
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.");
4311         }
4312 
4313         if (mTransition != null) {
4314             mTransition.addChild(this, child);
4315         }
4316 
4317         if (!checkLayoutParams(params)) {
4318             params = generateLayoutParams(params);
4319         }
4320 
4321         if (preventRequestLayout) {
4322             child.mLayoutParams = params;
4323         } else {
4324             child.setLayoutParams(params);
4325         }
4326 
4327         if (index < 0) {
4328             index = mChildrenCount;
4329         }
4330 
4331         addInArray(child, index);
4332 
4333         // tell our children
4334         if (preventRequestLayout) {
4335             child.assignParent(this);
4336         } else {
4337             child.mParent = this;
4338         }
4339 
4340         if (child.hasFocus()) {
4341             requestChildFocus(child, child.findFocus());
4342         }
4343 
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);
4351             }
4352             ai.mKeepScreenOn = lastKeepOn;
4353         }
4354 
4355         if (child.isLayoutDirectionInherited()) {
4356             child.resetRtlProperties();
4357         }
4358 
4359         dispatchViewAdded(child);
4360 
4361         if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
4362             mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
4363         }
4364 
4365         if (child.hasTransientState()) {
4366             childHasTransientStateChanged(child, true);
4367         }
4368 
4369         if (child.getVisibility() != View.GONE) {
4370             notifySubtreeAccessibilityStateChangedIfNeeded();
4371         }
4372 
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);
4379                 }
4380             }
4381         }
4382     }
4383 
addInArray(View child, int index)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;
4393             }
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);
4403             }
4404             children[index] = child;
4405             mChildrenCount++;
4406             if (mLastTouchDownIndex >= index) {
4407                 mLastTouchDownIndex++;
4408             }
4409         } else {
4410             throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
4411         }
4412     }
4413 
4414     // This method also sets the child's mParent to null
removeFromArray(int index)4415     private void removeFromArray(int index) {
4416         final View[] children = mChildren;
4417         if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
4418             children[index].mParent = null;
4419         }
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();
4428         }
4429         if (mLastTouchDownIndex == index) {
4430             mLastTouchDownTime = 0;
4431             mLastTouchDownIndex = -1;
4432         } else if (mLastTouchDownIndex > index) {
4433             mLastTouchDownIndex--;
4434         }
4435     }
4436 
4437     // This method also sets the children's mParent to null
removeFromArray(int start, int count)4438     private void removeFromArray(int start, int count) {
4439         final View[] children = mChildren;
4440         final int childrenCount = mChildrenCount;
4441 
4442         start = Math.max(0, start);
4443         final int end = Math.min(childrenCount, start + count);
4444 
4445         if (start == end) {
4446             return;
4447         }
4448 
4449         if (end == childrenCount) {
4450             for (int i = start; i < end; i++) {
4451                 children[i].mParent = null;
4452                 children[i] = null;
4453             }
4454         } else {
4455             for (int i = start; i < end; i++) {
4456                 children[i].mParent = null;
4457             }
4458 
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);
4462 
4463             for (int i = childrenCount - (end - start); i < childrenCount; i++) {
4464                 children[i] = null;
4465             }
4466         }
4467 
4468         mChildrenCount -= (end - start);
4469     }
4470 
bindLayoutAnimation(View child)4471     private void bindLayoutAnimation(View child) {
4472         Animation a = mLayoutAnimationController.getAnimationForView(child);
4473         child.setAnimation(a);
4474     }
4475 
4476     /**
4477      * Subclasses should override this method to set layout animation
4478      * parameters on the supplied child.
4479      *
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
4485      */
attachLayoutAnimationParameters(View child, LayoutParams params, int index, int count)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;
4493         }
4494 
4495         animationParams.count = count;
4496         animationParams.index = index;
4497     }
4498 
4499     /**
4500      * {@inheritDoc}
4501      *
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>
4505      */
removeView(View view)4506     public void removeView(View view) {
4507         if (removeViewInternal(view)) {
4508             requestLayout();
4509             invalidate(true);
4510         }
4511     }
4512 
4513     /**
4514      * Removes a view during layout. This is useful if in your onLayout() method,
4515      * you need to remove more views.
4516      *
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>
4520      *
4521      * @param view the view to remove from the group
4522      */
removeViewInLayout(View view)4523     public void removeViewInLayout(View view) {
4524         removeViewInternal(view);
4525     }
4526 
4527     /**
4528      * Removes a range of views during layout. This is useful if in your onLayout() method,
4529      * you need to remove more views.
4530      *
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>
4534      *
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
4537      */
removeViewsInLayout(int start, int count)4538     public void removeViewsInLayout(int start, int count) {
4539         removeViewsInternal(start, count);
4540     }
4541 
4542     /**
4543      * Removes the view at the specified position in the group.
4544      *
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>
4548      *
4549      * @param index the position in the group of the view to remove
4550      */
removeViewAt(int index)4551     public void removeViewAt(int index) {
4552         removeViewInternal(index, getChildAt(index));
4553         requestLayout();
4554         invalidate(true);
4555     }
4556 
4557     /**
4558      * Removes the specified range of views from the group.
4559      *
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>
4563      *
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
4566      */
removeViews(int start, int count)4567     public void removeViews(int start, int count) {
4568         removeViewsInternal(start, count);
4569         requestLayout();
4570         invalidate(true);
4571     }
4572 
removeViewInternal(View view)4573     private boolean removeViewInternal(View view) {
4574         final int index = indexOfChild(view);
4575         if (index >= 0) {
4576             removeViewInternal(index, view);
4577             return true;
4578         }
4579         return false;
4580     }
4581 
removeViewInternal(int index, View view)4582     private void removeViewInternal(int index, View view) {
4583 
4584         if (mTransition != null) {
4585             mTransition.removeChild(this, view);
4586         }
4587 
4588         boolean clearChildFocus = false;
4589         if (view == mFocused) {
4590             view.unFocus(null);
4591             clearChildFocus = true;
4592         }
4593 
4594         view.clearAccessibilityFocus();
4595 
4596         cancelTouchTarget(view);
4597         cancelHoverTarget(view);
4598 
4599         if (view.getAnimation() != null ||
4600                 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4601             addDisappearingView(view);
4602         } else if (view.mAttachInfo != null) {
4603            view.dispatchDetachedFromWindow();
4604         }
4605 
4606         if (view.hasTransientState()) {
4607             childHasTransientStateChanged(view, false);
4608         }
4609 
4610         needGlobalAttributesUpdate(false);
4611 
4612         removeFromArray(index);
4613 
4614         if (clearChildFocus) {
4615             clearChildFocus(view);
4616             if (!rootViewRequestFocus()) {
4617                 notifyGlobalFocusCleared(this);
4618             }
4619         }
4620 
4621         dispatchViewRemoved(view);
4622 
4623         if (view.getVisibility() != View.GONE) {
4624             notifySubtreeAccessibilityStateChangedIfNeeded();
4625         }
4626 
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);
4632             }
4633         }
4634     }
4635 
4636     /**
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).
4641      *
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>
4645      *
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
4649      */
setLayoutTransition(LayoutTransition transition)4650     public void setLayoutTransition(LayoutTransition transition) {
4651         if (mTransition != null) {
4652             LayoutTransition previousTransition = mTransition;
4653             previousTransition.cancel();
4654             previousTransition.removeTransitionListener(mLayoutTransitionListener);
4655         }
4656         mTransition = transition;
4657         if (mTransition != null) {
4658             mTransition.addTransitionListener(mLayoutTransitionListener);
4659         }
4660     }
4661 
4662     /**
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).
4667      *
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.
4670      */
getLayoutTransition()4671     public LayoutTransition getLayoutTransition() {
4672         return mTransition;
4673     }
4674 
removeViewsInternal(int start, int count)4675     private void removeViewsInternal(int start, int count) {
4676         final View focused = mFocused;
4677         final boolean detach = mAttachInfo != null;
4678         boolean clearChildFocus = false;
4679 
4680         final View[] children = mChildren;
4681         final int end = start + count;
4682 
4683         for (int i = start; i < end; i++) {
4684             final View view = children[i];
4685 
4686             if (mTransition != null) {
4687                 mTransition.removeChild(this, view);
4688             }
4689 
4690             if (view == focused) {
4691                 view.unFocus(null);
4692                 clearChildFocus = true;
4693             }
4694 
4695             view.clearAccessibilityFocus();
4696 
4697             cancelTouchTarget(view);
4698             cancelHoverTarget(view);
4699 
4700             if (view.getAnimation() != null ||
4701                 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4702                 addDisappearingView(view);
4703             } else if (detach) {
4704                view.dispatchDetachedFromWindow();
4705             }
4706 
4707             if (view.hasTransientState()) {
4708                 childHasTransientStateChanged(view, false);
4709             }
4710 
4711             needGlobalAttributesUpdate(false);
4712 
4713             dispatchViewRemoved(view);
4714         }
4715 
4716         removeFromArray(start, count);
4717 
4718         if (clearChildFocus) {
4719             clearChildFocus(focused);
4720             if (!rootViewRequestFocus()) {
4721                 notifyGlobalFocusCleared(focused);
4722             }
4723         }
4724     }
4725 
4726     /**
4727      * Call this method to remove all child views from the
4728      * ViewGroup.
4729      *
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>
4733      */
removeAllViews()4734     public void removeAllViews() {
4735         removeAllViewsInLayout();
4736         requestLayout();
4737         invalidate(true);
4738     }
4739 
4740     /**
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.
4748      *
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>
4752      */
removeAllViewsInLayout()4753     public void removeAllViewsInLayout() {
4754         final int count = mChildrenCount;
4755         if (count <= 0) {
4756             return;
4757         }
4758 
4759         final View[] children = mChildren;
4760         mChildrenCount = 0;
4761 
4762         final View focused = mFocused;
4763         final boolean detach = mAttachInfo != null;
4764         boolean clearChildFocus = false;
4765 
4766         needGlobalAttributesUpdate(false);
4767 
4768         for (int i = count - 1; i >= 0; i--) {
4769             final View view = children[i];
4770 
4771             if (mTransition != null) {
4772                 mTransition.removeChild(this, view);
4773             }
4774 
4775             if (view == focused) {
4776                 view.unFocus(null);
4777                 clearChildFocus = true;
4778             }
4779 
4780             view.clearAccessibilityFocus();
4781 
4782             cancelTouchTarget(view);
4783             cancelHoverTarget(view);
4784 
4785             if (view.getAnimation() != null ||
4786                     (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4787                 addDisappearingView(view);
4788             } else if (detach) {
4789                view.dispatchDetachedFromWindow();
4790             }
4791 
4792             if (view.hasTransientState()) {
4793                 childHasTransientStateChanged(view, false);
4794             }
4795 
4796             dispatchViewRemoved(view);
4797 
4798             view.mParent = null;
4799             children[i] = null;
4800         }
4801 
4802         if (clearChildFocus) {
4803             clearChildFocus(focused);
4804             if (!rootViewRequestFocus()) {
4805                 notifyGlobalFocusCleared(focused);
4806             }
4807         }
4808     }
4809 
4810     /**
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.
4820      *
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
4824      *
4825      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4826      * @see #detachAllViewsFromParent()
4827      * @see #detachViewFromParent(View)
4828      * @see #detachViewFromParent(int)
4829      */
removeDetachedView(View child, boolean animate)4830     protected void removeDetachedView(View child, boolean animate) {
4831         if (mTransition != null) {
4832             mTransition.removeChild(this, child);
4833         }
4834 
4835         if (child == mFocused) {
4836             child.clearFocus();
4837         }
4838 
4839         child.clearAccessibilityFocus();
4840 
4841         cancelTouchTarget(child);
4842         cancelHoverTarget(child);
4843 
4844         if ((animate && child.getAnimation() != null) ||
4845                 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
4846             addDisappearingView(child);
4847         } else if (child.mAttachInfo != null) {
4848             child.dispatchDetachedFromWindow();
4849         }
4850 
4851         if (child.hasTransientState()) {
4852             childHasTransientStateChanged(child, false);
4853         }
4854 
4855         dispatchViewRemoved(child);
4856     }
4857 
4858     /**
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.
4871      *
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
4875      *
4876      * @see #removeDetachedView(View, boolean)
4877      * @see #detachAllViewsFromParent()
4878      * @see #detachViewFromParent(View)
4879      * @see #detachViewFromParent(int)
4880      */
attachViewToParent(View child, int index, LayoutParams params)4881     protected void attachViewToParent(View child, int index, LayoutParams params) {
4882         child.mLayoutParams = params;
4883 
4884         if (index < 0) {
4885             index = mChildrenCount;
4886         }
4887 
4888         addInArray(child, index);
4889 
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;
4895 
4896         if (child.hasFocus()) {
4897             requestChildFocus(child, child.findFocus());
4898         }
4899     }
4900 
4901     /**
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)}.
4909      *
4910      * @param child the child to detach
4911      *
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)
4917      */
detachViewFromParent(View child)4918     protected void detachViewFromParent(View child) {
4919         removeFromArray(indexOfChild(child));
4920     }
4921 
4922     /**
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)}.
4930      *
4931      * @param index the index of the child to detach
4932      *
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)
4938      */
detachViewFromParent(int index)4939     protected void detachViewFromParent(int index) {
4940         removeFromArray(index);
4941     }
4942 
4943     /**
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)}.
4951      *
4952      * @param start the first index of the childrend range to detach
4953      * @param count the number of children to detach
4954      *
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)
4960      */
detachViewsFromParent(int start, int count)4961     protected void detachViewsFromParent(int start, int count) {
4962         removeFromArray(start, count);
4963     }
4964 
4965     /**
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)}.
4973      *
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)
4979      */
detachAllViewsFromParent()4980     protected void detachAllViewsFromParent() {
4981         final int count = mChildrenCount;
4982         if (count <= 0) {
4983             return;
4984         }
4985 
4986         final View[] children = mChildren;
4987         mChildrenCount = 0;
4988 
4989         for (int i = count - 1; i >= 0; i--) {
4990             children[i].mParent = null;
4991             children[i] = null;
4992         }
4993     }
4994 
4995     /**
4996      * Don't call or override this method. It is used for the implementation of
4997      * the view hierarchy.
4998      */
invalidateChild(View child, final Rect dirty)4999     public final void invalidateChild(View child, final Rect dirty) {
5000         ViewParent parent = this;
5001 
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;
5009 
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;
5019 
5020             if (child.mLayerType != LAYER_TYPE_NONE) {
5021                 mPrivateFlags |= PFLAG_INVALIDATED;
5022                 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5023             }
5024 
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);
5041                         }
5042                     } else {
5043                         transformMatrix = childMatrix;
5044                     }
5045                 } else {
5046                     transformMatrix = childMatrix;
5047                 }
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));
5053             }
5054 
5055             do {
5056                 View view = null;
5057                 if (parent instanceof View) {
5058                     view = (View) parent;
5059                 }
5060 
5061                 if (drawAnimation) {
5062                     if (view != null) {
5063                         view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
5064                     } else if (parent instanceof ViewRootImpl) {
5065                         ((ViewRootImpl) parent).mIsAnimating = true;
5066                     }
5067                 }
5068 
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;
5075                     }
5076                     if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
5077                         view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
5078                     }
5079                 }
5080 
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));
5093                     }
5094                 }
5095             } while (parent != null);
5096         }
5097     }
5098 
5099     /**
5100      * Don't call or override this method. It is used for the implementation of
5101      * the view hierarchy.
5102      *
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.
5106      */
invalidateChildInParent(final int[] location, final Rect dirty)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);
5116                 }
5117 
5118                 final int left = mLeft;
5119                 final int top = mTop;
5120 
5121                 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
5122                     if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
5123                         dirty.setEmpty();
5124                     }
5125                 }
5126                 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5127 
5128                 location[CHILD_LEFT_INDEX] = left;
5129                 location[CHILD_TOP_INDEX] = top;
5130 
5131                 if (mLayerType != LAYER_TYPE_NONE) {
5132                     mPrivateFlags |= PFLAG_INVALIDATED;
5133                 }
5134 
5135                 return mParent;
5136 
5137             } else {
5138                 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
5139 
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);
5147                 }
5148 
5149                 if (mLayerType != LAYER_TYPE_NONE) {
5150                     mPrivateFlags |= PFLAG_INVALIDATED;
5151                 }
5152 
5153                 return mParent;
5154             }
5155         }
5156 
5157         return null;
5158     }
5159 
5160     /**
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
5166      */
damageChildDeferred(View child)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;
5177             }
5178         }
5179         return false;
5180     }
5181 
5182     /**
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.
5186      *
5187      * @hide
5188      */
damageChild(View child, final Rect dirty)5189     public void damageChild(View child, final Rect dirty) {
5190         if (damageChildDeferred(child)) {
5191             return;
5192         }
5193 
5194         ViewParent parent = this;
5195 
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);
5202             }
5203 
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;
5215                     }
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);
5223                 }
5224             } while (parent != null);
5225         }
5226     }
5227 
5228     /**
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.
5231      *
5232      * @hide
5233      */
damageChildInParent(int left, int top, final Rect dirty)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);
5240             }
5241 
5242             if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
5243                     dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
5244 
5245                 if (!getMatrix().isIdentity()) {
5246                     transformRect(dirty);
5247                 }
5248 
5249                 return mParent;
5250             }
5251         }
5252 
5253         return null;
5254     }
5255 
5256     /**
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.
5261      */
offsetDescendantRectToMyCoords(View descendant, Rect rect)5262     public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
5263         offsetRectBetweenParentAndChild(descendant, rect, true, false);
5264     }
5265 
5266     /**
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.
5271      */
offsetRectIntoDescendantCoords(View descendant, Rect rect)5272     public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
5273         offsetRectBetweenParentAndChild(descendant, rect, false, false);
5274     }
5275 
5276     /**
5277      * Helper method that offsets a rect either from parent to descendant or
5278      * descendant to parent.
5279      */
offsetRectBetweenParentAndChild(View descendant, Rect rect, boolean offsetFromChildToParent, boolean clipToBounds)5280     void offsetRectBetweenParentAndChild(View descendant, Rect rect,
5281             boolean offsetFromChildToParent, boolean clipToBounds) {
5282 
5283         // already in the same coord system :)
5284         if (descendant == this) {
5285             return;
5286         }
5287 
5288         ViewParent theParent = descendant.mParent;
5289 
5290         // search and offset up to the parent
5291         while ((theParent != null)
5292                 && (theParent instanceof View)
5293                 && (theParent != this)) {
5294 
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();
5304                     }
5305                 }
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();
5313                     }
5314                 }
5315                 rect.offset(descendant.mScrollX - descendant.mLeft,
5316                         descendant.mScrollY - descendant.mTop);
5317             }
5318 
5319             descendant = (View) theParent;
5320             theParent = descendant.mParent;
5321         }
5322 
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);
5332             }
5333         } else {
5334             throw new IllegalArgumentException("parameter must be a descendant of this view");
5335         }
5336     }
5337 
5338     /**
5339      * Offset the vertical location of all children of this view by the specified number of pixels.
5340      *
5341      * @param offset the number of pixels to offset
5342      *
5343      * @hide
5344      */
offsetChildrenTopAndBottom(int offset)5345     public void offsetChildrenTopAndBottom(int offset) {
5346         final int count = mChildrenCount;
5347         final View[] children = mChildren;
5348         boolean invalidate = false;
5349 
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);
5357             }
5358         }
5359 
5360         if (invalidate) {
5361             invalidateViewProperty(false, false);
5362         }
5363         notifySubtreeAccessibilityStateChangedIfNeeded();
5364     }
5365 
5366     /**
5367      * {@inheritDoc}
5368      */
getChildVisibleRect(View child, Rect r, android.graphics.Point offset)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);
5375 
5376         if (!child.hasIdentityMatrix()) {
5377             child.getMatrix().mapRect(rect);
5378         }
5379 
5380         final int dx = child.mLeft - mScrollX;
5381         final int dy = child.mTop - mScrollY;
5382 
5383         rect.offset(dx, dy);
5384 
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);
5394             }
5395             offset.x += dx;
5396             offset.y += dy;
5397         }
5398 
5399         final int width = mRight - mLeft;
5400         final int height = mBottom - mTop;
5401 
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);
5407         }
5408 
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);
5413         }
5414 
5415         if (rectIsVisible && mClipBounds != null) {
5416             // Clip to clipBounds.
5417             rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
5418                     mClipBounds.bottom);
5419         }
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);
5424         }
5425         return rectIsVisible;
5426     }
5427 
5428     /**
5429      * {@inheritDoc}
5430      */
5431     @Override
layout(int l, int t, int r, int b)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);
5436             }
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;
5441         }
5442     }
5443 
5444     /**
5445      * {@inheritDoc}
5446      */
5447     @Override
onLayout(boolean changed, int l, int t, int r, int b)5448     protected abstract void onLayout(boolean changed,
5449             int l, int t, int r, int b);
5450 
5451     /**
5452      * Indicates whether the view group has the ability to animate its children
5453      * after the first layout.
5454      *
5455      * @return true if the children can be animated, false otherwise
5456      */
canAnimate()5457     protected boolean canAnimate() {
5458         return mLayoutAnimationController != null;
5459     }
5460 
5461     /**
5462      * Runs the layout animation. Calling this method triggers a relayout of
5463      * this view group.
5464      */
startLayoutAnimation()5465     public void startLayoutAnimation() {
5466         if (mLayoutAnimationController != null) {
5467             mGroupFlags |= FLAG_RUN_ANIMATION;
5468             requestLayout();
5469         }
5470     }
5471 
5472     /**
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.
5477      */
scheduleLayoutAnimation()5478     public void scheduleLayoutAnimation() {
5479         mGroupFlags |= FLAG_RUN_ANIMATION;
5480     }
5481 
5482     /**
5483      * Sets the layout animation controller used to animate the group's
5484      * children after the first layout.
5485      *
5486      * @param controller the animation controller
5487      */
setLayoutAnimation(LayoutAnimationController controller)5488     public void setLayoutAnimation(LayoutAnimationController controller) {
5489         mLayoutAnimationController = controller;
5490         if (mLayoutAnimationController != null) {
5491             mGroupFlags |= FLAG_RUN_ANIMATION;
5492         }
5493     }
5494 
5495     /**
5496      * Returns the layout animation controller used to animate the group's
5497      * children.
5498      *
5499      * @return the current animation controller
5500      */
getLayoutAnimation()5501     public LayoutAnimationController getLayoutAnimation() {
5502         return mLayoutAnimationController;
5503     }
5504 
5505     /**
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.
5510      *
5511      * @return true if the animation cache is enabled, false otherwise
5512      *
5513      * @see #setAnimationCacheEnabled(boolean)
5514      * @see View#setDrawingCacheEnabled(boolean)
5515      *
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)}.
5518      */
isAnimationCacheEnabled()5519     public boolean isAnimationCacheEnabled() {
5520         return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
5521     }
5522 
5523     /**
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.
5528      *
5529      * @param enabled true to enable the animation cache, false otherwise
5530      *
5531      * @see #isAnimationCacheEnabled()
5532      * @see View#setDrawingCacheEnabled(boolean)
5533      *
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)}.
5536      */
setAnimationCacheEnabled(boolean enabled)5537     public void setAnimationCacheEnabled(boolean enabled) {
5538         setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
5539     }
5540 
5541     /**
5542      * Indicates whether this ViewGroup will always try to draw its children using their
5543      * drawing cache. By default this property is enabled.
5544      *
5545      * @return true if the animation cache is enabled, false otherwise
5546      *
5547      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5548      * @see #setChildrenDrawnWithCacheEnabled(boolean)
5549      * @see View#setDrawingCacheEnabled(boolean)
5550      *
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.
5553      */
isAlwaysDrawnWithCacheEnabled()5554     public boolean isAlwaysDrawnWithCacheEnabled() {
5555         return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
5556     }
5557 
5558     /**
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.
5563      *
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.
5567      *
5568      * @param always true to always draw with the drawing cache, false otherwise
5569      *
5570      * @see #isAlwaysDrawnWithCacheEnabled()
5571      * @see #setChildrenDrawnWithCacheEnabled(boolean)
5572      * @see View#setDrawingCacheEnabled(boolean)
5573      * @see View#setDrawingCacheQuality(int)
5574      *
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.
5577      */
setAlwaysDrawnWithCacheEnabled(boolean always)5578     public void setAlwaysDrawnWithCacheEnabled(boolean always) {
5579         setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
5580     }
5581 
5582     /**
5583      * Indicates whether the ViewGroup is currently drawing its children using
5584      * their drawing cache.
5585      *
5586      * @return true if children should be drawn with their cache, false otherwise
5587      *
5588      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5589      * @see #setChildrenDrawnWithCacheEnabled(boolean)
5590      *
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.
5594      */
isChildrenDrawnWithCacheEnabled()5595     protected boolean isChildrenDrawnWithCacheEnabled() {
5596         return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
5597     }
5598 
5599     /**
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.
5603      *
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.
5606      *
5607      * @param enabled true if children should be drawn with their cache, false otherwise
5608      *
5609      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5610      * @see #isChildrenDrawnWithCacheEnabled()
5611      *
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.
5615      */
setChildrenDrawnWithCacheEnabled(boolean enabled)5616     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
5617         setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
5618     }
5619 
5620     /**
5621      * Indicates whether the ViewGroup is drawing its children in the order defined by
5622      * {@link #getChildDrawingOrder(int, int)}.
5623      *
5624      * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
5625      *         false otherwise
5626      *
5627      * @see #setChildrenDrawingOrderEnabled(boolean)
5628      * @see #getChildDrawingOrder(int, int)
5629      */
5630     @ViewDebug.ExportedProperty(category = "drawing")
isChildrenDrawingOrderEnabled()5631     protected boolean isChildrenDrawingOrderEnabled() {
5632         return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
5633     }
5634 
5635     /**
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.
5641      *
5642      * @param enabled true if the order of the children when drawing is determined by
5643      *        {@link #getChildDrawingOrder(int, int)}, false otherwise
5644      *
5645      * @see #isChildrenDrawingOrderEnabled()
5646      * @see #getChildDrawingOrder(int, int)
5647      */
setChildrenDrawingOrderEnabled(boolean enabled)5648     protected void setChildrenDrawingOrderEnabled(boolean enabled) {
5649         setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
5650     }
5651 
hasBooleanFlag(int flag)5652     private boolean hasBooleanFlag(int flag) {
5653         return (mGroupFlags & flag) == flag;
5654     }
5655 
setBooleanFlag(int flag, boolean value)5656     private void setBooleanFlag(int flag, boolean value) {
5657         if (value) {
5658             mGroupFlags |= flag;
5659         } else {
5660             mGroupFlags &= ~flag;
5661         }
5662     }
5663 
5664     /**
5665      * Returns an integer indicating what types of drawing caches are kept in memory.
5666      *
5667      * @see #setPersistentDrawingCache(int)
5668      * @see #setAnimationCacheEnabled(boolean)
5669      *
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}
5673      */
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")
5679     })
getPersistentDrawingCache()5680     public int getPersistentDrawingCache() {
5681         return mPersistentDrawingCache;
5682     }
5683 
5684     /**
5685      * Indicates what types of drawing caches should be kept in memory after
5686      * they have been created.
5687      *
5688      * @see #getPersistentDrawingCache()
5689      * @see #setAnimationCacheEnabled(boolean)
5690      *
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}
5694      */
setPersistentDrawingCache(int drawingCacheToKeep)5695     public void setPersistentDrawingCache(int drawingCacheToKeep) {
5696         mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
5697     }
5698 
setLayoutMode(int layoutMode, boolean explicitly)5699     private void setLayoutMode(int layoutMode, boolean explicitly) {
5700         mLayoutMode = layoutMode;
5701         setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
5702     }
5703 
5704     /**
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
5715      */
5716     @Override
invalidateInheritedLayoutMode(int layoutModeOfRoot)5717     void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
5718         if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
5719             mLayoutMode == layoutModeOfRoot ||
5720             hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
5721             return;
5722         }
5723         setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
5724 
5725         // apply recursively
5726         for (int i = 0, N = getChildCount(); i < N; i++) {
5727             getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
5728         }
5729     }
5730 
5731     /**
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}.
5738      *
5739      * @return the layout mode to use during layout operations
5740      *
5741      * @see #setLayoutMode(int)
5742      */
getLayoutMode()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);
5748         }
5749         return mLayoutMode;
5750     }
5751 
5752     /**
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}.
5756      *
5757      * @param layoutMode the layout mode to use during layout operations
5758      *
5759      * @see #getLayoutMode()
5760      * @attr ref android.R.styleable#ViewGroup_layoutMode
5761      */
setLayoutMode(int layoutMode)5762     public void setLayoutMode(int layoutMode) {
5763         if (mLayoutMode != layoutMode) {
5764             invalidateInheritedLayoutMode(layoutMode);
5765             setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
5766             requestLayout();
5767         }
5768     }
5769 
5770     /**
5771      * Returns a new set of layout parameters based on the supplied attributes set.
5772      *
5773      * @param attrs the attributes to build the layout parameters from
5774      *
5775      * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5776      *         of its descendants
5777      */
generateLayoutParams(AttributeSet attrs)5778     public LayoutParams generateLayoutParams(AttributeSet attrs) {
5779         return new LayoutParams(getContext(), attrs);
5780     }
5781 
5782     /**
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.
5789      *
5790      * @param p The layout parameters to convert into a suitable set of layout parameters
5791      *          for this ViewGroup.
5792      *
5793      * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5794      *         of its descendants
5795      */
generateLayoutParams(ViewGroup.LayoutParams p)5796     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
5797         return p;
5798     }
5799 
5800     /**
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.
5804      *
5805      * @return a set of default layout parameters or null
5806      */
generateDefaultLayoutParams()5807     protected LayoutParams generateDefaultLayoutParams() {
5808         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
5809     }
5810 
5811     /**
5812      * {@inheritDoc}
5813      */
5814     @Override
debug(int depth)5815     protected void debug(int depth) {
5816         super.debug(depth);
5817         String output;
5818 
5819         if (mFocused != null) {
5820             output = debugIndent(depth);
5821             output += "mFocused";
5822             Log.d(VIEW_LOG_TAG, output);
5823         }
5824         if (mChildrenCount != 0) {
5825             output = debugIndent(depth);
5826             output += "{";
5827             Log.d(VIEW_LOG_TAG, output);
5828         }
5829         int count = mChildrenCount;
5830         for (int i = 0; i < count; i++) {
5831             View child = mChildren[i];
5832             child.debug(depth + 1);
5833         }
5834 
5835         if (mChildrenCount != 0) {
5836             output = debugIndent(depth);
5837             output += "}";
5838             Log.d(VIEW_LOG_TAG, output);
5839         }
5840     }
5841 
5842     /**
5843      * Returns the position in the group of the specified child view.
5844      *
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
5848      */
indexOfChild(View child)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;
5855             }
5856         }
5857         return -1;
5858     }
5859 
5860     /**
5861      * Returns the number of children in the group.
5862      *
5863      * @return a positive integer representing the number of children in
5864      *         the group
5865      */
getChildCount()5866     public int getChildCount() {
5867         return mChildrenCount;
5868     }
5869 
5870     /**
5871      * Returns the view at the specified position in the group.
5872      *
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
5876      */
getChildAt(int index)5877     public View getChildAt(int index) {
5878         if (index < 0 || index >= mChildrenCount) {
5879             return null;
5880         }
5881         return mChildren[index];
5882     }
5883 
5884     /**
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.
5889      *
5890      * @param widthMeasureSpec The width requirements for this view
5891      * @param heightMeasureSpec The height requirements for this view
5892      */
measureChildren(int widthMeasureSpec, int heightMeasureSpec)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);
5900             }
5901         }
5902     }
5903 
5904     /**
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.
5908      *
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
5912      */
measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)5913     protected void measureChild(View child, int parentWidthMeasureSpec,
5914             int parentHeightMeasureSpec) {
5915         final LayoutParams lp = child.getLayoutParams();
5916 
5917         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5918                 mPaddingLeft + mPaddingRight, lp.width);
5919         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5920                 mPaddingTop + mPaddingBottom, lp.height);
5921 
5922         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5923     }
5924 
5925     /**
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.
5930      *
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)
5938      */
measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)5939     protected void measureChildWithMargins(View child,
5940             int parentWidthMeasureSpec, int widthUsed,
5941             int parentHeightMeasureSpec, int heightUsed) {
5942         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
5943 
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);
5950 
5951         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5952     }
5953 
5954     /**
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.
5958      *
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.
5965      *
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
5972      */
getChildMeasureSpec(int spec, int padding, int childDimension)5973     public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
5974         int specMode = MeasureSpec.getMode(spec);
5975         int specSize = MeasureSpec.getSize(spec);
5976 
5977         int size = Math.max(0, specSize - padding);
5978 
5979         int resultSize = 0;
5980         int resultMode = 0;
5981 
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;
5997             }
5998             break;
5999 
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;
6016             }
6017             break;
6018 
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;
6035             }
6036             break;
6037         }
6038         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
6039     }
6040 
6041 
6042     /**
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.
6045      */
clearDisappearingChildren()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();
6054                 }
6055                 view.clearAnimation();
6056             }
6057             disappearingChildren.clear();
6058             invalidate();
6059         }
6060     }
6061 
6062     /**
6063      * Add a view which is removed from mChildren but still needs animation
6064      *
6065      * @param v View to add
6066      */
addDisappearingView(View v)6067     private void addDisappearingView(View v) {
6068         ArrayList<View> disappearingChildren = mDisappearingChildren;
6069 
6070         if (disappearingChildren == null) {
6071             disappearingChildren = mDisappearingChildren = new ArrayList<View>();
6072         }
6073 
6074         disappearingChildren.add(v);
6075     }
6076 
6077     /**
6078      * Cleanup a view when its animation is done. This may mean removing it from
6079      * the list of disappearing views.
6080      *
6081      * @param view The view whose animation has finished
6082      * @param animation The animation, cannot be null
6083      */
finishAnimatingView(final View view, Animation animation)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);
6089 
6090                 if (view.mAttachInfo != null) {
6091                     view.dispatchDetachedFromWindow();
6092                 }
6093 
6094                 view.clearAnimation();
6095                 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
6096             }
6097         }
6098 
6099         if (animation != null && !animation.getFillAfter()) {
6100             view.clearAnimation();
6101         }
6102 
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;
6110         }
6111     }
6112 
6113     /**
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).
6117      */
isViewTransitioning(View view)6118     boolean isViewTransitioning(View view) {
6119         return (mTransitioningViews != null && mTransitioningViews.contains(view));
6120     }
6121 
6122     /**
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.
6131      *
6132      * @param view The View object to be kept visible even if it gets removed from its parent.
6133      */
startViewTransition(View view)6134     public void startViewTransition(View view) {
6135         if (view.mParent == this) {
6136             if (mTransitioningViews == null) {
6137                 mTransitioningViews = new ArrayList<View>();
6138             }
6139             mTransitioningViews.add(view);
6140         }
6141     }
6142 
6143     /**
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.
6149      *
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)}.
6152      */
endViewTransition(View 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();
6165                     }
6166                     if (view.mParent != null) {
6167                         view.mParent = null;
6168                     }
6169                 }
6170                 invalidate();
6171             }
6172         }
6173     }
6174 
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);
6184             }
6185         }
6186 
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;
6193             }
6194             if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
6195                 endViewTransition(view);
6196             }
6197         }
6198     };
6199 
6200     /**
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.
6205      *
6206      * @hide
6207      */
suppressLayout(boolean suppress)6208     public void suppressLayout(boolean suppress) {
6209         mSuppressLayout = suppress;
6210         if (!suppress) {
6211             if (mLayoutCalledWhileSuppressed) {
6212                 requestLayout();
6213                 mLayoutCalledWhileSuppressed = false;
6214             }
6215         }
6216     }
6217 
6218     /**
6219      * Returns whether layout calls on this container are currently being
6220      * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
6221      *
6222      * @return true if layout calls are currently suppressed, false otherwise.
6223      *
6224      * @hide
6225      */
isLayoutSuppressed()6226     public boolean isLayoutSuppressed() {
6227         return mSuppressLayout;
6228     }
6229 
6230     /**
6231      * {@inheritDoc}
6232      */
6233     @Override
gatherTransparentRegion(Region region)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;
6240         }
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;
6250                 }
6251             }
6252         }
6253         return meOpaque || noneOfTheChildrenAreTransparent;
6254     }
6255 
6256     /**
6257      * {@inheritDoc}
6258      */
requestTransparentRegion(View child)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);
6264             }
6265         }
6266     }
6267 
6268     @Override
dispatchApplyWindowInsets(WindowInsets insets)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;
6277                 }
6278             }
6279         }
6280         return insets;
6281     }
6282 
6283     /**
6284      * Returns the animation listener to which layout animation events are
6285      * sent.
6286      *
6287      * @return an {@link android.view.animation.Animation.AnimationListener}
6288      */
getLayoutAnimationListener()6289     public Animation.AnimationListener getLayoutAnimationListener() {
6290         return mAnimationListener;
6291     }
6292 
6293     @Override
drawableStateChanged()6294     protected void drawableStateChanged() {
6295         super.drawableStateChanged();
6296 
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");
6301             }
6302 
6303             final View[] children = mChildren;
6304             final int count = mChildrenCount;
6305 
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();
6310                 }
6311             }
6312         }
6313     }
6314 
6315     @Override
jumpDrawablesToCurrentState()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();
6322         }
6323     }
6324 
6325     @Override
onCreateDrawableState(int extraSpace)6326     protected int[] onCreateDrawableState(int extraSpace) {
6327         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
6328             return super.onCreateDrawableState(extraSpace);
6329         }
6330 
6331         int need = 0;
6332         int n = getChildCount();
6333         for (int i = 0; i < n; i++) {
6334             int[] childState = getChildAt(i).getDrawableState();
6335 
6336             if (childState != null) {
6337                 need += childState.length;
6338             }
6339         }
6340 
6341         int[] state = super.onCreateDrawableState(extraSpace + need);
6342 
6343         for (int i = 0; i < n; i++) {
6344             int[] childState = getChildAt(i).getDrawableState();
6345 
6346             if (childState != null) {
6347                 state = mergeDrawableStates(state, childState);
6348             }
6349         }
6350 
6351         return state;
6352     }
6353 
6354     /**
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.
6359      */
setAddStatesFromChildren(boolean addsStates)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;
6365         }
6366 
6367         refreshDrawableState();
6368     }
6369 
6370     /**
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.
6375      */
addStatesFromChildren()6376     public boolean addStatesFromChildren() {
6377         return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
6378     }
6379 
6380     /**
6381      * If {@link #addStatesFromChildren} is true, refreshes this group's
6382      * drawable state (to include the states from its children).
6383      */
childDrawableStateChanged(View child)6384     public void childDrawableStateChanged(View child) {
6385         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
6386             refreshDrawableState();
6387         }
6388     }
6389 
6390     /**
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.
6397      *
6398      * @param animationListener the layout animation listener
6399      */
setLayoutAnimationListener(Animation.AnimationListener animationListener)6400     public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
6401         mAnimationListener = animationListener;
6402     }
6403 
6404     /**
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.
6408      *
6409      * @param transition The LayoutTransition to be started on the next traversal.
6410      *
6411      * @hide
6412      */
requestTransitionStart(LayoutTransition transition)6413     public void requestTransitionStart(LayoutTransition transition) {
6414         ViewRootImpl viewAncestor = getViewRootImpl();
6415         if (viewAncestor != null) {
6416             viewAncestor.requestTransitionStart(transition);
6417         }
6418     }
6419 
6420     /**
6421      * @hide
6422      */
6423     @Override
resolveRtlPropertiesIfNeeded()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();
6433                 }
6434             }
6435         }
6436         return result;
6437     }
6438 
6439     /**
6440      * @hide
6441      */
6442     @Override
resolveLayoutDirection()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();
6451                 }
6452             }
6453         }
6454         return result;
6455     }
6456 
6457     /**
6458      * @hide
6459      */
6460     @Override
resolveTextDirection()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();
6469                 }
6470             }
6471         }
6472         return result;
6473     }
6474 
6475     /**
6476      * @hide
6477      */
6478     @Override
resolveTextAlignment()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();
6487                 }
6488             }
6489         }
6490         return result;
6491     }
6492 
6493     /**
6494      * @hide
6495      */
6496     @Override
resolvePadding()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();
6504             }
6505         }
6506     }
6507 
6508     /**
6509      * @hide
6510      */
6511     @Override
resolveDrawables()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();
6519             }
6520         }
6521     }
6522 
6523     /**
6524      * @hide
6525      */
6526     @Override
resolveLayoutParams()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();
6533         }
6534     }
6535 
6536     /**
6537      * @hide
6538      */
6539     @Override
resetResolvedLayoutDirection()6540     public void resetResolvedLayoutDirection() {
6541         super.resetResolvedLayoutDirection();
6542 
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();
6548             }
6549         }
6550     }
6551 
6552     /**
6553      * @hide
6554      */
6555     @Override
resetResolvedTextDirection()6556     public void resetResolvedTextDirection() {
6557         super.resetResolvedTextDirection();
6558 
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();
6564             }
6565         }
6566     }
6567 
6568     /**
6569      * @hide
6570      */
6571     @Override
resetResolvedTextAlignment()6572     public void resetResolvedTextAlignment() {
6573         super.resetResolvedTextAlignment();
6574 
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();
6580             }
6581         }
6582     }
6583 
6584     /**
6585      * @hide
6586      */
6587     @Override
resetResolvedPadding()6588     public void resetResolvedPadding() {
6589         super.resetResolvedPadding();
6590 
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();
6596             }
6597         }
6598     }
6599 
6600     /**
6601      * @hide
6602      */
6603     @Override
resetResolvedDrawables()6604     protected void resetResolvedDrawables() {
6605         super.resetResolvedDrawables();
6606 
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();
6612             }
6613         }
6614     }
6615 
6616     /**
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.
6621      *
6622      * The default implementation returns true for compatibility reasons. Subclasses that do
6623      * not scroll should generally override this method and return false.
6624      */
shouldDelayChildPressedState()6625     public boolean shouldDelayChildPressedState() {
6626         return true;
6627     }
6628 
6629     /**
6630      * @inheritDoc
6631      */
6632     @Override
onStartNestedScroll(View child, View target, int nestedScrollAxes)6633     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
6634         return false;
6635     }
6636 
6637     /**
6638      * @inheritDoc
6639      */
6640     @Override
onNestedScrollAccepted(View child, View target, int axes)6641     public void onNestedScrollAccepted(View child, View target, int axes) {
6642         mNestedScrollAxes = axes;
6643     }
6644 
6645     /**
6646      * @inheritDoc
6647      *
6648      * <p>The default implementation of onStopNestedScroll calls
6649      * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
6650      */
6651     @Override
onStopNestedScroll(View child)6652     public void onStopNestedScroll(View child) {
6653         // Stop any recursive nested scrolling.
6654         stopNestedScroll();
6655         mNestedScrollAxes = 0;
6656     }
6657 
6658     /**
6659      * @inheritDoc
6660      */
6661     @Override
onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)6662     public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
6663             int dxUnconsumed, int dyUnconsumed) {
6664         // Do nothing
6665     }
6666 
6667     /**
6668      * @inheritDoc
6669      */
6670     @Override
onNestedPreScroll(View target, int dx, int dy, int[] consumed)6671     public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
6672         // Do nothing
6673     }
6674 
6675     /**
6676      * @inheritDoc
6677      */
6678     @Override
onNestedFling(View target, float velocityX, float velocityY, boolean consumed)6679     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
6680         return false;
6681     }
6682 
6683     /**
6684      * @inheritDoc
6685      */
6686     @Override
onNestedPreFling(View target, float velocityX, float velocityY)6687     public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
6688         return false;
6689     }
6690 
6691     /**
6692      * Return the current axes of nested scrolling for this ViewGroup.
6693      *
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>
6696      *
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
6701      */
getNestedScrollAxes()6702     public int getNestedScrollAxes() {
6703         return mNestedScrollAxes;
6704     }
6705 
6706     /** @hide */
onSetLayoutParams(View child, LayoutParams layoutParams)6707     protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
6708     }
6709 
6710     /** @hide */
6711     @Override
captureTransitioningViews(List<View> transitioningViews)6712     public void captureTransitioningViews(List<View> transitioningViews) {
6713         if (getVisibility() != View.VISIBLE) {
6714             return;
6715         }
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);
6723             }
6724         }
6725     }
6726 
6727     /** @hide */
6728     @Override
findNamedViews(Map<String, View> namedElements)6729     public void findNamedViews(Map<String, View> namedElements) {
6730         if (getVisibility() != VISIBLE && mGhostView == null) {
6731             return;
6732         }
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);
6738         }
6739     }
6740 
6741     /**
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.
6746      *
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>
6760      *
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>
6766      *
6767      * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
6768      * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
6769      */
6770     public static class LayoutParams {
6771         /**
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}.
6776          */
6777         @SuppressWarnings({"UnusedDeclaration"})
6778         @Deprecated
6779         public static final int FILL_PARENT = -1;
6780 
6781         /**
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.
6785          */
6786         public static final int MATCH_PARENT = -1;
6787 
6788         /**
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.
6792          */
6793         public static final int WRAP_CONTENT = -2;
6794 
6795         /**
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.
6799          */
6800         @ViewDebug.ExportedProperty(category = "layout", mapping = {
6801             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6802             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6803         })
6804         public int width;
6805 
6806         /**
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.
6810          */
6811         @ViewDebug.ExportedProperty(category = "layout", mapping = {
6812             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6813             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6814         })
6815         public int height;
6816 
6817         /**
6818          * Used to animate layouts.
6819          */
6820         public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
6821 
6822         /**
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:
6826          *
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>
6835          *
6836          * @param c the application environment
6837          * @param attrs the set of attributes from which to extract the layout
6838          *              parameters' values
6839          */
LayoutParams(Context c, AttributeSet attrs)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();
6846         }
6847 
6848         /**
6849          * Creates a new set of layout parameters with the specified width
6850          * and height.
6851          *
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
6858          */
LayoutParams(int width, int height)6859         public LayoutParams(int width, int height) {
6860             this.width = width;
6861             this.height = height;
6862         }
6863 
6864         /**
6865          * Copy constructor. Clones the width and height values of the source.
6866          *
6867          * @param source The layout params to copy from.
6868          */
LayoutParams(LayoutParams source)6869         public LayoutParams(LayoutParams source) {
6870             this.width = source.width;
6871             this.height = source.height;
6872         }
6873 
6874         /**
6875          * Used internally by MarginLayoutParams.
6876          * @hide
6877          */
LayoutParams()6878         LayoutParams() {
6879         }
6880 
6881         /**
6882          * Extracts the layout parameters from the supplied attributes.
6883          *
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
6887          */
setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)6888         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
6889             width = a.getLayoutDimension(widthAttr, "layout_width");
6890             height = a.getLayoutDimension(heightAttr, "layout_height");
6891         }
6892 
6893         /**
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.
6897          *
6898          * @param layoutDirection the direction of the layout
6899          *
6900          * {@link View#LAYOUT_DIRECTION_LTR}
6901          * {@link View#LAYOUT_DIRECTION_RTL}
6902          */
resolveLayoutDirection(int layoutDirection)6903         public void resolveLayoutDirection(int layoutDirection) {
6904         }
6905 
6906         /**
6907          * Returns a String representation of this set of layout parameters.
6908          *
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 }"
6912          *
6913          * @hide
6914          */
debug(String output)6915         public String debug(String output) {
6916             return output + "ViewGroup.LayoutParams={ width="
6917                     + sizeToString(width) + ", height=" + sizeToString(height) + " }";
6918         }
6919 
6920         /**
6921          * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
6922          *
6923          * @param view the view that contains these layout parameters
6924          * @param canvas the canvas on which to draw
6925          *
6926          * @hide
6927          */
onDebugDraw(View view, Canvas canvas, Paint paint)6928         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
6929         }
6930 
6931         /**
6932          * Converts the specified size to a readable String.
6933          *
6934          * @param size the size to convert
6935          * @return a String instance representing the supplied size
6936          *
6937          * @hide
6938          */
sizeToString(int size)6939         protected static String sizeToString(int size) {
6940             if (size == WRAP_CONTENT) {
6941                 return "wrap-content";
6942             }
6943             if (size == MATCH_PARENT) {
6944                 return "match-parent";
6945             }
6946             return String.valueOf(size);
6947         }
6948 
6949         /** @hide */
encode(@onNull ViewHierarchyEncoder encoder)6950         void encode(@NonNull ViewHierarchyEncoder encoder) {
6951             encoder.beginObject(this);
6952             encodeProperties(encoder);
6953             encoder.endObject();
6954         }
6955 
6956         /** @hide */
encodeProperties(@onNull ViewHierarchyEncoder encoder)6957         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
6958             encoder.addProperty("width", width);
6959             encoder.addProperty("height", height);
6960         }
6961     }
6962 
6963     /**
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.
6968      */
6969     public static class MarginLayoutParams extends ViewGroup.LayoutParams {
6970         /**
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.
6974          */
6975         @ViewDebug.ExportedProperty(category = "layout")
6976         public int leftMargin;
6977 
6978         /**
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.
6982          */
6983         @ViewDebug.ExportedProperty(category = "layout")
6984         public int topMargin;
6985 
6986         /**
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.
6990          */
6991         @ViewDebug.ExportedProperty(category = "layout")
6992         public int rightMargin;
6993 
6994         /**
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.
6998          */
6999         @ViewDebug.ExportedProperty(category = "layout")
7000         public int bottomMargin;
7001 
7002         /**
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.
7006          */
7007         @ViewDebug.ExportedProperty(category = "layout")
7008         private int startMargin = DEFAULT_MARGIN_RELATIVE;
7009 
7010         /**
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.
7014          */
7015         @ViewDebug.ExportedProperty(category = "layout")
7016         private int endMargin = DEFAULT_MARGIN_RELATIVE;
7017 
7018         /**
7019          * The default start and end margin.
7020          * @hide
7021          */
7022         public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
7023 
7024         /**
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
7031          *
7032          * Bit 6 to 7 not used
7033          *
7034          * @hide
7035          */
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;
7049 
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;
7055 
7056         private static final int DEFAULT_MARGIN_RESOLVED = 0;
7057         private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
7058 
7059         /**
7060          * Creates a new set of layout parameters. The values are extracted from
7061          * the supplied attributes set and context.
7062          *
7063          * @param c the application environment
7064          * @param attrs the set of attributes from which to extract the layout
7065          *              parameters' values
7066          */
MarginLayoutParams(Context c, AttributeSet attrs)7067         public MarginLayoutParams(Context c, AttributeSet attrs) {
7068             super();
7069 
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);
7074 
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;
7089                 }
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;
7096                 }
7097 
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);
7104 
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);
7111 
7112                 if (isMarginRelative()) {
7113                    mMarginFlags |= NEED_RESOLUTION_MASK;
7114                 }
7115             }
7116 
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;
7121             }
7122 
7123             // Layout direction is LTR by default
7124             mMarginFlags |= LAYOUT_DIRECTION_LTR;
7125 
7126             a.recycle();
7127         }
7128 
7129         /**
7130          * {@inheritDoc}
7131          */
MarginLayoutParams(int width, int height)7132         public MarginLayoutParams(int width, int height) {
7133             super(width, height);
7134 
7135             mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7136             mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7137 
7138             mMarginFlags &= ~NEED_RESOLUTION_MASK;
7139             mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
7140         }
7141 
7142         /**
7143          * Copy constructor. Clones the width, height and margin values of the source.
7144          *
7145          * @param source The layout params to copy from.
7146          */
MarginLayoutParams(MarginLayoutParams source)7147         public MarginLayoutParams(MarginLayoutParams source) {
7148             this.width = source.width;
7149             this.height = source.height;
7150 
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;
7157 
7158             this.mMarginFlags = source.mMarginFlags;
7159         }
7160 
7161         /**
7162          * {@inheritDoc}
7163          */
MarginLayoutParams(LayoutParams source)7164         public MarginLayoutParams(LayoutParams source) {
7165             super(source);
7166 
7167             mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7168             mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7169 
7170             mMarginFlags &= ~NEED_RESOLUTION_MASK;
7171             mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
7172         }
7173 
7174         /**
7175          * @hide Used internally.
7176          */
copyMarginsFrom(MarginLayoutParams source)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;
7184 
7185             this.mMarginFlags = source.mMarginFlags;
7186         }
7187 
7188         /**
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.
7193          *
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
7198          *
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
7203          */
setMargins(int left, int top, int right, int bottom)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;
7215             }
7216         }
7217 
7218         /**
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.
7223          *
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
7228          *
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
7233          *
7234          * @hide
7235          */
setMarginsRelative(int start, int top, int end, int bottom)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;
7242         }
7243 
7244         /**
7245          * Sets the relative start margin. Margin values should be positive.
7246          *
7247          * @param start the start margin size
7248          *
7249          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7250          */
setMarginStart(int start)7251         public void setMarginStart(int start) {
7252             startMargin = start;
7253             mMarginFlags |= NEED_RESOLUTION_MASK;
7254         }
7255 
7256         /**
7257          * Returns the start margin in pixels.
7258          *
7259          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7260          *
7261          * @return the start margin in pixels.
7262          */
getMarginStart()7263         public int getMarginStart() {
7264             if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
7265             if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
7266                 doResolveMargins();
7267             }
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;
7274             }
7275         }
7276 
7277         /**
7278          * Sets the relative end margin. Margin values should be positive.
7279          *
7280          * @param end the end margin size
7281          *
7282          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7283          */
setMarginEnd(int end)7284         public void setMarginEnd(int end) {
7285             endMargin = end;
7286             mMarginFlags |= NEED_RESOLUTION_MASK;
7287         }
7288 
7289         /**
7290          * Returns the end margin in pixels.
7291          *
7292          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7293          *
7294          * @return the end margin in pixels.
7295          */
getMarginEnd()7296         public int getMarginEnd() {
7297             if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
7298             if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
7299                 doResolveMargins();
7300             }
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;
7307             }
7308         }
7309 
7310         /**
7311          * Check if margins are relative.
7312          *
7313          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7314          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7315          *
7316          * @return true if either marginStart or marginEnd has been set.
7317          */
isMarginRelative()7318         public boolean isMarginRelative() {
7319             return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
7320         }
7321 
7322         /**
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}.
7327          */
setLayoutDirection(int layoutDirection)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;
7338                 }
7339             }
7340         }
7341 
7342         /**
7343          * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
7344          * {@link View#LAYOUT_DIRECTION_RTL}.
7345          *
7346          * @return the layout direction.
7347          */
getLayoutDirection()7348         public int getLayoutDirection() {
7349             return (mMarginFlags & LAYOUT_DIRECTION_MASK);
7350         }
7351 
7352         /**
7353          * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
7354          * may be overridden depending on layout direction.
7355          */
7356         @Override
resolveLayoutDirection(int layoutDirection)7357         public void resolveLayoutDirection(int layoutDirection) {
7358             setLayoutDirection(layoutDirection);
7359 
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;
7364 
7365             // Proceed with resolution
7366             doResolveMargins();
7367         }
7368 
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;
7376                 }
7377                 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
7378                         && endMargin > DEFAULT_MARGIN_RELATIVE) {
7379                     rightMargin = endMargin;
7380                 }
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;
7399                 }
7400             }
7401             mMarginFlags &= ~NEED_RESOLUTION_MASK;
7402         }
7403 
7404         /**
7405          * @hide
7406          */
isLayoutRtl()7407         public boolean isLayoutRtl() {
7408             return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
7409         }
7410 
7411         /**
7412          * @hide
7413          */
7414         @Override
onDebugDraw(View view, Canvas canvas, Paint paint)7415         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
7416             Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
7417 
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);
7428         }
7429 
7430         /** @hide */
7431         @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)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);
7440         }
7441     }
7442 
7443     /* Describes a touched view and the ids of the pointers that it has captured.
7444      *
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...
7449      */
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;
7455 
7456         public static final int ALL_POINTER_IDS = -1; // all ones
7457 
7458         // The touched child view.
7459         public View child;
7460 
7461         // The combined bit mask of pointer ids for all pointers captured by the target.
7462         public int pointerIdBits;
7463 
7464         // The next target in the target list.
7465         public TouchTarget next;
7466 
TouchTarget()7467         private TouchTarget() {
7468         }
7469 
obtain(View child, int pointerIdBits)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;
7480                 }
7481             }
7482             target.child = child;
7483             target.pointerIdBits = pointerIdBits;
7484             return target;
7485         }
7486 
recycle()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;
7495                 }
7496                 child = null;
7497             }
7498         }
7499     }
7500 
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;
7507 
7508         // The hovered child view.
7509         public View child;
7510 
7511         // The next target in the target list.
7512         public HoverTarget next;
7513 
HoverTarget()7514         private HoverTarget() {
7515         }
7516 
obtain(View child)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;
7527                 }
7528             }
7529             target.child = child;
7530             return target;
7531         }
7532 
recycle()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;
7541                 }
7542                 child = null;
7543             }
7544         }
7545     }
7546 
7547     /**
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.
7550      */
7551     static class ChildListForAccessibility {
7552 
7553         private static final int MAX_POOL_SIZE = 32;
7554 
7555         private static final SynchronizedPool<ChildListForAccessibility> sPool =
7556                 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
7557 
7558         private final ArrayList<View> mChildren = new ArrayList<View>();
7559 
7560         private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
7561 
obtain(ViewGroup parent, boolean sort)7562         public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
7563             ChildListForAccessibility list = sPool.acquire();
7564             if (list == null) {
7565                 list = new ChildListForAccessibility();
7566             }
7567             list.init(parent, sort);
7568             return list;
7569         }
7570 
recycle()7571         public void recycle() {
7572             clear();
7573             sPool.release(this);
7574         }
7575 
getChildCount()7576         public int getChildCount() {
7577             return mChildren.size();
7578         }
7579 
getChildAt(int index)7580         public View getChildAt(int index) {
7581             return mChildren.get(index);
7582         }
7583 
getChildIndex(View child)7584         public int getChildIndex(View child) {
7585             return mChildren.indexOf(child);
7586         }
7587 
init(ViewGroup parent, boolean sort)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);
7594             }
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);
7601                 }
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();
7607                 }
7608                 holders.clear();
7609             }
7610         }
7611 
sort(ArrayList<ViewLocationHolder> holders)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);
7626             }
7627         }
7628 
clear()7629         private void clear() {
7630             mChildren.clear();
7631         }
7632     }
7633 
7634     /**
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.
7639      */
7640     static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
7641 
7642         private static final int MAX_POOL_SIZE = 32;
7643 
7644         private static final SynchronizedPool<ViewLocationHolder> sPool =
7645                 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
7646 
7647         public static final int COMPARISON_STRATEGY_STRIPE = 1;
7648 
7649         public static final int COMPARISON_STRATEGY_LOCATION = 2;
7650 
7651         private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
7652 
7653         private final Rect mLocation = new Rect();
7654 
7655         public View mView;
7656 
7657         private int mLayoutDirection;
7658 
obtain(ViewGroup root, View view)7659         public static ViewLocationHolder obtain(ViewGroup root, View view) {
7660             ViewLocationHolder holder = sPool.acquire();
7661             if (holder == null) {
7662                 holder = new ViewLocationHolder();
7663             }
7664             holder.init(root, view);
7665             return holder;
7666         }
7667 
setComparisonStrategy(int strategy)7668         public static void setComparisonStrategy(int strategy) {
7669             sComparisonStrategy = strategy;
7670         }
7671 
recycle()7672         public void recycle() {
7673             clear();
7674             sPool.release(this);
7675         }
7676 
7677         @Override
compareTo(ViewLocationHolder another)7678         public int compareTo(ViewLocationHolder another) {
7679             // This instance is greater than an invalid argument.
7680             if (another == null) {
7681                 return 1;
7682             }
7683 
7684             if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
7685                 // First is above second.
7686                 if (mLocation.bottom - another.mLocation.top <= 0) {
7687                     return -1;
7688                 }
7689                 // First is below second.
7690                 if (mLocation.top - another.mLocation.bottom >= 0) {
7691                     return 1;
7692                 }
7693             }
7694 
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;
7700                 }
7701             } else { // RTL
7702                 final int rightDifference = mLocation.right - another.mLocation.right;
7703                 if (rightDifference != 0) {
7704                     return -rightDifference;
7705                 }
7706             }
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;
7711             }
7712             // Break tie by height.
7713             final int heightDiference = mLocation.height() - another.mLocation.height();
7714             if (heightDiference != 0) {
7715                 return -heightDiference;
7716             }
7717             // Break tie by width.
7718             final int widthDiference = mLocation.width() - another.mLocation.width();
7719             if (widthDiference != 0) {
7720                 return -widthDiference;
7721             }
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();
7725         }
7726 
init(ViewGroup root, View view)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();
7733         }
7734 
clear()7735         private void clear() {
7736             mView = null;
7737             mLocation.set(0, 0, 0, 0);
7738         }
7739     }
7740 
getDebugPaint()7741     private static Paint getDebugPaint() {
7742         if (sDebugPaint == null) {
7743             sDebugPaint = new Paint();
7744             sDebugPaint.setAntiAlias(false);
7745         }
7746         return sDebugPaint;
7747     }
7748 
drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)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];
7753         }
7754 
7755         sDebugLines[0] = x1;
7756         sDebugLines[1] = y1;
7757         sDebugLines[2] = x2;
7758         sDebugLines[3] = y1;
7759 
7760         sDebugLines[4] = x2;
7761         sDebugLines[5] = y1;
7762         sDebugLines[6] = x2;
7763         sDebugLines[7] = y2;
7764 
7765         sDebugLines[8] = x2;
7766         sDebugLines[9] = y2;
7767         sDebugLines[10] = x1;
7768         sDebugLines[11] = y2;
7769 
7770         sDebugLines[12] = x1;
7771         sDebugLines[13] = y2;
7772         sDebugLines[14] = x1;
7773         sDebugLines[15] = y1;
7774 
7775         canvas.drawLines(sDebugLines, paint);
7776     }
7777 
7778     /** @hide */
7779     @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)7780     protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
7781         super.encodeProperties(encoder);
7782 
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());
7788 
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);
7794         }
7795     }
7796 }
7797