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