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