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