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