• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.TypedArray;
23 import android.graphics.Bitmap;
24 import android.graphics.Canvas;
25 import android.graphics.Matrix;
26 import android.graphics.Paint;
27 import android.graphics.PointF;
28 import android.graphics.Rect;
29 import android.graphics.RectF;
30 import android.graphics.Region;
31 import android.os.Build;
32 import android.os.Parcelable;
33 import android.os.SystemClock;
34 import android.util.AttributeSet;
35 import android.util.Log;
36 import android.util.SparseArray;
37 import android.view.accessibility.AccessibilityEvent;
38 import android.view.accessibility.AccessibilityNodeInfo;
39 import android.view.animation.Animation;
40 import android.view.animation.AnimationUtils;
41 import android.view.animation.LayoutAnimationController;
42 import android.view.animation.Transformation;
43 
44 import com.android.internal.R;
45 import com.android.internal.util.Predicate;
46 
47 import java.util.ArrayList;
48 import java.util.HashSet;
49 
50 /**
51  * <p>
52  * A <code>ViewGroup</code> is a special view that can contain other views
53  * (called children.) The view group is the base class for layouts and views
54  * containers. This class also defines the
55  * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
56  * class for layouts parameters.
57  * </p>
58  *
59  * <p>
60  * Also see {@link LayoutParams} for layout attributes.
61  * </p>
62  *
63  * <div class="special reference">
64  * <h3>Developer Guides</h3>
65  * <p>For more information about creating user interface layouts, read the
66  * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
67  * guide.</p></div>
68  *
69  * @attr ref android.R.styleable#ViewGroup_clipChildren
70  * @attr ref android.R.styleable#ViewGroup_clipToPadding
71  * @attr ref android.R.styleable#ViewGroup_layoutAnimation
72  * @attr ref android.R.styleable#ViewGroup_animationCache
73  * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
74  * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
75  * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
76  * @attr ref android.R.styleable#ViewGroup_descendantFocusability
77  * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
78  */
79 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
80 
81     private static final boolean DBG = false;
82 
83     /**
84      * Views which have been hidden or removed which need to be animated on
85      * their way out.
86      * This field should be made private, so it is hidden from the SDK.
87      * {@hide}
88      */
89     protected ArrayList<View> mDisappearingChildren;
90 
91     /**
92      * Listener used to propagate events indicating when children are added
93      * and/or removed from a view group.
94      * This field should be made private, so it is hidden from the SDK.
95      * {@hide}
96      */
97     protected OnHierarchyChangeListener mOnHierarchyChangeListener;
98 
99     // The view contained within this ViewGroup that has or contains focus.
100     private View mFocused;
101 
102     /**
103      * A Transformation used when drawing children, to
104      * apply on the child being drawn.
105      */
106     private final Transformation mChildTransformation = new Transformation();
107 
108     /**
109      * Used to track the current invalidation region.
110      */
111     private RectF mInvalidateRegion;
112 
113     /**
114      * A Transformation used to calculate a correct
115      * invalidation area when the application is autoscaled.
116      */
117     private Transformation mInvalidationTransformation;
118 
119     // View currently under an ongoing drag
120     private View mCurrentDragView;
121 
122     // Metadata about the ongoing drag
123     private DragEvent mCurrentDrag;
124     private HashSet<View> mDragNotifiedChildren;
125 
126     // Does this group have a child that can accept the current drag payload?
127     private boolean mChildAcceptsDrag;
128 
129     // Used during drag dispatch
130     private final PointF mLocalPoint = new PointF();
131 
132     // Layout animation
133     private LayoutAnimationController mLayoutAnimationController;
134     private Animation.AnimationListener mAnimationListener;
135 
136     // First touch target in the linked list of touch targets.
137     private TouchTarget mFirstTouchTarget;
138 
139     // For debugging only.  You can see these in hierarchyviewer.
140     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
141     @ViewDebug.ExportedProperty(category = "events")
142     private long mLastTouchDownTime;
143     @ViewDebug.ExportedProperty(category = "events")
144     private int mLastTouchDownIndex = -1;
145     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
146     @ViewDebug.ExportedProperty(category = "events")
147     private float mLastTouchDownX;
148     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
149     @ViewDebug.ExportedProperty(category = "events")
150     private float mLastTouchDownY;
151 
152     // First hover target in the linked list of hover targets.
153     // The hover targets are children which have received ACTION_HOVER_ENTER.
154     // They might not have actually handled the hover event, but we will
155     // continue sending hover events to them as long as the pointer remains over
156     // their bounds and the view group does not intercept hover.
157     private HoverTarget mFirstHoverTarget;
158 
159     // True if the view group itself received a hover event.
160     // It might not have actually handled the hover event.
161     private boolean mHoveredSelf;
162 
163     /**
164      * Internal flags.
165      *
166      * This field should be made private, so it is hidden from the SDK.
167      * {@hide}
168      */
169     protected int mGroupFlags;
170 
171     // When set, ViewGroup invalidates only the child's rectangle
172     // Set by default
173     private static final int FLAG_CLIP_CHILDREN = 0x1;
174 
175     // When set, ViewGroup excludes the padding area from the invalidate rectangle
176     // Set by default
177     private static final int FLAG_CLIP_TO_PADDING = 0x2;
178 
179     // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
180     // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
181     private static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
182 
183     // When set, dispatchDraw() will run the layout animation and unset the flag
184     private static final int FLAG_RUN_ANIMATION = 0x8;
185 
186     // When set, there is either no layout animation on the ViewGroup or the layout
187     // animation is over
188     // Set by default
189     private static final int FLAG_ANIMATION_DONE = 0x10;
190 
191     // If set, this ViewGroup has padding; if unset there is no padding and we don't need
192     // to clip it, even if FLAG_CLIP_TO_PADDING is set
193     private static final int FLAG_PADDING_NOT_NULL = 0x20;
194 
195     // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
196     // Set by default
197     private static final int FLAG_ANIMATION_CACHE = 0x40;
198 
199     // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
200     // layout animation; this avoid clobbering the hierarchy
201     // Automatically set when the layout animation starts, depending on the animation's
202     // characteristics
203     private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
204 
205     // When set, the next call to drawChild() will clear mChildTransformation's matrix
206     private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
207 
208     // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
209     // the children's Bitmap caches if necessary
210     // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
211     private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
212 
213     /**
214      * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
215      * to get the index of the child to draw for that iteration.
216      *
217      * @hide
218      */
219     protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
220 
221     /**
222      * When set, this ViewGroup supports static transformations on children; this causes
223      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
224      * invoked when a child is drawn.
225      *
226      * Any subclass overriding
227      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
228      * set this flags in {@link #mGroupFlags}.
229      *
230      * {@hide}
231      */
232     protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
233 
234     // When the previous drawChild() invocation used an alpha value that was lower than
235     // 1.0 and set it in mCachePaint
236     private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
237 
238     /**
239      * When set, this ViewGroup's drawable states also include those
240      * of its children.
241      */
242     private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
243 
244     /**
245      * When set, this ViewGroup tries to always draw its children using their drawing cache.
246      */
247     private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
248 
249     /**
250      * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
251      * draw its children with their drawing cache.
252      */
253     private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
254 
255     /**
256      * When set, this group will go through its list of children to notify them of
257      * any drawable state change.
258      */
259     private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
260 
261     private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
262 
263     /**
264      * This view will get focus before any of its descendants.
265      */
266     public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
267 
268     /**
269      * This view will get focus only if none of its descendants want it.
270      */
271     public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
272 
273     /**
274      * This view will block any of its descendants from getting focus, even
275      * if they are focusable.
276      */
277     public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
278 
279     /**
280      * Used to map between enum in attrubutes and flag values.
281      */
282     private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
283             {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
284                     FOCUS_BLOCK_DESCENDANTS};
285 
286     /**
287      * When set, this ViewGroup should not intercept touch events.
288      * {@hide}
289      */
290     protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
291 
292     /**
293      * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
294      */
295     private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
296 
297     /**
298      * When set, this ViewGroup will not dispatch onAttachedToWindow calls
299      * to children when adding new views. This is used to prevent multiple
300      * onAttached calls when a ViewGroup adds children in its own onAttached method.
301      */
302     private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
303 
304     /**
305      * Indicates which types of drawing caches are to be kept in memory.
306      * This field should be made private, so it is hidden from the SDK.
307      * {@hide}
308      */
309     protected int mPersistentDrawingCache;
310 
311     /**
312      * Used to indicate that no drawing cache should be kept in memory.
313      */
314     public static final int PERSISTENT_NO_CACHE = 0x0;
315 
316     /**
317      * Used to indicate that the animation drawing cache should be kept in memory.
318      */
319     public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
320 
321     /**
322      * Used to indicate that the scrolling drawing cache should be kept in memory.
323      */
324     public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
325 
326     /**
327      * Used to indicate that all drawing caches should be kept in memory.
328      */
329     public static final int PERSISTENT_ALL_CACHES = 0x3;
330 
331     /**
332      * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
333      * are set at the same time.
334      */
335     protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
336 
337     // Index of the child's left position in the mLocation array
338     private static final int CHILD_LEFT_INDEX = 0;
339     // Index of the child's top position in the mLocation array
340     private static final int CHILD_TOP_INDEX = 1;
341 
342     // Child views of this ViewGroup
343     private View[] mChildren;
344     // Number of valid children in the mChildren array, the rest should be null or not
345     // considered as children
346 
347     private boolean mLayoutSuppressed = false;
348 
349     private int mChildrenCount;
350 
351     private static final int ARRAY_INITIAL_CAPACITY = 12;
352     private static final int ARRAY_CAPACITY_INCREMENT = 12;
353 
354     // Used to draw cached views
355     private final Paint mCachePaint = new Paint();
356 
357     // Used to animate add/remove changes in layout
358     private LayoutTransition mTransition;
359 
360     // The set of views that are currently being transitioned. This list is used to track views
361     // being removed that should not actually be removed from the parent yet because they are
362     // being animated.
363     private ArrayList<View> mTransitioningViews;
364 
365     // List of children changing visibility. This is used to potentially keep rendering
366     // views during a transition when they otherwise would have become gone/invisible
367     private ArrayList<View> mVisibilityChangingChildren;
368 
369     // Indicates whether this container will use its children layers to draw
370     @ViewDebug.ExportedProperty(category = "drawing")
371     private boolean mDrawLayers = true;
372 
ViewGroup(Context context)373     public ViewGroup(Context context) {
374         super(context);
375         initViewGroup();
376     }
377 
ViewGroup(Context context, AttributeSet attrs)378     public ViewGroup(Context context, AttributeSet attrs) {
379         super(context, attrs);
380         initViewGroup();
381         initFromAttributes(context, attrs);
382     }
383 
ViewGroup(Context context, AttributeSet attrs, int defStyle)384     public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
385         super(context, attrs, defStyle);
386         initViewGroup();
387         initFromAttributes(context, attrs);
388     }
389 
initViewGroup()390     private void initViewGroup() {
391         // ViewGroup doesn't draw by default
392         setFlags(WILL_NOT_DRAW, DRAW_MASK);
393         mGroupFlags |= FLAG_CLIP_CHILDREN;
394         mGroupFlags |= FLAG_CLIP_TO_PADDING;
395         mGroupFlags |= FLAG_ANIMATION_DONE;
396         mGroupFlags |= FLAG_ANIMATION_CACHE;
397         mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
398 
399         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
400             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
401         }
402 
403         setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
404 
405         mChildren = new View[ARRAY_INITIAL_CAPACITY];
406         mChildrenCount = 0;
407 
408         mCachePaint.setDither(false);
409 
410         mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
411     }
412 
initFromAttributes(Context context, AttributeSet attrs)413     private void initFromAttributes(Context context, AttributeSet attrs) {
414         TypedArray a = context.obtainStyledAttributes(attrs,
415                 R.styleable.ViewGroup);
416 
417         final int N = a.getIndexCount();
418         for (int i = 0; i < N; i++) {
419             int attr = a.getIndex(i);
420             switch (attr) {
421                 case R.styleable.ViewGroup_clipChildren:
422                     setClipChildren(a.getBoolean(attr, true));
423                     break;
424                 case R.styleable.ViewGroup_clipToPadding:
425                     setClipToPadding(a.getBoolean(attr, true));
426                     break;
427                 case R.styleable.ViewGroup_animationCache:
428                     setAnimationCacheEnabled(a.getBoolean(attr, true));
429                     break;
430                 case R.styleable.ViewGroup_persistentDrawingCache:
431                     setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
432                     break;
433                 case R.styleable.ViewGroup_addStatesFromChildren:
434                     setAddStatesFromChildren(a.getBoolean(attr, false));
435                     break;
436                 case R.styleable.ViewGroup_alwaysDrawnWithCache:
437                     setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
438                     break;
439                 case R.styleable.ViewGroup_layoutAnimation:
440                     int id = a.getResourceId(attr, -1);
441                     if (id > 0) {
442                         setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
443                     }
444                     break;
445                 case R.styleable.ViewGroup_descendantFocusability:
446                     setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
447                     break;
448                 case R.styleable.ViewGroup_splitMotionEvents:
449                     setMotionEventSplittingEnabled(a.getBoolean(attr, false));
450                     break;
451                 case R.styleable.ViewGroup_animateLayoutChanges:
452                     boolean animateLayoutChanges = a.getBoolean(attr, false);
453                     if (animateLayoutChanges) {
454                         setLayoutTransition(new LayoutTransition());
455                     }
456                     break;
457             }
458         }
459 
460         a.recycle();
461     }
462 
463     /**
464      * Gets the descendant focusability of this view group.  The descendant
465      * focusability defines the relationship between this view group and its
466      * descendants when looking for a view to take focus in
467      * {@link #requestFocus(int, android.graphics.Rect)}.
468      *
469      * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
470      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
471      */
472     @ViewDebug.ExportedProperty(category = "focus", mapping = {
473         @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
474         @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
475         @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
476     })
getDescendantFocusability()477     public int getDescendantFocusability() {
478         return mGroupFlags & FLAG_MASK_FOCUSABILITY;
479     }
480 
481     /**
482      * Set the descendant focusability of this view group. This defines the relationship
483      * between this view group and its descendants when looking for a view to
484      * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
485      *
486      * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
487      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
488      */
setDescendantFocusability(int focusability)489     public void setDescendantFocusability(int focusability) {
490         switch (focusability) {
491             case FOCUS_BEFORE_DESCENDANTS:
492             case FOCUS_AFTER_DESCENDANTS:
493             case FOCUS_BLOCK_DESCENDANTS:
494                 break;
495             default:
496                 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
497                         + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
498         }
499         mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
500         mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
501     }
502 
503     /**
504      * {@inheritDoc}
505      */
506     @Override
handleFocusGainInternal(int direction, Rect previouslyFocusedRect)507     void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
508         if (mFocused != null) {
509             mFocused.unFocus();
510             mFocused = null;
511         }
512         super.handleFocusGainInternal(direction, previouslyFocusedRect);
513     }
514 
515     /**
516      * {@inheritDoc}
517      */
requestChildFocus(View child, View focused)518     public void requestChildFocus(View child, View focused) {
519         if (DBG) {
520             System.out.println(this + " requestChildFocus()");
521         }
522         if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
523             return;
524         }
525 
526         // Unfocus us, if necessary
527         super.unFocus();
528 
529         // We had a previous notion of who had focus. Clear it.
530         if (mFocused != child) {
531             if (mFocused != null) {
532                 mFocused.unFocus();
533             }
534 
535             mFocused = child;
536         }
537         if (mParent != null) {
538             mParent.requestChildFocus(this, focused);
539         }
540     }
541 
542     /**
543      * {@inheritDoc}
544      */
focusableViewAvailable(View v)545     public void focusableViewAvailable(View v) {
546         if (mParent != null
547                 // shortcut: don't report a new focusable view if we block our descendants from
548                 // getting focus
549                 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
550                 // shortcut: don't report a new focusable view if we already are focused
551                 // (and we don't prefer our descendants)
552                 //
553                 // note: knowing that mFocused is non-null is not a good enough reason
554                 // to break the traversal since in that case we'd actually have to find
555                 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
556                 // an ancestor of v; this will get checked for at ViewAncestor
557                 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
558             mParent.focusableViewAvailable(v);
559         }
560     }
561 
562     /**
563      * {@inheritDoc}
564      */
showContextMenuForChild(View originalView)565     public boolean showContextMenuForChild(View originalView) {
566         return mParent != null && mParent.showContextMenuForChild(originalView);
567     }
568 
569     /**
570      * {@inheritDoc}
571      */
startActionModeForChild(View originalView, ActionMode.Callback callback)572     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
573         return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
574     }
575 
576     /**
577      * Find the nearest view in the specified direction that wants to take
578      * focus.
579      *
580      * @param focused The view that currently has focus
581      * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
582      *        FOCUS_RIGHT, or 0 for not applicable.
583      */
focusSearch(View focused, int direction)584     public View focusSearch(View focused, int direction) {
585         if (isRootNamespace()) {
586             // root namespace means we should consider ourselves the top of the
587             // tree for focus searching; otherwise we could be focus searching
588             // into other tabs.  see LocalActivityManager and TabHost for more info
589             return FocusFinder.getInstance().findNextFocus(this, focused, direction);
590         } else if (mParent != null) {
591             return mParent.focusSearch(focused, direction);
592         }
593         return null;
594     }
595 
596     /**
597      * {@inheritDoc}
598      */
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)599     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
600         return false;
601     }
602 
603     /**
604      * {@inheritDoc}
605      */
requestSendAccessibilityEvent(View child, AccessibilityEvent event)606     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
607         ViewParent parent = getParent();
608         if (parent == null) {
609             return false;
610         }
611         final boolean propagate = onRequestSendAccessibilityEvent(child, event);
612         //noinspection SimplifiableIfStatement
613         if (!propagate) {
614             return false;
615         }
616         return parent.requestSendAccessibilityEvent(this, event);
617     }
618 
619     /**
620      * Called when a child has requested sending an {@link AccessibilityEvent} and
621      * gives an opportunity to its parent to augment the event.
622      * <p>
623      * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
624      * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
625      * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
626      * is responsible for handling this call.
627      * </p>
628      *
629      * @param child The child which requests sending the event.
630      * @param event The event to be sent.
631      * @return True if the event should be sent.
632      *
633      * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
634      */
onRequestSendAccessibilityEvent(View child, AccessibilityEvent event)635     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
636         if (mAccessibilityDelegate != null) {
637             return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
638         } else {
639             return onRequestSendAccessibilityEventInternal(child, event);
640         }
641     }
642 
643     /**
644      * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
645      *
646      * Note: Called from the default {@link View.AccessibilityDelegate}.
647      */
onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event)648     boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
649         return true;
650     }
651 
652     /**
653      * {@inheritDoc}
654      */
655     @Override
dispatchUnhandledMove(View focused, int direction)656     public boolean dispatchUnhandledMove(View focused, int direction) {
657         return mFocused != null &&
658                 mFocused.dispatchUnhandledMove(focused, direction);
659     }
660 
661     /**
662      * {@inheritDoc}
663      */
clearChildFocus(View child)664     public void clearChildFocus(View child) {
665         if (DBG) {
666             System.out.println(this + " clearChildFocus()");
667         }
668 
669         mFocused = null;
670         if (mParent != null) {
671             mParent.clearChildFocus(this);
672         }
673     }
674 
675     /**
676      * {@inheritDoc}
677      */
678     @Override
clearFocus()679     public void clearFocus() {
680         super.clearFocus();
681 
682         // clear any child focus if it exists
683         if (mFocused != null) {
684             mFocused.clearFocus();
685         }
686     }
687 
688     /**
689      * {@inheritDoc}
690      */
691     @Override
unFocus()692     void unFocus() {
693         if (DBG) {
694             System.out.println(this + " unFocus()");
695         }
696 
697         super.unFocus();
698         if (mFocused != null) {
699             mFocused.unFocus();
700         }
701         mFocused = null;
702     }
703 
704     /**
705      * Returns the focused child of this view, if any. The child may have focus
706      * or contain focus.
707      *
708      * @return the focused child or null.
709      */
getFocusedChild()710     public View getFocusedChild() {
711         return mFocused;
712     }
713 
714     /**
715      * Returns true if this view has or contains focus
716      *
717      * @return true if this view has or contains focus
718      */
719     @Override
hasFocus()720     public boolean hasFocus() {
721         return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
722     }
723 
724     /*
725      * (non-Javadoc)
726      *
727      * @see android.view.View#findFocus()
728      */
729     @Override
findFocus()730     public View findFocus() {
731         if (DBG) {
732             System.out.println("Find focus in " + this + ": flags="
733                     + isFocused() + ", child=" + mFocused);
734         }
735 
736         if (isFocused()) {
737             return this;
738         }
739 
740         if (mFocused != null) {
741             return mFocused.findFocus();
742         }
743         return null;
744     }
745 
746     /**
747      * {@inheritDoc}
748      */
749     @Override
hasFocusable()750     public boolean hasFocusable() {
751         if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
752             return false;
753         }
754 
755         if (isFocusable()) {
756             return true;
757         }
758 
759         final int descendantFocusability = getDescendantFocusability();
760         if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
761             final int count = mChildrenCount;
762             final View[] children = mChildren;
763 
764             for (int i = 0; i < count; i++) {
765                 final View child = children[i];
766                 if (child.hasFocusable()) {
767                     return true;
768                 }
769             }
770         }
771 
772         return false;
773     }
774 
775     /**
776      * {@inheritDoc}
777      */
778     @Override
addFocusables(ArrayList<View> views, int direction)779     public void addFocusables(ArrayList<View> views, int direction) {
780         addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
781     }
782 
783     /**
784      * {@inheritDoc}
785      */
786     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)787     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
788         final int focusableCount = views.size();
789 
790         final int descendantFocusability = getDescendantFocusability();
791 
792         if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
793             final int count = mChildrenCount;
794             final View[] children = mChildren;
795 
796             for (int i = 0; i < count; i++) {
797                 final View child = children[i];
798                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
799                     child.addFocusables(views, direction, focusableMode);
800                 }
801             }
802         }
803 
804         // we add ourselves (if focusable) in all cases except for when we are
805         // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
806         // to avoid the focus search finding layouts when a more precise search
807         // among the focusable children would be more interesting.
808         if (
809             descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
810                 // No focusable descendants
811                 (focusableCount == views.size())) {
812             super.addFocusables(views, direction, focusableMode);
813         }
814     }
815 
816     @Override
findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags)817     public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
818         super.findViewsWithText(outViews, text, flags);
819         final int childrenCount = mChildrenCount;
820         final View[] children = mChildren;
821         for (int i = 0; i < childrenCount; i++) {
822             View child = children[i];
823             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
824                     && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
825                 child.findViewsWithText(outViews, text, flags);
826             }
827         }
828     }
829 
830     @Override
findViewByAccessibilityIdTraversal(int accessibilityId)831     View findViewByAccessibilityIdTraversal(int accessibilityId) {
832         View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
833         if (foundView != null) {
834             return foundView;
835         }
836         final int childrenCount = mChildrenCount;
837         final View[] children = mChildren;
838         for (int i = 0; i < childrenCount; i++) {
839             View child = children[i];
840             foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
841             if (foundView != null) {
842                 return foundView;
843             }
844         }
845         return null;
846     }
847 
848     /**
849      * {@inheritDoc}
850      */
851     @Override
dispatchWindowFocusChanged(boolean hasFocus)852     public void dispatchWindowFocusChanged(boolean hasFocus) {
853         super.dispatchWindowFocusChanged(hasFocus);
854         final int count = mChildrenCount;
855         final View[] children = mChildren;
856         for (int i = 0; i < count; i++) {
857             children[i].dispatchWindowFocusChanged(hasFocus);
858         }
859     }
860 
861     /**
862      * {@inheritDoc}
863      */
864     @Override
addTouchables(ArrayList<View> views)865     public void addTouchables(ArrayList<View> views) {
866         super.addTouchables(views);
867 
868         final int count = mChildrenCount;
869         final View[] children = mChildren;
870 
871         for (int i = 0; i < count; i++) {
872             final View child = children[i];
873             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
874                 child.addTouchables(views);
875             }
876         }
877     }
878 
879     /**
880      * {@inheritDoc}
881      */
882     @Override
dispatchDisplayHint(int hint)883     public void dispatchDisplayHint(int hint) {
884         super.dispatchDisplayHint(hint);
885         final int count = mChildrenCount;
886         final View[] children = mChildren;
887         for (int i = 0; i < count; i++) {
888             children[i].dispatchDisplayHint(hint);
889         }
890     }
891 
892     /**
893      * @hide
894      * @param child
895      * @param visibility
896      */
onChildVisibilityChanged(View child, int visibility)897     protected void onChildVisibilityChanged(View child, int visibility) {
898         if (mTransition != null) {
899             if (visibility == VISIBLE) {
900                 mTransition.showChild(this, child);
901             } else {
902                 mTransition.hideChild(this, child);
903             }
904             if (visibility != VISIBLE) {
905                 // Only track this on disappearing views - appearing views are already visible
906                 // and don't need special handling during drawChild()
907                 if (mVisibilityChangingChildren == null) {
908                     mVisibilityChangingChildren = new ArrayList<View>();
909                 }
910                 mVisibilityChangingChildren.add(child);
911                 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
912                     addDisappearingView(child);
913                 }
914             }
915         }
916 
917         // in all cases, for drags
918         if (mCurrentDrag != null) {
919             if (visibility == VISIBLE) {
920                 notifyChildOfDrag(child);
921             }
922         }
923     }
924 
925     /**
926      * {@inheritDoc}
927      */
928     @Override
dispatchVisibilityChanged(View changedView, int visibility)929     protected void dispatchVisibilityChanged(View changedView, int visibility) {
930         super.dispatchVisibilityChanged(changedView, visibility);
931         final int count = mChildrenCount;
932         final View[] children = mChildren;
933         for (int i = 0; i < count; i++) {
934             children[i].dispatchVisibilityChanged(changedView, visibility);
935         }
936     }
937 
938     /**
939      * {@inheritDoc}
940      */
941     @Override
dispatchWindowVisibilityChanged(int visibility)942     public void dispatchWindowVisibilityChanged(int visibility) {
943         super.dispatchWindowVisibilityChanged(visibility);
944         final int count = mChildrenCount;
945         final View[] children = mChildren;
946         for (int i = 0; i < count; i++) {
947             children[i].dispatchWindowVisibilityChanged(visibility);
948         }
949     }
950 
951     /**
952      * {@inheritDoc}
953      */
954     @Override
dispatchConfigurationChanged(Configuration newConfig)955     public void dispatchConfigurationChanged(Configuration newConfig) {
956         super.dispatchConfigurationChanged(newConfig);
957         final int count = mChildrenCount;
958         final View[] children = mChildren;
959         for (int i = 0; i < count; i++) {
960             children[i].dispatchConfigurationChanged(newConfig);
961         }
962     }
963 
964     /**
965      * {@inheritDoc}
966      */
recomputeViewAttributes(View child)967     public void recomputeViewAttributes(View child) {
968         if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
969             ViewParent parent = mParent;
970             if (parent != null) parent.recomputeViewAttributes(this);
971         }
972     }
973 
974     @Override
dispatchCollectViewAttributes(int visibility)975     void dispatchCollectViewAttributes(int visibility) {
976         visibility |= mViewFlags&VISIBILITY_MASK;
977         super.dispatchCollectViewAttributes(visibility);
978         final int count = mChildrenCount;
979         final View[] children = mChildren;
980         for (int i = 0; i < count; i++) {
981             children[i].dispatchCollectViewAttributes(visibility);
982         }
983     }
984 
985     /**
986      * {@inheritDoc}
987      */
bringChildToFront(View child)988     public void bringChildToFront(View child) {
989         int index = indexOfChild(child);
990         if (index >= 0) {
991             removeFromArray(index);
992             addInArray(child, mChildrenCount);
993             child.mParent = this;
994         }
995     }
996 
997     /**
998      * {@inheritDoc}
999      *
1000      * !!! TODO: write real docs
1001      */
1002     @Override
dispatchDragEvent(DragEvent event)1003     public boolean dispatchDragEvent(DragEvent event) {
1004         boolean retval = false;
1005         final float tx = event.mX;
1006         final float ty = event.mY;
1007 
1008         ViewRootImpl root = getViewRootImpl();
1009 
1010         // Dispatch down the view hierarchy
1011         switch (event.mAction) {
1012         case DragEvent.ACTION_DRAG_STARTED: {
1013             // clear state to recalculate which views we drag over
1014             mCurrentDragView = null;
1015 
1016             // Set up our tracking of drag-started notifications
1017             mCurrentDrag = DragEvent.obtain(event);
1018             if (mDragNotifiedChildren == null) {
1019                 mDragNotifiedChildren = new HashSet<View>();
1020             } else {
1021                 mDragNotifiedChildren.clear();
1022             }
1023 
1024             // Now dispatch down to our children, caching the responses
1025             mChildAcceptsDrag = false;
1026             final int count = mChildrenCount;
1027             final View[] children = mChildren;
1028             for (int i = 0; i < count; i++) {
1029                 final View child = children[i];
1030                 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1031                 if (child.getVisibility() == VISIBLE) {
1032                     final boolean handled = notifyChildOfDrag(children[i]);
1033                     if (handled) {
1034                         mChildAcceptsDrag = true;
1035                     }
1036                 }
1037             }
1038 
1039             // Return HANDLED if one of our children can accept the drag
1040             if (mChildAcceptsDrag) {
1041                 retval = true;
1042             }
1043         } break;
1044 
1045         case DragEvent.ACTION_DRAG_ENDED: {
1046             // Release the bookkeeping now that the drag lifecycle has ended
1047             if (mDragNotifiedChildren != null) {
1048                 for (View child : mDragNotifiedChildren) {
1049                     // If a child was notified about an ongoing drag, it's told that it's over
1050                     child.dispatchDragEvent(event);
1051                     child.mPrivateFlags2 &= ~View.DRAG_MASK;
1052                     child.refreshDrawableState();
1053                 }
1054 
1055                 mDragNotifiedChildren.clear();
1056                 mCurrentDrag.recycle();
1057                 mCurrentDrag = null;
1058             }
1059 
1060             // We consider drag-ended to have been handled if one of our children
1061             // had offered to handle the drag.
1062             if (mChildAcceptsDrag) {
1063                 retval = true;
1064             }
1065         } break;
1066 
1067         case DragEvent.ACTION_DRAG_LOCATION: {
1068             // Find the [possibly new] drag target
1069             final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1070 
1071             // If we've changed apparent drag target, tell the view root which view
1072             // we're over now [for purposes of the eventual drag-recipient-changed
1073             // notifications to the framework] and tell the new target that the drag
1074             // has entered its bounds.  The root will see setDragFocus() calls all
1075             // the way down to the final leaf view that is handling the LOCATION event
1076             // before reporting the new potential recipient to the framework.
1077             if (mCurrentDragView != target) {
1078                 root.setDragFocus(target);
1079 
1080                 final int action = event.mAction;
1081                 // If we've dragged off of a child view, send it the EXITED message
1082                 if (mCurrentDragView != null) {
1083                     final View view = mCurrentDragView;
1084                     event.mAction = DragEvent.ACTION_DRAG_EXITED;
1085                     view.dispatchDragEvent(event);
1086                     view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1087                     view.refreshDrawableState();
1088                 }
1089                 mCurrentDragView = target;
1090 
1091                 // If we've dragged over a new child view, send it the ENTERED message
1092                 if (target != null) {
1093                     event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1094                     target.dispatchDragEvent(event);
1095                     target.mPrivateFlags2 |= View.DRAG_HOVERED;
1096                     target.refreshDrawableState();
1097                 }
1098                 event.mAction = action;  // restore the event's original state
1099             }
1100 
1101             // Dispatch the actual drag location notice, localized into its coordinates
1102             if (target != null) {
1103                 event.mX = mLocalPoint.x;
1104                 event.mY = mLocalPoint.y;
1105 
1106                 retval = target.dispatchDragEvent(event);
1107 
1108                 event.mX = tx;
1109                 event.mY = ty;
1110             }
1111         } break;
1112 
1113         /* Entered / exited dispatch
1114          *
1115          * DRAG_ENTERED is not dispatched downwards from ViewGroup.  The reason for this is
1116          * that we're about to get the corresponding LOCATION event, which we will use to
1117          * determine which of our children is the new target; at that point we will
1118          * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1119          *
1120          * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1121          * drag has left this ViewGroup, we know by definition that every contained subview
1122          * is also no longer under the drag point.
1123          */
1124 
1125         case DragEvent.ACTION_DRAG_EXITED: {
1126             if (mCurrentDragView != null) {
1127                 final View view = mCurrentDragView;
1128                 view.dispatchDragEvent(event);
1129                 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1130                 view.refreshDrawableState();
1131 
1132                 mCurrentDragView = null;
1133             }
1134         } break;
1135 
1136         case DragEvent.ACTION_DROP: {
1137             if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
1138             View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1139             if (target != null) {
1140                 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "   dispatch drop to " + target);
1141                 event.mX = mLocalPoint.x;
1142                 event.mY = mLocalPoint.y;
1143                 retval = target.dispatchDragEvent(event);
1144                 event.mX = tx;
1145                 event.mY = ty;
1146             } else {
1147                 if (ViewDebug.DEBUG_DRAG) {
1148                     Log.d(View.VIEW_LOG_TAG, "   not dropped on an accepting view");
1149                 }
1150             }
1151         } break;
1152         }
1153 
1154         // If none of our children could handle the event, try here
1155         if (!retval) {
1156             // Call up to the View implementation that dispatches to installed listeners
1157             retval = super.dispatchDragEvent(event);
1158         }
1159         return retval;
1160     }
1161 
1162     // Find the frontmost child view that lies under the given point, and calculate
1163     // the position within its own local coordinate system.
findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint)1164     View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
1165         final int count = mChildrenCount;
1166         final View[] children = mChildren;
1167         for (int i = count - 1; i >= 0; i--) {
1168             final View child = children[i];
1169             if (!child.canAcceptDrag()) {
1170                 continue;
1171             }
1172 
1173             if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1174                 return child;
1175             }
1176         }
1177         return null;
1178     }
1179 
notifyChildOfDrag(View child)1180     boolean notifyChildOfDrag(View child) {
1181         if (ViewDebug.DEBUG_DRAG) {
1182             Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1183         }
1184 
1185         boolean canAccept = false;
1186         if (! mDragNotifiedChildren.contains(child)) {
1187             mDragNotifiedChildren.add(child);
1188             canAccept = child.dispatchDragEvent(mCurrentDrag);
1189             if (canAccept && !child.canAcceptDrag()) {
1190                 child.mPrivateFlags2 |= View.DRAG_CAN_ACCEPT;
1191                 child.refreshDrawableState();
1192             }
1193         }
1194         return canAccept;
1195     }
1196 
1197     @Override
dispatchSystemUiVisibilityChanged(int visible)1198     public void dispatchSystemUiVisibilityChanged(int visible) {
1199         super.dispatchSystemUiVisibilityChanged(visible);
1200 
1201         final int count = mChildrenCount;
1202         final View[] children = mChildren;
1203         for (int i=0; i <count; i++) {
1204             final View child = children[i];
1205             child.dispatchSystemUiVisibilityChanged(visible);
1206         }
1207     }
1208 
1209     @Override
updateLocalSystemUiVisibility(int localValue, int localChanges)1210     void updateLocalSystemUiVisibility(int localValue, int localChanges) {
1211         super.updateLocalSystemUiVisibility(localValue, localChanges);
1212 
1213         final int count = mChildrenCount;
1214         final View[] children = mChildren;
1215         for (int i=0; i <count; i++) {
1216             final View child = children[i];
1217             child.updateLocalSystemUiVisibility(localValue, localChanges);
1218         }
1219     }
1220 
1221     /**
1222      * {@inheritDoc}
1223      */
1224     @Override
dispatchKeyEventPreIme(KeyEvent event)1225     public boolean dispatchKeyEventPreIme(KeyEvent event) {
1226         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1227             return super.dispatchKeyEventPreIme(event);
1228         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1229             return mFocused.dispatchKeyEventPreIme(event);
1230         }
1231         return false;
1232     }
1233 
1234     /**
1235      * {@inheritDoc}
1236      */
1237     @Override
dispatchKeyEvent(KeyEvent event)1238     public boolean dispatchKeyEvent(KeyEvent event) {
1239         if (mInputEventConsistencyVerifier != null) {
1240             mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1241         }
1242 
1243         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1244             if (super.dispatchKeyEvent(event)) {
1245                 return true;
1246             }
1247         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1248             if (mFocused.dispatchKeyEvent(event)) {
1249                 return true;
1250             }
1251         }
1252 
1253         if (mInputEventConsistencyVerifier != null) {
1254             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1255         }
1256         return false;
1257     }
1258 
1259     /**
1260      * {@inheritDoc}
1261      */
1262     @Override
dispatchKeyShortcutEvent(KeyEvent event)1263     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1264         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1265             return super.dispatchKeyShortcutEvent(event);
1266         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1267             return mFocused.dispatchKeyShortcutEvent(event);
1268         }
1269         return false;
1270     }
1271 
1272     /**
1273      * {@inheritDoc}
1274      */
1275     @Override
dispatchTrackballEvent(MotionEvent event)1276     public boolean dispatchTrackballEvent(MotionEvent event) {
1277         if (mInputEventConsistencyVerifier != null) {
1278             mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1279         }
1280 
1281         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1282             if (super.dispatchTrackballEvent(event)) {
1283                 return true;
1284             }
1285         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1286             if (mFocused.dispatchTrackballEvent(event)) {
1287                 return true;
1288             }
1289         }
1290 
1291         if (mInputEventConsistencyVerifier != null) {
1292             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1293         }
1294         return false;
1295     }
1296 
1297     /**
1298      * {@inheritDoc}
1299      */
1300     @SuppressWarnings({"ConstantConditions"})
1301     @Override
dispatchHoverEvent(MotionEvent event)1302     protected boolean dispatchHoverEvent(MotionEvent event) {
1303         final int action = event.getAction();
1304 
1305         // First check whether the view group wants to intercept the hover event.
1306         final boolean interceptHover = onInterceptHoverEvent(event);
1307         event.setAction(action); // restore action in case it was changed
1308 
1309         MotionEvent eventNoHistory = event;
1310         boolean handled = false;
1311 
1312         // Send events to the hovered children and build a new list of hover targets until
1313         // one is found that handles the event.
1314         HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1315         mFirstHoverTarget = null;
1316         if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
1317             final float x = event.getX();
1318             final float y = event.getY();
1319             final int childrenCount = mChildrenCount;
1320             if (childrenCount != 0) {
1321                 final View[] children = mChildren;
1322                 HoverTarget lastHoverTarget = null;
1323                 for (int i = childrenCount - 1; i >= 0; i--) {
1324                     final View child = children[i];
1325                     if (!canViewReceivePointerEvents(child)
1326                             || !isTransformedTouchPointInView(x, y, child, null)) {
1327                         continue;
1328                     }
1329 
1330                     // Obtain a hover target for this child.  Dequeue it from the
1331                     // old hover target list if the child was previously hovered.
1332                     HoverTarget hoverTarget = firstOldHoverTarget;
1333                     final boolean wasHovered;
1334                     for (HoverTarget predecessor = null; ;) {
1335                         if (hoverTarget == null) {
1336                             hoverTarget = HoverTarget.obtain(child);
1337                             wasHovered = false;
1338                             break;
1339                         }
1340 
1341                         if (hoverTarget.child == child) {
1342                             if (predecessor != null) {
1343                                 predecessor.next = hoverTarget.next;
1344                             } else {
1345                                 firstOldHoverTarget = hoverTarget.next;
1346                             }
1347                             hoverTarget.next = null;
1348                             wasHovered = true;
1349                             break;
1350                         }
1351 
1352                         predecessor = hoverTarget;
1353                         hoverTarget = hoverTarget.next;
1354                     }
1355 
1356                     // Enqueue the hover target onto the new hover target list.
1357                     if (lastHoverTarget != null) {
1358                         lastHoverTarget.next = hoverTarget;
1359                     } else {
1360                         lastHoverTarget = hoverTarget;
1361                         mFirstHoverTarget = hoverTarget;
1362                     }
1363 
1364                     // Dispatch the event to the child.
1365                     if (action == MotionEvent.ACTION_HOVER_ENTER) {
1366                         if (!wasHovered) {
1367                             // Send the enter as is.
1368                             handled |= dispatchTransformedGenericPointerEvent(
1369                                     event, child); // enter
1370                         }
1371                     } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1372                         if (!wasHovered) {
1373                             // Synthesize an enter from a move.
1374                             eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1375                             eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1376                             handled |= dispatchTransformedGenericPointerEvent(
1377                                     eventNoHistory, child); // enter
1378                             eventNoHistory.setAction(action);
1379 
1380                             handled |= dispatchTransformedGenericPointerEvent(
1381                                     eventNoHistory, child); // move
1382                         } else {
1383                             // Send the move as is.
1384                             handled |= dispatchTransformedGenericPointerEvent(event, child);
1385                         }
1386                     }
1387                     if (handled) {
1388                         break;
1389                     }
1390                 }
1391             }
1392         }
1393 
1394         // Send exit events to all previously hovered children that are no longer hovered.
1395         while (firstOldHoverTarget != null) {
1396             final View child = firstOldHoverTarget.child;
1397 
1398             // Exit the old hovered child.
1399             if (action == MotionEvent.ACTION_HOVER_EXIT) {
1400                 // Send the exit as is.
1401                 handled |= dispatchTransformedGenericPointerEvent(
1402                         event, child); // exit
1403             } else {
1404                 // Synthesize an exit from a move or enter.
1405                 // Ignore the result because hover focus has moved to a different view.
1406                 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1407                     dispatchTransformedGenericPointerEvent(
1408                             event, child); // move
1409                 }
1410                 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1411                 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1412                 dispatchTransformedGenericPointerEvent(
1413                         eventNoHistory, child); // exit
1414                 eventNoHistory.setAction(action);
1415             }
1416 
1417             final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1418             firstOldHoverTarget.recycle();
1419             firstOldHoverTarget = nextOldHoverTarget;
1420         }
1421 
1422         // Send events to the view group itself if no children have handled it.
1423         boolean newHoveredSelf = !handled;
1424         if (newHoveredSelf == mHoveredSelf) {
1425             if (newHoveredSelf) {
1426                 // Send event to the view group as before.
1427                 handled |= super.dispatchHoverEvent(event);
1428             }
1429         } else {
1430             if (mHoveredSelf) {
1431                 // Exit the view group.
1432                 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1433                     // Send the exit as is.
1434                     handled |= super.dispatchHoverEvent(event); // exit
1435                 } else {
1436                     // Synthesize an exit from a move or enter.
1437                     // Ignore the result because hover focus is moving to a different view.
1438                     if (action == MotionEvent.ACTION_HOVER_MOVE) {
1439                         super.dispatchHoverEvent(event); // move
1440                     }
1441                     eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1442                     eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1443                     super.dispatchHoverEvent(eventNoHistory); // exit
1444                     eventNoHistory.setAction(action);
1445                 }
1446                 mHoveredSelf = false;
1447             }
1448 
1449             if (newHoveredSelf) {
1450                 // Enter the view group.
1451                 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1452                     // Send the enter as is.
1453                     handled |= super.dispatchHoverEvent(event); // enter
1454                     mHoveredSelf = true;
1455                 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1456                     // Synthesize an enter from a move.
1457                     eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1458                     eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1459                     handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1460                     eventNoHistory.setAction(action);
1461 
1462                     handled |= super.dispatchHoverEvent(eventNoHistory); // move
1463                     mHoveredSelf = true;
1464                 }
1465             }
1466         }
1467 
1468         // Recycle the copy of the event that we made.
1469         if (eventNoHistory != event) {
1470             eventNoHistory.recycle();
1471         }
1472 
1473         // Done.
1474         return handled;
1475     }
1476 
1477     /** @hide */
1478     @Override
hasHoveredChild()1479     protected boolean hasHoveredChild() {
1480         return mFirstHoverTarget != null;
1481     }
1482 
1483     /**
1484      * Implement this method to intercept hover events before they are handled
1485      * by child views.
1486      * <p>
1487      * This method is called before dispatching a hover event to a child of
1488      * the view group or to the view group's own {@link #onHoverEvent} to allow
1489      * the view group a chance to intercept the hover event.
1490      * This method can also be used to watch all pointer motions that occur within
1491      * the bounds of the view group even when the pointer is hovering over
1492      * a child of the view group rather than over the view group itself.
1493      * </p><p>
1494      * The view group can prevent its children from receiving hover events by
1495      * implementing this method and returning <code>true</code> to indicate
1496      * that it would like to intercept hover events.  The view group must
1497      * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1498      * for as long as it wishes to continue intercepting hover events from
1499      * its children.
1500      * </p><p>
1501      * Interception preserves the invariant that at most one view can be
1502      * hovered at a time by transferring hover focus from the currently hovered
1503      * child to the view group or vice-versa as needed.
1504      * </p><p>
1505      * If this method returns <code>true</code> and a child is already hovered, then the
1506      * child view will first receive a hover exit event and then the view group
1507      * itself will receive a hover enter event in {@link #onHoverEvent}.
1508      * Likewise, if this method had previously returned <code>true</code> to intercept hover
1509      * events and instead returns <code>false</code> while the pointer is hovering
1510      * within the bounds of one of a child, then the view group will first receive a
1511      * hover exit event in {@link #onHoverEvent} and then the hovered child will
1512      * receive a hover enter event.
1513      * </p><p>
1514      * The default implementation always returns false.
1515      * </p>
1516      *
1517      * @param event The motion event that describes the hover.
1518      * @return True if the view group would like to intercept the hover event
1519      * and prevent its children from receiving it.
1520      */
onInterceptHoverEvent(MotionEvent event)1521     public boolean onInterceptHoverEvent(MotionEvent event) {
1522         return false;
1523     }
1524 
obtainMotionEventNoHistoryOrSelf(MotionEvent event)1525     private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1526         if (event.getHistorySize() == 0) {
1527             return event;
1528         }
1529         return MotionEvent.obtainNoHistory(event);
1530     }
1531 
1532     /**
1533      * {@inheritDoc}
1534      */
1535     @Override
dispatchGenericPointerEvent(MotionEvent event)1536     protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1537         // Send the event to the child under the pointer.
1538         final int childrenCount = mChildrenCount;
1539         if (childrenCount != 0) {
1540             final View[] children = mChildren;
1541             final float x = event.getX();
1542             final float y = event.getY();
1543 
1544             for (int i = childrenCount - 1; i >= 0; i--) {
1545                 final View child = children[i];
1546                 if (!canViewReceivePointerEvents(child)
1547                         || !isTransformedTouchPointInView(x, y, child, null)) {
1548                     continue;
1549                 }
1550 
1551                 if (dispatchTransformedGenericPointerEvent(event, child)) {
1552                     return true;
1553                 }
1554             }
1555         }
1556 
1557         // No child handled the event.  Send it to this view group.
1558         return super.dispatchGenericPointerEvent(event);
1559     }
1560 
1561     /**
1562      * {@inheritDoc}
1563      */
1564     @Override
dispatchGenericFocusedEvent(MotionEvent event)1565     protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
1566         // Send the event to the focused child or to this view group if it has focus.
1567         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1568             return super.dispatchGenericFocusedEvent(event);
1569         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1570             return mFocused.dispatchGenericMotionEvent(event);
1571         }
1572         return false;
1573     }
1574 
1575     /**
1576      * Dispatches a generic pointer event to a child, taking into account
1577      * transformations that apply to the child.
1578      *
1579      * @param event The event to send.
1580      * @param child The view to send the event to.
1581      * @return {@code true} if the child handled the event.
1582      */
dispatchTransformedGenericPointerEvent(MotionEvent event, View child)1583     private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
1584         final float offsetX = mScrollX - child.mLeft;
1585         final float offsetY = mScrollY - child.mTop;
1586 
1587         boolean handled;
1588         if (!child.hasIdentityMatrix()) {
1589             MotionEvent transformedEvent = MotionEvent.obtain(event);
1590             transformedEvent.offsetLocation(offsetX, offsetY);
1591             transformedEvent.transform(child.getInverseMatrix());
1592             handled = child.dispatchGenericMotionEvent(transformedEvent);
1593             transformedEvent.recycle();
1594         } else {
1595             event.offsetLocation(offsetX, offsetY);
1596             handled = child.dispatchGenericMotionEvent(event);
1597             event.offsetLocation(-offsetX, -offsetY);
1598         }
1599         return handled;
1600     }
1601 
1602     /**
1603      * {@inheritDoc}
1604      */
1605     @Override
dispatchTouchEvent(MotionEvent ev)1606     public boolean dispatchTouchEvent(MotionEvent ev) {
1607         if (mInputEventConsistencyVerifier != null) {
1608             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
1609         }
1610 
1611         boolean handled = false;
1612         if (onFilterTouchEventForSecurity(ev)) {
1613             final int action = ev.getAction();
1614             final int actionMasked = action & MotionEvent.ACTION_MASK;
1615 
1616             // Handle an initial down.
1617             if (actionMasked == MotionEvent.ACTION_DOWN) {
1618                 // Throw away all previous state when starting a new touch gesture.
1619                 // The framework may have dropped the up or cancel event for the previous gesture
1620                 // due to an app switch, ANR, or some other state change.
1621                 cancelAndClearTouchTargets(ev);
1622                 resetTouchState();
1623             }
1624 
1625             // Check for interception.
1626             final boolean intercepted;
1627             if (actionMasked == MotionEvent.ACTION_DOWN
1628                     || mFirstTouchTarget != null) {
1629                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1630                 if (!disallowIntercept) {
1631                     intercepted = onInterceptTouchEvent(ev);
1632                     ev.setAction(action); // restore action in case it was changed
1633                 } else {
1634                     intercepted = false;
1635                 }
1636             } else {
1637                 // There are no touch targets and this action is not an initial down
1638                 // so this view group continues to intercept touches.
1639                 intercepted = true;
1640             }
1641 
1642             // Check for cancelation.
1643             final boolean canceled = resetCancelNextUpFlag(this)
1644                     || actionMasked == MotionEvent.ACTION_CANCEL;
1645 
1646             // Update list of touch targets for pointer down, if needed.
1647             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1648             TouchTarget newTouchTarget = null;
1649             boolean alreadyDispatchedToNewTouchTarget = false;
1650             if (!canceled && !intercepted) {
1651                 if (actionMasked == MotionEvent.ACTION_DOWN
1652                         || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1653                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1654                     final int actionIndex = ev.getActionIndex(); // always 0 for down
1655                     final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1656                             : TouchTarget.ALL_POINTER_IDS;
1657 
1658                     // Clean up earlier touch targets for this pointer id in case they
1659                     // have become out of sync.
1660                     removePointersFromTouchTargets(idBitsToAssign);
1661 
1662                     final int childrenCount = mChildrenCount;
1663                     if (childrenCount != 0) {
1664                         // Find a child that can receive the event.
1665                         // Scan children from front to back.
1666                         final View[] children = mChildren;
1667                         final float x = ev.getX(actionIndex);
1668                         final float y = ev.getY(actionIndex);
1669 
1670                         for (int i = childrenCount - 1; i >= 0; i--) {
1671                             final View child = children[i];
1672                             if (!canViewReceivePointerEvents(child)
1673                                     || !isTransformedTouchPointInView(x, y, child, null)) {
1674                                 continue;
1675                             }
1676 
1677                             newTouchTarget = getTouchTarget(child);
1678                             if (newTouchTarget != null) {
1679                                 // Child is already receiving touch within its bounds.
1680                                 // Give it the new pointer in addition to the ones it is handling.
1681                                 newTouchTarget.pointerIdBits |= idBitsToAssign;
1682                                 break;
1683                             }
1684 
1685                             resetCancelNextUpFlag(child);
1686                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1687                                 // Child wants to receive touch within its bounds.
1688                                 mLastTouchDownTime = ev.getDownTime();
1689                                 mLastTouchDownIndex = i;
1690                                 mLastTouchDownX = ev.getX();
1691                                 mLastTouchDownY = ev.getY();
1692                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
1693                                 alreadyDispatchedToNewTouchTarget = true;
1694                                 break;
1695                             }
1696                         }
1697                     }
1698 
1699                     if (newTouchTarget == null && mFirstTouchTarget != null) {
1700                         // Did not find a child to receive the event.
1701                         // Assign the pointer to the least recently added target.
1702                         newTouchTarget = mFirstTouchTarget;
1703                         while (newTouchTarget.next != null) {
1704                             newTouchTarget = newTouchTarget.next;
1705                         }
1706                         newTouchTarget.pointerIdBits |= idBitsToAssign;
1707                     }
1708                 }
1709             }
1710 
1711             // Dispatch to touch targets.
1712             if (mFirstTouchTarget == null) {
1713                 // No touch targets so treat this as an ordinary view.
1714                 handled = dispatchTransformedTouchEvent(ev, canceled, null,
1715                         TouchTarget.ALL_POINTER_IDS);
1716             } else {
1717                 // Dispatch to touch targets, excluding the new touch target if we already
1718                 // dispatched to it.  Cancel touch targets if necessary.
1719                 TouchTarget predecessor = null;
1720                 TouchTarget target = mFirstTouchTarget;
1721                 while (target != null) {
1722                     final TouchTarget next = target.next;
1723                     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1724                         handled = true;
1725                     } else {
1726                         final boolean cancelChild = resetCancelNextUpFlag(target.child)
1727                         || intercepted;
1728                         if (dispatchTransformedTouchEvent(ev, cancelChild,
1729                                 target.child, target.pointerIdBits)) {
1730                             handled = true;
1731                         }
1732                         if (cancelChild) {
1733                             if (predecessor == null) {
1734                                 mFirstTouchTarget = next;
1735                             } else {
1736                                 predecessor.next = next;
1737                             }
1738                             target.recycle();
1739                             target = next;
1740                             continue;
1741                         }
1742                     }
1743                     predecessor = target;
1744                     target = next;
1745                 }
1746             }
1747 
1748             // Update list of touch targets for pointer up or cancel, if needed.
1749             if (canceled
1750                     || actionMasked == MotionEvent.ACTION_UP
1751                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1752                 resetTouchState();
1753             } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1754                 final int actionIndex = ev.getActionIndex();
1755                 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1756                 removePointersFromTouchTargets(idBitsToRemove);
1757             }
1758         }
1759 
1760         if (!handled && mInputEventConsistencyVerifier != null) {
1761             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
1762         }
1763         return handled;
1764     }
1765 
1766     /**
1767      * Resets all touch state in preparation for a new cycle.
1768      */
resetTouchState()1769     private void resetTouchState() {
1770         clearTouchTargets();
1771         resetCancelNextUpFlag(this);
1772         mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1773     }
1774 
1775     /**
1776      * Resets the cancel next up flag.
1777      * Returns true if the flag was previously set.
1778      */
resetCancelNextUpFlag(View view)1779     private boolean resetCancelNextUpFlag(View view) {
1780         if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1781             view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1782             return true;
1783         }
1784         return false;
1785     }
1786 
1787     /**
1788      * Clears all touch targets.
1789      */
clearTouchTargets()1790     private void clearTouchTargets() {
1791         TouchTarget target = mFirstTouchTarget;
1792         if (target != null) {
1793             do {
1794                 TouchTarget next = target.next;
1795                 target.recycle();
1796                 target = next;
1797             } while (target != null);
1798             mFirstTouchTarget = null;
1799         }
1800     }
1801 
1802     /**
1803      * Cancels and clears all touch targets.
1804      */
cancelAndClearTouchTargets(MotionEvent event)1805     private void cancelAndClearTouchTargets(MotionEvent event) {
1806         if (mFirstTouchTarget != null) {
1807             boolean syntheticEvent = false;
1808             if (event == null) {
1809                 final long now = SystemClock.uptimeMillis();
1810                 event = MotionEvent.obtain(now, now,
1811                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1812                 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1813                 syntheticEvent = true;
1814             }
1815 
1816             for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1817                 resetCancelNextUpFlag(target.child);
1818                 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
1819             }
1820             clearTouchTargets();
1821 
1822             if (syntheticEvent) {
1823                 event.recycle();
1824             }
1825         }
1826     }
1827 
1828     /**
1829      * Gets the touch target for specified child view.
1830      * Returns null if not found.
1831      */
getTouchTarget(View child)1832     private TouchTarget getTouchTarget(View child) {
1833         for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1834             if (target.child == child) {
1835                 return target;
1836             }
1837         }
1838         return null;
1839     }
1840 
1841     /**
1842      * Adds a touch target for specified child to the beginning of the list.
1843      * Assumes the target child is not already present.
1844      */
addTouchTarget(View child, int pointerIdBits)1845     private TouchTarget addTouchTarget(View child, int pointerIdBits) {
1846         TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
1847         target.next = mFirstTouchTarget;
1848         mFirstTouchTarget = target;
1849         return target;
1850     }
1851 
1852     /**
1853      * Removes the pointer ids from consideration.
1854      */
removePointersFromTouchTargets(int pointerIdBits)1855     private void removePointersFromTouchTargets(int pointerIdBits) {
1856         TouchTarget predecessor = null;
1857         TouchTarget target = mFirstTouchTarget;
1858         while (target != null) {
1859             final TouchTarget next = target.next;
1860             if ((target.pointerIdBits & pointerIdBits) != 0) {
1861                 target.pointerIdBits &= ~pointerIdBits;
1862                 if (target.pointerIdBits == 0) {
1863                     if (predecessor == null) {
1864                         mFirstTouchTarget = next;
1865                     } else {
1866                         predecessor.next = next;
1867                     }
1868                     target.recycle();
1869                     target = next;
1870                     continue;
1871                 }
1872             }
1873             predecessor = target;
1874             target = next;
1875         }
1876     }
1877 
1878     /**
1879      * Returns true if a child view can receive pointer events.
1880      * @hide
1881      */
canViewReceivePointerEvents(View child)1882     private static boolean canViewReceivePointerEvents(View child) {
1883         return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1884                 || child.getAnimation() != null;
1885     }
1886 
1887     /**
1888      * Returns true if a child view contains the specified point when transformed
1889      * into its coordinate space.
1890      * Child must not be null.
1891      * @hide
1892      */
isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint)1893     protected boolean isTransformedTouchPointInView(float x, float y, View child,
1894             PointF outLocalPoint) {
1895         float localX = x + mScrollX - child.mLeft;
1896         float localY = y + mScrollY - child.mTop;
1897         if (! child.hasIdentityMatrix() && mAttachInfo != null) {
1898             final float[] localXY = mAttachInfo.mTmpTransformLocation;
1899             localXY[0] = localX;
1900             localXY[1] = localY;
1901             child.getInverseMatrix().mapPoints(localXY);
1902             localX = localXY[0];
1903             localY = localXY[1];
1904         }
1905         final boolean isInView = child.pointInView(localX, localY);
1906         if (isInView && outLocalPoint != null) {
1907             outLocalPoint.set(localX, localY);
1908         }
1909         return isInView;
1910     }
1911 
1912     /**
1913      * Transforms a motion event into the coordinate space of a particular child view,
1914      * filters out irrelevant pointer ids, and overrides its action if necessary.
1915      * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
1916      */
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)1917     private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
1918             View child, int desiredPointerIdBits) {
1919         final boolean handled;
1920 
1921         // Canceling motions is a special case.  We don't need to perform any transformations
1922         // or filtering.  The important part is the action, not the contents.
1923         final int oldAction = event.getAction();
1924         if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
1925             event.setAction(MotionEvent.ACTION_CANCEL);
1926             if (child == null) {
1927                 handled = super.dispatchTouchEvent(event);
1928             } else {
1929                 handled = child.dispatchTouchEvent(event);
1930             }
1931             event.setAction(oldAction);
1932             return handled;
1933         }
1934 
1935         // Calculate the number of pointers to deliver.
1936         final int oldPointerIdBits = event.getPointerIdBits();
1937         final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
1938 
1939         // If for some reason we ended up in an inconsistent state where it looks like we
1940         // might produce a motion event with no pointers in it, then drop the event.
1941         if (newPointerIdBits == 0) {
1942             return false;
1943         }
1944 
1945         // If the number of pointers is the same and we don't need to perform any fancy
1946         // irreversible transformations, then we can reuse the motion event for this
1947         // dispatch as long as we are careful to revert any changes we make.
1948         // Otherwise we need to make a copy.
1949         final MotionEvent transformedEvent;
1950         if (newPointerIdBits == oldPointerIdBits) {
1951             if (child == null || child.hasIdentityMatrix()) {
1952                 if (child == null) {
1953                     handled = super.dispatchTouchEvent(event);
1954                 } else {
1955                     final float offsetX = mScrollX - child.mLeft;
1956                     final float offsetY = mScrollY - child.mTop;
1957                     event.offsetLocation(offsetX, offsetY);
1958 
1959                     handled = child.dispatchTouchEvent(event);
1960 
1961                     event.offsetLocation(-offsetX, -offsetY);
1962                 }
1963                 return handled;
1964             }
1965             transformedEvent = MotionEvent.obtain(event);
1966         } else {
1967             transformedEvent = event.split(newPointerIdBits);
1968         }
1969 
1970         // Perform any necessary transformations and dispatch.
1971         if (child == null) {
1972             handled = super.dispatchTouchEvent(transformedEvent);
1973         } else {
1974             final float offsetX = mScrollX - child.mLeft;
1975             final float offsetY = mScrollY - child.mTop;
1976             transformedEvent.offsetLocation(offsetX, offsetY);
1977             if (! child.hasIdentityMatrix()) {
1978                 transformedEvent.transform(child.getInverseMatrix());
1979             }
1980 
1981             handled = child.dispatchTouchEvent(transformedEvent);
1982         }
1983 
1984         // Done.
1985         transformedEvent.recycle();
1986         return handled;
1987     }
1988 
1989     /**
1990      * Enable or disable the splitting of MotionEvents to multiple children during touch event
1991      * dispatch. This behavior is enabled by default for applications that target an
1992      * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
1993      *
1994      * <p>When this option is enabled MotionEvents may be split and dispatched to different child
1995      * views depending on where each pointer initially went down. This allows for user interactions
1996      * such as scrolling two panes of content independently, chording of buttons, and performing
1997      * independent gestures on different pieces of content.
1998      *
1999      * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2000      *              child views. <code>false</code> to only allow one child view to be the target of
2001      *              any MotionEvent received by this ViewGroup.
2002      */
setMotionEventSplittingEnabled(boolean split)2003     public void setMotionEventSplittingEnabled(boolean split) {
2004         // TODO Applications really shouldn't change this setting mid-touch event,
2005         // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2006         // with gestures in progress when this is changed.
2007         if (split) {
2008             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2009         } else {
2010             mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
2011         }
2012     }
2013 
2014     /**
2015      * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2016      * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2017      */
isMotionEventSplittingEnabled()2018     public boolean isMotionEventSplittingEnabled() {
2019         return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2020     }
2021 
2022     /**
2023      * {@inheritDoc}
2024      */
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2025     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2026 
2027         if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2028             // We're already in this state, assume our ancestors are too
2029             return;
2030         }
2031 
2032         if (disallowIntercept) {
2033             mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2034         } else {
2035             mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2036         }
2037 
2038         // Pass it up to our parent
2039         if (mParent != null) {
2040             mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2041         }
2042     }
2043 
2044     /**
2045      * Implement this method to intercept all touch screen motion events.  This
2046      * allows you to watch events as they are dispatched to your children, and
2047      * take ownership of the current gesture at any point.
2048      *
2049      * <p>Using this function takes some care, as it has a fairly complicated
2050      * interaction with {@link View#onTouchEvent(MotionEvent)
2051      * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2052      * that method as well as this one in the correct way.  Events will be
2053      * received in the following order:
2054      *
2055      * <ol>
2056      * <li> You will receive the down event here.
2057      * <li> The down event will be handled either by a child of this view
2058      * group, or given to your own onTouchEvent() method to handle; this means
2059      * you should implement onTouchEvent() to return true, so you will
2060      * continue to see the rest of the gesture (instead of looking for
2061      * a parent view to handle it).  Also, by returning true from
2062      * onTouchEvent(), you will not receive any following
2063      * events in onInterceptTouchEvent() and all touch processing must
2064      * happen in onTouchEvent() like normal.
2065      * <li> For as long as you return false from this function, each following
2066      * event (up to and including the final up) will be delivered first here
2067      * and then to the target's onTouchEvent().
2068      * <li> If you return true from here, you will not receive any
2069      * following events: the target view will receive the same event but
2070      * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2071      * events will be delivered to your onTouchEvent() method and no longer
2072      * appear here.
2073      * </ol>
2074      *
2075      * @param ev The motion event being dispatched down the hierarchy.
2076      * @return Return true to steal motion events from the children and have
2077      * them dispatched to this ViewGroup through onTouchEvent().
2078      * The current target will receive an ACTION_CANCEL event, and no further
2079      * messages will be delivered here.
2080      */
onInterceptTouchEvent(MotionEvent ev)2081     public boolean onInterceptTouchEvent(MotionEvent ev) {
2082         return false;
2083     }
2084 
2085     /**
2086      * {@inheritDoc}
2087      *
2088      * Looks for a view to give focus to respecting the setting specified by
2089      * {@link #getDescendantFocusability()}.
2090      *
2091      * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2092      * find focus within the children of this group when appropriate.
2093      *
2094      * @see #FOCUS_BEFORE_DESCENDANTS
2095      * @see #FOCUS_AFTER_DESCENDANTS
2096      * @see #FOCUS_BLOCK_DESCENDANTS
2097      * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
2098      */
2099     @Override
requestFocus(int direction, Rect previouslyFocusedRect)2100     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2101         if (DBG) {
2102             System.out.println(this + " ViewGroup.requestFocus direction="
2103                     + direction);
2104         }
2105         int descendantFocusability = getDescendantFocusability();
2106 
2107         switch (descendantFocusability) {
2108             case FOCUS_BLOCK_DESCENDANTS:
2109                 return super.requestFocus(direction, previouslyFocusedRect);
2110             case FOCUS_BEFORE_DESCENDANTS: {
2111                 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2112                 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2113             }
2114             case FOCUS_AFTER_DESCENDANTS: {
2115                 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2116                 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2117             }
2118             default:
2119                 throw new IllegalStateException("descendant focusability must be "
2120                         + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2121                         + "but is " + descendantFocusability);
2122         }
2123     }
2124 
2125     /**
2126      * Look for a descendant to call {@link View#requestFocus} on.
2127      * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2128      * when it wants to request focus within its children.  Override this to
2129      * customize how your {@link ViewGroup} requests focus within its children.
2130      * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2131      * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2132      *        to give a finer grained hint about where focus is coming from.  May be null
2133      *        if there is no hint.
2134      * @return Whether focus was taken.
2135      */
2136     @SuppressWarnings({"ConstantConditions"})
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2137     protected boolean onRequestFocusInDescendants(int direction,
2138             Rect previouslyFocusedRect) {
2139         int index;
2140         int increment;
2141         int end;
2142         int count = mChildrenCount;
2143         if ((direction & FOCUS_FORWARD) != 0) {
2144             index = 0;
2145             increment = 1;
2146             end = count;
2147         } else {
2148             index = count - 1;
2149             increment = -1;
2150             end = -1;
2151         }
2152         final View[] children = mChildren;
2153         for (int i = index; i != end; i += increment) {
2154             View child = children[i];
2155             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2156                 if (child.requestFocus(direction, previouslyFocusedRect)) {
2157                     return true;
2158                 }
2159             }
2160         }
2161         return false;
2162     }
2163 
2164     /**
2165      * {@inheritDoc}
2166      *
2167      * @hide
2168      */
2169     @Override
dispatchStartTemporaryDetach()2170     public void dispatchStartTemporaryDetach() {
2171         super.dispatchStartTemporaryDetach();
2172         final int count = mChildrenCount;
2173         final View[] children = mChildren;
2174         for (int i = 0; i < count; i++) {
2175             children[i].dispatchStartTemporaryDetach();
2176         }
2177     }
2178 
2179     /**
2180      * {@inheritDoc}
2181      *
2182      * @hide
2183      */
2184     @Override
dispatchFinishTemporaryDetach()2185     public void dispatchFinishTemporaryDetach() {
2186         super.dispatchFinishTemporaryDetach();
2187         final int count = mChildrenCount;
2188         final View[] children = mChildren;
2189         for (int i = 0; i < count; i++) {
2190             children[i].dispatchFinishTemporaryDetach();
2191         }
2192     }
2193 
2194     /**
2195      * {@inheritDoc}
2196      */
2197     @Override
dispatchAttachedToWindow(AttachInfo info, int visibility)2198     void dispatchAttachedToWindow(AttachInfo info, int visibility) {
2199         mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2200         super.dispatchAttachedToWindow(info, visibility);
2201         mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2202 
2203         visibility |= mViewFlags & VISIBILITY_MASK;
2204 
2205         final int count = mChildrenCount;
2206         final View[] children = mChildren;
2207         for (int i = 0; i < count; i++) {
2208             children[i].dispatchAttachedToWindow(info, visibility);
2209         }
2210     }
2211 
2212     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)2213     boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
2214         boolean handled = super.dispatchPopulateAccessibilityEventInternal(event);
2215         if (handled) {
2216             return handled;
2217         }
2218         // Let our children have a shot in populating the event.
2219         for (int i = 0, count = getChildCount(); i < count; i++) {
2220             View child = getChildAt(i);
2221             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2222                 handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
2223                 if (handled) {
2224                     return handled;
2225                 }
2226             }
2227         }
2228         return false;
2229     }
2230 
2231     @Override
onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)2232     void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2233         super.onInitializeAccessibilityNodeInfoInternal(info);
2234         // If the view is not the topmost one in the view hierarchy and it is
2235         // marked as the logical root of a view hierarchy, do not go any deeper.
2236         if ((!(getParent() instanceof ViewRootImpl)) && (mPrivateFlags & IS_ROOT_NAMESPACE) != 0) {
2237             return;
2238         }
2239         for (int i = 0, count = mChildrenCount; i < count; i++) {
2240             View child = mChildren[i];
2241             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2242                 info.addChild(child);
2243             }
2244         }
2245     }
2246 
2247     /**
2248      * {@inheritDoc}
2249      */
2250     @Override
dispatchDetachedFromWindow()2251     void dispatchDetachedFromWindow() {
2252         // If we still have a touch target, we are still in the process of
2253         // dispatching motion events to a child; we need to get rid of that
2254         // child to avoid dispatching events to it after the window is torn
2255         // down. To make sure we keep the child in a consistent state, we
2256         // first send it an ACTION_CANCEL motion event.
2257         cancelAndClearTouchTargets(null);
2258 
2259         // In case view is detached while transition is running
2260         mLayoutSuppressed = false;
2261 
2262         // Tear down our drag tracking
2263         mDragNotifiedChildren = null;
2264         if (mCurrentDrag != null) {
2265             mCurrentDrag.recycle();
2266             mCurrentDrag = null;
2267         }
2268 
2269         final int count = mChildrenCount;
2270         final View[] children = mChildren;
2271         for (int i = 0; i < count; i++) {
2272             children[i].dispatchDetachedFromWindow();
2273         }
2274         super.dispatchDetachedFromWindow();
2275     }
2276 
2277     /**
2278      * {@inheritDoc}
2279      */
2280     @Override
setPadding(int left, int top, int right, int bottom)2281     public void setPadding(int left, int top, int right, int bottom) {
2282         super.setPadding(left, top, right, bottom);
2283 
2284         if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
2285             mGroupFlags |= FLAG_PADDING_NOT_NULL;
2286         } else {
2287             mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2288         }
2289     }
2290 
2291     /**
2292      * {@inheritDoc}
2293      */
2294     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)2295     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2296         super.dispatchSaveInstanceState(container);
2297         final int count = mChildrenCount;
2298         final View[] children = mChildren;
2299         for (int i = 0; i < count; i++) {
2300             View c = children[i];
2301             if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2302                 c.dispatchSaveInstanceState(container);
2303             }
2304         }
2305     }
2306 
2307     /**
2308      * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)}  freeze()}
2309      * to only this view, not to its children.  For use when overriding
2310      * {@link #dispatchSaveInstanceState(android.util.SparseArray)}  dispatchFreeze()} to allow
2311      * subclasses to freeze their own state but not the state of their children.
2312      *
2313      * @param container the container
2314      */
dispatchFreezeSelfOnly(SparseArray<Parcelable> container)2315     protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2316         super.dispatchSaveInstanceState(container);
2317     }
2318 
2319     /**
2320      * {@inheritDoc}
2321      */
2322     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)2323     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2324         super.dispatchRestoreInstanceState(container);
2325         final int count = mChildrenCount;
2326         final View[] children = mChildren;
2327         for (int i = 0; i < count; i++) {
2328             View c = children[i];
2329             if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2330                 c.dispatchRestoreInstanceState(container);
2331             }
2332         }
2333     }
2334 
2335     /**
2336      * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2337      * to only this view, not to its children.  For use when overriding
2338      * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2339      * subclasses to thaw their own state but not the state of their children.
2340      *
2341      * @param container the container
2342      */
dispatchThawSelfOnly(SparseArray<Parcelable> container)2343     protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2344         super.dispatchRestoreInstanceState(container);
2345     }
2346 
2347     /**
2348      * Enables or disables the drawing cache for each child of this view group.
2349      *
2350      * @param enabled true to enable the cache, false to dispose of it
2351      */
setChildrenDrawingCacheEnabled(boolean enabled)2352     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2353         if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2354             final View[] children = mChildren;
2355             final int count = mChildrenCount;
2356             for (int i = 0; i < count; i++) {
2357                 children[i].setDrawingCacheEnabled(enabled);
2358             }
2359         }
2360     }
2361 
2362     @Override
onAnimationStart()2363     protected void onAnimationStart() {
2364         super.onAnimationStart();
2365 
2366         // When this ViewGroup's animation starts, build the cache for the children
2367         if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2368             final int count = mChildrenCount;
2369             final View[] children = mChildren;
2370             final boolean buildCache = !isHardwareAccelerated();
2371 
2372             for (int i = 0; i < count; i++) {
2373                 final View child = children[i];
2374                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2375                     child.setDrawingCacheEnabled(true);
2376                     if (buildCache) {
2377                         child.buildDrawingCache(true);
2378                     }
2379                 }
2380             }
2381 
2382             mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2383         }
2384     }
2385 
2386     @Override
onAnimationEnd()2387     protected void onAnimationEnd() {
2388         super.onAnimationEnd();
2389 
2390         // When this ViewGroup's animation ends, destroy the cache of the children
2391         if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2392             mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2393 
2394             if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2395                 setChildrenDrawingCacheEnabled(false);
2396             }
2397         }
2398     }
2399 
2400     @Override
createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren)2401     Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
2402         int count = mChildrenCount;
2403         int[] visibilities = null;
2404 
2405         if (skipChildren) {
2406             visibilities = new int[count];
2407             for (int i = 0; i < count; i++) {
2408                 View child = getChildAt(i);
2409                 visibilities[i] = child.getVisibility();
2410                 if (visibilities[i] == View.VISIBLE) {
2411                     child.setVisibility(INVISIBLE);
2412                 }
2413             }
2414         }
2415 
2416         Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
2417 
2418         if (skipChildren) {
2419             for (int i = 0; i < count; i++) {
2420                 getChildAt(i).setVisibility(visibilities[i]);
2421             }
2422         }
2423 
2424         return b;
2425     }
2426 
2427     /**
2428      * {@inheritDoc}
2429      */
2430     @Override
dispatchDraw(Canvas canvas)2431     protected void dispatchDraw(Canvas canvas) {
2432         final int count = mChildrenCount;
2433         final View[] children = mChildren;
2434         int flags = mGroupFlags;
2435 
2436         if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
2437             final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2438 
2439             final boolean buildCache = !isHardwareAccelerated();
2440             for (int i = 0; i < count; i++) {
2441                 final View child = children[i];
2442                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2443                     final LayoutParams params = child.getLayoutParams();
2444                     attachLayoutAnimationParameters(child, params, i, count);
2445                     bindLayoutAnimation(child);
2446                     if (cache) {
2447                         child.setDrawingCacheEnabled(true);
2448                         if (buildCache) {
2449                             child.buildDrawingCache(true);
2450                         }
2451                     }
2452                 }
2453             }
2454 
2455             final LayoutAnimationController controller = mLayoutAnimationController;
2456             if (controller.willOverlap()) {
2457                 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
2458             }
2459 
2460             controller.start();
2461 
2462             mGroupFlags &= ~FLAG_RUN_ANIMATION;
2463             mGroupFlags &= ~FLAG_ANIMATION_DONE;
2464 
2465             if (cache) {
2466                 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2467             }
2468 
2469             if (mAnimationListener != null) {
2470                 mAnimationListener.onAnimationStart(controller.getAnimation());
2471             }
2472         }
2473 
2474         int saveCount = 0;
2475         final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2476         if (clipToPadding) {
2477             saveCount = canvas.save();
2478             canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2479                     mScrollX + mRight - mLeft - mPaddingRight,
2480                     mScrollY + mBottom - mTop - mPaddingBottom);
2481 
2482         }
2483 
2484         // We will draw our child's animation, let's reset the flag
2485         mPrivateFlags &= ~DRAW_ANIMATION;
2486         mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2487 
2488         boolean more = false;
2489         final long drawingTime = getDrawingTime();
2490 
2491         if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2492             for (int i = 0; i < count; i++) {
2493                 final View child = children[i];
2494                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2495                     more |= drawChild(canvas, child, drawingTime);
2496                 }
2497             }
2498         } else {
2499             for (int i = 0; i < count; i++) {
2500                 final View child = children[getChildDrawingOrder(count, i)];
2501                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2502                     more |= drawChild(canvas, child, drawingTime);
2503                 }
2504             }
2505         }
2506 
2507         // Draw any disappearing views that have animations
2508         if (mDisappearingChildren != null) {
2509             final ArrayList<View> disappearingChildren = mDisappearingChildren;
2510             final int disappearingCount = disappearingChildren.size() - 1;
2511             // Go backwards -- we may delete as animations finish
2512             for (int i = disappearingCount; i >= 0; i--) {
2513                 final View child = disappearingChildren.get(i);
2514                 more |= drawChild(canvas, child, drawingTime);
2515             }
2516         }
2517 
2518         if (clipToPadding) {
2519             canvas.restoreToCount(saveCount);
2520         }
2521 
2522         // mGroupFlags might have been updated by drawChild()
2523         flags = mGroupFlags;
2524 
2525         if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
2526             invalidate(true);
2527         }
2528 
2529         if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2530                 mLayoutAnimationController.isDone() && !more) {
2531             // We want to erase the drawing cache and notify the listener after the
2532             // next frame is drawn because one extra invalidate() is caused by
2533             // drawChild() after the animation is over
2534             mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2535             final Runnable end = new Runnable() {
2536                public void run() {
2537                    notifyAnimationListener();
2538                }
2539             };
2540             post(end);
2541         }
2542     }
2543 
2544     /**
2545      * Returns the index of the child to draw for this iteration. Override this
2546      * if you want to change the drawing order of children. By default, it
2547      * returns i.
2548      * <p>
2549      * NOTE: In order for this method to be called, you must enable child ordering
2550      * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
2551      *
2552      * @param i The current iteration.
2553      * @return The index of the child to draw this iteration.
2554      *
2555      * @see #setChildrenDrawingOrderEnabled(boolean)
2556      * @see #isChildrenDrawingOrderEnabled()
2557      */
getChildDrawingOrder(int childCount, int i)2558     protected int getChildDrawingOrder(int childCount, int i) {
2559         return i;
2560     }
2561 
notifyAnimationListener()2562     private void notifyAnimationListener() {
2563         mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
2564         mGroupFlags |= FLAG_ANIMATION_DONE;
2565 
2566         if (mAnimationListener != null) {
2567            final Runnable end = new Runnable() {
2568                public void run() {
2569                    mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
2570                }
2571            };
2572            post(end);
2573         }
2574 
2575         if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2576             mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2577             if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2578                 setChildrenDrawingCacheEnabled(false);
2579             }
2580         }
2581 
2582         invalidate(true);
2583     }
2584 
2585     /**
2586      * This method is used to cause children of this ViewGroup to restore or recreate their
2587      * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
2588      * to recreate its own display list, which would happen if it went through the normal
2589      * draw/dispatchDraw mechanisms.
2590      *
2591      * @hide
2592      */
2593     @Override
dispatchGetDisplayList()2594     protected void dispatchGetDisplayList() {
2595         final int count = mChildrenCount;
2596         final View[] children = mChildren;
2597         for (int i = 0; i < count; i++) {
2598             final View child = children[i];
2599             if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
2600                     child.hasStaticLayer()) {
2601                 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2602                 child.mPrivateFlags &= ~INVALIDATED;
2603                 child.getDisplayList();
2604                 child.mRecreateDisplayList = false;
2605             }
2606         }
2607     }
2608 
2609     /**
2610      * Draw one child of this View Group. This method is responsible for getting
2611      * the canvas in the right state. This includes clipping, translating so
2612      * that the child's scrolled origin is at 0, 0, and applying any animation
2613      * transformations.
2614      *
2615      * @param canvas The canvas on which to draw the child
2616      * @param child Who to draw
2617      * @param drawingTime The time at which draw is occuring
2618      * @return True if an invalidate() was issued
2619      */
drawChild(Canvas canvas, View child, long drawingTime)2620     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2621         boolean more = false;
2622 
2623         final int cl = child.mLeft;
2624         final int ct = child.mTop;
2625         final int cr = child.mRight;
2626         final int cb = child.mBottom;
2627 
2628         final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
2629 
2630         final int flags = mGroupFlags;
2631 
2632         if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
2633             mChildTransformation.clear();
2634             mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
2635         }
2636 
2637         Transformation transformToApply = null;
2638         Transformation invalidationTransform;
2639         final Animation a = child.getAnimation();
2640         boolean concatMatrix = false;
2641 
2642         boolean scalingRequired = false;
2643         boolean caching;
2644         int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE;
2645 
2646         final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
2647         if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
2648                 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
2649             caching = true;
2650             if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
2651         } else {
2652             caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
2653         }
2654 
2655         if (a != null) {
2656             final boolean initialized = a.isInitialized();
2657             if (!initialized) {
2658                 a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
2659                 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
2660                 child.onAnimationStart();
2661             }
2662 
2663             more = a.getTransformation(drawingTime, mChildTransformation,
2664                     scalingRequired ? mAttachInfo.mApplicationScale : 1f);
2665             if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
2666                 if (mInvalidationTransformation == null) {
2667                     mInvalidationTransformation = new Transformation();
2668                 }
2669                 invalidationTransform = mInvalidationTransformation;
2670                 a.getTransformation(drawingTime, invalidationTransform, 1f);
2671             } else {
2672                 invalidationTransform = mChildTransformation;
2673             }
2674             transformToApply = mChildTransformation;
2675 
2676             concatMatrix = a.willChangeTransformationMatrix();
2677 
2678             if (more) {
2679                 if (!a.willChangeBounds()) {
2680                     if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
2681                             FLAG_OPTIMIZE_INVALIDATE) {
2682                         mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
2683                     } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
2684                         // The child need to draw an animation, potentially offscreen, so
2685                         // make sure we do not cancel invalidate requests
2686                         mPrivateFlags |= DRAW_ANIMATION;
2687                         invalidate(cl, ct, cr, cb);
2688                     }
2689                 } else {
2690                     if (mInvalidateRegion == null) {
2691                         mInvalidateRegion = new RectF();
2692                     }
2693                     final RectF region = mInvalidateRegion;
2694                     a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
2695 
2696                     // The child need to draw an animation, potentially offscreen, so
2697                     // make sure we do not cancel invalidate requests
2698                     mPrivateFlags |= DRAW_ANIMATION;
2699 
2700                     final int left = cl + (int) region.left;
2701                     final int top = ct + (int) region.top;
2702                     invalidate(left, top, left + (int) (region.width() + .5f),
2703                             top + (int) (region.height() + .5f));
2704                 }
2705             }
2706         } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
2707                 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
2708             final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
2709             if (hasTransform) {
2710                 final int transformType = mChildTransformation.getTransformationType();
2711                 transformToApply = transformType != Transformation.TYPE_IDENTITY ?
2712                         mChildTransformation : null;
2713                 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
2714             }
2715         }
2716 
2717         concatMatrix |= !childHasIdentityMatrix;
2718 
2719         // Sets the flag as early as possible to allow draw() implementations
2720         // to call invalidate() successfully when doing animations
2721         child.mPrivateFlags |= DRAWN;
2722 
2723         if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
2724                 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
2725             return more;
2726         }
2727 
2728         float alpha = child.getAlpha();
2729         // Bail out early if the view does not need to be drawn
2730         if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
2731                 !(child instanceof SurfaceView)) {
2732             return more;
2733         }
2734 
2735         if (hardwareAccelerated) {
2736             // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
2737             // retain the flag's value temporarily in the mRecreateDisplayList flag
2738             child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2739             child.mPrivateFlags &= ~INVALIDATED;
2740         }
2741 
2742         child.computeScroll();
2743 
2744         final int sx = child.mScrollX;
2745         final int sy = child.mScrollY;
2746 
2747         DisplayList displayList = null;
2748         Bitmap cache = null;
2749         boolean hasDisplayList = false;
2750         if (caching) {
2751             if (!hardwareAccelerated) {
2752                 if (layerType != LAYER_TYPE_NONE) {
2753                     layerType = LAYER_TYPE_SOFTWARE;
2754                     child.buildDrawingCache(true);
2755                 }
2756                 cache = child.getDrawingCache(true);
2757             } else {
2758                 switch (layerType) {
2759                     case LAYER_TYPE_SOFTWARE:
2760                         child.buildDrawingCache(true);
2761                         cache = child.getDrawingCache(true);
2762                         break;
2763                     case LAYER_TYPE_NONE:
2764                         // Delay getting the display list until animation-driven alpha values are
2765                         // set up and possibly passed on to the view
2766                         hasDisplayList = child.canHaveDisplayList();
2767                         break;
2768                 }
2769             }
2770         }
2771 
2772         final boolean hasNoCache = cache == null || hasDisplayList;
2773         final boolean offsetForScroll = cache == null && !hasDisplayList &&
2774                 layerType != LAYER_TYPE_HARDWARE;
2775 
2776         final int restoreTo = canvas.save();
2777         if (offsetForScroll) {
2778             canvas.translate(cl - sx, ct - sy);
2779         } else {
2780             canvas.translate(cl, ct);
2781             if (scalingRequired) {
2782                 // mAttachInfo cannot be null, otherwise scalingRequired == false
2783                 final float scale = 1.0f / mAttachInfo.mApplicationScale;
2784                 canvas.scale(scale, scale);
2785             }
2786         }
2787 
2788         if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
2789             if (transformToApply != null || !childHasIdentityMatrix) {
2790                 int transX = 0;
2791                 int transY = 0;
2792 
2793                 if (offsetForScroll) {
2794                     transX = -sx;
2795                     transY = -sy;
2796                 }
2797 
2798                 if (transformToApply != null) {
2799                     if (concatMatrix) {
2800                         // Undo the scroll translation, apply the transformation matrix,
2801                         // then redo the scroll translate to get the correct result.
2802                         canvas.translate(-transX, -transY);
2803                         canvas.concat(transformToApply.getMatrix());
2804                         canvas.translate(transX, transY);
2805                         mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2806                     }
2807 
2808                     float transformAlpha = transformToApply.getAlpha();
2809                     if (transformAlpha < 1.0f) {
2810                         alpha *= transformToApply.getAlpha();
2811                         mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2812                     }
2813                 }
2814 
2815                 if (!childHasIdentityMatrix) {
2816                     canvas.translate(-transX, -transY);
2817                     canvas.concat(child.getMatrix());
2818                     canvas.translate(transX, transY);
2819                 }
2820             }
2821 
2822             if (alpha < 1.0f) {
2823                 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2824                 if (hasNoCache) {
2825                     final int multipliedAlpha = (int) (255 * alpha);
2826                     if (!child.onSetAlpha(multipliedAlpha)) {
2827                         int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
2828                         if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN ||
2829                                 layerType != LAYER_TYPE_NONE) {
2830                             layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
2831                         }
2832                         if (layerType == LAYER_TYPE_NONE) {
2833                             final int scrollX = hasDisplayList ? 0 : sx;
2834                             final int scrollY = hasDisplayList ? 0 : sy;
2835                             canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl,
2836                                     scrollY + cb - ct, multipliedAlpha, layerFlags);
2837                         }
2838                     } else {
2839                         // Alpha is handled by the child directly, clobber the layer's alpha
2840                         child.mPrivateFlags |= ALPHA_SET;
2841                     }
2842                 }
2843             }
2844         } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
2845             child.onSetAlpha(255);
2846             child.mPrivateFlags &= ~ALPHA_SET;
2847         }
2848 
2849         if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
2850             if (offsetForScroll) {
2851                 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
2852             } else {
2853                 if (!scalingRequired || cache == null) {
2854                     canvas.clipRect(0, 0, cr - cl, cb - ct);
2855                 } else {
2856                     canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
2857                 }
2858             }
2859         }
2860 
2861         if (hasDisplayList) {
2862             displayList = child.getDisplayList();
2863             if (!displayList.isValid()) {
2864                 // Uncommon, but possible. If a view is removed from the hierarchy during the call
2865                 // to getDisplayList(), the display list will be marked invalid and we should not
2866                 // try to use it again.
2867                 displayList = null;
2868                 hasDisplayList = false;
2869             }
2870         }
2871 
2872         if (hasNoCache) {
2873             boolean layerRendered = false;
2874             if (layerType == LAYER_TYPE_HARDWARE) {
2875                 final HardwareLayer layer = child.getHardwareLayer();
2876                 if (layer != null && layer.isValid()) {
2877                     child.mLayerPaint.setAlpha((int) (alpha * 255));
2878                     ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint);
2879                     layerRendered = true;
2880                 } else {
2881                     final int scrollX = hasDisplayList ? 0 : sx;
2882                     final int scrollY = hasDisplayList ? 0 : sy;
2883                     canvas.saveLayer(scrollX, scrollY,
2884                             scrollX + cr - cl, scrollY + cb - ct, child.mLayerPaint,
2885                             Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
2886                 }
2887             }
2888 
2889             if (!layerRendered) {
2890                 if (!hasDisplayList) {
2891                     // Fast path for layouts with no backgrounds
2892                     if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
2893                         if (ViewDebug.TRACE_HIERARCHY) {
2894                             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
2895                         }
2896                         child.mPrivateFlags &= ~DIRTY_MASK;
2897                         child.dispatchDraw(canvas);
2898                     } else {
2899                         child.draw(canvas);
2900                     }
2901                 } else {
2902                     child.mPrivateFlags &= ~DIRTY_MASK;
2903                     ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null);
2904                 }
2905             }
2906         } else if (cache != null) {
2907             child.mPrivateFlags &= ~DIRTY_MASK;
2908             Paint cachePaint;
2909 
2910             if (layerType == LAYER_TYPE_NONE) {
2911                 cachePaint = mCachePaint;
2912                 if (alpha < 1.0f) {
2913                     cachePaint.setAlpha((int) (alpha * 255));
2914                     mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
2915                 } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
2916                     cachePaint.setAlpha(255);
2917                     mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
2918                 }
2919             } else {
2920                 cachePaint = child.mLayerPaint;
2921                 cachePaint.setAlpha((int) (alpha * 255));
2922             }
2923             canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
2924         }
2925 
2926         canvas.restoreToCount(restoreTo);
2927 
2928         if (a != null && !more) {
2929             if (!hardwareAccelerated && !a.getFillAfter()) {
2930                 child.onSetAlpha(255);
2931             }
2932             finishAnimatingView(child, a);
2933         }
2934 
2935         if (more && hardwareAccelerated) {
2936             // invalidation is the trigger to recreate display lists, so if we're using
2937             // display lists to render, force an invalidate to allow the animation to
2938             // continue drawing another frame
2939             invalidate(true);
2940             if (a.hasAlpha() && (child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
2941                 // alpha animations should cause the child to recreate its display list
2942                 child.invalidate(true);
2943             }
2944         }
2945 
2946         child.mRecreateDisplayList = false;
2947 
2948         return more;
2949     }
2950 
2951     /**
2952      *
2953      * @param enabled True if children should be drawn with layers, false otherwise.
2954      *
2955      * @hide
2956      */
setChildrenLayersEnabled(boolean enabled)2957     public void setChildrenLayersEnabled(boolean enabled) {
2958         if (enabled != mDrawLayers) {
2959             mDrawLayers = enabled;
2960             invalidate(true);
2961 
2962             // We need to invalidate any child with a layer. For instance,
2963             // if a child is backed by a hardware layer and we disable layers
2964             // the child is marked as not dirty (flags cleared the last time
2965             // the child was drawn inside its layer.) However, that child might
2966             // never have created its own display list or have an obsolete
2967             // display list. By invalidating the child we ensure the display
2968             // list is in sync with the content of the hardware layer.
2969             for (int i = 0; i < mChildrenCount; i++) {
2970                 View child = mChildren[i];
2971                 if (child.mLayerType != LAYER_TYPE_NONE) {
2972                     child.invalidate(true);
2973                 }
2974             }
2975         }
2976     }
2977 
2978     /**
2979      * By default, children are clipped to their bounds before drawing. This
2980      * allows view groups to override this behavior for animations, etc.
2981      *
2982      * @param clipChildren true to clip children to their bounds,
2983      *        false otherwise
2984      * @attr ref android.R.styleable#ViewGroup_clipChildren
2985      */
setClipChildren(boolean clipChildren)2986     public void setClipChildren(boolean clipChildren) {
2987         setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2988     }
2989 
2990     /**
2991      * By default, children are clipped to the padding of the ViewGroup. This
2992      * allows view groups to override this behavior
2993      *
2994      * @param clipToPadding true to clip children to the padding of the
2995      *        group, false otherwise
2996      * @attr ref android.R.styleable#ViewGroup_clipToPadding
2997      */
setClipToPadding(boolean clipToPadding)2998     public void setClipToPadding(boolean clipToPadding) {
2999         setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3000     }
3001 
3002     /**
3003      * {@inheritDoc}
3004      */
3005     @Override
dispatchSetSelected(boolean selected)3006     public void dispatchSetSelected(boolean selected) {
3007         final View[] children = mChildren;
3008         final int count = mChildrenCount;
3009         for (int i = 0; i < count; i++) {
3010             children[i].setSelected(selected);
3011         }
3012     }
3013 
3014     /**
3015      * {@inheritDoc}
3016      */
3017     @Override
dispatchSetActivated(boolean activated)3018     public void dispatchSetActivated(boolean activated) {
3019         final View[] children = mChildren;
3020         final int count = mChildrenCount;
3021         for (int i = 0; i < count; i++) {
3022             children[i].setActivated(activated);
3023         }
3024     }
3025 
3026     @Override
dispatchSetPressed(boolean pressed)3027     protected void dispatchSetPressed(boolean pressed) {
3028         final View[] children = mChildren;
3029         final int count = mChildrenCount;
3030         for (int i = 0; i < count; i++) {
3031             children[i].setPressed(pressed);
3032         }
3033     }
3034 
3035     /**
3036      * When this property is set to true, this ViewGroup supports static transformations on
3037      * children; this causes
3038      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3039      * invoked when a child is drawn.
3040      *
3041      * Any subclass overriding
3042      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3043      * set this property to true.
3044      *
3045      * @param enabled True to enable static transformations on children, false otherwise.
3046      *
3047      * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
3048      */
setStaticTransformationsEnabled(boolean enabled)3049     protected void setStaticTransformationsEnabled(boolean enabled) {
3050         setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3051     }
3052 
3053     /**
3054      * {@inheritDoc}
3055      *
3056      * @see #setStaticTransformationsEnabled(boolean)
3057      */
getChildStaticTransformation(View child, Transformation t)3058     protected boolean getChildStaticTransformation(View child, Transformation t) {
3059         return false;
3060     }
3061 
3062     /**
3063      * {@hide}
3064      */
3065     @Override
findViewTraversal(int id)3066     protected View findViewTraversal(int id) {
3067         if (id == mID) {
3068             return this;
3069         }
3070 
3071         final View[] where = mChildren;
3072         final int len = mChildrenCount;
3073 
3074         for (int i = 0; i < len; i++) {
3075             View v = where[i];
3076 
3077             if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3078                 v = v.findViewById(id);
3079 
3080                 if (v != null) {
3081                     return v;
3082                 }
3083             }
3084         }
3085 
3086         return null;
3087     }
3088 
3089     /**
3090      * {@hide}
3091      */
3092     @Override
findViewWithTagTraversal(Object tag)3093     protected View findViewWithTagTraversal(Object tag) {
3094         if (tag != null && tag.equals(mTag)) {
3095             return this;
3096         }
3097 
3098         final View[] where = mChildren;
3099         final int len = mChildrenCount;
3100 
3101         for (int i = 0; i < len; i++) {
3102             View v = where[i];
3103 
3104             if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3105                 v = v.findViewWithTag(tag);
3106 
3107                 if (v != null) {
3108                     return v;
3109                 }
3110             }
3111         }
3112 
3113         return null;
3114     }
3115 
3116     /**
3117      * {@hide}
3118      */
3119     @Override
findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip)3120     protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
3121         if (predicate.apply(this)) {
3122             return this;
3123         }
3124 
3125         final View[] where = mChildren;
3126         final int len = mChildrenCount;
3127 
3128         for (int i = 0; i < len; i++) {
3129             View v = where[i];
3130 
3131             if (v != childToSkip && (v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3132                 v = v.findViewByPredicate(predicate);
3133 
3134                 if (v != null) {
3135                     return v;
3136                 }
3137             }
3138         }
3139 
3140         return null;
3141     }
3142 
3143     /**
3144      * Adds a child view. If no layout parameters are already set on the child, the
3145      * default parameters for this ViewGroup are set on the child.
3146      *
3147      * @param child the child view to add
3148      *
3149      * @see #generateDefaultLayoutParams()
3150      */
addView(View child)3151     public void addView(View child) {
3152         addView(child, -1);
3153     }
3154 
3155     /**
3156      * Adds a child view. If no layout parameters are already set on the child, the
3157      * default parameters for this ViewGroup are set on the child.
3158      *
3159      * @param child the child view to add
3160      * @param index the position at which to add the child
3161      *
3162      * @see #generateDefaultLayoutParams()
3163      */
addView(View child, int index)3164     public void addView(View child, int index) {
3165         LayoutParams params = child.getLayoutParams();
3166         if (params == null) {
3167             params = generateDefaultLayoutParams();
3168             if (params == null) {
3169                 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3170             }
3171         }
3172         addView(child, index, params);
3173     }
3174 
3175     /**
3176      * Adds a child view with this ViewGroup's default layout parameters and the
3177      * specified width and height.
3178      *
3179      * @param child the child view to add
3180      */
addView(View child, int width, int height)3181     public void addView(View child, int width, int height) {
3182         final LayoutParams params = generateDefaultLayoutParams();
3183         params.width = width;
3184         params.height = height;
3185         addView(child, -1, params);
3186     }
3187 
3188     /**
3189      * Adds a child view with the specified layout parameters.
3190      *
3191      * @param child the child view to add
3192      * @param params the layout parameters to set on the child
3193      */
addView(View child, LayoutParams params)3194     public void addView(View child, LayoutParams params) {
3195         addView(child, -1, params);
3196     }
3197 
3198     /**
3199      * Adds a child view with the specified layout parameters.
3200      *
3201      * @param child the child view to add
3202      * @param index the position at which to add the child
3203      * @param params the layout parameters to set on the child
3204      */
addView(View child, int index, LayoutParams params)3205     public void addView(View child, int index, LayoutParams params) {
3206         if (DBG) {
3207             System.out.println(this + " addView");
3208         }
3209 
3210         // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3211         // therefore, we call requestLayout() on ourselves before, so that the child's request
3212         // will be blocked at our level
3213         requestLayout();
3214         invalidate(true);
3215         addViewInner(child, index, params, false);
3216     }
3217 
3218     /**
3219      * {@inheritDoc}
3220      */
updateViewLayout(View view, ViewGroup.LayoutParams params)3221     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3222         if (!checkLayoutParams(params)) {
3223             throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3224         }
3225         if (view.mParent != this) {
3226             throw new IllegalArgumentException("Given view not a child of " + this);
3227         }
3228         view.setLayoutParams(params);
3229     }
3230 
3231     /**
3232      * {@inheritDoc}
3233      */
checkLayoutParams(ViewGroup.LayoutParams p)3234     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3235         return  p != null;
3236     }
3237 
3238     /**
3239      * Interface definition for a callback to be invoked when the hierarchy
3240      * within this view changed. The hierarchy changes whenever a child is added
3241      * to or removed from this view.
3242      */
3243     public interface OnHierarchyChangeListener {
3244         /**
3245          * Called when a new child is added to a parent view.
3246          *
3247          * @param parent the view in which a child was added
3248          * @param child the new child view added in the hierarchy
3249          */
onChildViewAdded(View parent, View child)3250         void onChildViewAdded(View parent, View child);
3251 
3252         /**
3253          * Called when a child is removed from a parent view.
3254          *
3255          * @param parent the view from which the child was removed
3256          * @param child the child removed from the hierarchy
3257          */
onChildViewRemoved(View parent, View child)3258         void onChildViewRemoved(View parent, View child);
3259     }
3260 
3261     /**
3262      * Register a callback to be invoked when a child is added to or removed
3263      * from this view.
3264      *
3265      * @param listener the callback to invoke on hierarchy change
3266      */
setOnHierarchyChangeListener(OnHierarchyChangeListener listener)3267     public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3268         mOnHierarchyChangeListener = listener;
3269     }
3270 
3271     /**
3272      * @hide
3273      */
onViewAdded(View child)3274     protected void onViewAdded(View child) {
3275         if (mOnHierarchyChangeListener != null) {
3276             mOnHierarchyChangeListener.onChildViewAdded(this, child);
3277         }
3278     }
3279 
3280     /**
3281      * @hide
3282      */
onViewRemoved(View child)3283     protected void onViewRemoved(View child) {
3284         if (mOnHierarchyChangeListener != null) {
3285             mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3286         }
3287     }
3288 
3289     /**
3290      * Adds a view during layout. This is useful if in your onLayout() method,
3291      * you need to add more views (as does the list view for example).
3292      *
3293      * If index is negative, it means put it at the end of the list.
3294      *
3295      * @param child the view to add to the group
3296      * @param index the index at which the child must be added
3297      * @param params the layout parameters to associate with the child
3298      * @return true if the child was added, false otherwise
3299      */
addViewInLayout(View child, int index, LayoutParams params)3300     protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3301         return addViewInLayout(child, index, params, false);
3302     }
3303 
3304     /**
3305      * Adds a view during layout. This is useful if in your onLayout() method,
3306      * you need to add more views (as does the list view for example).
3307      *
3308      * If index is negative, it means put it at the end of the list.
3309      *
3310      * @param child the view to add to the group
3311      * @param index the index at which the child must be added
3312      * @param params the layout parameters to associate with the child
3313      * @param preventRequestLayout if true, calling this method will not trigger a
3314      *        layout request on child
3315      * @return true if the child was added, false otherwise
3316      */
addViewInLayout(View child, int index, LayoutParams params, boolean preventRequestLayout)3317     protected boolean addViewInLayout(View child, int index, LayoutParams params,
3318             boolean preventRequestLayout) {
3319         child.mParent = null;
3320         addViewInner(child, index, params, preventRequestLayout);
3321         child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
3322         return true;
3323     }
3324 
3325     /**
3326      * Prevents the specified child to be laid out during the next layout pass.
3327      *
3328      * @param child the child on which to perform the cleanup
3329      */
cleanupLayoutState(View child)3330     protected void cleanupLayoutState(View child) {
3331         child.mPrivateFlags &= ~View.FORCE_LAYOUT;
3332     }
3333 
addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout)3334     private void addViewInner(View child, int index, LayoutParams params,
3335             boolean preventRequestLayout) {
3336 
3337         if (mTransition != null) {
3338             // Don't prevent other add transitions from completing, but cancel remove
3339             // transitions to let them complete the process before we add to the container
3340             mTransition.cancel(LayoutTransition.DISAPPEARING);
3341         }
3342 
3343         if (child.getParent() != null) {
3344             throw new IllegalStateException("The specified child already has a parent. " +
3345                     "You must call removeView() on the child's parent first.");
3346         }
3347 
3348         if (mTransition != null) {
3349             mTransition.addChild(this, child);
3350         }
3351 
3352         if (!checkLayoutParams(params)) {
3353             params = generateLayoutParams(params);
3354         }
3355 
3356         if (preventRequestLayout) {
3357             child.mLayoutParams = params;
3358         } else {
3359             child.setLayoutParams(params);
3360         }
3361 
3362         if (index < 0) {
3363             index = mChildrenCount;
3364         }
3365 
3366         addInArray(child, index);
3367 
3368         // tell our children
3369         if (preventRequestLayout) {
3370             child.assignParent(this);
3371         } else {
3372             child.mParent = this;
3373         }
3374 
3375         if (child.hasFocus()) {
3376             requestChildFocus(child, child.findFocus());
3377         }
3378 
3379         AttachInfo ai = mAttachInfo;
3380         if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
3381             boolean lastKeepOn = ai.mKeepScreenOn;
3382             ai.mKeepScreenOn = false;
3383             child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3384             if (ai.mKeepScreenOn) {
3385                 needGlobalAttributesUpdate(true);
3386             }
3387             ai.mKeepScreenOn = lastKeepOn;
3388         }
3389 
3390         onViewAdded(child);
3391 
3392         if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3393             mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3394         }
3395     }
3396 
addInArray(View child, int index)3397     private void addInArray(View child, int index) {
3398         View[] children = mChildren;
3399         final int count = mChildrenCount;
3400         final int size = children.length;
3401         if (index == count) {
3402             if (size == count) {
3403                 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3404                 System.arraycopy(children, 0, mChildren, 0, size);
3405                 children = mChildren;
3406             }
3407             children[mChildrenCount++] = child;
3408         } else if (index < count) {
3409             if (size == count) {
3410                 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3411                 System.arraycopy(children, 0, mChildren, 0, index);
3412                 System.arraycopy(children, index, mChildren, index + 1, count - index);
3413                 children = mChildren;
3414             } else {
3415                 System.arraycopy(children, index, children, index + 1, count - index);
3416             }
3417             children[index] = child;
3418             mChildrenCount++;
3419             if (mLastTouchDownIndex >= index) {
3420                 mLastTouchDownIndex++;
3421             }
3422         } else {
3423             throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3424         }
3425     }
3426 
3427     // This method also sets the child's mParent to null
removeFromArray(int index)3428     private void removeFromArray(int index) {
3429         final View[] children = mChildren;
3430         if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3431             children[index].mParent = null;
3432         }
3433         final int count = mChildrenCount;
3434         if (index == count - 1) {
3435             children[--mChildrenCount] = null;
3436         } else if (index >= 0 && index < count) {
3437             System.arraycopy(children, index + 1, children, index, count - index - 1);
3438             children[--mChildrenCount] = null;
3439         } else {
3440             throw new IndexOutOfBoundsException();
3441         }
3442         if (mLastTouchDownIndex == index) {
3443             mLastTouchDownTime = 0;
3444             mLastTouchDownIndex = -1;
3445         } else if (mLastTouchDownIndex > index) {
3446             mLastTouchDownIndex--;
3447         }
3448     }
3449 
3450     // This method also sets the children's mParent to null
removeFromArray(int start, int count)3451     private void removeFromArray(int start, int count) {
3452         final View[] children = mChildren;
3453         final int childrenCount = mChildrenCount;
3454 
3455         start = Math.max(0, start);
3456         final int end = Math.min(childrenCount, start + count);
3457 
3458         if (start == end) {
3459             return;
3460         }
3461 
3462         if (end == childrenCount) {
3463             for (int i = start; i < end; i++) {
3464                 children[i].mParent = null;
3465                 children[i] = null;
3466             }
3467         } else {
3468             for (int i = start; i < end; i++) {
3469                 children[i].mParent = null;
3470             }
3471 
3472             // Since we're looping above, we might as well do the copy, but is arraycopy()
3473             // faster than the extra 2 bounds checks we would do in the loop?
3474             System.arraycopy(children, end, children, start, childrenCount - end);
3475 
3476             for (int i = childrenCount - (end - start); i < childrenCount; i++) {
3477                 children[i] = null;
3478             }
3479         }
3480 
3481         mChildrenCount -= (end - start);
3482     }
3483 
bindLayoutAnimation(View child)3484     private void bindLayoutAnimation(View child) {
3485         Animation a = mLayoutAnimationController.getAnimationForView(child);
3486         child.setAnimation(a);
3487     }
3488 
3489     /**
3490      * Subclasses should override this method to set layout animation
3491      * parameters on the supplied child.
3492      *
3493      * @param child the child to associate with animation parameters
3494      * @param params the child's layout parameters which hold the animation
3495      *        parameters
3496      * @param index the index of the child in the view group
3497      * @param count the number of children in the view group
3498      */
attachLayoutAnimationParameters(View child, LayoutParams params, int index, int count)3499     protected void attachLayoutAnimationParameters(View child,
3500             LayoutParams params, int index, int count) {
3501         LayoutAnimationController.AnimationParameters animationParams =
3502                     params.layoutAnimationParameters;
3503         if (animationParams == null) {
3504             animationParams = new LayoutAnimationController.AnimationParameters();
3505             params.layoutAnimationParameters = animationParams;
3506         }
3507 
3508         animationParams.count = count;
3509         animationParams.index = index;
3510     }
3511 
3512     /**
3513      * {@inheritDoc}
3514      */
removeView(View view)3515     public void removeView(View view) {
3516         removeViewInternal(view);
3517         requestLayout();
3518         invalidate(true);
3519     }
3520 
3521     /**
3522      * Removes a view during layout. This is useful if in your onLayout() method,
3523      * you need to remove more views.
3524      *
3525      * @param view the view to remove from the group
3526      */
removeViewInLayout(View view)3527     public void removeViewInLayout(View view) {
3528         removeViewInternal(view);
3529     }
3530 
3531     /**
3532      * Removes a range of views during layout. This is useful if in your onLayout() method,
3533      * you need to remove more views.
3534      *
3535      * @param start the index of the first view to remove from the group
3536      * @param count the number of views to remove from the group
3537      */
removeViewsInLayout(int start, int count)3538     public void removeViewsInLayout(int start, int count) {
3539         removeViewsInternal(start, count);
3540     }
3541 
3542     /**
3543      * Removes the view at the specified position in the group.
3544      *
3545      * @param index the position in the group of the view to remove
3546      */
removeViewAt(int index)3547     public void removeViewAt(int index) {
3548         removeViewInternal(index, getChildAt(index));
3549         requestLayout();
3550         invalidate(true);
3551     }
3552 
3553     /**
3554      * Removes the specified range of views from the group.
3555      *
3556      * @param start the first position in the group of the range of views to remove
3557      * @param count the number of views to remove
3558      */
removeViews(int start, int count)3559     public void removeViews(int start, int count) {
3560         removeViewsInternal(start, count);
3561         requestLayout();
3562         invalidate(true);
3563     }
3564 
removeViewInternal(View view)3565     private void removeViewInternal(View view) {
3566         final int index = indexOfChild(view);
3567         if (index >= 0) {
3568             removeViewInternal(index, view);
3569         }
3570     }
3571 
removeViewInternal(int index, View view)3572     private void removeViewInternal(int index, View view) {
3573 
3574         if (mTransition != null) {
3575             mTransition.removeChild(this, view);
3576         }
3577 
3578         boolean clearChildFocus = false;
3579         if (view == mFocused) {
3580             view.clearFocusForRemoval();
3581             clearChildFocus = true;
3582         }
3583 
3584         if (view.getAnimation() != null ||
3585                 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3586             addDisappearingView(view);
3587         } else if (view.mAttachInfo != null) {
3588            view.dispatchDetachedFromWindow();
3589         }
3590 
3591         onViewRemoved(view);
3592 
3593         needGlobalAttributesUpdate(false);
3594 
3595         removeFromArray(index);
3596 
3597         if (clearChildFocus) {
3598             clearChildFocus(view);
3599         }
3600     }
3601 
3602     /**
3603      * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3604      * not null, changes in layout which occur because of children being added to or removed from
3605      * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3606      * object. By default, the transition object is null (so layout changes are not animated).
3607      *
3608      * @param transition The LayoutTransition object that will animated changes in layout. A value
3609      * of <code>null</code> means no transition will run on layout changes.
3610      * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
3611      */
setLayoutTransition(LayoutTransition transition)3612     public void setLayoutTransition(LayoutTransition transition) {
3613         if (mTransition != null) {
3614             mTransition.removeTransitionListener(mLayoutTransitionListener);
3615         }
3616         mTransition = transition;
3617         if (mTransition != null) {
3618             mTransition.addTransitionListener(mLayoutTransitionListener);
3619         }
3620     }
3621 
3622     /**
3623      * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3624      * not null, changes in layout which occur because of children being added to or removed from
3625      * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3626      * object. By default, the transition object is null (so layout changes are not animated).
3627      *
3628      * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
3629      * A value of <code>null</code> means no transition will run on layout changes.
3630      */
getLayoutTransition()3631     public LayoutTransition getLayoutTransition() {
3632         return mTransition;
3633     }
3634 
removeViewsInternal(int start, int count)3635     private void removeViewsInternal(int start, int count) {
3636         final View focused = mFocused;
3637         final boolean detach = mAttachInfo != null;
3638         View clearChildFocus = null;
3639 
3640         final View[] children = mChildren;
3641         final int end = start + count;
3642 
3643         for (int i = start; i < end; i++) {
3644             final View view = children[i];
3645 
3646             if (mTransition != null) {
3647                 mTransition.removeChild(this, view);
3648             }
3649 
3650             if (view == focused) {
3651                 view.clearFocusForRemoval();
3652                 clearChildFocus = view;
3653             }
3654 
3655             if (view.getAnimation() != null ||
3656                 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3657                 addDisappearingView(view);
3658             } else if (detach) {
3659                view.dispatchDetachedFromWindow();
3660             }
3661 
3662             needGlobalAttributesUpdate(false);
3663 
3664             onViewRemoved(view);
3665         }
3666 
3667         removeFromArray(start, count);
3668 
3669         if (clearChildFocus != null) {
3670             clearChildFocus(clearChildFocus);
3671         }
3672     }
3673 
3674     /**
3675      * Call this method to remove all child views from the
3676      * ViewGroup.
3677      */
removeAllViews()3678     public void removeAllViews() {
3679         removeAllViewsInLayout();
3680         requestLayout();
3681         invalidate(true);
3682     }
3683 
3684     /**
3685      * Called by a ViewGroup subclass to remove child views from itself,
3686      * when it must first know its size on screen before it can calculate how many
3687      * child views it will render. An example is a Gallery or a ListView, which
3688      * may "have" 50 children, but actually only render the number of children
3689      * that can currently fit inside the object on screen. Do not call
3690      * this method unless you are extending ViewGroup and understand the
3691      * view measuring and layout pipeline.
3692      */
removeAllViewsInLayout()3693     public void removeAllViewsInLayout() {
3694         final int count = mChildrenCount;
3695         if (count <= 0) {
3696             return;
3697         }
3698 
3699         final View[] children = mChildren;
3700         mChildrenCount = 0;
3701 
3702         final View focused = mFocused;
3703         final boolean detach = mAttachInfo != null;
3704         View clearChildFocus = null;
3705 
3706         needGlobalAttributesUpdate(false);
3707 
3708         for (int i = count - 1; i >= 0; i--) {
3709             final View view = children[i];
3710 
3711             if (mTransition != null) {
3712                 mTransition.removeChild(this, view);
3713             }
3714 
3715             if (view == focused) {
3716                 view.clearFocusForRemoval();
3717                 clearChildFocus = view;
3718             }
3719 
3720             if (view.getAnimation() != null ||
3721                     (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3722                 addDisappearingView(view);
3723             } else if (detach) {
3724                view.dispatchDetachedFromWindow();
3725             }
3726 
3727             onViewRemoved(view);
3728 
3729             view.mParent = null;
3730             children[i] = null;
3731         }
3732 
3733         if (clearChildFocus != null) {
3734             clearChildFocus(clearChildFocus);
3735         }
3736     }
3737 
3738     /**
3739      * Finishes the removal of a detached view. This method will dispatch the detached from
3740      * window event and notify the hierarchy change listener.
3741      *
3742      * @param child the child to be definitely removed from the view hierarchy
3743      * @param animate if true and the view has an animation, the view is placed in the
3744      *                disappearing views list, otherwise, it is detached from the window
3745      *
3746      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3747      * @see #detachAllViewsFromParent()
3748      * @see #detachViewFromParent(View)
3749      * @see #detachViewFromParent(int)
3750      */
removeDetachedView(View child, boolean animate)3751     protected void removeDetachedView(View child, boolean animate) {
3752         if (mTransition != null) {
3753             mTransition.removeChild(this, child);
3754         }
3755 
3756         if (child == mFocused) {
3757             child.clearFocus();
3758         }
3759 
3760         if ((animate && child.getAnimation() != null) ||
3761                 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
3762             addDisappearingView(child);
3763         } else if (child.mAttachInfo != null) {
3764             child.dispatchDetachedFromWindow();
3765         }
3766 
3767         onViewRemoved(child);
3768     }
3769 
3770     /**
3771      * Attaches a view to this view group. Attaching a view assigns this group as the parent,
3772      * sets the layout parameters and puts the view in the list of children so it can be retrieved
3773      * by calling {@link #getChildAt(int)}.
3774      *
3775      * This method should be called only for view which were detached from their parent.
3776      *
3777      * @param child the child to attach
3778      * @param index the index at which the child should be attached
3779      * @param params the layout parameters of the child
3780      *
3781      * @see #removeDetachedView(View, boolean)
3782      * @see #detachAllViewsFromParent()
3783      * @see #detachViewFromParent(View)
3784      * @see #detachViewFromParent(int)
3785      */
attachViewToParent(View child, int index, LayoutParams params)3786     protected void attachViewToParent(View child, int index, LayoutParams params) {
3787         child.mLayoutParams = params;
3788 
3789         if (index < 0) {
3790             index = mChildrenCount;
3791         }
3792 
3793         addInArray(child, index);
3794 
3795         child.mParent = this;
3796         child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) |
3797                 DRAWN | INVALIDATED;
3798         this.mPrivateFlags |= INVALIDATED;
3799 
3800         if (child.hasFocus()) {
3801             requestChildFocus(child, child.findFocus());
3802         }
3803     }
3804 
3805     /**
3806      * Detaches a view from its parent. Detaching a view should be temporary and followed
3807      * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3808      * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3809      * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3810      *
3811      * @param child the child to detach
3812      *
3813      * @see #detachViewFromParent(int)
3814      * @see #detachViewsFromParent(int, int)
3815      * @see #detachAllViewsFromParent()
3816      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3817      * @see #removeDetachedView(View, boolean)
3818      */
detachViewFromParent(View child)3819     protected void detachViewFromParent(View child) {
3820         removeFromArray(indexOfChild(child));
3821     }
3822 
3823     /**
3824      * Detaches a view from its parent. Detaching a view should be temporary and followed
3825      * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3826      * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3827      * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3828      *
3829      * @param index the index of the child to detach
3830      *
3831      * @see #detachViewFromParent(View)
3832      * @see #detachAllViewsFromParent()
3833      * @see #detachViewsFromParent(int, int)
3834      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3835      * @see #removeDetachedView(View, boolean)
3836      */
detachViewFromParent(int index)3837     protected void detachViewFromParent(int index) {
3838         removeFromArray(index);
3839     }
3840 
3841     /**
3842      * Detaches a range of view from their parent. Detaching a view should be temporary and followed
3843      * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3844      * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
3845      * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3846      *
3847      * @param start the first index of the childrend range to detach
3848      * @param count the number of children to detach
3849      *
3850      * @see #detachViewFromParent(View)
3851      * @see #detachViewFromParent(int)
3852      * @see #detachAllViewsFromParent()
3853      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3854      * @see #removeDetachedView(View, boolean)
3855      */
detachViewsFromParent(int start, int count)3856     protected void detachViewsFromParent(int start, int count) {
3857         removeFromArray(start, count);
3858     }
3859 
3860     /**
3861      * Detaches all views from the parent. Detaching a view should be temporary and followed
3862      * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3863      * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3864      * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3865      *
3866      * @see #detachViewFromParent(View)
3867      * @see #detachViewFromParent(int)
3868      * @see #detachViewsFromParent(int, int)
3869      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3870      * @see #removeDetachedView(View, boolean)
3871      */
detachAllViewsFromParent()3872     protected void detachAllViewsFromParent() {
3873         final int count = mChildrenCount;
3874         if (count <= 0) {
3875             return;
3876         }
3877 
3878         final View[] children = mChildren;
3879         mChildrenCount = 0;
3880 
3881         for (int i = count - 1; i >= 0; i--) {
3882             children[i].mParent = null;
3883             children[i] = null;
3884         }
3885     }
3886 
3887     /**
3888      * Don't call or override this method. It is used for the implementation of
3889      * the view hierarchy.
3890      */
invalidateChild(View child, final Rect dirty)3891     public final void invalidateChild(View child, final Rect dirty) {
3892         if (ViewDebug.TRACE_HIERARCHY) {
3893             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
3894         }
3895 
3896         ViewParent parent = this;
3897 
3898         final AttachInfo attachInfo = mAttachInfo;
3899         if (attachInfo != null) {
3900             // If the child is drawing an animation, we want to copy this flag onto
3901             // ourselves and the parent to make sure the invalidate request goes
3902             // through
3903             final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
3904 
3905             if (dirty == null) {
3906                 if (child.mLayerType != LAYER_TYPE_NONE) {
3907                     mPrivateFlags |= INVALIDATED;
3908                     mPrivateFlags &= ~DRAWING_CACHE_VALID;
3909                     child.mLocalDirtyRect.setEmpty();
3910                 }
3911                 do {
3912                     View view = null;
3913                     if (parent instanceof View) {
3914                         view = (View) parent;
3915                         if (view.mLayerType != LAYER_TYPE_NONE) {
3916                             view.mLocalDirtyRect.setEmpty();
3917                             if (view.getParent() instanceof View) {
3918                                 final View grandParent = (View) view.getParent();
3919                                 grandParent.mPrivateFlags |= INVALIDATED;
3920                                 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3921                             }
3922                         }
3923                         if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3924                             // already marked dirty - we're done
3925                             break;
3926                         }
3927                     }
3928 
3929                     if (drawAnimation) {
3930                         if (view != null) {
3931                             view.mPrivateFlags |= DRAW_ANIMATION;
3932                         } else if (parent instanceof ViewRootImpl) {
3933                             ((ViewRootImpl) parent).mIsAnimating = true;
3934                         }
3935                     }
3936 
3937                     if (parent instanceof ViewRootImpl) {
3938                         ((ViewRootImpl) parent).invalidate();
3939                         parent = null;
3940                     } else if (view != null) {
3941                         if ((view.mPrivateFlags & DRAWN) == DRAWN ||
3942                                 (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
3943                             view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3944                             view.mPrivateFlags |= DIRTY;
3945                             parent = view.mParent;
3946                         } else {
3947                             parent = null;
3948                         }
3949                     }
3950                 } while (parent != null);
3951             } else {
3952                 // Check whether the child that requests the invalidate is fully opaque
3953                 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
3954                         child.getAnimation() == null;
3955                 // Mark the child as dirty, using the appropriate flag
3956                 // Make sure we do not set both flags at the same time
3957                 int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
3958 
3959                 if (child.mLayerType != LAYER_TYPE_NONE) {
3960                     mPrivateFlags |= INVALIDATED;
3961                     mPrivateFlags &= ~DRAWING_CACHE_VALID;
3962                     child.mLocalDirtyRect.union(dirty);
3963                 }
3964 
3965                 final int[] location = attachInfo.mInvalidateChildLocation;
3966                 location[CHILD_LEFT_INDEX] = child.mLeft;
3967                 location[CHILD_TOP_INDEX] = child.mTop;
3968                 Matrix childMatrix = child.getMatrix();
3969                 if (!childMatrix.isIdentity()) {
3970                     RectF boundingRect = attachInfo.mTmpTransformRect;
3971                     boundingRect.set(dirty);
3972                     //boundingRect.inset(-0.5f, -0.5f);
3973                     childMatrix.mapRect(boundingRect);
3974                     dirty.set((int) (boundingRect.left - 0.5f),
3975                             (int) (boundingRect.top - 0.5f),
3976                             (int) (boundingRect.right + 0.5f),
3977                             (int) (boundingRect.bottom + 0.5f));
3978                 }
3979 
3980                 do {
3981                     View view = null;
3982                     if (parent instanceof View) {
3983                         view = (View) parent;
3984                         if (view.mLayerType != LAYER_TYPE_NONE &&
3985                                 view.getParent() instanceof View) {
3986                             final View grandParent = (View) view.getParent();
3987                             grandParent.mPrivateFlags |= INVALIDATED;
3988                             grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3989                         }
3990                     }
3991 
3992                     if (drawAnimation) {
3993                         if (view != null) {
3994                             view.mPrivateFlags |= DRAW_ANIMATION;
3995                         } else if (parent instanceof ViewRootImpl) {
3996                             ((ViewRootImpl) parent).mIsAnimating = true;
3997                         }
3998                     }
3999 
4000                     // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4001                     // flag coming from the child that initiated the invalidate
4002                     if (view != null) {
4003                         if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
4004                                 view.getSolidColor() == 0) {
4005                             opaqueFlag = DIRTY;
4006                         }
4007                         if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
4008                             view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
4009                         }
4010                     }
4011 
4012                     parent = parent.invalidateChildInParent(location, dirty);
4013                     if (view != null) {
4014                         // Account for transform on current parent
4015                         Matrix m = view.getMatrix();
4016                         if (!m.isIdentity()) {
4017                             RectF boundingRect = attachInfo.mTmpTransformRect;
4018                             boundingRect.set(dirty);
4019                             m.mapRect(boundingRect);
4020                             dirty.set((int) boundingRect.left, (int) boundingRect.top,
4021                                     (int) (boundingRect.right + 0.5f),
4022                                     (int) (boundingRect.bottom + 0.5f));
4023                         }
4024                     }
4025                 } while (parent != null);
4026             }
4027         }
4028     }
4029 
4030     /**
4031      * Don't call or override this method. It is used for the implementation of
4032      * the view hierarchy.
4033      *
4034      * This implementation returns null if this ViewGroup does not have a parent,
4035      * if this ViewGroup is already fully invalidated or if the dirty rectangle
4036      * does not intersect with this ViewGroup's bounds.
4037      */
invalidateChildInParent(final int[] location, final Rect dirty)4038     public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
4039         if (ViewDebug.TRACE_HIERARCHY) {
4040             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
4041         }
4042 
4043         if ((mPrivateFlags & DRAWN) == DRAWN ||
4044                 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
4045             if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
4046                         FLAG_OPTIMIZE_INVALIDATE) {
4047                 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
4048                         location[CHILD_TOP_INDEX] - mScrollY);
4049 
4050                 final int left = mLeft;
4051                 final int top = mTop;
4052 
4053                 if ((mGroupFlags & FLAG_CLIP_CHILDREN) != FLAG_CLIP_CHILDREN ||
4054                         dirty.intersect(0, 0, mRight - left, mBottom - top) ||
4055                         (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
4056                     mPrivateFlags &= ~DRAWING_CACHE_VALID;
4057 
4058                     location[CHILD_LEFT_INDEX] = left;
4059                     location[CHILD_TOP_INDEX] = top;
4060 
4061                     if (mLayerType != LAYER_TYPE_NONE) {
4062                         mLocalDirtyRect.union(dirty);
4063                     }
4064 
4065                     return mParent;
4066                 }
4067             } else {
4068                 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
4069 
4070                 location[CHILD_LEFT_INDEX] = mLeft;
4071                 location[CHILD_TOP_INDEX] = mTop;
4072                 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4073                     dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
4074                 } else {
4075                     // in case the dirty rect extends outside the bounds of this container
4076                     dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4077                 }
4078 
4079                 if (mLayerType != LAYER_TYPE_NONE) {
4080                     mLocalDirtyRect.union(dirty);
4081                 }
4082 
4083                 return mParent;
4084             }
4085         }
4086 
4087         return null;
4088     }
4089 
4090     /**
4091      * Offset a rectangle that is in a descendant's coordinate
4092      * space into our coordinate space.
4093      * @param descendant A descendant of this view
4094      * @param rect A rectangle defined in descendant's coordinate space.
4095      */
offsetDescendantRectToMyCoords(View descendant, Rect rect)4096     public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4097         offsetRectBetweenParentAndChild(descendant, rect, true, false);
4098     }
4099 
4100     /**
4101      * Offset a rectangle that is in our coordinate space into an ancestor's
4102      * coordinate space.
4103      * @param descendant A descendant of this view
4104      * @param rect A rectangle defined in descendant's coordinate space.
4105      */
offsetRectIntoDescendantCoords(View descendant, Rect rect)4106     public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4107         offsetRectBetweenParentAndChild(descendant, rect, false, false);
4108     }
4109 
4110     /**
4111      * Helper method that offsets a rect either from parent to descendant or
4112      * descendant to parent.
4113      */
offsetRectBetweenParentAndChild(View descendant, Rect rect, boolean offsetFromChildToParent, boolean clipToBounds)4114     void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4115             boolean offsetFromChildToParent, boolean clipToBounds) {
4116 
4117         // already in the same coord system :)
4118         if (descendant == this) {
4119             return;
4120         }
4121 
4122         ViewParent theParent = descendant.mParent;
4123 
4124         // search and offset up to the parent
4125         while ((theParent != null)
4126                 && (theParent instanceof View)
4127                 && (theParent != this)) {
4128 
4129             if (offsetFromChildToParent) {
4130                 rect.offset(descendant.mLeft - descendant.mScrollX,
4131                         descendant.mTop - descendant.mScrollY);
4132                 if (clipToBounds) {
4133                     View p = (View) theParent;
4134                     rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4135                 }
4136             } else {
4137                 if (clipToBounds) {
4138                     View p = (View) theParent;
4139                     rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4140                 }
4141                 rect.offset(descendant.mScrollX - descendant.mLeft,
4142                         descendant.mScrollY - descendant.mTop);
4143             }
4144 
4145             descendant = (View) theParent;
4146             theParent = descendant.mParent;
4147         }
4148 
4149         // now that we are up to this view, need to offset one more time
4150         // to get into our coordinate space
4151         if (theParent == this) {
4152             if (offsetFromChildToParent) {
4153                 rect.offset(descendant.mLeft - descendant.mScrollX,
4154                         descendant.mTop - descendant.mScrollY);
4155             } else {
4156                 rect.offset(descendant.mScrollX - descendant.mLeft,
4157                         descendant.mScrollY - descendant.mTop);
4158             }
4159         } else {
4160             throw new IllegalArgumentException("parameter must be a descendant of this view");
4161         }
4162     }
4163 
4164     /**
4165      * Offset the vertical location of all children of this view by the specified number of pixels.
4166      *
4167      * @param offset the number of pixels to offset
4168      *
4169      * @hide
4170      */
offsetChildrenTopAndBottom(int offset)4171     public void offsetChildrenTopAndBottom(int offset) {
4172         final int count = mChildrenCount;
4173         final View[] children = mChildren;
4174 
4175         for (int i = 0; i < count; i++) {
4176             final View v = children[i];
4177             v.mTop += offset;
4178             v.mBottom += offset;
4179         }
4180     }
4181 
4182     /**
4183      * {@inheritDoc}
4184      */
getChildVisibleRect(View child, Rect r, android.graphics.Point offset)4185     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
4186         int dx = child.mLeft - mScrollX;
4187         int dy = child.mTop - mScrollY;
4188         if (offset != null) {
4189             offset.x += dx;
4190             offset.y += dy;
4191         }
4192         r.offset(dx, dy);
4193         return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
4194                (mParent == null || mParent.getChildVisibleRect(this, r, offset));
4195     }
4196 
4197     /**
4198      * {@inheritDoc}
4199      */
4200     @Override
layout(int l, int t, int r, int b)4201     public final void layout(int l, int t, int r, int b) {
4202         if (mTransition == null || !mTransition.isChangingLayout()) {
4203             super.layout(l, t, r, b);
4204         } else {
4205             // record the fact that we noop'd it; request layout when transition finishes
4206             mLayoutSuppressed = true;
4207         }
4208     }
4209 
4210     /**
4211      * {@inheritDoc}
4212      */
4213     @Override
onLayout(boolean changed, int l, int t, int r, int b)4214     protected abstract void onLayout(boolean changed,
4215             int l, int t, int r, int b);
4216 
4217     /**
4218      * Indicates whether the view group has the ability to animate its children
4219      * after the first layout.
4220      *
4221      * @return true if the children can be animated, false otherwise
4222      */
canAnimate()4223     protected boolean canAnimate() {
4224         return mLayoutAnimationController != null;
4225     }
4226 
4227     /**
4228      * Runs the layout animation. Calling this method triggers a relayout of
4229      * this view group.
4230      */
startLayoutAnimation()4231     public void startLayoutAnimation() {
4232         if (mLayoutAnimationController != null) {
4233             mGroupFlags |= FLAG_RUN_ANIMATION;
4234             requestLayout();
4235         }
4236     }
4237 
4238     /**
4239      * Schedules the layout animation to be played after the next layout pass
4240      * of this view group. This can be used to restart the layout animation
4241      * when the content of the view group changes or when the activity is
4242      * paused and resumed.
4243      */
scheduleLayoutAnimation()4244     public void scheduleLayoutAnimation() {
4245         mGroupFlags |= FLAG_RUN_ANIMATION;
4246     }
4247 
4248     /**
4249      * Sets the layout animation controller used to animate the group's
4250      * children after the first layout.
4251      *
4252      * @param controller the animation controller
4253      */
setLayoutAnimation(LayoutAnimationController controller)4254     public void setLayoutAnimation(LayoutAnimationController controller) {
4255         mLayoutAnimationController = controller;
4256         if (mLayoutAnimationController != null) {
4257             mGroupFlags |= FLAG_RUN_ANIMATION;
4258         }
4259     }
4260 
4261     /**
4262      * Returns the layout animation controller used to animate the group's
4263      * children.
4264      *
4265      * @return the current animation controller
4266      */
getLayoutAnimation()4267     public LayoutAnimationController getLayoutAnimation() {
4268         return mLayoutAnimationController;
4269     }
4270 
4271     /**
4272      * Indicates whether the children's drawing cache is used during a layout
4273      * animation. By default, the drawing cache is enabled but this will prevent
4274      * nested layout animations from working. To nest animations, you must disable
4275      * the cache.
4276      *
4277      * @return true if the animation cache is enabled, false otherwise
4278      *
4279      * @see #setAnimationCacheEnabled(boolean)
4280      * @see View#setDrawingCacheEnabled(boolean)
4281      */
4282     @ViewDebug.ExportedProperty
isAnimationCacheEnabled()4283     public boolean isAnimationCacheEnabled() {
4284         return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
4285     }
4286 
4287     /**
4288      * Enables or disables the children's drawing cache during a layout animation.
4289      * By default, the drawing cache is enabled but this will prevent nested
4290      * layout animations from working. To nest animations, you must disable the
4291      * cache.
4292      *
4293      * @param enabled true to enable the animation cache, false otherwise
4294      *
4295      * @see #isAnimationCacheEnabled()
4296      * @see View#setDrawingCacheEnabled(boolean)
4297      */
setAnimationCacheEnabled(boolean enabled)4298     public void setAnimationCacheEnabled(boolean enabled) {
4299         setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
4300     }
4301 
4302     /**
4303      * Indicates whether this ViewGroup will always try to draw its children using their
4304      * drawing cache. By default this property is enabled.
4305      *
4306      * @return true if the animation cache is enabled, false otherwise
4307      *
4308      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4309      * @see #setChildrenDrawnWithCacheEnabled(boolean)
4310      * @see View#setDrawingCacheEnabled(boolean)
4311      */
4312     @ViewDebug.ExportedProperty(category = "drawing")
isAlwaysDrawnWithCacheEnabled()4313     public boolean isAlwaysDrawnWithCacheEnabled() {
4314         return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
4315     }
4316 
4317     /**
4318      * Indicates whether this ViewGroup will always try to draw its children using their
4319      * drawing cache. This property can be set to true when the cache rendering is
4320      * slightly different from the children's normal rendering. Renderings can be different,
4321      * for instance, when the cache's quality is set to low.
4322      *
4323      * When this property is disabled, the ViewGroup will use the drawing cache of its
4324      * children only when asked to. It's usually the task of subclasses to tell ViewGroup
4325      * when to start using the drawing cache and when to stop using it.
4326      *
4327      * @param always true to always draw with the drawing cache, false otherwise
4328      *
4329      * @see #isAlwaysDrawnWithCacheEnabled()
4330      * @see #setChildrenDrawnWithCacheEnabled(boolean)
4331      * @see View#setDrawingCacheEnabled(boolean)
4332      * @see View#setDrawingCacheQuality(int)
4333      */
setAlwaysDrawnWithCacheEnabled(boolean always)4334     public void setAlwaysDrawnWithCacheEnabled(boolean always) {
4335         setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
4336     }
4337 
4338     /**
4339      * Indicates whether the ViewGroup is currently drawing its children using
4340      * their drawing cache.
4341      *
4342      * @return true if children should be drawn with their cache, false otherwise
4343      *
4344      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4345      * @see #setChildrenDrawnWithCacheEnabled(boolean)
4346      */
4347     @ViewDebug.ExportedProperty(category = "drawing")
isChildrenDrawnWithCacheEnabled()4348     protected boolean isChildrenDrawnWithCacheEnabled() {
4349         return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
4350     }
4351 
4352     /**
4353      * Tells the ViewGroup to draw its children using their drawing cache. This property
4354      * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
4355      * will be used only if it has been enabled.
4356      *
4357      * Subclasses should call this method to start and stop using the drawing cache when
4358      * they perform performance sensitive operations, like scrolling or animating.
4359      *
4360      * @param enabled true if children should be drawn with their cache, false otherwise
4361      *
4362      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4363      * @see #isChildrenDrawnWithCacheEnabled()
4364      */
setChildrenDrawnWithCacheEnabled(boolean enabled)4365     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
4366         setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
4367     }
4368 
4369     /**
4370      * Indicates whether the ViewGroup is drawing its children in the order defined by
4371      * {@link #getChildDrawingOrder(int, int)}.
4372      *
4373      * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
4374      *         false otherwise
4375      *
4376      * @see #setChildrenDrawingOrderEnabled(boolean)
4377      * @see #getChildDrawingOrder(int, int)
4378      */
4379     @ViewDebug.ExportedProperty(category = "drawing")
isChildrenDrawingOrderEnabled()4380     protected boolean isChildrenDrawingOrderEnabled() {
4381         return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
4382     }
4383 
4384     /**
4385      * Tells the ViewGroup whether to draw its children in the order defined by the method
4386      * {@link #getChildDrawingOrder(int, int)}.
4387      *
4388      * @param enabled true if the order of the children when drawing is determined by
4389      *        {@link #getChildDrawingOrder(int, int)}, false otherwise
4390      *
4391      * @see #isChildrenDrawingOrderEnabled()
4392      * @see #getChildDrawingOrder(int, int)
4393      */
setChildrenDrawingOrderEnabled(boolean enabled)4394     protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4395         setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4396     }
4397 
setBooleanFlag(int flag, boolean value)4398     private void setBooleanFlag(int flag, boolean value) {
4399         if (value) {
4400             mGroupFlags |= flag;
4401         } else {
4402             mGroupFlags &= ~flag;
4403         }
4404     }
4405 
4406     /**
4407      * Returns an integer indicating what types of drawing caches are kept in memory.
4408      *
4409      * @see #setPersistentDrawingCache(int)
4410      * @see #setAnimationCacheEnabled(boolean)
4411      *
4412      * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
4413      *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4414      *         and {@link #PERSISTENT_ALL_CACHES}
4415      */
4416     @ViewDebug.ExportedProperty(category = "drawing", mapping = {
4417         @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
4418         @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
4419         @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
4420         @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
4421     })
getPersistentDrawingCache()4422     public int getPersistentDrawingCache() {
4423         return mPersistentDrawingCache;
4424     }
4425 
4426     /**
4427      * Indicates what types of drawing caches should be kept in memory after
4428      * they have been created.
4429      *
4430      * @see #getPersistentDrawingCache()
4431      * @see #setAnimationCacheEnabled(boolean)
4432      *
4433      * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
4434      *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4435      *        and {@link #PERSISTENT_ALL_CACHES}
4436      */
setPersistentDrawingCache(int drawingCacheToKeep)4437     public void setPersistentDrawingCache(int drawingCacheToKeep) {
4438         mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4439     }
4440 
4441     /**
4442      * Returns a new set of layout parameters based on the supplied attributes set.
4443      *
4444      * @param attrs the attributes to build the layout parameters from
4445      *
4446      * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4447      *         of its descendants
4448      */
generateLayoutParams(AttributeSet attrs)4449     public LayoutParams generateLayoutParams(AttributeSet attrs) {
4450         return new LayoutParams(getContext(), attrs);
4451     }
4452 
4453     /**
4454      * Returns a safe set of layout parameters based on the supplied layout params.
4455      * When a ViewGroup is passed a View whose layout params do not pass the test of
4456      * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
4457      * is invoked. This method should return a new set of layout params suitable for
4458      * this ViewGroup, possibly by copying the appropriate attributes from the
4459      * specified set of layout params.
4460      *
4461      * @param p The layout parameters to convert into a suitable set of layout parameters
4462      *          for this ViewGroup.
4463      *
4464      * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4465      *         of its descendants
4466      */
generateLayoutParams(ViewGroup.LayoutParams p)4467     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4468         return p;
4469     }
4470 
4471     /**
4472      * Returns a set of default layout parameters. These parameters are requested
4473      * when the View passed to {@link #addView(View)} has no layout parameters
4474      * already set. If null is returned, an exception is thrown from addView.
4475      *
4476      * @return a set of default layout parameters or null
4477      */
generateDefaultLayoutParams()4478     protected LayoutParams generateDefaultLayoutParams() {
4479         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4480     }
4481 
4482     /**
4483      * @hide
4484      */
4485     @Override
dispatchConsistencyCheck(int consistency)4486     protected boolean dispatchConsistencyCheck(int consistency) {
4487         boolean result = super.dispatchConsistencyCheck(consistency);
4488 
4489         final int count = mChildrenCount;
4490         final View[] children = mChildren;
4491         for (int i = 0; i < count; i++) {
4492             if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
4493         }
4494 
4495         return result;
4496     }
4497 
4498     /**
4499      * @hide
4500      */
4501     @Override
onConsistencyCheck(int consistency)4502     protected boolean onConsistencyCheck(int consistency) {
4503         boolean result = super.onConsistencyCheck(consistency);
4504 
4505         final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
4506         final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
4507 
4508         if (checkLayout) {
4509             final int count = mChildrenCount;
4510             final View[] children = mChildren;
4511             for (int i = 0; i < count; i++) {
4512                 if (children[i].getParent() != this) {
4513                     result = false;
4514                     android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4515                             "View " + children[i] + " has no parent/a parent that is not " + this);
4516                 }
4517             }
4518         }
4519 
4520         if (checkDrawing) {
4521             // If this group is dirty, check that the parent is dirty as well
4522             if ((mPrivateFlags & DIRTY_MASK) != 0) {
4523                 final ViewParent parent = getParent();
4524                 if (parent != null && !(parent instanceof ViewRootImpl)) {
4525                     if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
4526                         result = false;
4527                         android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4528                                 "ViewGroup " + this + " is dirty but its parent is not: " + this);
4529                     }
4530                 }
4531             }
4532         }
4533 
4534         return result;
4535     }
4536 
4537     /**
4538      * {@inheritDoc}
4539      */
4540     @Override
debug(int depth)4541     protected void debug(int depth) {
4542         super.debug(depth);
4543         String output;
4544 
4545         if (mFocused != null) {
4546             output = debugIndent(depth);
4547             output += "mFocused";
4548             Log.d(VIEW_LOG_TAG, output);
4549         }
4550         if (mChildrenCount != 0) {
4551             output = debugIndent(depth);
4552             output += "{";
4553             Log.d(VIEW_LOG_TAG, output);
4554         }
4555         int count = mChildrenCount;
4556         for (int i = 0; i < count; i++) {
4557             View child = mChildren[i];
4558             child.debug(depth + 1);
4559         }
4560 
4561         if (mChildrenCount != 0) {
4562             output = debugIndent(depth);
4563             output += "}";
4564             Log.d(VIEW_LOG_TAG, output);
4565         }
4566     }
4567 
4568     /**
4569      * Returns the position in the group of the specified child view.
4570      *
4571      * @param child the view for which to get the position
4572      * @return a positive integer representing the position of the view in the
4573      *         group, or -1 if the view does not exist in the group
4574      */
indexOfChild(View child)4575     public int indexOfChild(View child) {
4576         final int count = mChildrenCount;
4577         final View[] children = mChildren;
4578         for (int i = 0; i < count; i++) {
4579             if (children[i] == child) {
4580                 return i;
4581             }
4582         }
4583         return -1;
4584     }
4585 
4586     /**
4587      * Returns the number of children in the group.
4588      *
4589      * @return a positive integer representing the number of children in
4590      *         the group
4591      */
getChildCount()4592     public int getChildCount() {
4593         return mChildrenCount;
4594     }
4595 
4596     /**
4597      * Returns the view at the specified position in the group.
4598      *
4599      * @param index the position at which to get the view from
4600      * @return the view at the specified position or null if the position
4601      *         does not exist within the group
4602      */
getChildAt(int index)4603     public View getChildAt(int index) {
4604         if (index < 0 || index >= mChildrenCount) {
4605             return null;
4606         }
4607         return mChildren[index];
4608     }
4609 
4610     /**
4611      * Ask all of the children of this view to measure themselves, taking into
4612      * account both the MeasureSpec requirements for this view and its padding.
4613      * We skip children that are in the GONE state The heavy lifting is done in
4614      * getChildMeasureSpec.
4615      *
4616      * @param widthMeasureSpec The width requirements for this view
4617      * @param heightMeasureSpec The height requirements for this view
4618      */
measureChildren(int widthMeasureSpec, int heightMeasureSpec)4619     protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
4620         final int size = mChildrenCount;
4621         final View[] children = mChildren;
4622         for (int i = 0; i < size; ++i) {
4623             final View child = children[i];
4624             if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
4625                 measureChild(child, widthMeasureSpec, heightMeasureSpec);
4626             }
4627         }
4628     }
4629 
4630     /**
4631      * Ask one of the children of this view to measure itself, taking into
4632      * account both the MeasureSpec requirements for this view and its padding.
4633      * The heavy lifting is done in getChildMeasureSpec.
4634      *
4635      * @param child The child to measure
4636      * @param parentWidthMeasureSpec The width requirements for this view
4637      * @param parentHeightMeasureSpec The height requirements for this view
4638      */
measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)4639     protected void measureChild(View child, int parentWidthMeasureSpec,
4640             int parentHeightMeasureSpec) {
4641         final LayoutParams lp = child.getLayoutParams();
4642 
4643         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4644                 mPaddingLeft + mPaddingRight, lp.width);
4645         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4646                 mPaddingTop + mPaddingBottom, lp.height);
4647 
4648         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4649     }
4650 
4651     /**
4652      * Ask one of the children of this view to measure itself, taking into
4653      * account both the MeasureSpec requirements for this view and its padding
4654      * and margins. The child must have MarginLayoutParams The heavy lifting is
4655      * done in getChildMeasureSpec.
4656      *
4657      * @param child The child to measure
4658      * @param parentWidthMeasureSpec The width requirements for this view
4659      * @param widthUsed Extra space that has been used up by the parent
4660      *        horizontally (possibly by other children of the parent)
4661      * @param parentHeightMeasureSpec The height requirements for this view
4662      * @param heightUsed Extra space that has been used up by the parent
4663      *        vertically (possibly by other children of the parent)
4664      */
measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)4665     protected void measureChildWithMargins(View child,
4666             int parentWidthMeasureSpec, int widthUsed,
4667             int parentHeightMeasureSpec, int heightUsed) {
4668         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
4669 
4670         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4671                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
4672                         + widthUsed, lp.width);
4673         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4674                 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
4675                         + heightUsed, lp.height);
4676 
4677         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4678     }
4679 
4680     /**
4681      * Does the hard part of measureChildren: figuring out the MeasureSpec to
4682      * pass to a particular child. This method figures out the right MeasureSpec
4683      * for one dimension (height or width) of one child view.
4684      *
4685      * The goal is to combine information from our MeasureSpec with the
4686      * LayoutParams of the child to get the best possible results. For example,
4687      * if the this view knows its size (because its MeasureSpec has a mode of
4688      * EXACTLY), and the child has indicated in its LayoutParams that it wants
4689      * to be the same size as the parent, the parent should ask the child to
4690      * layout given an exact size.
4691      *
4692      * @param spec The requirements for this view
4693      * @param padding The padding of this view for the current dimension and
4694      *        margins, if applicable
4695      * @param childDimension How big the child wants to be in the current
4696      *        dimension
4697      * @return a MeasureSpec integer for the child
4698      */
getChildMeasureSpec(int spec, int padding, int childDimension)4699     public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
4700         int specMode = MeasureSpec.getMode(spec);
4701         int specSize = MeasureSpec.getSize(spec);
4702 
4703         int size = Math.max(0, specSize - padding);
4704 
4705         int resultSize = 0;
4706         int resultMode = 0;
4707 
4708         switch (specMode) {
4709         // Parent has imposed an exact size on us
4710         case MeasureSpec.EXACTLY:
4711             if (childDimension >= 0) {
4712                 resultSize = childDimension;
4713                 resultMode = MeasureSpec.EXACTLY;
4714             } else if (childDimension == LayoutParams.MATCH_PARENT) {
4715                 // Child wants to be our size. So be it.
4716                 resultSize = size;
4717                 resultMode = MeasureSpec.EXACTLY;
4718             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4719                 // Child wants to determine its own size. It can't be
4720                 // bigger than us.
4721                 resultSize = size;
4722                 resultMode = MeasureSpec.AT_MOST;
4723             }
4724             break;
4725 
4726         // Parent has imposed a maximum size on us
4727         case MeasureSpec.AT_MOST:
4728             if (childDimension >= 0) {
4729                 // Child wants a specific size... so be it
4730                 resultSize = childDimension;
4731                 resultMode = MeasureSpec.EXACTLY;
4732             } else if (childDimension == LayoutParams.MATCH_PARENT) {
4733                 // Child wants to be our size, but our size is not fixed.
4734                 // Constrain child to not be bigger than us.
4735                 resultSize = size;
4736                 resultMode = MeasureSpec.AT_MOST;
4737             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4738                 // Child wants to determine its own size. It can't be
4739                 // bigger than us.
4740                 resultSize = size;
4741                 resultMode = MeasureSpec.AT_MOST;
4742             }
4743             break;
4744 
4745         // Parent asked to see how big we want to be
4746         case MeasureSpec.UNSPECIFIED:
4747             if (childDimension >= 0) {
4748                 // Child wants a specific size... let him have it
4749                 resultSize = childDimension;
4750                 resultMode = MeasureSpec.EXACTLY;
4751             } else if (childDimension == LayoutParams.MATCH_PARENT) {
4752                 // Child wants to be our size... find out how big it should
4753                 // be
4754                 resultSize = 0;
4755                 resultMode = MeasureSpec.UNSPECIFIED;
4756             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4757                 // Child wants to determine its own size.... find out how
4758                 // big it should be
4759                 resultSize = 0;
4760                 resultMode = MeasureSpec.UNSPECIFIED;
4761             }
4762             break;
4763         }
4764         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4765     }
4766 
4767 
4768     /**
4769      * Removes any pending animations for views that have been removed. Call
4770      * this if you don't want animations for exiting views to stack up.
4771      */
clearDisappearingChildren()4772     public void clearDisappearingChildren() {
4773         if (mDisappearingChildren != null) {
4774             mDisappearingChildren.clear();
4775         }
4776     }
4777 
4778     /**
4779      * Add a view which is removed from mChildren but still needs animation
4780      *
4781      * @param v View to add
4782      */
addDisappearingView(View v)4783     private void addDisappearingView(View v) {
4784         ArrayList<View> disappearingChildren = mDisappearingChildren;
4785 
4786         if (disappearingChildren == null) {
4787             disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4788         }
4789 
4790         disappearingChildren.add(v);
4791     }
4792 
4793     /**
4794      * Cleanup a view when its animation is done. This may mean removing it from
4795      * the list of disappearing views.
4796      *
4797      * @param view The view whose animation has finished
4798      * @param animation The animation, cannot be null
4799      */
finishAnimatingView(final View view, Animation animation)4800     private void finishAnimatingView(final View view, Animation animation) {
4801         final ArrayList<View> disappearingChildren = mDisappearingChildren;
4802         if (disappearingChildren != null) {
4803             if (disappearingChildren.contains(view)) {
4804                 disappearingChildren.remove(view);
4805 
4806                 if (view.mAttachInfo != null) {
4807                     view.dispatchDetachedFromWindow();
4808                 }
4809 
4810                 view.clearAnimation();
4811                 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4812             }
4813         }
4814 
4815         if (animation != null && !animation.getFillAfter()) {
4816             view.clearAnimation();
4817         }
4818 
4819         if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
4820             view.onAnimationEnd();
4821             // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4822             // so we'd rather be safe than sorry
4823             view.mPrivateFlags &= ~ANIMATION_STARTED;
4824             // Draw one more frame after the animation is done
4825             mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4826         }
4827     }
4828 
4829     /**
4830      * Utility function called by View during invalidation to determine whether a view that
4831      * is invisible or gone should still be invalidated because it is being transitioned (and
4832      * therefore still needs to be drawn).
4833      */
isViewTransitioning(View view)4834     boolean isViewTransitioning(View view) {
4835         return (mTransitioningViews != null && mTransitioningViews.contains(view));
4836     }
4837 
4838     /**
4839      * This method tells the ViewGroup that the given View object, which should have this
4840      * ViewGroup as its parent,
4841      * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
4842      * is removed from its parent. This allows animations, such as those used by
4843      * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4844      * the removal of views. A call to this method should always be accompanied by a later call
4845      * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4846      * so that the View finally gets removed.
4847      *
4848      * @param view The View object to be kept visible even if it gets removed from its parent.
4849      */
startViewTransition(View view)4850     public void startViewTransition(View view) {
4851         if (view.mParent == this) {
4852             if (mTransitioningViews == null) {
4853                 mTransitioningViews = new ArrayList<View>();
4854             }
4855             mTransitioningViews.add(view);
4856         }
4857     }
4858 
4859     /**
4860      * This method should always be called following an earlier call to
4861      * {@link #startViewTransition(View)}. The given View is finally removed from its parent
4862      * and will no longer be displayed. Note that this method does not perform the functionality
4863      * of removing a view from its parent; it just discontinues the display of a View that
4864      * has previously been removed.
4865      *
4866      * @return view The View object that has been removed but is being kept around in the visible
4867      * hierarchy by an earlier call to {@link #startViewTransition(View)}.
4868      */
endViewTransition(View view)4869     public void endViewTransition(View view) {
4870         if (mTransitioningViews != null) {
4871             mTransitioningViews.remove(view);
4872             final ArrayList<View> disappearingChildren = mDisappearingChildren;
4873             if (disappearingChildren != null && disappearingChildren.contains(view)) {
4874                 disappearingChildren.remove(view);
4875                 if (mVisibilityChangingChildren != null &&
4876                         mVisibilityChangingChildren.contains(view)) {
4877                     mVisibilityChangingChildren.remove(view);
4878                 } else {
4879                     if (view.mAttachInfo != null) {
4880                         view.dispatchDetachedFromWindow();
4881                     }
4882                     if (view.mParent != null) {
4883                         view.mParent = null;
4884                     }
4885                 }
4886                 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4887             }
4888         }
4889     }
4890 
4891     private LayoutTransition.TransitionListener mLayoutTransitionListener =
4892             new LayoutTransition.TransitionListener() {
4893         @Override
4894         public void startTransition(LayoutTransition transition, ViewGroup container,
4895                 View view, int transitionType) {
4896             // We only care about disappearing items, since we need special logic to keep
4897             // those items visible after they've been 'removed'
4898             if (transitionType == LayoutTransition.DISAPPEARING) {
4899                 startViewTransition(view);
4900             }
4901         }
4902 
4903         @Override
4904         public void endTransition(LayoutTransition transition, ViewGroup container,
4905                 View view, int transitionType) {
4906             if (mLayoutSuppressed && !transition.isChangingLayout()) {
4907                 requestLayout();
4908                 mLayoutSuppressed = false;
4909             }
4910             if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
4911                 endViewTransition(view);
4912             }
4913         }
4914     };
4915 
4916     /**
4917      * {@inheritDoc}
4918      */
4919     @Override
gatherTransparentRegion(Region region)4920     public boolean gatherTransparentRegion(Region region) {
4921         // If no transparent regions requested, we are always opaque.
4922         final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
4923         if (meOpaque && region == null) {
4924             // The caller doesn't care about the region, so stop now.
4925             return true;
4926         }
4927         super.gatherTransparentRegion(region);
4928         final View[] children = mChildren;
4929         final int count = mChildrenCount;
4930         boolean noneOfTheChildrenAreTransparent = true;
4931         for (int i = 0; i < count; i++) {
4932             final View child = children[i];
4933             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
4934                 if (!child.gatherTransparentRegion(region)) {
4935                     noneOfTheChildrenAreTransparent = false;
4936                 }
4937             }
4938         }
4939         return meOpaque || noneOfTheChildrenAreTransparent;
4940     }
4941 
4942     /**
4943      * {@inheritDoc}
4944      */
requestTransparentRegion(View child)4945     public void requestTransparentRegion(View child) {
4946         if (child != null) {
4947             child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
4948             if (mParent != null) {
4949                 mParent.requestTransparentRegion(this);
4950             }
4951         }
4952     }
4953 
4954 
4955     @Override
fitSystemWindows(Rect insets)4956     protected boolean fitSystemWindows(Rect insets) {
4957         boolean done = super.fitSystemWindows(insets);
4958         if (!done) {
4959             final int count = mChildrenCount;
4960             final View[] children = mChildren;
4961             for (int i = 0; i < count; i++) {
4962                 done = children[i].fitSystemWindows(insets);
4963                 if (done) {
4964                     break;
4965                 }
4966             }
4967         }
4968         return done;
4969     }
4970 
4971     /**
4972      * Returns the animation listener to which layout animation events are
4973      * sent.
4974      *
4975      * @return an {@link android.view.animation.Animation.AnimationListener}
4976      */
getLayoutAnimationListener()4977     public Animation.AnimationListener getLayoutAnimationListener() {
4978         return mAnimationListener;
4979     }
4980 
4981     @Override
drawableStateChanged()4982     protected void drawableStateChanged() {
4983         super.drawableStateChanged();
4984 
4985         if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
4986             if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4987                 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
4988                         + " child has duplicateParentState set to true");
4989             }
4990 
4991             final View[] children = mChildren;
4992             final int count = mChildrenCount;
4993 
4994             for (int i = 0; i < count; i++) {
4995                 final View child = children[i];
4996                 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
4997                     child.refreshDrawableState();
4998                 }
4999             }
5000         }
5001     }
5002 
5003     @Override
jumpDrawablesToCurrentState()5004     public void jumpDrawablesToCurrentState() {
5005         super.jumpDrawablesToCurrentState();
5006         final View[] children = mChildren;
5007         final int count = mChildrenCount;
5008         for (int i = 0; i < count; i++) {
5009             children[i].jumpDrawablesToCurrentState();
5010         }
5011     }
5012 
5013     @Override
onCreateDrawableState(int extraSpace)5014     protected int[] onCreateDrawableState(int extraSpace) {
5015         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
5016             return super.onCreateDrawableState(extraSpace);
5017         }
5018 
5019         int need = 0;
5020         int n = getChildCount();
5021         for (int i = 0; i < n; i++) {
5022             int[] childState = getChildAt(i).getDrawableState();
5023 
5024             if (childState != null) {
5025                 need += childState.length;
5026             }
5027         }
5028 
5029         int[] state = super.onCreateDrawableState(extraSpace + need);
5030 
5031         for (int i = 0; i < n; i++) {
5032             int[] childState = getChildAt(i).getDrawableState();
5033 
5034             if (childState != null) {
5035                 state = mergeDrawableStates(state, childState);
5036             }
5037         }
5038 
5039         return state;
5040     }
5041 
5042     /**
5043      * Sets whether this ViewGroup's drawable states also include
5044      * its children's drawable states.  This is used, for example, to
5045      * make a group appear to be focused when its child EditText or button
5046      * is focused.
5047      */
setAddStatesFromChildren(boolean addsStates)5048     public void setAddStatesFromChildren(boolean addsStates) {
5049         if (addsStates) {
5050             mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
5051         } else {
5052             mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
5053         }
5054 
5055         refreshDrawableState();
5056     }
5057 
5058     /**
5059      * Returns whether this ViewGroup's drawable states also include
5060      * its children's drawable states.  This is used, for example, to
5061      * make a group appear to be focused when its child EditText or button
5062      * is focused.
5063      */
addStatesFromChildren()5064     public boolean addStatesFromChildren() {
5065         return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
5066     }
5067 
5068     /**
5069      * If {link #addStatesFromChildren} is true, refreshes this group's
5070      * drawable state (to include the states from its children).
5071      */
childDrawableStateChanged(View child)5072     public void childDrawableStateChanged(View child) {
5073         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5074             refreshDrawableState();
5075         }
5076     }
5077 
5078     /**
5079      * Specifies the animation listener to which layout animation events must
5080      * be sent. Only
5081      * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
5082      * and
5083      * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
5084      * are invoked.
5085      *
5086      * @param animationListener the layout animation listener
5087      */
setLayoutAnimationListener(Animation.AnimationListener animationListener)5088     public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
5089         mAnimationListener = animationListener;
5090     }
5091 
5092     /**
5093      * This method is called by LayoutTransition when there are 'changing' animations that need
5094      * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
5095      * starts all pending transitions prior to the drawing phase in the current traversal.
5096      *
5097      * @param transition The LayoutTransition to be started on the next traversal.
5098      *
5099      * @hide
5100      */
requestTransitionStart(LayoutTransition transition)5101     public void requestTransitionStart(LayoutTransition transition) {
5102         ViewRootImpl viewAncestor = getViewRootImpl();
5103         if (viewAncestor != null) {
5104             viewAncestor.requestTransitionStart(transition);
5105         }
5106     }
5107 
5108     @Override
resetResolvedLayoutDirection()5109     protected void resetResolvedLayoutDirection() {
5110         super.resetResolvedLayoutDirection();
5111 
5112         // Take care of resetting the children resolution too
5113         final int count = getChildCount();
5114         for (int i = 0; i < count; i++) {
5115             final View child = getChildAt(i);
5116             if (child.getLayoutDirection() == LAYOUT_DIRECTION_INHERIT) {
5117                 child.resetResolvedLayoutDirection();
5118             }
5119         }
5120     }
5121 
5122     @Override
resetResolvedTextDirection()5123     protected void resetResolvedTextDirection() {
5124         super.resetResolvedTextDirection();
5125 
5126         // Take care of resetting the children resolution too
5127         final int count = getChildCount();
5128         for (int i = 0; i < count; i++) {
5129             final View child = getChildAt(i);
5130             if (child.getTextDirection() == TEXT_DIRECTION_INHERIT) {
5131                 child.resetResolvedTextDirection();
5132             }
5133         }
5134     }
5135 
5136     /**
5137      * Return true if the pressed state should be delayed for children or descendants of this
5138      * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
5139      * This prevents the pressed state from appearing when the user is actually trying to scroll
5140      * the content.
5141      *
5142      * The default implementation returns true for compatibility reasons. Subclasses that do
5143      * not scroll should generally override this method and return false.
5144      */
shouldDelayChildPressedState()5145     public boolean shouldDelayChildPressedState() {
5146         return true;
5147     }
5148 
5149     /**
5150      * LayoutParams are used by views to tell their parents how they want to be
5151      * laid out. See
5152      * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
5153      * for a list of all child view attributes that this class supports.
5154      *
5155      * <p>
5156      * The base LayoutParams class just describes how big the view wants to be
5157      * for both width and height. For each dimension, it can specify one of:
5158      * <ul>
5159      * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
5160      * means that the view wants to be as big as its parent (minus padding)
5161      * <li> WRAP_CONTENT, which means that the view wants to be just big enough
5162      * to enclose its content (plus padding)
5163      * <li> an exact number
5164      * </ul>
5165      * There are subclasses of LayoutParams for different subclasses of
5166      * ViewGroup. For example, AbsoluteLayout has its own subclass of
5167      * LayoutParams which adds an X and Y value.</p>
5168      *
5169      * <div class="special reference">
5170      * <h3>Developer Guides</h3>
5171      * <p>For more information about creating user interface layouts, read the
5172      * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
5173      * guide.</p></div>
5174      *
5175      * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
5176      * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
5177      */
5178     public static class LayoutParams {
5179         /**
5180          * Special value for the height or width requested by a View.
5181          * FILL_PARENT means that the view wants to be as big as its parent,
5182          * minus the parent's padding, if any. This value is deprecated
5183          * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
5184          */
5185         @SuppressWarnings({"UnusedDeclaration"})
5186         @Deprecated
5187         public static final int FILL_PARENT = -1;
5188 
5189         /**
5190          * Special value for the height or width requested by a View.
5191          * MATCH_PARENT means that the view wants to be as big as its parent,
5192          * minus the parent's padding, if any. Introduced in API Level 8.
5193          */
5194         public static final int MATCH_PARENT = -1;
5195 
5196         /**
5197          * Special value for the height or width requested by a View.
5198          * WRAP_CONTENT means that the view wants to be just large enough to fit
5199          * its own internal content, taking its own padding into account.
5200          */
5201         public static final int WRAP_CONTENT = -2;
5202 
5203         /**
5204          * Information about how wide the view wants to be. Can be one of the
5205          * constants FILL_PARENT (replaced by MATCH_PARENT ,
5206          * in API Level 8) or WRAP_CONTENT. or an exact size.
5207          */
5208         @ViewDebug.ExportedProperty(category = "layout", mapping = {
5209             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
5210             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5211         })
5212         public int width;
5213 
5214         /**
5215          * Information about how tall the view wants to be. Can be one of the
5216          * constants FILL_PARENT (replaced by MATCH_PARENT ,
5217          * in API Level 8) or WRAP_CONTENT. or an exact size.
5218          */
5219         @ViewDebug.ExportedProperty(category = "layout", mapping = {
5220             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
5221             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5222         })
5223         public int height;
5224 
5225         /**
5226          * Used to animate layouts.
5227          */
5228         public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
5229 
5230         /**
5231          * Creates a new set of layout parameters. The values are extracted from
5232          * the supplied attributes set and context. The XML attributes mapped
5233          * to this set of layout parameters are:
5234          *
5235          * <ul>
5236          *   <li><code>layout_width</code>: the width, either an exact value,
5237          *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5238          *   {@link #MATCH_PARENT} in API Level 8)</li>
5239          *   <li><code>layout_height</code>: the height, either an exact value,
5240          *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5241          *   {@link #MATCH_PARENT} in API Level 8)</li>
5242          * </ul>
5243          *
5244          * @param c the application environment
5245          * @param attrs the set of attributes from which to extract the layout
5246          *              parameters' values
5247          */
LayoutParams(Context c, AttributeSet attrs)5248         public LayoutParams(Context c, AttributeSet attrs) {
5249             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
5250             setBaseAttributes(a,
5251                     R.styleable.ViewGroup_Layout_layout_width,
5252                     R.styleable.ViewGroup_Layout_layout_height);
5253             a.recycle();
5254         }
5255 
5256         /**
5257          * Creates a new set of layout parameters with the specified width
5258          * and height.
5259          *
5260          * @param width the width, either {@link #WRAP_CONTENT},
5261          *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5262          *        API Level 8), or a fixed size in pixels
5263          * @param height the height, either {@link #WRAP_CONTENT},
5264          *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5265          *        API Level 8), or a fixed size in pixels
5266          */
LayoutParams(int width, int height)5267         public LayoutParams(int width, int height) {
5268             this.width = width;
5269             this.height = height;
5270         }
5271 
5272         /**
5273          * Copy constructor. Clones the width and height values of the source.
5274          *
5275          * @param source The layout params to copy from.
5276          */
LayoutParams(LayoutParams source)5277         public LayoutParams(LayoutParams source) {
5278             this.width = source.width;
5279             this.height = source.height;
5280         }
5281 
5282         /**
5283          * Used internally by MarginLayoutParams.
5284          * @hide
5285          */
LayoutParams()5286         LayoutParams() {
5287         }
5288 
5289         /**
5290          * Extracts the layout parameters from the supplied attributes.
5291          *
5292          * @param a the style attributes to extract the parameters from
5293          * @param widthAttr the identifier of the width attribute
5294          * @param heightAttr the identifier of the height attribute
5295          */
setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)5296         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
5297             width = a.getLayoutDimension(widthAttr, "layout_width");
5298             height = a.getLayoutDimension(heightAttr, "layout_height");
5299         }
5300 
5301         /**
5302          * Resolve layout parameters depending on the layout direction. Subclasses that care about
5303          * layoutDirection changes should override this method. The default implementation does
5304          * nothing.
5305          *
5306          * @param layoutDirection the direction of the layout
5307          *
5308          * {@link View#LAYOUT_DIRECTION_LTR}
5309          * {@link View#LAYOUT_DIRECTION_RTL}
5310          *
5311          * @hide
5312          */
resolveWithDirection(int layoutDirection)5313         protected void resolveWithDirection(int layoutDirection) {
5314         }
5315 
5316         /**
5317          * Returns a String representation of this set of layout parameters.
5318          *
5319          * @param output the String to prepend to the internal representation
5320          * @return a String with the following format: output +
5321          *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
5322          *
5323          * @hide
5324          */
debug(String output)5325         public String debug(String output) {
5326             return output + "ViewGroup.LayoutParams={ width="
5327                     + sizeToString(width) + ", height=" + sizeToString(height) + " }";
5328         }
5329 
5330         /**
5331          * Converts the specified size to a readable String.
5332          *
5333          * @param size the size to convert
5334          * @return a String instance representing the supplied size
5335          *
5336          * @hide
5337          */
sizeToString(int size)5338         protected static String sizeToString(int size) {
5339             if (size == WRAP_CONTENT) {
5340                 return "wrap-content";
5341             }
5342             if (size == MATCH_PARENT) {
5343                 return "match-parent";
5344             }
5345             return String.valueOf(size);
5346         }
5347     }
5348 
5349     /**
5350      * Per-child layout information for layouts that support margins.
5351      * See
5352      * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
5353      * for a list of all child view attributes that this class supports.
5354      */
5355     public static class MarginLayoutParams extends ViewGroup.LayoutParams {
5356         /**
5357          * The left margin in pixels of the child. Whenever this value is changed, a call to
5358          * {@link android.view.View#requestLayout()} needs to be done.
5359          */
5360         @ViewDebug.ExportedProperty(category = "layout")
5361         public int leftMargin;
5362 
5363         /**
5364          * The top margin in pixels of the child. Whenever this value is changed, a call to
5365          * {@link android.view.View#requestLayout()} needs to be done.
5366          */
5367         @ViewDebug.ExportedProperty(category = "layout")
5368         public int topMargin;
5369 
5370         /**
5371          * The right margin in pixels of the child. Whenever this value is changed, a call to
5372          * {@link android.view.View#requestLayout()} needs to be done.
5373          */
5374         @ViewDebug.ExportedProperty(category = "layout")
5375         public int rightMargin;
5376 
5377         /**
5378          * The bottom margin in pixels of the child. Whenever this value is changed, a call to
5379          * {@link android.view.View#requestLayout()} needs to be done.
5380          */
5381         @ViewDebug.ExportedProperty(category = "layout")
5382         public int bottomMargin;
5383 
5384         /**
5385          * The start margin in pixels of the child.
5386          *
5387          * @hide
5388          *
5389          */
5390         @ViewDebug.ExportedProperty(category = "layout")
5391         protected int startMargin = DEFAULT_RELATIVE;
5392 
5393         /**
5394          * The end margin in pixels of the child.
5395          *
5396          * @hide
5397          */
5398         @ViewDebug.ExportedProperty(category = "layout")
5399         protected int endMargin = DEFAULT_RELATIVE;
5400 
5401         /**
5402          * The default start and end margin.
5403          */
5404         static private final int DEFAULT_RELATIVE = Integer.MIN_VALUE;
5405 
5406         /**
5407          * Creates a new set of layout parameters. The values are extracted from
5408          * the supplied attributes set and context.
5409          *
5410          * @param c the application environment
5411          * @param attrs the set of attributes from which to extract the layout
5412          *              parameters' values
5413          */
MarginLayoutParams(Context c, AttributeSet attrs)5414         public MarginLayoutParams(Context c, AttributeSet attrs) {
5415             super();
5416 
5417             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
5418             setBaseAttributes(a,
5419                     R.styleable.ViewGroup_MarginLayout_layout_width,
5420                     R.styleable.ViewGroup_MarginLayout_layout_height);
5421 
5422             int margin = a.getDimensionPixelSize(
5423                     com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
5424             if (margin >= 0) {
5425                 leftMargin = margin;
5426                 topMargin = margin;
5427                 rightMargin= margin;
5428                 bottomMargin = margin;
5429             } else {
5430                 leftMargin = a.getDimensionPixelSize(
5431                         R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
5432                 topMargin = a.getDimensionPixelSize(
5433                         R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
5434                 rightMargin = a.getDimensionPixelSize(
5435                         R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
5436                 bottomMargin = a.getDimensionPixelSize(
5437                         R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
5438                 startMargin = a.getDimensionPixelSize(
5439                         R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE);
5440                 endMargin = a.getDimensionPixelSize(
5441                         R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE);
5442             }
5443 
5444             a.recycle();
5445         }
5446 
5447         /**
5448          * {@inheritDoc}
5449          */
MarginLayoutParams(int width, int height)5450         public MarginLayoutParams(int width, int height) {
5451             super(width, height);
5452         }
5453 
5454         /**
5455          * Copy constructor. Clones the width, height and margin values of the source.
5456          *
5457          * @param source The layout params to copy from.
5458          */
MarginLayoutParams(MarginLayoutParams source)5459         public MarginLayoutParams(MarginLayoutParams source) {
5460             this.width = source.width;
5461             this.height = source.height;
5462 
5463             this.leftMargin = source.leftMargin;
5464             this.topMargin = source.topMargin;
5465             this.rightMargin = source.rightMargin;
5466             this.bottomMargin = source.bottomMargin;
5467             this.startMargin = source.startMargin;
5468             this.endMargin = source.endMargin;
5469         }
5470 
5471         /**
5472          * {@inheritDoc}
5473          */
MarginLayoutParams(LayoutParams source)5474         public MarginLayoutParams(LayoutParams source) {
5475             super(source);
5476         }
5477 
5478         /**
5479          * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
5480          * to be done so that the new margins are taken into account. Left and right margins may be
5481          * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
5482          *
5483          * @param left the left margin size
5484          * @param top the top margin size
5485          * @param right the right margin size
5486          * @param bottom the bottom margin size
5487          *
5488          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
5489          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5490          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
5491          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5492          */
setMargins(int left, int top, int right, int bottom)5493         public void setMargins(int left, int top, int right, int bottom) {
5494             leftMargin = left;
5495             topMargin = top;
5496             rightMargin = right;
5497             bottomMargin = bottom;
5498         }
5499 
5500         /**
5501          * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
5502          * needs to be done so that the new relative margins are taken into account. Left and right
5503          * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
5504          * direction.
5505          *
5506          * @param start the start margin size
5507          * @param top the top margin size
5508          * @param end the right margin size
5509          * @param bottom the bottom margin size
5510          *
5511          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5512          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5513          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5514          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5515          *
5516          * @hide
5517          */
setMarginsRelative(int start, int top, int end, int bottom)5518         public void setMarginsRelative(int start, int top, int end, int bottom) {
5519             startMargin = start;
5520             topMargin = top;
5521             endMargin = end;
5522             bottomMargin = bottom;
5523         }
5524 
5525         /**
5526          * Returns the start margin in pixels.
5527          *
5528          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5529          *
5530          * @return the start margin in pixels.
5531          *
5532          * @hide
5533          */
getMarginStart()5534         public int getMarginStart() {
5535             return startMargin;
5536         }
5537 
5538         /**
5539          * Returns the end margin in pixels.
5540          *
5541          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5542          *
5543          * @return the end margin in pixels.
5544          *
5545          * @hide
5546          */
getMarginEnd()5547         public int getMarginEnd() {
5548             return endMargin;
5549         }
5550 
5551         /**
5552          * Check if margins are relative.
5553          *
5554          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5555          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5556          *
5557          * @return true if either marginStart or marginEnd has been set
5558          *
5559          * @hide
5560          */
isMarginRelative()5561         public boolean isMarginRelative() {
5562             return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
5563         }
5564 
5565         /**
5566          * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
5567          * maybe overriden depending on layout direction.
5568          *
5569          * @hide
5570          */
5571         @Override
resolveWithDirection(int layoutDirection)5572         protected void resolveWithDirection(int layoutDirection) {
5573             switch(layoutDirection) {
5574                 case View.LAYOUT_DIRECTION_RTL:
5575                     leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : leftMargin;
5576                     rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : rightMargin;
5577                     break;
5578                 case View.LAYOUT_DIRECTION_LTR:
5579                 default:
5580                     leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : leftMargin;
5581                     rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : rightMargin;
5582                     break;
5583             }
5584         }
5585     }
5586 
5587     /* Describes a touched view and the ids of the pointers that it has captured.
5588      *
5589      * This code assumes that pointer ids are always in the range 0..31 such that
5590      * it can use a bitfield to track which pointer ids are present.
5591      * As it happens, the lower layers of the input dispatch pipeline also use the
5592      * same trick so the assumption should be safe here...
5593      */
5594     private static final class TouchTarget {
5595         private static final int MAX_RECYCLED = 32;
5596         private static final Object sRecycleLock = new Object();
5597         private static TouchTarget sRecycleBin;
5598         private static int sRecycledCount;
5599 
5600         public static final int ALL_POINTER_IDS = -1; // all ones
5601 
5602         // The touched child view.
5603         public View child;
5604 
5605         // The combined bit mask of pointer ids for all pointers captured by the target.
5606         public int pointerIdBits;
5607 
5608         // The next target in the target list.
5609         public TouchTarget next;
5610 
TouchTarget()5611         private TouchTarget() {
5612         }
5613 
obtain(View child, int pointerIdBits)5614         public static TouchTarget obtain(View child, int pointerIdBits) {
5615             final TouchTarget target;
5616             synchronized (sRecycleLock) {
5617                 if (sRecycleBin == null) {
5618                     target = new TouchTarget();
5619                 } else {
5620                     target = sRecycleBin;
5621                     sRecycleBin = target.next;
5622                      sRecycledCount--;
5623                     target.next = null;
5624                 }
5625             }
5626             target.child = child;
5627             target.pointerIdBits = pointerIdBits;
5628             return target;
5629         }
5630 
recycle()5631         public void recycle() {
5632             synchronized (sRecycleLock) {
5633                 if (sRecycledCount < MAX_RECYCLED) {
5634                     next = sRecycleBin;
5635                     sRecycleBin = this;
5636                     sRecycledCount += 1;
5637                 } else {
5638                     next = null;
5639                 }
5640                 child = null;
5641             }
5642         }
5643     }
5644 
5645     /* Describes a hovered view. */
5646     private static final class HoverTarget {
5647         private static final int MAX_RECYCLED = 32;
5648         private static final Object sRecycleLock = new Object();
5649         private static HoverTarget sRecycleBin;
5650         private static int sRecycledCount;
5651 
5652         // The hovered child view.
5653         public View child;
5654 
5655         // The next target in the target list.
5656         public HoverTarget next;
5657 
HoverTarget()5658         private HoverTarget() {
5659         }
5660 
obtain(View child)5661         public static HoverTarget obtain(View child) {
5662             final HoverTarget target;
5663             synchronized (sRecycleLock) {
5664                 if (sRecycleBin == null) {
5665                     target = new HoverTarget();
5666                 } else {
5667                     target = sRecycleBin;
5668                     sRecycleBin = target.next;
5669                      sRecycledCount--;
5670                     target.next = null;
5671                 }
5672             }
5673             target.child = child;
5674             return target;
5675         }
5676 
recycle()5677         public void recycle() {
5678             synchronized (sRecycleLock) {
5679                 if (sRecycledCount < MAX_RECYCLED) {
5680                     next = sRecycleBin;
5681                     sRecycleBin = this;
5682                     sRecycledCount += 1;
5683                 } else {
5684                     next = null;
5685                 }
5686                 child = null;
5687             }
5688         }
5689     }
5690 }
5691