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