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