ViewGroup.java revision d6a463a9f23b3901bf729f2f27a6bb8f78b95248
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 com.android.internal.R;
20
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
25import android.graphics.Paint;
26import android.graphics.Rect;
27import android.graphics.RectF;
28import android.graphics.Region;
29import android.os.Parcelable;
30import android.os.SystemClock;
31import android.util.AttributeSet;
32import android.util.Config;
33import android.util.EventLog;
34import android.util.Log;
35import android.util.SparseArray;
36import android.view.accessibility.AccessibilityEvent;
37import android.view.animation.Animation;
38import android.view.animation.AnimationUtils;
39import android.view.animation.LayoutAnimationController;
40import android.view.animation.Transformation;
41
42import java.util.ArrayList;
43
44/**
45 * <p>
46 * A <code>ViewGroup</code> is a special view that can contain other views
47 * (called children.) The view group is the base class for layouts and views
48 * containers. This class also defines the
49 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
50 * class for layouts parameters.
51 * </p>
52 *
53 * <p>
54 * Also see {@link LayoutParams} for layout attributes.
55 * </p>
56 *
57 * @attr ref android.R.styleable#ViewGroup_clipChildren
58 * @attr ref android.R.styleable#ViewGroup_clipToPadding
59 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
60 * @attr ref android.R.styleable#ViewGroup_animationCache
61 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
62 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
63 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
64 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
65 */
66public abstract class ViewGroup extends View implements ViewParent, ViewManager {
67    private static final boolean DBG = false;
68
69    /**
70     * Views which have been hidden or removed which need to be animated on
71     * their way out.
72     * This field should be made private, so it is hidden from the SDK.
73     * {@hide}
74     */
75    protected ArrayList<View> mDisappearingChildren;
76
77    /**
78     * Listener used to propagate events indicating when children are added
79     * and/or removed from a view group.
80     * This field should be made private, so it is hidden from the SDK.
81     * {@hide}
82     */
83    protected OnHierarchyChangeListener mOnHierarchyChangeListener;
84
85    // The view contained within this ViewGroup that has or contains focus.
86    private View mFocused;
87
88    // The current transformation to apply on the child being drawn
89    private Transformation mChildTransformation;
90    private RectF mInvalidateRegion;
91
92    // Target of Motion events
93    private View mMotionTarget;
94    private final Rect mTempRect = new Rect();
95
96    // Layout animation
97    private LayoutAnimationController mLayoutAnimationController;
98    private Animation.AnimationListener mAnimationListener;
99
100    /**
101     * Internal flags.
102     *
103     * This field should be made private, so it is hidden from the SDK.
104     * {@hide}
105     */
106    protected int mGroupFlags;
107
108    // When set, ViewGroup invalidates only the child's rectangle
109    // Set by default
110    private static final int FLAG_CLIP_CHILDREN = 0x1;
111
112    // When set, ViewGroup excludes the padding area from the invalidate rectangle
113    // Set by default
114    private static final int FLAG_CLIP_TO_PADDING = 0x2;
115
116    // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
117    // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
118    private static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
119
120    // When set, dispatchDraw() will run the layout animation and unset the flag
121    private static final int FLAG_RUN_ANIMATION = 0x8;
122
123    // When set, there is either no layout animation on the ViewGroup or the layout
124    // animation is over
125    // Set by default
126    private static final int FLAG_ANIMATION_DONE = 0x10;
127
128    // If set, this ViewGroup has padding; if unset there is no padding and we don't need
129    // to clip it, even if FLAG_CLIP_TO_PADDING is set
130    private static final int FLAG_PADDING_NOT_NULL = 0x20;
131
132    // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
133    // Set by default
134    private static final int FLAG_ANIMATION_CACHE = 0x40;
135
136    // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
137    // layout animation; this avoid clobbering the hierarchy
138    // Automatically set when the layout animation starts, depending on the animation's
139    // characteristics
140    private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
141
142    // When set, the next call to drawChild() will clear mChildTransformation's matrix
143    private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
144
145    // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
146    // the children's Bitmap caches if necessary
147    // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
148    private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
149
150    /**
151     * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
152     * to get the index of the child to draw for that iteration.
153     */
154    protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
155
156    /**
157     * When set, this ViewGroup supports static transformations on children; this causes
158     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
159     * invoked when a child is drawn.
160     *
161     * Any subclass overriding
162     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
163     * set this flags in {@link #mGroupFlags}.
164     *
165     * {@hide}
166     */
167    protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
168
169    // When the previous drawChild() invocation used an alpha value that was lower than
170    // 1.0 and set it in mCachePaint
171    private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
172
173    /**
174     * When set, this ViewGroup's drawable states also include those
175     * of its children.
176     */
177    private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
178
179    /**
180     * When set, this ViewGroup tries to always draw its children using their drawing cache.
181     */
182    private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
183
184    /**
185     * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
186     * draw its children with their drawing cache.
187     */
188    private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
189
190    /**
191     * When set, this group will go through its list of children to notify them of
192     * any drawable state change.
193     */
194    private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
195
196    private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
197
198    /**
199     * This view will get focus before any of its descendants.
200     */
201    public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
202
203    /**
204     * This view will get focus only if none of its descendants want it.
205     */
206    public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
207
208    /**
209     * This view will block any of its descendants from getting focus, even
210     * if they are focusable.
211     */
212    public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
213
214    /**
215     * Used to map between enum in attrubutes and flag values.
216     */
217    private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
218            {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
219                    FOCUS_BLOCK_DESCENDANTS};
220
221    /**
222     * When set, this ViewGroup should not intercept touch events.
223     */
224    private static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
225
226    /**
227     * Indicates which types of drawing caches are to be kept in memory.
228     * This field should be made private, so it is hidden from the SDK.
229     * {@hide}
230     */
231    protected int mPersistentDrawingCache;
232
233    /**
234     * Used to indicate that no drawing cache should be kept in memory.
235     */
236    public static final int PERSISTENT_NO_CACHE = 0x0;
237
238    /**
239     * Used to indicate that the animation drawing cache should be kept in memory.
240     */
241    public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
242
243    /**
244     * Used to indicate that the scrolling drawing cache should be kept in memory.
245     */
246    public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
247
248    /**
249     * Used to indicate that all drawing caches should be kept in memory.
250     */
251    public static final int PERSISTENT_ALL_CACHES = 0x3;
252
253    /**
254     * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
255     * are set at the same time.
256     */
257    protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
258
259    // Index of the child's left position in the mLocation array
260    private static final int CHILD_LEFT_INDEX = 0;
261    // Index of the child's top position in the mLocation array
262    private static final int CHILD_TOP_INDEX = 1;
263
264    // Child views of this ViewGroup
265    private View[] mChildren;
266    // Number of valid children in the mChildren array, the rest should be null or not
267    // considered as children
268    private int mChildrenCount;
269
270    private static final int ARRAY_INITIAL_CAPACITY = 12;
271    private static final int ARRAY_CAPACITY_INCREMENT = 12;
272
273    // Used to draw cached views
274    private final Paint mCachePaint = new Paint();
275
276    public ViewGroup(Context context) {
277        super(context);
278        initViewGroup();
279    }
280
281    public ViewGroup(Context context, AttributeSet attrs) {
282        super(context, attrs);
283        initViewGroup();
284        initFromAttributes(context, attrs);
285    }
286
287    public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
288        super(context, attrs, defStyle);
289        initViewGroup();
290        initFromAttributes(context, attrs);
291    }
292
293    private void initViewGroup() {
294        // ViewGroup doesn't draw by default
295        setFlags(WILL_NOT_DRAW, DRAW_MASK);
296        mGroupFlags |= FLAG_CLIP_CHILDREN;
297        mGroupFlags |= FLAG_CLIP_TO_PADDING;
298        mGroupFlags |= FLAG_ANIMATION_DONE;
299        mGroupFlags |= FLAG_ANIMATION_CACHE;
300        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
301
302        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
303
304        mChildren = new View[ARRAY_INITIAL_CAPACITY];
305        mChildrenCount = 0;
306
307        mCachePaint.setDither(false);
308
309        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
310    }
311
312    private void initFromAttributes(Context context, AttributeSet attrs) {
313        TypedArray a = context.obtainStyledAttributes(attrs,
314                R.styleable.ViewGroup);
315
316        final int N = a.getIndexCount();
317        for (int i = 0; i < N; i++) {
318            int attr = a.getIndex(i);
319            switch (attr) {
320                case R.styleable.ViewGroup_clipChildren:
321                    setClipChildren(a.getBoolean(attr, true));
322                    break;
323                case R.styleable.ViewGroup_clipToPadding:
324                    setClipToPadding(a.getBoolean(attr, true));
325                    break;
326                case R.styleable.ViewGroup_animationCache:
327                    setAnimationCacheEnabled(a.getBoolean(attr, true));
328                    break;
329                case R.styleable.ViewGroup_persistentDrawingCache:
330                    setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
331                    break;
332                case R.styleable.ViewGroup_addStatesFromChildren:
333                    setAddStatesFromChildren(a.getBoolean(attr, false));
334                    break;
335                case R.styleable.ViewGroup_alwaysDrawnWithCache:
336                    setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
337                    break;
338                case R.styleable.ViewGroup_layoutAnimation:
339                    int id = a.getResourceId(attr, -1);
340                    if (id > 0) {
341                        setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
342                    }
343                    break;
344                case R.styleable.ViewGroup_descendantFocusability:
345                    setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
346                    break;
347            }
348        }
349
350        a.recycle();
351    }
352
353    /**
354     * Gets the descendant focusability of this view group.  The descendant
355     * focusability defines the relationship between this view group and its
356     * descendants when looking for a view to take focus in
357     * {@link #requestFocus(int, android.graphics.Rect)}.
358     *
359     * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
360     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
361     */
362    @ViewDebug.ExportedProperty(mapping = {
363        @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
364        @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
365        @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
366    })
367    public int getDescendantFocusability() {
368        return mGroupFlags & FLAG_MASK_FOCUSABILITY;
369    }
370
371    /**
372     * Set the descendant focusability of this view group. This defines the relationship
373     * between this view group and its descendants when looking for a view to
374     * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
375     *
376     * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
377     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
378     */
379    public void setDescendantFocusability(int focusability) {
380        switch (focusability) {
381            case FOCUS_BEFORE_DESCENDANTS:
382            case FOCUS_AFTER_DESCENDANTS:
383            case FOCUS_BLOCK_DESCENDANTS:
384                break;
385            default:
386                throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
387                        + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
388        }
389        mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
390        mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
391    }
392
393    /**
394     * {@inheritDoc}
395     */
396    @Override
397    void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
398        if (mFocused != null) {
399            mFocused.unFocus();
400            mFocused = null;
401        }
402        super.handleFocusGainInternal(direction, previouslyFocusedRect);
403    }
404
405    /**
406     * {@inheritDoc}
407     */
408    public void requestChildFocus(View child, View focused) {
409        if (DBG) {
410            System.out.println(this + " requestChildFocus()");
411        }
412        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
413            return;
414        }
415
416        // Unfocus us, if necessary
417        super.unFocus();
418
419        // We had a previous notion of who had focus. Clear it.
420        if (mFocused != child) {
421            if (mFocused != null) {
422                mFocused.unFocus();
423            }
424
425            mFocused = child;
426        }
427        if (mParent != null) {
428            mParent.requestChildFocus(this, focused);
429        }
430    }
431
432    /**
433     * {@inheritDoc}
434     */
435    public void focusableViewAvailable(View v) {
436        if (mParent != null
437                // shortcut: don't report a new focusable view if we block our descendants from
438                // getting focus
439                && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
440                // shortcut: don't report a new focusable view if we already are focused
441                // (and we don't prefer our descendants)
442                //
443                // note: knowing that mFocused is non-null is not a good enough reason
444                // to break the traversal since in that case we'd actually have to find
445                // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
446                // an ancestor of v; this will get checked for at ViewRoot
447                && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
448            mParent.focusableViewAvailable(v);
449        }
450    }
451
452    /**
453     * {@inheritDoc}
454     */
455    public boolean showContextMenuForChild(View originalView) {
456        return mParent != null && mParent.showContextMenuForChild(originalView);
457    }
458
459    /**
460     * Find the nearest view in the specified direction that wants to take
461     * focus.
462     *
463     * @param focused The view that currently has focus
464     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
465     *        FOCUS_RIGHT, or 0 for not applicable.
466     */
467    public View focusSearch(View focused, int direction) {
468        if (isRootNamespace()) {
469            // root namespace means we should consider ourselves the top of the
470            // tree for focus searching; otherwise we could be focus searching
471            // into other tabs.  see LocalActivityManager and TabHost for more info
472            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
473        } else if (mParent != null) {
474            return mParent.focusSearch(focused, direction);
475        }
476        return null;
477    }
478
479    /**
480     * {@inheritDoc}
481     */
482    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
483        return false;
484    }
485
486    /**
487     * {@inheritDoc}
488     */
489    @Override
490    public boolean dispatchUnhandledMove(View focused, int direction) {
491        return mFocused != null &&
492                mFocused.dispatchUnhandledMove(focused, direction);
493    }
494
495    /**
496     * {@inheritDoc}
497     */
498    public void clearChildFocus(View child) {
499        if (DBG) {
500            System.out.println(this + " clearChildFocus()");
501        }
502
503        mFocused = null;
504        if (mParent != null) {
505            mParent.clearChildFocus(this);
506        }
507    }
508
509    /**
510     * {@inheritDoc}
511     */
512    @Override
513    public void clearFocus() {
514        super.clearFocus();
515
516        // clear any child focus if it exists
517        if (mFocused != null) {
518            mFocused.clearFocus();
519        }
520    }
521
522    /**
523     * {@inheritDoc}
524     */
525    @Override
526    void unFocus() {
527        if (DBG) {
528            System.out.println(this + " unFocus()");
529        }
530
531        super.unFocus();
532        if (mFocused != null) {
533            mFocused.unFocus();
534        }
535        mFocused = null;
536    }
537
538    /**
539     * Returns the focused child of this view, if any. The child may have focus
540     * or contain focus.
541     *
542     * @return the focused child or null.
543     */
544    public View getFocusedChild() {
545        return mFocused;
546    }
547
548    /**
549     * Returns true if this view has or contains focus
550     *
551     * @return true if this view has or contains focus
552     */
553    @Override
554    public boolean hasFocus() {
555        return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
556    }
557
558    /*
559     * (non-Javadoc)
560     *
561     * @see android.view.View#findFocus()
562     */
563    @Override
564    public View findFocus() {
565        if (DBG) {
566            System.out.println("Find focus in " + this + ": flags="
567                    + isFocused() + ", child=" + mFocused);
568        }
569
570        if (isFocused()) {
571            return this;
572        }
573
574        if (mFocused != null) {
575            return mFocused.findFocus();
576        }
577        return null;
578    }
579
580    /**
581     * {@inheritDoc}
582     */
583    @Override
584    public boolean hasFocusable() {
585        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
586            return false;
587        }
588
589        if (isFocusable()) {
590            return true;
591        }
592
593        final int descendantFocusability = getDescendantFocusability();
594        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
595            final int count = mChildrenCount;
596            final View[] children = mChildren;
597
598            for (int i = 0; i < count; i++) {
599                final View child = children[i];
600                if (child.hasFocusable()) {
601                    return true;
602                }
603            }
604        }
605
606        return false;
607    }
608
609    /**
610     * {@inheritDoc}
611     */
612    @Override
613    public void addFocusables(ArrayList<View> views, int direction) {
614        addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
615    }
616
617    /**
618     * {@inheritDoc}
619     */
620    @Override
621    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
622        final int focusableCount = views.size();
623
624        final int descendantFocusability = getDescendantFocusability();
625
626        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
627            final int count = mChildrenCount;
628            final View[] children = mChildren;
629
630            for (int i = 0; i < count; i++) {
631                final View child = children[i];
632                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
633                    child.addFocusables(views, direction, focusableMode);
634                }
635            }
636        }
637
638        // we add ourselves (if focusable) in all cases except for when we are
639        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
640        // to avoid the focus search finding layouts when a more precise search
641        // among the focusable children would be more interesting.
642        if (
643            descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
644                // No focusable descendants
645                (focusableCount == views.size())) {
646            super.addFocusables(views, direction, focusableMode);
647        }
648    }
649
650    /**
651     * {@inheritDoc}
652     */
653    @Override
654    public void dispatchWindowFocusChanged(boolean hasFocus) {
655        super.dispatchWindowFocusChanged(hasFocus);
656        final int count = mChildrenCount;
657        final View[] children = mChildren;
658        for (int i = 0; i < count; i++) {
659            children[i].dispatchWindowFocusChanged(hasFocus);
660        }
661    }
662
663    /**
664     * {@inheritDoc}
665     */
666    @Override
667    public void addTouchables(ArrayList<View> views) {
668        super.addTouchables(views);
669
670        final int count = mChildrenCount;
671        final View[] children = mChildren;
672
673        for (int i = 0; i < count; i++) {
674            final View child = children[i];
675            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
676                child.addTouchables(views);
677            }
678        }
679    }
680
681    /**
682     * {@inheritDoc}
683     */
684    @Override
685    public void dispatchWindowVisibilityChanged(int visibility) {
686        super.dispatchWindowVisibilityChanged(visibility);
687        final int count = mChildrenCount;
688        final View[] children = mChildren;
689        for (int i = 0; i < count; i++) {
690            children[i].dispatchWindowVisibilityChanged(visibility);
691        }
692    }
693
694    /**
695     * {@inheritDoc}
696     */
697    public void recomputeViewAttributes(View child) {
698        ViewParent parent = mParent;
699        if (parent != null) parent.recomputeViewAttributes(this);
700    }
701
702    @Override
703    void dispatchCollectViewAttributes(int visibility) {
704        visibility |= mViewFlags&VISIBILITY_MASK;
705        super.dispatchCollectViewAttributes(visibility);
706        final int count = mChildrenCount;
707        final View[] children = mChildren;
708        for (int i = 0; i < count; i++) {
709            children[i].dispatchCollectViewAttributes(visibility);
710        }
711    }
712
713    /**
714     * {@inheritDoc}
715     */
716    public void bringChildToFront(View child) {
717        int index = indexOfChild(child);
718        if (index >= 0) {
719            removeFromArray(index);
720            addInArray(child, mChildrenCount);
721            child.mParent = this;
722        }
723    }
724
725    /**
726     * {@inheritDoc}
727     */
728    @Override
729    public boolean dispatchKeyEventPreIme(KeyEvent event) {
730        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
731            return super.dispatchKeyEventPreIme(event);
732        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
733            return mFocused.dispatchKeyEventPreIme(event);
734        }
735        return false;
736    }
737
738    /**
739     * {@inheritDoc}
740     */
741    @Override
742    public boolean dispatchKeyEvent(KeyEvent event) {
743        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
744            return super.dispatchKeyEvent(event);
745        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
746            return mFocused.dispatchKeyEvent(event);
747        }
748        return false;
749    }
750
751    /**
752     * {@inheritDoc}
753     */
754    @Override
755    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
756        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
757            return super.dispatchKeyShortcutEvent(event);
758        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
759            return mFocused.dispatchKeyShortcutEvent(event);
760        }
761        return false;
762    }
763
764    /**
765     * {@inheritDoc}
766     */
767    @Override
768    public boolean dispatchTrackballEvent(MotionEvent event) {
769        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
770            return super.dispatchTrackballEvent(event);
771        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
772            return mFocused.dispatchTrackballEvent(event);
773        }
774        return false;
775    }
776
777    /**
778     * {@inheritDoc}
779     */
780    @Override
781    public boolean dispatchTouchEvent(MotionEvent ev) {
782        final int action = ev.getAction();
783        final float xf = ev.getX();
784        final float yf = ev.getY();
785        final float scrolledXFloat = xf + mScrollX;
786        final float scrolledYFloat = yf + mScrollY;
787        final Rect frame = mTempRect;
788
789        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
790
791        if (action == MotionEvent.ACTION_DOWN) {
792            if (mMotionTarget != null) {
793                // this is weird, we got a pen down, but we thought it was
794                // already down!
795                // XXX: We should probably send an ACTION_UP to the current
796                // target.
797                mMotionTarget = null;
798            }
799            // If we're disallowing intercept or if we're allowing and we didn't
800            // intercept
801            if (disallowIntercept || !onInterceptTouchEvent(ev)) {
802                // reset this event's action (just to protect ourselves)
803                ev.setAction(MotionEvent.ACTION_DOWN);
804                // We know we want to dispatch the event down, find a child
805                // who can handle it, start with the front-most child.
806                final int scrolledXInt = (int) scrolledXFloat;
807                final int scrolledYInt = (int) scrolledYFloat;
808                final View[] children = mChildren;
809                final int count = mChildrenCount;
810                for (int i = count - 1; i >= 0; i--) {
811                    final View child = children[i];
812                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
813                            || child.getAnimation() != null) {
814                        child.getHitRect(frame);
815                        if (frame.contains(scrolledXInt, scrolledYInt)) {
816                            // offset the event to the view's coordinate system
817                            final float xc = scrolledXFloat - child.mLeft;
818                            final float yc = scrolledYFloat - child.mTop;
819                            ev.setLocation(xc, yc);
820                            if (child.dispatchTouchEvent(ev))  {
821                                // Event handled, we have a target now.
822                                mMotionTarget = child;
823                                return true;
824                            }
825                            // The event didn't get handled, try the next view.
826                            // Don't reset the event's location, it's not
827                            // necessary here.
828                        }
829                    }
830                }
831            }
832        }
833
834        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
835                (action == MotionEvent.ACTION_CANCEL);
836
837        if (isUpOrCancel) {
838            // Note, we've already copied the previous state to our local
839            // variable, so this takes effect on the next event
840            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
841        }
842
843        // The event wasn't an ACTION_DOWN, dispatch it to our target if
844        // we have one.
845        final View target = mMotionTarget;
846        if (target == null) {
847            // We don't have a target, this means we're handling the
848            // event as a regular view.
849            ev.setLocation(xf, yf);
850            return super.dispatchTouchEvent(ev);
851        }
852
853        // if have a target, see if we're allowed to and want to intercept its
854        // events
855        if (!disallowIntercept && onInterceptTouchEvent(ev)) {
856            final float xc = scrolledXFloat - (float) target.mLeft;
857            final float yc = scrolledYFloat - (float) target.mTop;
858            ev.setAction(MotionEvent.ACTION_CANCEL);
859            ev.setLocation(xc, yc);
860            if (!target.dispatchTouchEvent(ev)) {
861                // target didn't handle ACTION_CANCEL. not much we can do
862                // but they should have.
863            }
864            // clear the target
865            mMotionTarget = null;
866            // Don't dispatch this event to our own view, because we already
867            // saw it when intercepting; we just want to give the following
868            // event to the normal onTouchEvent().
869            return true;
870        }
871
872        if (isUpOrCancel) {
873            mMotionTarget = null;
874        }
875
876        // finally offset the event to the target's coordinate system and
877        // dispatch the event.
878        final float xc = scrolledXFloat - (float) target.mLeft;
879        final float yc = scrolledYFloat - (float) target.mTop;
880        ev.setLocation(xc, yc);
881
882        return target.dispatchTouchEvent(ev);
883    }
884
885    /**
886     * {@inheritDoc}
887     */
888    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
889
890        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
891            // We're already in this state, assume our ancestors are too
892            return;
893        }
894
895        if (disallowIntercept) {
896            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
897        } else {
898            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
899        }
900
901        // Pass it up to our parent
902        if (mParent != null) {
903            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
904        }
905    }
906
907    /**
908     * Implement this method to intercept all touch screen motion events.  This
909     * allows you to watch events as they are dispatched to your children, and
910     * take ownership of the current gesture at any point.
911     *
912     * <p>Using this function takes some care, as it has a fairly complicated
913     * interaction with {@link View#onTouchEvent(MotionEvent)
914     * View.onTouchEvent(MotionEvent)}, and using it requires implementing
915     * that method as well as this one in the correct way.  Events will be
916     * received in the following order:
917     *
918     * <ol>
919     * <li> You will receive the down event here.
920     * <li> The down event will be handled either by a child of this view
921     * group, or given to your own onTouchEvent() method to handle; this means
922     * you should implement onTouchEvent() to return true, so you will
923     * continue to see the rest of the gesture (instead of looking for
924     * a parent view to handle it).  Also, by returning true from
925     * onTouchEvent(), you will not receive any following
926     * events in onInterceptTouchEvent() and all touch processing must
927     * happen in onTouchEvent() like normal.
928     * <li> For as long as you return false from this function, each following
929     * event (up to and including the final up) will be delivered first here
930     * and then to the target's onTouchEvent().
931     * <li> If you return true from here, you will not receive any
932     * following events: the target view will receive the same event but
933     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
934     * events will be delivered to your onTouchEvent() method and no longer
935     * appear here.
936     * </ol>
937     *
938     * @param ev The motion event being dispatched down the hierarchy.
939     * @return Return true to steal motion events from the children and have
940     * them dispatched to this ViewGroup through onTouchEvent().
941     * The current target will receive an ACTION_CANCEL event, and no further
942     * messages will be delivered here.
943     */
944    public boolean onInterceptTouchEvent(MotionEvent ev) {
945        return false;
946    }
947
948    /**
949     * {@inheritDoc}
950     *
951     * Looks for a view to give focus to respecting the setting specified by
952     * {@link #getDescendantFocusability()}.
953     *
954     * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
955     * find focus within the children of this group when appropriate.
956     *
957     * @see #FOCUS_BEFORE_DESCENDANTS
958     * @see #FOCUS_AFTER_DESCENDANTS
959     * @see #FOCUS_BLOCK_DESCENDANTS
960     * @see #onRequestFocusInDescendants
961     */
962    @Override
963    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
964        if (DBG) {
965            System.out.println(this + " ViewGroup.requestFocus direction="
966                    + direction);
967        }
968        int descendantFocusability = getDescendantFocusability();
969
970        switch (descendantFocusability) {
971            case FOCUS_BLOCK_DESCENDANTS:
972                return super.requestFocus(direction, previouslyFocusedRect);
973            case FOCUS_BEFORE_DESCENDANTS: {
974                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
975                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
976            }
977            case FOCUS_AFTER_DESCENDANTS: {
978                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
979                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
980            }
981            default:
982                throw new IllegalStateException("descendant focusability must be "
983                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
984                        + "but is " + descendantFocusability);
985        }
986    }
987
988    /**
989     * Look for a descendant to call {@link View#requestFocus} on.
990     * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
991     * when it wants to request focus within its children.  Override this to
992     * customize how your {@link ViewGroup} requests focus within its children.
993     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
994     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
995     *        to give a finer grained hint about where focus is coming from.  May be null
996     *        if there is no hint.
997     * @return Whether focus was taken.
998     */
999    @SuppressWarnings({"ConstantConditions"})
1000    protected boolean onRequestFocusInDescendants(int direction,
1001            Rect previouslyFocusedRect) {
1002        int index;
1003        int increment;
1004        int end;
1005        int count = mChildrenCount;
1006        if ((direction & FOCUS_FORWARD) != 0) {
1007            index = 0;
1008            increment = 1;
1009            end = count;
1010        } else {
1011            index = count - 1;
1012            increment = -1;
1013            end = -1;
1014        }
1015        final View[] children = mChildren;
1016        for (int i = index; i != end; i += increment) {
1017            View child = children[i];
1018            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1019                if (child.requestFocus(direction, previouslyFocusedRect)) {
1020                    return true;
1021                }
1022            }
1023        }
1024        return false;
1025    }
1026
1027    /**
1028     * {@inheritDoc}
1029     */
1030    @Override
1031    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
1032        super.dispatchAttachedToWindow(info, visibility);
1033        visibility |= mViewFlags & VISIBILITY_MASK;
1034        final int count = mChildrenCount;
1035        final View[] children = mChildren;
1036        for (int i = 0; i < count; i++) {
1037            children[i].dispatchAttachedToWindow(info, visibility);
1038        }
1039    }
1040
1041    @Override
1042    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1043        boolean populated = false;
1044        for (int i = 0, count = getChildCount(); i < count; i++) {
1045            populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
1046        }
1047        return populated;
1048    }
1049
1050    /**
1051     * {@inheritDoc}
1052     */
1053    @Override
1054    void dispatchDetachedFromWindow() {
1055        // If we still have a motion target, we are still in the process of
1056        // dispatching motion events to a child; we need to get rid of that
1057        // child to avoid dispatching events to it after the window is torn
1058        // down. To make sure we keep the child in a consistent state, we
1059        // first send it an ACTION_CANCEL motion event.
1060        if (mMotionTarget != null) {
1061            final long now = SystemClock.uptimeMillis();
1062            final MotionEvent event = MotionEvent.obtain(now, now,
1063                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1064            mMotionTarget.dispatchTouchEvent(event);
1065            event.recycle();
1066            mMotionTarget = null;
1067        }
1068
1069        final int count = mChildrenCount;
1070        final View[] children = mChildren;
1071        for (int i = 0; i < count; i++) {
1072            children[i].dispatchDetachedFromWindow();
1073        }
1074        super.dispatchDetachedFromWindow();
1075    }
1076
1077    /**
1078     * {@inheritDoc}
1079     */
1080    @Override
1081    public void setPadding(int left, int top, int right, int bottom) {
1082        super.setPadding(left, top, right, bottom);
1083
1084        if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) {
1085            mGroupFlags |= FLAG_PADDING_NOT_NULL;
1086        } else {
1087            mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
1088        }
1089    }
1090
1091    /**
1092     * {@inheritDoc}
1093     */
1094    @Override
1095    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1096        super.dispatchSaveInstanceState(container);
1097        final int count = mChildrenCount;
1098        final View[] children = mChildren;
1099        for (int i = 0; i < count; i++) {
1100            children[i].dispatchSaveInstanceState(container);
1101        }
1102    }
1103
1104    /**
1105     * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view,
1106     * not to its children.  For use when overriding
1107     * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze
1108     * their own state but not the state of their children.
1109     *
1110     * @param container the container
1111     */
1112    protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
1113        super.dispatchSaveInstanceState(container);
1114    }
1115
1116    /**
1117     * {@inheritDoc}
1118     */
1119    @Override
1120    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1121        super.dispatchRestoreInstanceState(container);
1122        final int count = mChildrenCount;
1123        final View[] children = mChildren;
1124        for (int i = 0; i < count; i++) {
1125            children[i].dispatchRestoreInstanceState(container);
1126        }
1127    }
1128
1129    /**
1130     * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view,
1131     * not to its children.  For use when overriding
1132     * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw
1133     * their own state but not the state of their children.
1134     *
1135     * @param container the container
1136     */
1137    protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
1138        super.dispatchRestoreInstanceState(container);
1139    }
1140
1141    /**
1142     * Enables or disables the drawing cache for each child of this view group.
1143     *
1144     * @param enabled true to enable the cache, false to dispose of it
1145     */
1146    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
1147        if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
1148            final View[] children = mChildren;
1149            final int count = mChildrenCount;
1150            for (int i = 0; i < count; i++) {
1151                children[i].setDrawingCacheEnabled(enabled);
1152            }
1153        }
1154    }
1155
1156    @Override
1157    protected void onAnimationStart() {
1158        super.onAnimationStart();
1159
1160        // When this ViewGroup's animation starts, build the cache for the children
1161        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1162            final int count = mChildrenCount;
1163            final View[] children = mChildren;
1164
1165            for (int i = 0; i < count; i++) {
1166                final View child = children[i];
1167                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1168                    child.setDrawingCacheEnabled(true);
1169                    child.buildDrawingCache();
1170                }
1171            }
1172
1173            mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
1174        }
1175    }
1176
1177    @Override
1178    protected void onAnimationEnd() {
1179        super.onAnimationEnd();
1180
1181        // When this ViewGroup's animation ends, destroy the cache of the children
1182        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1183            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
1184
1185            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
1186                setChildrenDrawingCacheEnabled(false);
1187            }
1188        }
1189    }
1190
1191    /**
1192     * {@inheritDoc}
1193     */
1194    @Override
1195    protected void dispatchDraw(Canvas canvas) {
1196        final int count = mChildrenCount;
1197        final View[] children = mChildren;
1198        int flags = mGroupFlags;
1199
1200        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
1201            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
1202
1203            for (int i = 0; i < count; i++) {
1204                final View child = children[i];
1205                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1206                    final LayoutParams params = child.getLayoutParams();
1207                    attachLayoutAnimationParameters(child, params, i, count);
1208                    bindLayoutAnimation(child);
1209                    if (cache) {
1210                        child.setDrawingCacheEnabled(true);
1211                        child.buildDrawingCache();
1212                    }
1213                }
1214            }
1215
1216            final LayoutAnimationController controller = mLayoutAnimationController;
1217            if (controller.willOverlap()) {
1218                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
1219            }
1220
1221            controller.start();
1222
1223            mGroupFlags &= ~FLAG_RUN_ANIMATION;
1224            mGroupFlags &= ~FLAG_ANIMATION_DONE;
1225
1226            if (cache) {
1227                mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
1228            }
1229
1230            if (mAnimationListener != null) {
1231                mAnimationListener.onAnimationStart(controller.getAnimation());
1232            }
1233        }
1234
1235        int saveCount = 0;
1236        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
1237        if (clipToPadding) {
1238            saveCount = canvas.save();
1239            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
1240                    mScrollX + mRight - mLeft - mPaddingRight,
1241                    mScrollY + mBottom - mTop - mPaddingBottom);
1242
1243        }
1244
1245        // We will draw our child's animation, let's reset the flag
1246        mPrivateFlags &= ~DRAW_ANIMATION;
1247        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
1248
1249        boolean more = false;
1250        final long drawingTime = getDrawingTime();
1251
1252        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
1253            for (int i = 0; i < count; i++) {
1254                final View child = children[i];
1255                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
1256                    more |= drawChild(canvas, child, drawingTime);
1257                }
1258            }
1259        } else {
1260            for (int i = 0; i < count; i++) {
1261                final View child = children[getChildDrawingOrder(count, i)];
1262                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
1263                    more |= drawChild(canvas, child, drawingTime);
1264                }
1265            }
1266        }
1267
1268        // Draw any disappearing views that have animations
1269        if (mDisappearingChildren != null) {
1270            final ArrayList<View> disappearingChildren = mDisappearingChildren;
1271            final int disappearingCount = disappearingChildren.size() - 1;
1272            // Go backwards -- we may delete as animations finish
1273            for (int i = disappearingCount; i >= 0; i--) {
1274                final View child = disappearingChildren.get(i);
1275                more |= drawChild(canvas, child, drawingTime);
1276            }
1277        }
1278
1279        if (clipToPadding) {
1280            canvas.restoreToCount(saveCount);
1281        }
1282
1283        // mGroupFlags might have been updated by drawChild()
1284        flags = mGroupFlags;
1285
1286        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
1287            invalidate();
1288        }
1289
1290        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
1291                mLayoutAnimationController.isDone() && !more) {
1292            // We want to erase the drawing cache and notify the listener after the
1293            // next frame is drawn because one extra invalidate() is caused by
1294            // drawChild() after the animation is over
1295            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
1296            final Runnable end = new Runnable() {
1297               public void run() {
1298                   notifyAnimationListener();
1299               }
1300            };
1301            post(end);
1302        }
1303    }
1304
1305    /**
1306     * Returns the index of the child to draw for this iteration. Override this
1307     * if you want to change the drawing order of children. By default, it
1308     * returns i.
1309     * <p>
1310     * NOTE: In order for this method to be called, the
1311     * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set.
1312     *
1313     * @param i The current iteration.
1314     * @return The index of the child to draw this iteration.
1315     */
1316    protected int getChildDrawingOrder(int childCount, int i) {
1317        return i;
1318    }
1319
1320    private void notifyAnimationListener() {
1321        mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
1322        mGroupFlags |= FLAG_ANIMATION_DONE;
1323
1324        if (mAnimationListener != null) {
1325           final Runnable end = new Runnable() {
1326               public void run() {
1327                   mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
1328               }
1329           };
1330           post(end);
1331        }
1332
1333        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1334            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
1335            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
1336                setChildrenDrawingCacheEnabled(false);
1337            }
1338        }
1339
1340        invalidate();
1341    }
1342
1343    /**
1344     * Draw one child of this View Group. This method is responsible for getting
1345     * the canvas in the right state. This includes clipping, translating so
1346     * that the child's scrolled origin is at 0, 0, and applying any animation
1347     * transformations.
1348     *
1349     * @param canvas The canvas on which to draw the child
1350     * @param child Who to draw
1351     * @param drawingTime The time at which draw is occuring
1352     * @return True if an invalidate() was issued
1353     */
1354    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
1355        boolean more = false;
1356
1357        final int cl = child.mLeft;
1358        final int ct = child.mTop;
1359        final int cr = child.mRight;
1360        final int cb = child.mBottom;
1361
1362        final int flags = mGroupFlags;
1363
1364        if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
1365            if (mChildTransformation != null) {
1366                mChildTransformation.clear();
1367            }
1368            mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
1369        }
1370
1371        Transformation transformToApply = null;
1372        final Animation a = child.getAnimation();
1373        boolean concatMatrix = false;
1374
1375        if (a != null) {
1376            if (mInvalidateRegion == null) {
1377                mInvalidateRegion = new RectF();
1378            }
1379            final RectF region = mInvalidateRegion;
1380
1381            final boolean initialized = a.isInitialized();
1382            if (!initialized) {
1383                a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
1384                a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
1385                child.onAnimationStart();
1386            }
1387
1388            if (mChildTransformation == null) {
1389                mChildTransformation = new Transformation();
1390            }
1391            more = a.getTransformation(drawingTime, mChildTransformation);
1392            transformToApply = mChildTransformation;
1393
1394            concatMatrix = a.willChangeTransformationMatrix();
1395
1396            if (more) {
1397                if (!a.willChangeBounds()) {
1398                    if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
1399                            FLAG_OPTIMIZE_INVALIDATE) {
1400                        mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
1401                    } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
1402                        // The child need to draw an animation, potentially offscreen, so
1403                        // make sure we do not cancel invalidate requests
1404                        mPrivateFlags |= DRAW_ANIMATION;
1405                        invalidate(cl, ct, cr, cb);
1406                    }
1407                } else {
1408                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, transformToApply);
1409
1410                    // The child need to draw an animation, potentially offscreen, so
1411                    // make sure we do not cancel invalidate requests
1412                    mPrivateFlags |= DRAW_ANIMATION;
1413
1414                    final int left = cl + (int) region.left;
1415                    final int top = ct + (int) region.top;
1416                    invalidate(left, top, left + (int) region.width(), top + (int) region.height());
1417                }
1418            }
1419        } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
1420                FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
1421            if (mChildTransformation == null) {
1422                mChildTransformation = new Transformation();
1423            }
1424            final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
1425            if (hasTransform) {
1426                final int transformType = mChildTransformation.getTransformationType();
1427                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
1428                        mChildTransformation : null;
1429                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
1430            }
1431        }
1432
1433        // Sets the flag as early as possible to allow draw() implementations
1434        // to call invalidate() successfully when doing animations
1435        child.mPrivateFlags |= DRAWN;
1436
1437        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
1438                (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
1439            return more;
1440        }
1441
1442        child.computeScroll();
1443
1444        final int sx = child.mScrollX;
1445        final int sy = child.mScrollY;
1446
1447        Bitmap cache = null;
1448        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
1449                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
1450            cache = child.getDrawingCache();
1451        }
1452
1453        final boolean hasNoCache = cache == null;
1454
1455        final int restoreTo = canvas.save();
1456        if (hasNoCache) {
1457            canvas.translate(cl - sx, ct - sy);
1458        } else {
1459            canvas.translate(cl, ct);
1460        }
1461
1462        float alpha = 1.0f;
1463
1464        if (transformToApply != null) {
1465            if (concatMatrix) {
1466                int transX = 0;
1467                int transY = 0;
1468                if (hasNoCache) {
1469                    transX = -sx;
1470                    transY = -sy;
1471                }
1472                // Undo the scroll translation, apply the transformation matrix,
1473                // then redo the scroll translate to get the correct result.
1474                canvas.translate(-transX, -transY);
1475                canvas.concat(transformToApply.getMatrix());
1476                canvas.translate(transX, transY);
1477                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1478            }
1479
1480            alpha = transformToApply.getAlpha();
1481            if (alpha < 1.0f) {
1482                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1483            }
1484
1485            if (alpha < 1.0f && hasNoCache) {
1486                final int multipliedAlpha = (int) (255 * alpha);
1487                if (!child.onSetAlpha(multipliedAlpha)) {
1488                    canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
1489                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
1490                } else {
1491                    child.mPrivateFlags |= ALPHA_SET;
1492                }
1493            }
1494        } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
1495            child.onSetAlpha(255);
1496        }
1497
1498        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
1499            if (hasNoCache) {
1500                canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
1501            } else {
1502                canvas.clipRect(0, 0, cr - cl, cb - ct);
1503            }
1504        }
1505
1506        if (hasNoCache) {
1507            // Fast path for layouts with no backgrounds
1508            if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
1509                if (ViewDebug.TRACE_HIERARCHY) {
1510                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
1511                }
1512                child.mPrivateFlags &= ~DIRTY_MASK;
1513                child.dispatchDraw(canvas);
1514            } else {
1515                child.draw(canvas);
1516            }
1517        } else {
1518            final Paint cachePaint = mCachePaint;
1519            if (alpha < 1.0f) {
1520                cachePaint.setAlpha((int) (alpha * 255));
1521                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
1522            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
1523                cachePaint.setAlpha(255);
1524                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
1525            }
1526            if (Config.DEBUG && ViewDebug.profileDrawing) {
1527                EventLog.writeEvent(60003, hashCode());
1528            }
1529            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
1530        }
1531
1532        canvas.restoreToCount(restoreTo);
1533
1534        if (a != null && !more) {
1535            child.onSetAlpha(255);
1536            finishAnimatingView(child, a);
1537        }
1538
1539        return more;
1540    }
1541
1542    /**
1543     * By default, children are clipped to their bounds before drawing. This
1544     * allows view groups to override this behavior for animations, etc.
1545     *
1546     * @param clipChildren true to clip children to their bounds,
1547     *        false otherwise
1548     * @attr ref android.R.styleable#ViewGroup_clipChildren
1549     */
1550    public void setClipChildren(boolean clipChildren) {
1551        setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
1552    }
1553
1554    /**
1555     * By default, children are clipped to the padding of the ViewGroup. This
1556     * allows view groups to override this behavior
1557     *
1558     * @param clipToPadding true to clip children to the padding of the
1559     *        group, false otherwise
1560     * @attr ref android.R.styleable#ViewGroup_clipToPadding
1561     */
1562    public void setClipToPadding(boolean clipToPadding) {
1563        setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
1564    }
1565
1566    /**
1567     * {@inheritDoc}
1568     */
1569    @Override
1570    public void dispatchSetSelected(boolean selected) {
1571        final View[] children = mChildren;
1572        final int count = mChildrenCount;
1573        for (int i = 0; i < count; i++) {
1574            children[i].setSelected(selected);
1575        }
1576    }
1577
1578    @Override
1579    protected void dispatchSetPressed(boolean pressed) {
1580        final View[] children = mChildren;
1581        final int count = mChildrenCount;
1582        for (int i = 0; i < count; i++) {
1583            children[i].setPressed(pressed);
1584        }
1585    }
1586
1587    /**
1588     * When this property is set to true, this ViewGroup supports static transformations on
1589     * children; this causes
1590     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
1591     * invoked when a child is drawn.
1592     *
1593     * Any subclass overriding
1594     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
1595     * set this property to true.
1596     *
1597     * @param enabled True to enable static transformations on children, false otherwise.
1598     *
1599     * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
1600     */
1601    protected void setStaticTransformationsEnabled(boolean enabled) {
1602        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
1603    }
1604
1605    /**
1606     * {@inheritDoc}
1607     *
1608     * @see #setStaticTransformationsEnabled(boolean)
1609     */
1610    protected boolean getChildStaticTransformation(View child, Transformation t) {
1611        return false;
1612    }
1613
1614    /**
1615     * {@hide}
1616     */
1617    @Override
1618    protected View findViewTraversal(int id) {
1619        if (id == mID) {
1620            return this;
1621        }
1622
1623        final View[] where = mChildren;
1624        final int len = mChildrenCount;
1625
1626        for (int i = 0; i < len; i++) {
1627            View v = where[i];
1628
1629            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
1630                v = v.findViewById(id);
1631
1632                if (v != null) {
1633                    return v;
1634                }
1635            }
1636        }
1637
1638        return null;
1639    }
1640
1641    /**
1642     * {@hide}
1643     */
1644    @Override
1645    protected View findViewWithTagTraversal(Object tag) {
1646        if (tag != null && tag.equals(mTag)) {
1647            return this;
1648        }
1649
1650        final View[] where = mChildren;
1651        final int len = mChildrenCount;
1652
1653        for (int i = 0; i < len; i++) {
1654            View v = where[i];
1655
1656            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
1657                v = v.findViewWithTag(tag);
1658
1659                if (v != null) {
1660                    return v;
1661                }
1662            }
1663        }
1664
1665        return null;
1666    }
1667
1668    /**
1669     * Adds a child view. If no layout parameters are already set on the child, the
1670     * default parameters for this ViewGroup are set on the child.
1671     *
1672     * @param child the child view to add
1673     *
1674     * @see #generateDefaultLayoutParams()
1675     */
1676    public void addView(View child) {
1677        addView(child, -1);
1678    }
1679
1680    /**
1681     * Adds a child view. If no layout parameters are already set on the child, the
1682     * default parameters for this ViewGroup are set on the child.
1683     *
1684     * @param child the child view to add
1685     * @param index the position at which to add the child
1686     *
1687     * @see #generateDefaultLayoutParams()
1688     */
1689    public void addView(View child, int index) {
1690        LayoutParams params = child.getLayoutParams();
1691        if (params == null) {
1692            params = generateDefaultLayoutParams();
1693            if (params == null) {
1694                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
1695            }
1696        }
1697        addView(child, index, params);
1698    }
1699
1700    /**
1701     * Adds a child view with this ViewGroup's default layout parameters and the
1702     * specified width and height.
1703     *
1704     * @param child the child view to add
1705     */
1706    public void addView(View child, int width, int height) {
1707        final LayoutParams params = generateDefaultLayoutParams();
1708        params.width = width;
1709        params.height = height;
1710        addView(child, -1, params);
1711    }
1712
1713    /**
1714     * Adds a child view with the specified layout parameters.
1715     *
1716     * @param child the child view to add
1717     * @param params the layout parameters to set on the child
1718     */
1719    public void addView(View child, LayoutParams params) {
1720        addView(child, -1, params);
1721    }
1722
1723    /**
1724     * Adds a child view with the specified layout parameters.
1725     *
1726     * @param child the child view to add
1727     * @param index the position at which to add the child
1728     * @param params the layout parameters to set on the child
1729     */
1730    public void addView(View child, int index, LayoutParams params) {
1731        if (DBG) {
1732            System.out.println(this + " addView");
1733        }
1734
1735        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
1736        // therefore, we call requestLayout() on ourselves before, so that the child's request
1737        // will be blocked at our level
1738        requestLayout();
1739        invalidate();
1740        addViewInner(child, index, params, false);
1741    }
1742
1743    /**
1744     * {@inheritDoc}
1745     */
1746    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
1747        if (!checkLayoutParams(params)) {
1748            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
1749        }
1750        if (view.mParent != this) {
1751            throw new IllegalArgumentException("Given view not a child of " + this);
1752        }
1753        view.setLayoutParams(params);
1754    }
1755
1756    /**
1757     * {@inheritDoc}
1758     */
1759    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1760        return  p != null;
1761    }
1762
1763    /**
1764     * Interface definition for a callback to be invoked when the hierarchy
1765     * within this view changed. The hierarchy changes whenever a child is added
1766     * to or removed from this view.
1767     */
1768    public interface OnHierarchyChangeListener {
1769        /**
1770         * Called when a new child is added to a parent view.
1771         *
1772         * @param parent the view in which a child was added
1773         * @param child the new child view added in the hierarchy
1774         */
1775        void onChildViewAdded(View parent, View child);
1776
1777        /**
1778         * Called when a child is removed from a parent view.
1779         *
1780         * @param parent the view from which the child was removed
1781         * @param child the child removed from the hierarchy
1782         */
1783        void onChildViewRemoved(View parent, View child);
1784    }
1785
1786    /**
1787     * Register a callback to be invoked when a child is added to or removed
1788     * from this view.
1789     *
1790     * @param listener the callback to invoke on hierarchy change
1791     */
1792    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
1793        mOnHierarchyChangeListener = listener;
1794    }
1795
1796    /**
1797     * Adds a view during layout. This is useful if in your onLayout() method,
1798     * you need to add more views (as does the list view for example).
1799     *
1800     * If index is negative, it means put it at the end of the list.
1801     *
1802     * @param child the view to add to the group
1803     * @param index the index at which the child must be added
1804     * @param params the layout parameters to associate with the child
1805     * @return true if the child was added, false otherwise
1806     */
1807    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
1808        return addViewInLayout(child, index, params, false);
1809    }
1810
1811    /**
1812     * Adds a view during layout. This is useful if in your onLayout() method,
1813     * you need to add more views (as does the list view for example).
1814     *
1815     * If index is negative, it means put it at the end of the list.
1816     *
1817     * @param child the view to add to the group
1818     * @param index the index at which the child must be added
1819     * @param params the layout parameters to associate with the child
1820     * @param preventRequestLayout if true, calling this method will not trigger a
1821     *        layout request on child
1822     * @return true if the child was added, false otherwise
1823     */
1824    protected boolean addViewInLayout(View child, int index, LayoutParams params,
1825            boolean preventRequestLayout) {
1826        child.mParent = null;
1827        addViewInner(child, index, params, preventRequestLayout);
1828        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
1829        return true;
1830    }
1831
1832    /**
1833     * Prevents the specified child to be laid out during the next layout pass.
1834     *
1835     * @param child the child on which to perform the cleanup
1836     */
1837    protected void cleanupLayoutState(View child) {
1838        child.mPrivateFlags &= ~View.FORCE_LAYOUT;
1839    }
1840
1841    private void addViewInner(View child, int index, LayoutParams params,
1842            boolean preventRequestLayout) {
1843
1844        if (child.getParent() != null) {
1845            throw new IllegalStateException("The specified child already has a parent. " +
1846                    "You must call removeView() on the child's parent first.");
1847        }
1848
1849        if (!checkLayoutParams(params)) {
1850            params = generateLayoutParams(params);
1851        }
1852
1853        if (preventRequestLayout) {
1854            child.mLayoutParams = params;
1855        } else {
1856            child.setLayoutParams(params);
1857        }
1858
1859        if (index < 0) {
1860            index = mChildrenCount;
1861        }
1862
1863        addInArray(child, index);
1864
1865        // tell our children
1866        if (preventRequestLayout) {
1867            child.assignParent(this);
1868        } else {
1869            child.mParent = this;
1870        }
1871
1872        if (child.hasFocus()) {
1873            requestChildFocus(child, child.findFocus());
1874        }
1875
1876        AttachInfo ai = mAttachInfo;
1877        if (ai != null) {
1878            boolean lastKeepOn = ai.mKeepScreenOn;
1879            ai.mKeepScreenOn = false;
1880            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
1881            if (ai.mKeepScreenOn) {
1882                needGlobalAttributesUpdate(true);
1883            }
1884            ai.mKeepScreenOn = lastKeepOn;
1885        }
1886
1887        if (mOnHierarchyChangeListener != null) {
1888            mOnHierarchyChangeListener.onChildViewAdded(this, child);
1889        }
1890
1891        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
1892            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
1893        }
1894    }
1895
1896    private void addInArray(View child, int index) {
1897        View[] children = mChildren;
1898        final int count = mChildrenCount;
1899        final int size = children.length;
1900        if (index == count) {
1901            if (size == count) {
1902                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
1903                System.arraycopy(children, 0, mChildren, 0, size);
1904                children = mChildren;
1905            }
1906            children[mChildrenCount++] = child;
1907        } else if (index < count) {
1908            if (size == count) {
1909                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
1910                System.arraycopy(children, 0, mChildren, 0, index);
1911                System.arraycopy(children, index, mChildren, index + 1, count - index);
1912                children = mChildren;
1913            } else {
1914                System.arraycopy(children, index, children, index + 1, count - index);
1915            }
1916            children[index] = child;
1917            mChildrenCount++;
1918        } else {
1919            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
1920        }
1921    }
1922
1923    // This method also sets the child's mParent to null
1924    private void removeFromArray(int index) {
1925        final View[] children = mChildren;
1926        children[index].mParent = null;
1927        final int count = mChildrenCount;
1928        if (index == count - 1) {
1929            children[--mChildrenCount] = null;
1930        } else if (index >= 0 && index < count) {
1931            System.arraycopy(children, index + 1, children, index, count - index - 1);
1932            children[--mChildrenCount] = null;
1933        } else {
1934            throw new IndexOutOfBoundsException();
1935        }
1936    }
1937
1938    // This method also sets the children's mParent to null
1939    private void removeFromArray(int start, int count) {
1940        final View[] children = mChildren;
1941        final int childrenCount = mChildrenCount;
1942
1943        start = Math.max(0, start);
1944        final int end = Math.min(childrenCount, start + count);
1945
1946        if (start == end) {
1947            return;
1948        }
1949
1950        if (end == childrenCount) {
1951            for (int i = start; i < end; i++) {
1952                children[i].mParent = null;
1953                children[i] = null;
1954            }
1955        } else {
1956            for (int i = start; i < end; i++) {
1957                children[i].mParent = null;
1958            }
1959
1960            // Since we're looping above, we might as well do the copy, but is arraycopy()
1961            // faster than the extra 2 bounds checks we would do in the loop?
1962            System.arraycopy(children, end, children, start, childrenCount - end);
1963
1964            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
1965                children[i] = null;
1966            }
1967        }
1968
1969        mChildrenCount -= (end - start);
1970    }
1971
1972    private void bindLayoutAnimation(View child) {
1973        Animation a = mLayoutAnimationController.getAnimationForView(child);
1974        child.setAnimation(a);
1975    }
1976
1977    /**
1978     * Subclasses should override this method to set layout animation
1979     * parameters on the supplied child.
1980     *
1981     * @param child the child to associate with animation parameters
1982     * @param params the child's layout parameters which hold the animation
1983     *        parameters
1984     * @param index the index of the child in the view group
1985     * @param count the number of children in the view group
1986     */
1987    protected void attachLayoutAnimationParameters(View child,
1988            LayoutParams params, int index, int count) {
1989        LayoutAnimationController.AnimationParameters animationParams =
1990                    params.layoutAnimationParameters;
1991        if (animationParams == null) {
1992            animationParams = new LayoutAnimationController.AnimationParameters();
1993            params.layoutAnimationParameters = animationParams;
1994        }
1995
1996        animationParams.count = count;
1997        animationParams.index = index;
1998    }
1999
2000    /**
2001     * {@inheritDoc}
2002     */
2003    public void removeView(View view) {
2004        removeViewInternal(view);
2005        requestLayout();
2006        invalidate();
2007    }
2008
2009    /**
2010     * Removes a view during layout. This is useful if in your onLayout() method,
2011     * you need to remove more views.
2012     *
2013     * @param view the view to remove from the group
2014     */
2015    public void removeViewInLayout(View view) {
2016        removeViewInternal(view);
2017    }
2018
2019    /**
2020     * Removes a range of views during layout. This is useful if in your onLayout() method,
2021     * you need to remove more views.
2022     *
2023     * @param start the index of the first view to remove from the group
2024     * @param count the number of views to remove from the group
2025     */
2026    public void removeViewsInLayout(int start, int count) {
2027        removeViewsInternal(start, count);
2028    }
2029
2030    /**
2031     * Removes the view at the specified position in the group.
2032     *
2033     * @param index the position in the group of the view to remove
2034     */
2035    public void removeViewAt(int index) {
2036        removeViewInternal(index, getChildAt(index));
2037        requestLayout();
2038        invalidate();
2039    }
2040
2041    /**
2042     * Removes the specified range of views from the group.
2043     *
2044     * @param start the first position in the group of the range of views to remove
2045     * @param count the number of views to remove
2046     */
2047    public void removeViews(int start, int count) {
2048        removeViewsInternal(start, count);
2049        requestLayout();
2050        invalidate();
2051    }
2052
2053    private void removeViewInternal(View view) {
2054        final int index = indexOfChild(view);
2055        if (index >= 0) {
2056            removeViewInternal(index, view);
2057        }
2058    }
2059
2060    private void removeViewInternal(int index, View view) {
2061        boolean clearChildFocus = false;
2062        if (view == mFocused) {
2063            view.clearFocusForRemoval();
2064            clearChildFocus = true;
2065        }
2066
2067        if (view.getAnimation() != null) {
2068            addDisappearingView(view);
2069        } else if (view.mAttachInfo != null) {
2070           view.dispatchDetachedFromWindow();
2071        }
2072
2073        if (mOnHierarchyChangeListener != null) {
2074            mOnHierarchyChangeListener.onChildViewRemoved(this, view);
2075        }
2076
2077        needGlobalAttributesUpdate(false);
2078
2079        removeFromArray(index);
2080
2081        if (clearChildFocus) {
2082            clearChildFocus(view);
2083        }
2084    }
2085
2086    private void removeViewsInternal(int start, int count) {
2087        final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
2088        final boolean notifyListener = onHierarchyChangeListener != null;
2089        final View focused = mFocused;
2090        final boolean detach = mAttachInfo != null;
2091        View clearChildFocus = null;
2092
2093        final View[] children = mChildren;
2094        final int end = start + count;
2095
2096        for (int i = start; i < end; i++) {
2097            final View view = children[i];
2098
2099            if (view == focused) {
2100                view.clearFocusForRemoval();
2101                clearChildFocus = view;
2102            }
2103
2104            if (view.getAnimation() != null) {
2105                addDisappearingView(view);
2106            } else if (detach) {
2107               view.dispatchDetachedFromWindow();
2108            }
2109
2110            needGlobalAttributesUpdate(false);
2111
2112            if (notifyListener) {
2113                onHierarchyChangeListener.onChildViewRemoved(this, view);
2114            }
2115        }
2116
2117        removeFromArray(start, count);
2118
2119        if (clearChildFocus != null) {
2120            clearChildFocus(clearChildFocus);
2121        }
2122    }
2123
2124    /**
2125     * Call this method to remove all child views from the
2126     * ViewGroup.
2127     */
2128    public void removeAllViews() {
2129        removeAllViewsInLayout();
2130        requestLayout();
2131        invalidate();
2132    }
2133
2134    /**
2135     * Called by a ViewGroup subclass to remove child views from itself,
2136     * when it must first know its size on screen before it can calculate how many
2137     * child views it will render. An example is a Gallery or a ListView, which
2138     * may "have" 50 children, but actually only render the number of children
2139     * that can currently fit inside the object on screen. Do not call
2140     * this method unless you are extending ViewGroup and understand the
2141     * view measuring and layout pipeline.
2142     */
2143    public void removeAllViewsInLayout() {
2144        final int count = mChildrenCount;
2145        if (count <= 0) {
2146            return;
2147        }
2148
2149        final View[] children = mChildren;
2150        mChildrenCount = 0;
2151
2152        final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
2153        final boolean notify = listener != null;
2154        final View focused = mFocused;
2155        final boolean detach = mAttachInfo != null;
2156        View clearChildFocus = null;
2157
2158        needGlobalAttributesUpdate(false);
2159
2160        for (int i = count - 1; i >= 0; i--) {
2161            final View view = children[i];
2162
2163            if (view == focused) {
2164                view.clearFocusForRemoval();
2165                clearChildFocus = view;
2166            }
2167
2168            if (view.getAnimation() != null) {
2169                addDisappearingView(view);
2170            } else if (detach) {
2171               view.dispatchDetachedFromWindow();
2172            }
2173
2174            if (notify) {
2175                listener.onChildViewRemoved(this, view);
2176            }
2177
2178            view.mParent = null;
2179            children[i] = null;
2180        }
2181
2182        if (clearChildFocus != null) {
2183            clearChildFocus(clearChildFocus);
2184        }
2185    }
2186
2187    /**
2188     * Finishes the removal of a detached view. This method will dispatch the detached from
2189     * window event and notify the hierarchy change listener.
2190     *
2191     * @param child the child to be definitely removed from the view hierarchy
2192     * @param animate if true and the view has an animation, the view is placed in the
2193     *                disappearing views list, otherwise, it is detached from the window
2194     *
2195     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2196     * @see #detachAllViewsFromParent()
2197     * @see #detachViewFromParent(View)
2198     * @see #detachViewFromParent(int)
2199     */
2200    protected void removeDetachedView(View child, boolean animate) {
2201        if (child == mFocused) {
2202            child.clearFocus();
2203        }
2204
2205        if (animate && child.getAnimation() != null) {
2206            addDisappearingView(child);
2207        } else if (child.mAttachInfo != null) {
2208            child.dispatchDetachedFromWindow();
2209        }
2210
2211        if (mOnHierarchyChangeListener != null) {
2212            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
2213        }
2214    }
2215
2216    /**
2217     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
2218     * sets the layout parameters and puts the view in the list of children so it can be retrieved
2219     * by calling {@link #getChildAt(int)}.
2220     *
2221     * This method should be called only for view which were detached from their parent.
2222     *
2223     * @param child the child to attach
2224     * @param index the index at which the child should be attached
2225     * @param params the layout parameters of the child
2226     *
2227     * @see #removeDetachedView(View, boolean)
2228     * @see #detachAllViewsFromParent()
2229     * @see #detachViewFromParent(View)
2230     * @see #detachViewFromParent(int)
2231     */
2232    protected void attachViewToParent(View child, int index, LayoutParams params) {
2233        child.mLayoutParams = params;
2234
2235        if (index < 0) {
2236            index = mChildrenCount;
2237        }
2238
2239        addInArray(child, index);
2240
2241        child.mParent = this;
2242        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
2243
2244        if (child.hasFocus()) {
2245            requestChildFocus(child, child.findFocus());
2246        }
2247    }
2248
2249    /**
2250     * Detaches a view from its parent. Detaching a view should be temporary and followed
2251     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2252     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2253     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2254     *
2255     * @param child the child to detach
2256     *
2257     * @see #detachViewFromParent(int)
2258     * @see #detachViewsFromParent(int, int)
2259     * @see #detachAllViewsFromParent()
2260     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2261     * @see #removeDetachedView(View, boolean)
2262     */
2263    protected void detachViewFromParent(View child) {
2264        removeFromArray(indexOfChild(child));
2265    }
2266
2267    /**
2268     * Detaches a view from its parent. Detaching a view should be temporary and followed
2269     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2270     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2271     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2272     *
2273     * @param index the index of the child to detach
2274     *
2275     * @see #detachViewFromParent(View)
2276     * @see #detachAllViewsFromParent()
2277     * @see #detachViewsFromParent(int, int)
2278     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2279     * @see #removeDetachedView(View, boolean)
2280     */
2281    protected void detachViewFromParent(int index) {
2282        removeFromArray(index);
2283    }
2284
2285    /**
2286     * Detaches a range of view from their parent. Detaching a view should be temporary and followed
2287     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2288     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
2289     * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2290     *
2291     * @param start the first index of the childrend range to detach
2292     * @param count the number of children to detach
2293     *
2294     * @see #detachViewFromParent(View)
2295     * @see #detachViewFromParent(int)
2296     * @see #detachAllViewsFromParent()
2297     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2298     * @see #removeDetachedView(View, boolean)
2299     */
2300    protected void detachViewsFromParent(int start, int count) {
2301        removeFromArray(start, count);
2302    }
2303
2304    /**
2305     * Detaches all views from the parent. Detaching a view should be temporary and followed
2306     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2307     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2308     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2309     *
2310     * @see #detachViewFromParent(View)
2311     * @see #detachViewFromParent(int)
2312     * @see #detachViewsFromParent(int, int)
2313     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2314     * @see #removeDetachedView(View, boolean)
2315     */
2316    protected void detachAllViewsFromParent() {
2317        final int count = mChildrenCount;
2318        if (count <= 0) {
2319            return;
2320        }
2321
2322        final View[] children = mChildren;
2323        mChildrenCount = 0;
2324
2325        for (int i = count - 1; i >= 0; i--) {
2326            children[i].mParent = null;
2327            children[i] = null;
2328        }
2329    }
2330
2331    /**
2332     * Don't call or override this method. It is used for the implementation of
2333     * the view hierarchy.
2334     */
2335    public final void invalidateChild(View child, final Rect dirty) {
2336        if (ViewDebug.TRACE_HIERARCHY) {
2337            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
2338        }
2339
2340        ViewParent parent = this;
2341
2342        final AttachInfo attachInfo = mAttachInfo;
2343        if (attachInfo != null) {
2344            final int[] location = attachInfo.mInvalidateChildLocation;
2345            location[CHILD_LEFT_INDEX] = child.mLeft;
2346            location[CHILD_TOP_INDEX] = child.mTop;
2347
2348            // If the child is drawing an animation, we want to copy this flag onto
2349            // ourselves and the parent to make sure the invalidate request goes
2350            // through
2351            final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
2352
2353            // Check whether the child that requests the invalidate is fully opaque
2354            final boolean isOpaque = child.isOpaque();
2355            // Mark the child as dirty, using the appropriate flag
2356            // Make sure we do not set both flags at the same time
2357            final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
2358
2359            do {
2360                View view = null;
2361                if (parent instanceof View) {
2362                    view = (View) parent;
2363                }
2364
2365                if (drawAnimation) {
2366                    if (view != null) {
2367                        view.mPrivateFlags |= DRAW_ANIMATION;
2368                    } else if (parent instanceof ViewRoot) {
2369                        ((ViewRoot) parent).mIsAnimating = true;
2370                    }
2371                }
2372
2373                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
2374                // flag coming from the child that initiated the invalidate
2375                if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
2376                    view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
2377                }
2378
2379                parent = parent.invalidateChildInParent(location, dirty);
2380            } while (parent != null);
2381        }
2382    }
2383
2384    /**
2385     * Don't call or override this method. It is used for the implementation of
2386     * the view hierarchy.
2387     *
2388     * This implementation returns null if this ViewGroup does not have a parent,
2389     * if this ViewGroup is already fully invalidated or if the dirty rectangle
2390     * does not intersect with this ViewGroup's bounds.
2391     */
2392    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
2393        if (ViewDebug.TRACE_HIERARCHY) {
2394            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
2395        }
2396
2397        if ((mPrivateFlags & DRAWN) == DRAWN) {
2398            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
2399                        FLAG_OPTIMIZE_INVALIDATE) {
2400                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
2401                        location[CHILD_TOP_INDEX] - mScrollY);
2402
2403                final int left = mLeft;
2404                final int top = mTop;
2405
2406                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
2407                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
2408                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
2409
2410                    location[CHILD_LEFT_INDEX] = left;
2411                    location[CHILD_TOP_INDEX] = top;
2412
2413                    return mParent;
2414                }
2415            } else {
2416                mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
2417
2418                location[CHILD_LEFT_INDEX] = mLeft;
2419                location[CHILD_TOP_INDEX] = mTop;
2420
2421                dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
2422                        mBottom - location[CHILD_TOP_INDEX]);
2423
2424                return mParent;
2425            }
2426        }
2427
2428        return null;
2429    }
2430
2431    /**
2432     * Offset a rectangle that is in a descendant's coordinate
2433     * space into our coordinate space.
2434     * @param descendant A descendant of this view
2435     * @param rect A rectangle defined in descendant's coordinate space.
2436     */
2437    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
2438        offsetRectBetweenParentAndChild(descendant, rect, true, false);
2439    }
2440
2441    /**
2442     * Offset a rectangle that is in our coordinate space into an ancestor's
2443     * coordinate space.
2444     * @param descendant A descendant of this view
2445     * @param rect A rectangle defined in descendant's coordinate space.
2446     */
2447    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
2448        offsetRectBetweenParentAndChild(descendant, rect, false, false);
2449    }
2450
2451    /**
2452     * Helper method that offsets a rect either from parent to descendant or
2453     * descendant to parent.
2454     */
2455    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
2456            boolean offsetFromChildToParent, boolean clipToBounds) {
2457
2458        // already in the same coord system :)
2459        if (descendant == this) {
2460            return;
2461        }
2462
2463        ViewParent theParent = descendant.mParent;
2464
2465        // search and offset up to the parent
2466        while ((theParent != null)
2467                && (theParent instanceof View)
2468                && (theParent != this)) {
2469
2470            if (offsetFromChildToParent) {
2471                rect.offset(descendant.mLeft - descendant.mScrollX,
2472                        descendant.mTop - descendant.mScrollY);
2473                if (clipToBounds) {
2474                    View p = (View) theParent;
2475                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
2476                }
2477            } else {
2478                if (clipToBounds) {
2479                    View p = (View) theParent;
2480                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
2481                }
2482                rect.offset(descendant.mScrollX - descendant.mLeft,
2483                        descendant.mScrollY - descendant.mTop);
2484            }
2485
2486            descendant = (View) theParent;
2487            theParent = descendant.mParent;
2488        }
2489
2490        // now that we are up to this view, need to offset one more time
2491        // to get into our coordinate space
2492        if (theParent == this) {
2493            if (offsetFromChildToParent) {
2494                rect.offset(descendant.mLeft - descendant.mScrollX,
2495                        descendant.mTop - descendant.mScrollY);
2496            } else {
2497                rect.offset(descendant.mScrollX - descendant.mLeft,
2498                        descendant.mScrollY - descendant.mTop);
2499            }
2500        } else {
2501            throw new IllegalArgumentException("parameter must be a descendant of this view");
2502        }
2503    }
2504
2505    /**
2506     * Offset the vertical location of all children of this view by the specified number of pixels.
2507     *
2508     * @param offset the number of pixels to offset
2509     *
2510     * @hide
2511     */
2512    public void offsetChildrenTopAndBottom(int offset) {
2513        final int count = mChildrenCount;
2514        final View[] children = mChildren;
2515
2516        for (int i = 0; i < count; i++) {
2517            final View v = children[i];
2518            v.mTop += offset;
2519            v.mBottom += offset;
2520        }
2521    }
2522
2523    /**
2524     * {@inheritDoc}
2525     */
2526    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
2527        int dx = child.mLeft - mScrollX;
2528        int dy = child.mTop - mScrollY;
2529        if (offset != null) {
2530            offset.x += dx;
2531            offset.y += dy;
2532        }
2533        r.offset(dx, dy);
2534        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
2535               (mParent == null || mParent.getChildVisibleRect(this, r, offset));
2536    }
2537
2538    /**
2539     * {@inheritDoc}
2540     */
2541    @Override
2542    protected abstract void onLayout(boolean changed,
2543            int l, int t, int r, int b);
2544
2545    /**
2546     * Indicates whether the view group has the ability to animate its children
2547     * after the first layout.
2548     *
2549     * @return true if the children can be animated, false otherwise
2550     */
2551    protected boolean canAnimate() {
2552        return mLayoutAnimationController != null;
2553    }
2554
2555    /**
2556     * Runs the layout animation. Calling this method triggers a relayout of
2557     * this view group.
2558     */
2559    public void startLayoutAnimation() {
2560        if (mLayoutAnimationController != null) {
2561            mGroupFlags |= FLAG_RUN_ANIMATION;
2562            requestLayout();
2563        }
2564    }
2565
2566    /**
2567     * Schedules the layout animation to be played after the next layout pass
2568     * of this view group. This can be used to restart the layout animation
2569     * when the content of the view group changes or when the activity is
2570     * paused and resumed.
2571     */
2572    public void scheduleLayoutAnimation() {
2573        mGroupFlags |= FLAG_RUN_ANIMATION;
2574    }
2575
2576    /**
2577     * Sets the layout animation controller used to animate the group's
2578     * children after the first layout.
2579     *
2580     * @param controller the animation controller
2581     */
2582    public void setLayoutAnimation(LayoutAnimationController controller) {
2583        mLayoutAnimationController = controller;
2584        if (mLayoutAnimationController != null) {
2585            mGroupFlags |= FLAG_RUN_ANIMATION;
2586        }
2587    }
2588
2589    /**
2590     * Returns the layout animation controller used to animate the group's
2591     * children.
2592     *
2593     * @return the current animation controller
2594     */
2595    public LayoutAnimationController getLayoutAnimation() {
2596        return mLayoutAnimationController;
2597    }
2598
2599    /**
2600     * Indicates whether the children's drawing cache is used during a layout
2601     * animation. By default, the drawing cache is enabled but this will prevent
2602     * nested layout animations from working. To nest animations, you must disable
2603     * the cache.
2604     *
2605     * @return true if the animation cache is enabled, false otherwise
2606     *
2607     * @see #setAnimationCacheEnabled(boolean)
2608     * @see View#setDrawingCacheEnabled(boolean)
2609     */
2610    @ViewDebug.ExportedProperty
2611    public boolean isAnimationCacheEnabled() {
2612        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2613    }
2614
2615    /**
2616     * Enables or disables the children's drawing cache during a layout animation.
2617     * By default, the drawing cache is enabled but this will prevent nested
2618     * layout animations from working. To nest animations, you must disable the
2619     * cache.
2620     *
2621     * @param enabled true to enable the animation cache, false otherwise
2622     *
2623     * @see #isAnimationCacheEnabled()
2624     * @see View#setDrawingCacheEnabled(boolean)
2625     */
2626    public void setAnimationCacheEnabled(boolean enabled) {
2627        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
2628    }
2629
2630    /**
2631     * Indicates whether this ViewGroup will always try to draw its children using their
2632     * drawing cache. By default this property is enabled.
2633     *
2634     * @return true if the animation cache is enabled, false otherwise
2635     *
2636     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
2637     * @see #setChildrenDrawnWithCacheEnabled(boolean)
2638     * @see View#setDrawingCacheEnabled(boolean)
2639     */
2640    @ViewDebug.ExportedProperty
2641    public boolean isAlwaysDrawnWithCacheEnabled() {
2642        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
2643    }
2644
2645    /**
2646     * Indicates whether this ViewGroup will always try to draw its children using their
2647     * drawing cache. This property can be set to true when the cache rendering is
2648     * slightly different from the children's normal rendering. Renderings can be different,
2649     * for instance, when the cache's quality is set to low.
2650     *
2651     * When this property is disabled, the ViewGroup will use the drawing cache of its
2652     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
2653     * when to start using the drawing cache and when to stop using it.
2654     *
2655     * @param always true to always draw with the drawing cache, false otherwise
2656     *
2657     * @see #isAlwaysDrawnWithCacheEnabled()
2658     * @see #setChildrenDrawnWithCacheEnabled(boolean)
2659     * @see View#setDrawingCacheEnabled(boolean)
2660     * @see View#setDrawingCacheQuality(int)
2661     */
2662    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
2663        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
2664    }
2665
2666    /**
2667     * Indicates whether the ViewGroup is currently drawing its children using
2668     * their drawing cache.
2669     *
2670     * @return true if children should be drawn with their cache, false otherwise
2671     *
2672     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
2673     * @see #setChildrenDrawnWithCacheEnabled(boolean)
2674     */
2675    @ViewDebug.ExportedProperty
2676    protected boolean isChildrenDrawnWithCacheEnabled() {
2677        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
2678    }
2679
2680    /**
2681     * Tells the ViewGroup to draw its children using their drawing cache. This property
2682     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
2683     * will be used only if it has been enabled.
2684     *
2685     * Subclasses should call this method to start and stop using the drawing cache when
2686     * they perform performance sensitive operations, like scrolling or animating.
2687     *
2688     * @param enabled true if children should be drawn with their cache, false otherwise
2689     *
2690     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
2691     * @see #isChildrenDrawnWithCacheEnabled()
2692     */
2693    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
2694        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
2695    }
2696
2697    private void setBooleanFlag(int flag, boolean value) {
2698        if (value) {
2699            mGroupFlags |= flag;
2700        } else {
2701            mGroupFlags &= ~flag;
2702        }
2703    }
2704
2705    /**
2706     * Returns an integer indicating what types of drawing caches are kept in memory.
2707     *
2708     * @see #setPersistentDrawingCache(int)
2709     * @see #setAnimationCacheEnabled(boolean)
2710     *
2711     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
2712     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
2713     *         and {@link #PERSISTENT_ALL_CACHES}
2714     */
2715    @ViewDebug.ExportedProperty(mapping = {
2716        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
2717        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ANIMATION"),
2718        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
2719        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
2720    })
2721    public int getPersistentDrawingCache() {
2722        return mPersistentDrawingCache;
2723    }
2724
2725    /**
2726     * Indicates what types of drawing caches should be kept in memory after
2727     * they have been created.
2728     *
2729     * @see #getPersistentDrawingCache()
2730     * @see #setAnimationCacheEnabled(boolean)
2731     *
2732     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
2733     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
2734     *        and {@link #PERSISTENT_ALL_CACHES}
2735     */
2736    public void setPersistentDrawingCache(int drawingCacheToKeep) {
2737        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
2738    }
2739
2740    /**
2741     * Returns a new set of layout parameters based on the supplied attributes set.
2742     *
2743     * @param attrs the attributes to build the layout parameters from
2744     *
2745     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
2746     *         of its descendants
2747     */
2748    public LayoutParams generateLayoutParams(AttributeSet attrs) {
2749        return new LayoutParams(getContext(), attrs);
2750    }
2751
2752    /**
2753     * Returns a safe set of layout parameters based on the supplied layout params.
2754     * When a ViewGroup is passed a View whose layout params do not pass the test of
2755     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
2756     * is invoked. This method should return a new set of layout params suitable for
2757     * this ViewGroup, possibly by copying the appropriate attributes from the
2758     * specified set of layout params.
2759     *
2760     * @param p The layout parameters to convert into a suitable set of layout parameters
2761     *          for this ViewGroup.
2762     *
2763     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
2764     *         of its descendants
2765     */
2766    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
2767        return p;
2768    }
2769
2770    /**
2771     * Returns a set of default layout parameters. These parameters are requested
2772     * when the View passed to {@link #addView(View)} has no layout parameters
2773     * already set. If null is returned, an exception is thrown from addView.
2774     *
2775     * @return a set of default layout parameters or null
2776     */
2777    protected LayoutParams generateDefaultLayoutParams() {
2778        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
2779    }
2780
2781    /**
2782     * @hide
2783     */
2784    @Override
2785    protected boolean dispatchConsistencyCheck(int consistency) {
2786        boolean result = super.dispatchConsistencyCheck(consistency);
2787
2788        final int count = mChildrenCount;
2789        final View[] children = mChildren;
2790        for (int i = 0; i < count; i++) {
2791            if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
2792        }
2793
2794        return result;
2795    }
2796
2797    /**
2798     * @hide
2799     */
2800    @Override
2801    protected boolean onConsistencyCheck(int consistency) {
2802        boolean result = super.onConsistencyCheck(consistency);
2803
2804        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
2805        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
2806
2807        if (checkLayout) {
2808            final int count = mChildrenCount;
2809            final View[] children = mChildren;
2810            for (int i = 0; i < count; i++) {
2811                if (children[i].getParent() != this) {
2812                    result = false;
2813                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
2814                            "View " + children[i] + " has no parent/a parent that is not " + this);
2815                }
2816            }
2817        }
2818
2819        if (checkDrawing) {
2820            // If this group is dirty, check that the parent is dirty as well
2821            if ((mPrivateFlags & DIRTY_MASK) != 0) {
2822                final ViewParent parent = getParent();
2823                if (parent != null && !(parent instanceof ViewRoot)) {
2824                    if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
2825                        result = false;
2826                        android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
2827                                "ViewGroup " + this + " is dirty but its parent is not: " + this);
2828                    }
2829                }
2830            }
2831        }
2832
2833        return result;
2834    }
2835
2836    /**
2837     * {@inheritDoc}
2838     */
2839    @Override
2840    protected void debug(int depth) {
2841        super.debug(depth);
2842        String output;
2843
2844        if (mFocused != null) {
2845            output = debugIndent(depth);
2846            output += "mFocused";
2847            Log.d(VIEW_LOG_TAG, output);
2848        }
2849        if (mChildrenCount != 0) {
2850            output = debugIndent(depth);
2851            output += "{";
2852            Log.d(VIEW_LOG_TAG, output);
2853        }
2854        int count = mChildrenCount;
2855        for (int i = 0; i < count; i++) {
2856            View child = mChildren[i];
2857            child.debug(depth + 1);
2858        }
2859
2860        if (mChildrenCount != 0) {
2861            output = debugIndent(depth);
2862            output += "}";
2863            Log.d(VIEW_LOG_TAG, output);
2864        }
2865    }
2866
2867    /**
2868     * Returns the position in the group of the specified child view.
2869     *
2870     * @param child the view for which to get the position
2871     * @return a positive integer representing the position of the view in the
2872     *         group, or -1 if the view does not exist in the group
2873     */
2874    public int indexOfChild(View child) {
2875        final int count = mChildrenCount;
2876        final View[] children = mChildren;
2877        for (int i = 0; i < count; i++) {
2878            if (children[i] == child) {
2879                return i;
2880            }
2881        }
2882        return -1;
2883    }
2884
2885    /**
2886     * Returns the number of children in the group.
2887     *
2888     * @return a positive integer representing the number of children in
2889     *         the group
2890     */
2891    public int getChildCount() {
2892        return mChildrenCount;
2893    }
2894
2895    /**
2896     * Returns the view at the specified position in the group.
2897     *
2898     * @param index the position at which to get the view from
2899     * @return the view at the specified position or null if the position
2900     *         does not exist within the group
2901     */
2902    public View getChildAt(int index) {
2903        try {
2904            return mChildren[index];
2905        } catch (IndexOutOfBoundsException ex) {
2906            return null;
2907        }
2908    }
2909
2910    /**
2911     * Ask all of the children of this view to measure themselves, taking into
2912     * account both the MeasureSpec requirements for this view and its padding.
2913     * We skip children that are in the GONE state The heavy lifting is done in
2914     * getChildMeasureSpec.
2915     *
2916     * @param widthMeasureSpec The width requirements for this view
2917     * @param heightMeasureSpec The height requirements for this view
2918     */
2919    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
2920        final int size = mChildrenCount;
2921        final View[] children = mChildren;
2922        for (int i = 0; i < size; ++i) {
2923            final View child = children[i];
2924            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
2925                measureChild(child, widthMeasureSpec, heightMeasureSpec);
2926            }
2927        }
2928    }
2929
2930    /**
2931     * Ask one of the children of this view to measure itself, taking into
2932     * account both the MeasureSpec requirements for this view and its padding.
2933     * The heavy lifting is done in getChildMeasureSpec.
2934     *
2935     * @param child The child to measure
2936     * @param parentWidthMeasureSpec The width requirements for this view
2937     * @param parentHeightMeasureSpec The height requirements for this view
2938     */
2939    protected void measureChild(View child, int parentWidthMeasureSpec,
2940            int parentHeightMeasureSpec) {
2941        final LayoutParams lp = child.getLayoutParams();
2942
2943        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
2944                mPaddingLeft + mPaddingRight, lp.width);
2945        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
2946                mPaddingTop + mPaddingBottom, lp.height);
2947
2948        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
2949    }
2950
2951    /**
2952     * Ask one of the children of this view to measure itself, taking into
2953     * account both the MeasureSpec requirements for this view and its padding
2954     * and margins. The child must have MarginLayoutParams The heavy lifting is
2955     * done in getChildMeasureSpec.
2956     *
2957     * @param child The child to measure
2958     * @param parentWidthMeasureSpec The width requirements for this view
2959     * @param widthUsed Extra space that has been used up by the parent
2960     *        horizontally (possibly by other children of the parent)
2961     * @param parentHeightMeasureSpec The height requirements for this view
2962     * @param heightUsed Extra space that has been used up by the parent
2963     *        vertically (possibly by other children of the parent)
2964     */
2965    protected void measureChildWithMargins(View child,
2966            int parentWidthMeasureSpec, int widthUsed,
2967            int parentHeightMeasureSpec, int heightUsed) {
2968        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
2969
2970        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
2971                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
2972                        + widthUsed, lp.width);
2973        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
2974                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
2975                        + heightUsed, lp.height);
2976
2977        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
2978    }
2979
2980    /**
2981     * Does the hard part of measureChildren: figuring out the MeasureSpec to
2982     * pass to a particular child. This method figures out the right MeasureSpec
2983     * for one dimension (height or width) of one child view.
2984     *
2985     * The goal is to combine information from our MeasureSpec with the
2986     * LayoutParams of the child to get the best possible results. For example,
2987     * if the this view knows its size (because its MeasureSpec has a mode of
2988     * EXACTLY), and the child has indicated in its LayoutParams that it wants
2989     * to be the same size as the parent, the parent should ask the child to
2990     * layout given an exact size.
2991     *
2992     * @param spec The requirements for this view
2993     * @param padding The padding of this view for the current dimension and
2994     *        margins, if applicable
2995     * @param childDimension How big the child wants to be in the current
2996     *        dimension
2997     * @return a MeasureSpec integer for the child
2998     */
2999    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
3000        int specMode = MeasureSpec.getMode(spec);
3001        int specSize = MeasureSpec.getSize(spec);
3002
3003        int size = Math.max(0, specSize - padding);
3004
3005        int resultSize = 0;
3006        int resultMode = 0;
3007
3008        switch (specMode) {
3009        // Parent has imposed an exact size on us
3010        case MeasureSpec.EXACTLY:
3011            if (childDimension >= 0) {
3012                resultSize = childDimension;
3013                resultMode = MeasureSpec.EXACTLY;
3014            } else if (childDimension == LayoutParams.FILL_PARENT) {
3015                // Child wants to be our size. So be it.
3016                resultSize = size;
3017                resultMode = MeasureSpec.EXACTLY;
3018            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
3019                // Child wants to determine its own size. It can't be
3020                // bigger than us.
3021                resultSize = size;
3022                resultMode = MeasureSpec.AT_MOST;
3023            }
3024            break;
3025
3026        // Parent has imposed a maximum size on us
3027        case MeasureSpec.AT_MOST:
3028            if (childDimension >= 0) {
3029                // Child wants a specific size... so be it
3030                resultSize = childDimension;
3031                resultMode = MeasureSpec.EXACTLY;
3032            } else if (childDimension == LayoutParams.FILL_PARENT) {
3033                // Child wants to be our size, but our size is not fixed.
3034                // Constrain child to not be bigger than us.
3035                resultSize = size;
3036                resultMode = MeasureSpec.AT_MOST;
3037            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
3038                // Child wants to determine its own size. It can't be
3039                // bigger than us.
3040                resultSize = size;
3041                resultMode = MeasureSpec.AT_MOST;
3042            }
3043            break;
3044
3045        // Parent asked to see how big we want to be
3046        case MeasureSpec.UNSPECIFIED:
3047            if (childDimension >= 0) {
3048                // Child wants a specific size... let him have it
3049                resultSize = childDimension;
3050                resultMode = MeasureSpec.EXACTLY;
3051            } else if (childDimension == LayoutParams.FILL_PARENT) {
3052                // Child wants to be our size... find out how big it should
3053                // be
3054                resultSize = 0;
3055                resultMode = MeasureSpec.UNSPECIFIED;
3056            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
3057                // Child wants to determine its own size.... find out how
3058                // big it should be
3059                resultSize = 0;
3060                resultMode = MeasureSpec.UNSPECIFIED;
3061            }
3062            break;
3063        }
3064        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
3065    }
3066
3067
3068    /**
3069     * Removes any pending animations for views that have been removed. Call
3070     * this if you don't want animations for exiting views to stack up.
3071     */
3072    public void clearDisappearingChildren() {
3073        if (mDisappearingChildren != null) {
3074            mDisappearingChildren.clear();
3075        }
3076    }
3077
3078    /**
3079     * Add a view which is removed from mChildren but still needs animation
3080     *
3081     * @param v View to add
3082     */
3083    private void addDisappearingView(View v) {
3084        ArrayList<View> disappearingChildren = mDisappearingChildren;
3085
3086        if (disappearingChildren == null) {
3087            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
3088        }
3089
3090        disappearingChildren.add(v);
3091    }
3092
3093    /**
3094     * Cleanup a view when its animation is done. This may mean removing it from
3095     * the list of disappearing views.
3096     *
3097     * @param view The view whose animation has finished
3098     * @param animation The animation, cannot be null
3099     */
3100    private void finishAnimatingView(final View view, Animation animation) {
3101        final ArrayList<View> disappearingChildren = mDisappearingChildren;
3102        if (disappearingChildren != null) {
3103            if (disappearingChildren.contains(view)) {
3104                disappearingChildren.remove(view);
3105
3106                if (view.mAttachInfo != null) {
3107                    view.dispatchDetachedFromWindow();
3108                }
3109
3110                view.clearAnimation();
3111                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
3112            }
3113        }
3114
3115        if (animation != null && !animation.getFillAfter()) {
3116            view.clearAnimation();
3117        }
3118
3119        if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
3120            view.onAnimationEnd();
3121            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
3122            // so we'd rather be safe than sorry
3123            view.mPrivateFlags &= ~ANIMATION_STARTED;
3124            // Draw one more frame after the animation is done
3125            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
3126        }
3127    }
3128
3129    /**
3130     * {@inheritDoc}
3131     */
3132    @Override
3133    public boolean gatherTransparentRegion(Region region) {
3134        // If no transparent regions requested, we are always opaque.
3135        final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
3136        if (meOpaque && region == null) {
3137            // The caller doesn't care about the region, so stop now.
3138            return true;
3139        }
3140        super.gatherTransparentRegion(region);
3141        final View[] children = mChildren;
3142        final int count = mChildrenCount;
3143        boolean noneOfTheChildrenAreTransparent = true;
3144        for (int i = 0; i < count; i++) {
3145            final View child = children[i];
3146            if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) {
3147                if (!child.gatherTransparentRegion(region)) {
3148                    noneOfTheChildrenAreTransparent = false;
3149                }
3150            }
3151        }
3152        return meOpaque || noneOfTheChildrenAreTransparent;
3153    }
3154
3155    /**
3156     * {@inheritDoc}
3157     */
3158    public void requestTransparentRegion(View child) {
3159        if (child != null) {
3160            child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
3161            if (mParent != null) {
3162                mParent.requestTransparentRegion(this);
3163            }
3164        }
3165    }
3166
3167
3168    @Override
3169    protected boolean fitSystemWindows(Rect insets) {
3170        boolean done = super.fitSystemWindows(insets);
3171        if (!done) {
3172            final int count = mChildrenCount;
3173            final View[] children = mChildren;
3174            for (int i = 0; i < count; i++) {
3175                done = children[i].fitSystemWindows(insets);
3176                if (done) {
3177                    break;
3178                }
3179            }
3180        }
3181        return done;
3182    }
3183
3184    /**
3185     * Returns the animation listener to which layout animation events are
3186     * sent.
3187     *
3188     * @return an {@link android.view.animation.Animation.AnimationListener}
3189     */
3190    public Animation.AnimationListener getLayoutAnimationListener() {
3191        return mAnimationListener;
3192    }
3193
3194    @Override
3195    protected void drawableStateChanged() {
3196        super.drawableStateChanged();
3197
3198        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
3199            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
3200                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
3201                        + " child has duplicateParentState set to true");
3202            }
3203
3204            final View[] children = mChildren;
3205            final int count = mChildrenCount;
3206
3207            for (int i = 0; i < count; i++) {
3208                final View child = children[i];
3209                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
3210                    child.refreshDrawableState();
3211                }
3212            }
3213        }
3214    }
3215
3216    @Override
3217    protected int[] onCreateDrawableState(int extraSpace) {
3218        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
3219            return super.onCreateDrawableState(extraSpace);
3220        }
3221
3222        int need = 0;
3223        int n = getChildCount();
3224        for (int i = 0; i < n; i++) {
3225            int[] childState = getChildAt(i).getDrawableState();
3226
3227            if (childState != null) {
3228                need += childState.length;
3229            }
3230        }
3231
3232        int[] state = super.onCreateDrawableState(extraSpace + need);
3233
3234        for (int i = 0; i < n; i++) {
3235            int[] childState = getChildAt(i).getDrawableState();
3236
3237            if (childState != null) {
3238                state = mergeDrawableStates(state, childState);
3239            }
3240        }
3241
3242        return state;
3243    }
3244
3245    /**
3246     * Sets whether this ViewGroup's drawable states also include
3247     * its children's drawable states.  This is used, for example, to
3248     * make a group appear to be focused when its child EditText or button
3249     * is focused.
3250     */
3251    public void setAddStatesFromChildren(boolean addsStates) {
3252        if (addsStates) {
3253            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
3254        } else {
3255            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
3256        }
3257
3258        refreshDrawableState();
3259    }
3260
3261    /**
3262     * Returns whether this ViewGroup's drawable states also include
3263     * its children's drawable states.  This is used, for example, to
3264     * make a group appear to be focused when its child EditText or button
3265     * is focused.
3266     */
3267    public boolean addStatesFromChildren() {
3268        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
3269    }
3270
3271    /**
3272     * If {link #addStatesFromChildren} is true, refreshes this group's
3273     * drawable state (to include the states from its children).
3274     */
3275    public void childDrawableStateChanged(View child) {
3276        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
3277            refreshDrawableState();
3278        }
3279    }
3280
3281    /**
3282     * Specifies the animation listener to which layout animation events must
3283     * be sent. Only
3284     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
3285     * and
3286     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
3287     * are invoked.
3288     *
3289     * @param animationListener the layout animation listener
3290     */
3291    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
3292        mAnimationListener = animationListener;
3293    }
3294
3295    /**
3296     * LayoutParams are used by views to tell their parents how they want to be
3297     * laid out. See
3298     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
3299     * for a list of all child view attributes that this class supports.
3300     *
3301     * <p>
3302     * The base LayoutParams class just describes how big the view wants to be
3303     * for both width and height. For each dimension, it can specify one of:
3304     * <ul>
3305     * <li> an exact number
3306     * <li>FILL_PARENT, which means the view wants to be as big as its parent
3307     * (minus padding)
3308     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
3309     * to enclose its content (plus padding)
3310     * </ul>
3311     * There are subclasses of LayoutParams for different subclasses of
3312     * ViewGroup. For example, AbsoluteLayout has its own subclass of
3313     * LayoutParams which adds an X and Y value.
3314     *
3315     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
3316     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
3317     */
3318    public static class LayoutParams {
3319        /**
3320         * Special value for the height or width requested by a View.
3321         * FILL_PARENT means that the view wants to fill the available space
3322         * within the parent, taking the parent's padding into account.
3323         */
3324        public static final int FILL_PARENT = -1;
3325
3326        /**
3327         * Special value for the height or width requested by a View.
3328         * WRAP_CONTENT means that the view wants to be just large enough to fit
3329         * its own internal content, taking its own padding into account.
3330         */
3331        public static final int WRAP_CONTENT = -2;
3332
3333        /**
3334         * Information about how wide the view wants to be. Can be an exact
3335         * size, or one of the constants FILL_PARENT or WRAP_CONTENT.
3336         */
3337        @ViewDebug.ExportedProperty(mapping = {
3338            @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"),
3339            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
3340        })
3341        public int width;
3342
3343        /**
3344         * Information about how tall the view wants to be. Can be an exact
3345         * size, or one of the constants FILL_PARENT or WRAP_CONTENT.
3346         */
3347        @ViewDebug.ExportedProperty(mapping = {
3348            @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"),
3349            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
3350        })
3351        public int height;
3352
3353        /**
3354         * Used to animate layouts.
3355         */
3356        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
3357
3358        /**
3359         * Creates a new set of layout parameters. The values are extracted from
3360         * the supplied attributes set and context. The XML attributes mapped
3361         * to this set of layout parameters are:
3362         *
3363         * <ul>
3364         *   <li><code>layout_width</code>: the width, either an exact value,
3365         *   {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li>
3366         *   <li><code>layout_height</code>: the height, either an exact value,
3367         *   {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li>
3368         * </ul>
3369         *
3370         * @param c the application environment
3371         * @param attrs the set of attributes from which to extract the layout
3372         *              parameters' values
3373         */
3374        public LayoutParams(Context c, AttributeSet attrs) {
3375            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
3376            setBaseAttributes(a,
3377                    R.styleable.ViewGroup_Layout_layout_width,
3378                    R.styleable.ViewGroup_Layout_layout_height);
3379            a.recycle();
3380        }
3381
3382        /**
3383         * Creates a new set of layout parameters with the specified width
3384         * and height.
3385         *
3386         * @param width the width, either {@link #FILL_PARENT},
3387         *        {@link #WRAP_CONTENT} or a fixed size in pixels
3388         * @param height the height, either {@link #FILL_PARENT},
3389         *        {@link #WRAP_CONTENT} or a fixed size in pixels
3390         */
3391        public LayoutParams(int width, int height) {
3392            this.width = width;
3393            this.height = height;
3394        }
3395
3396        /**
3397         * Copy constructor. Clones the width and height values of the source.
3398         *
3399         * @param source The layout params to copy from.
3400         */
3401        public LayoutParams(LayoutParams source) {
3402            this.width = source.width;
3403            this.height = source.height;
3404        }
3405
3406        /**
3407         * Used internally by MarginLayoutParams.
3408         * @hide
3409         */
3410        LayoutParams() {
3411        }
3412
3413        /**
3414         * Extracts the layout parameters from the supplied attributes.
3415         *
3416         * @param a the style attributes to extract the parameters from
3417         * @param widthAttr the identifier of the width attribute
3418         * @param heightAttr the identifier of the height attribute
3419         */
3420        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
3421            width = a.getLayoutDimension(widthAttr, "layout_width");
3422            height = a.getLayoutDimension(heightAttr, "layout_height");
3423        }
3424
3425        /**
3426         * Returns a String representation of this set of layout parameters.
3427         *
3428         * @param output the String to prepend to the internal representation
3429         * @return a String with the following format: output +
3430         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
3431         *
3432         * @hide
3433         */
3434        public String debug(String output) {
3435            return output + "ViewGroup.LayoutParams={ width="
3436                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
3437        }
3438
3439        /**
3440         * Converts the specified size to a readable String.
3441         *
3442         * @param size the size to convert
3443         * @return a String instance representing the supplied size
3444         *
3445         * @hide
3446         */
3447        protected static String sizeToString(int size) {
3448            if (size == WRAP_CONTENT) {
3449                return "wrap-content";
3450            }
3451            if (size == FILL_PARENT) {
3452                return "fill-parent";
3453            }
3454            return String.valueOf(size);
3455        }
3456    }
3457
3458    /**
3459     * Per-child layout information for layouts that support margins.
3460     * See
3461     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
3462     * for a list of all child view attributes that this class supports.
3463     */
3464    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
3465        /**
3466         * The left margin in pixels of the child.
3467         */
3468        @ViewDebug.ExportedProperty
3469        public int leftMargin;
3470
3471        /**
3472         * The top margin in pixels of the child.
3473         */
3474        @ViewDebug.ExportedProperty
3475        public int topMargin;
3476
3477        /**
3478         * The right margin in pixels of the child.
3479         */
3480        @ViewDebug.ExportedProperty
3481        public int rightMargin;
3482
3483        /**
3484         * The bottom margin in pixels of the child.
3485         */
3486        @ViewDebug.ExportedProperty
3487        public int bottomMargin;
3488
3489        /**
3490         * Creates a new set of layout parameters. The values are extracted from
3491         * the supplied attributes set and context.
3492         *
3493         * @param c the application environment
3494         * @param attrs the set of attributes from which to extract the layout
3495         *              parameters' values
3496         */
3497        public MarginLayoutParams(Context c, AttributeSet attrs) {
3498            super();
3499
3500            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
3501            setBaseAttributes(a,
3502                    R.styleable.ViewGroup_MarginLayout_layout_width,
3503                    R.styleable.ViewGroup_MarginLayout_layout_height);
3504
3505            int margin = a.getDimensionPixelSize(
3506                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
3507            if (margin >= 0) {
3508                leftMargin = margin;
3509                topMargin = margin;
3510                rightMargin= margin;
3511                bottomMargin = margin;
3512            } else {
3513                leftMargin = a.getDimensionPixelSize(
3514                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
3515                topMargin = a.getDimensionPixelSize(
3516                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
3517                rightMargin = a.getDimensionPixelSize(
3518                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
3519                bottomMargin = a.getDimensionPixelSize(
3520                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
3521            }
3522
3523            a.recycle();
3524        }
3525
3526        /**
3527         * {@inheritDoc}
3528         */
3529        public MarginLayoutParams(int width, int height) {
3530            super(width, height);
3531        }
3532
3533        /**
3534         * Copy constructor. Clones the width, height and margin values of the source.
3535         *
3536         * @param source The layout params to copy from.
3537         */
3538        public MarginLayoutParams(MarginLayoutParams source) {
3539            this.width = source.width;
3540            this.height = source.height;
3541
3542            this.leftMargin = source.leftMargin;
3543            this.topMargin = source.topMargin;
3544            this.rightMargin = source.rightMargin;
3545            this.bottomMargin = source.bottomMargin;
3546        }
3547
3548        /**
3549         * {@inheritDoc}
3550         */
3551        public MarginLayoutParams(LayoutParams source) {
3552            super(source);
3553        }
3554
3555        /**
3556         * Sets the margins, in pixels.
3557         *
3558         * @param left the left margin size
3559         * @param top the top margin size
3560         * @param right the right margin size
3561         * @param bottom the bottom margin size
3562         *
3563         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
3564         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
3565         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
3566         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
3567         */
3568        public void setMargins(int left, int top, int right, int bottom) {
3569            leftMargin = left;
3570            topMargin = top;
3571            rightMargin = right;
3572            bottomMargin = bottom;
3573        }
3574    }
3575}
3576