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