ViewGroup.java revision 24443ea3992e372e47daa50266b0f2ec38cac388
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            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
1212                    mScrollX + mRight - mLeft - mPaddingRight,
1213                    mScrollY + mBottom - mTop - mPaddingBottom);
1214
1215        }
1216
1217        // We will draw our child's animation, let's reset the flag
1218        mPrivateFlags &= ~DRAW_ANIMATION;
1219        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
1220
1221        boolean more = false;
1222        final long drawingTime = getDrawingTime();
1223
1224        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
1225            for (int i = 0; i < count; i++) {
1226                final View child = children[i];
1227                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
1228                    more |= drawChild(canvas, child, drawingTime);
1229                }
1230            }
1231        } else {
1232            for (int i = 0; i < count; i++) {
1233                final View child = children[getChildDrawingOrder(count, i)];
1234                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
1235                    more |= drawChild(canvas, child, drawingTime);
1236                }
1237            }
1238        }
1239
1240        // Draw any disappearing views that have animations
1241        if (mDisappearingChildren != null) {
1242            final ArrayList<View> disappearingChildren = mDisappearingChildren;
1243            final int disappearingCount = disappearingChildren.size() - 1;
1244            // Go backwards -- we may delete as animations finish
1245            for (int i = disappearingCount; i >= 0; i--) {
1246                final View child = disappearingChildren.get(i);
1247                more |= drawChild(canvas, child, drawingTime);
1248            }
1249        }
1250
1251        if (clipToPadding) {
1252            canvas.restoreToCount(saveCount);
1253        }
1254
1255        // mGroupFlags might have been updated by drawChild()
1256        flags = mGroupFlags;
1257
1258        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
1259            invalidate();
1260        }
1261
1262        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
1263                mLayoutAnimationController.isDone() && !more) {
1264            // We want to erase the drawing cache and notify the listener after the
1265            // next frame is drawn because one extra invalidate() is caused by
1266            // drawChild() after the animation is over
1267            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
1268            final Runnable end = new Runnable() {
1269               public void run() {
1270                   notifyAnimationListener();
1271               }
1272            };
1273            post(end);
1274        }
1275    }
1276
1277    /**
1278     * Returns the index of the child to draw for this iteration. Override this
1279     * if you want to change the drawing order of children. By default, it
1280     * returns i.
1281     * <p>
1282     * NOTE: In order for this method to be called, the
1283     * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set.
1284     *
1285     * @param i The current iteration.
1286     * @return The index of the child to draw this iteration.
1287     */
1288    protected int getChildDrawingOrder(int childCount, int i) {
1289        return i;
1290    }
1291
1292    private void notifyAnimationListener() {
1293        mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
1294        mGroupFlags |= FLAG_ANIMATION_DONE;
1295
1296        if (mAnimationListener != null) {
1297           final Runnable end = new Runnable() {
1298               public void run() {
1299                   mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
1300               }
1301           };
1302           post(end);
1303        }
1304
1305        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1306            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
1307            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
1308                setChildrenDrawingCacheEnabled(false);
1309            }
1310        }
1311
1312        invalidate();
1313    }
1314
1315    /**
1316     * Draw one child of this View Group. This method is responsible for getting
1317     * the canvas in the right state. This includes clipping, translating so
1318     * that the child's scrolled origin is at 0, 0, and applying any animation
1319     * transformations.
1320     *
1321     * @param canvas The canvas on which to draw the child
1322     * @param child Who to draw
1323     * @param drawingTime The time at which draw is occuring
1324     * @return True if an invalidate() was issued
1325     */
1326    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
1327        boolean more = false;
1328
1329        final int cl = child.mLeft;
1330        final int ct = child.mTop;
1331        final int cr = child.mRight;
1332        final int cb = child.mBottom;
1333
1334        final int flags = mGroupFlags;
1335
1336        if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
1337            if (mChildTransformation != null) {
1338                mChildTransformation.clear();
1339            }
1340            mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
1341        }
1342
1343        Transformation transformToApply = null;
1344        final Animation a = child.getAnimation();
1345        boolean concatMatrix = false;
1346
1347        if (a != null) {
1348            if (mInvalidateRegion == null) {
1349                mInvalidateRegion = new RectF();
1350            }
1351            final RectF region = mInvalidateRegion;
1352
1353            final boolean initialized = a.isInitialized();
1354            if (!initialized) {
1355                a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
1356                a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
1357                child.onAnimationStart();
1358            }
1359
1360            if (mChildTransformation == null) {
1361                mChildTransformation = new Transformation();
1362            }
1363            more = a.getTransformation(drawingTime, mChildTransformation);
1364            transformToApply = mChildTransformation;
1365
1366            concatMatrix = a.willChangeTransformationMatrix();
1367
1368            if (more) {
1369                if (!a.willChangeBounds()) {
1370                    if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
1371                            FLAG_OPTIMIZE_INVALIDATE) {
1372                        mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
1373                    } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
1374                        // The child need to draw an animation, potentially offscreen, so
1375                        // make sure we do not cancel invalidate requests
1376                        mPrivateFlags |= DRAW_ANIMATION;
1377                        invalidate(cl, ct, cr, cb);
1378                    }
1379                } else {
1380                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, transformToApply);
1381
1382                    // The child need to draw an animation, potentially offscreen, so
1383                    // make sure we do not cancel invalidate requests
1384                    mPrivateFlags |= DRAW_ANIMATION;
1385
1386                    final int left = cl + (int) region.left;
1387                    final int top = ct + (int) region.top;
1388                    invalidate(left, top, left + (int) region.width(), top + (int) region.height());
1389                }
1390            }
1391        } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
1392                FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
1393            if (mChildTransformation == null) {
1394                mChildTransformation = new Transformation();
1395            }
1396            final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
1397            if (hasTransform) {
1398                final int transformType = mChildTransformation.getTransformationType();
1399                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
1400                        mChildTransformation : null;
1401                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
1402            }
1403        }
1404
1405        // Clear the flag as early as possible to allow draw() implementations
1406        // to call invalidate() successfully when doing animations
1407        child.mPrivateFlags |= DRAWN;
1408
1409        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
1410                (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
1411            return more;
1412        }
1413
1414        child.computeScroll();
1415
1416        final int sx = child.mScrollX;
1417        final int sy = child.mScrollY;
1418
1419        Bitmap cache = null;
1420        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
1421                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
1422            cache = child.getDrawingCache();
1423        }
1424
1425        final boolean hasNoCache = cache == null;
1426
1427        final int restoreTo = canvas.save();
1428        if (hasNoCache) {
1429            canvas.translate(cl - sx, ct - sy);
1430        } else {
1431            canvas.translate(cl, ct);
1432        }
1433
1434        float alpha = 1.0f;
1435
1436        if (transformToApply != null) {
1437            if (concatMatrix) {
1438                int transX = 0;
1439                int transY = 0;
1440                if (hasNoCache) {
1441                    transX = -sx;
1442                    transY = -sy;
1443                }
1444                // Undo the scroll translation, apply the transformation matrix,
1445                // then redo the scroll translate to get the correct result.
1446                canvas.translate(-transX, -transY);
1447                canvas.concat(transformToApply.getMatrix());
1448                canvas.translate(transX, transY);
1449                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1450            }
1451
1452            alpha = transformToApply.getAlpha();
1453            if (alpha < 1.0f) {
1454                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1455            }
1456
1457            if (alpha < 1.0f && hasNoCache) {
1458                final int multipliedAlpha = (int) (255 * alpha);
1459                if (!child.onSetAlpha(multipliedAlpha)) {
1460                    canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
1461                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
1462                } else {
1463                    child.mPrivateFlags |= ALPHA_SET;
1464                }
1465            }
1466        } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
1467            child.onSetAlpha(255);
1468        }
1469
1470        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
1471            if (hasNoCache) {
1472                canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
1473            } else {
1474                canvas.clipRect(0, 0, cr - cl, cb - ct);
1475            }
1476        }
1477
1478        if (hasNoCache) {
1479            // Fast path for layouts with no backgrounds
1480            if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
1481                if (ViewDebug.TRACE_HIERARCHY) {
1482                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
1483                }
1484                child.dispatchDraw(canvas);
1485            } else {
1486                child.draw(canvas);
1487            }
1488        } else {
1489            final Paint cachePaint = mCachePaint;
1490            if (alpha < 1.0f) {
1491                cachePaint.setAlpha((int) (alpha * 255));
1492                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
1493            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
1494                cachePaint.setAlpha(255);
1495                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
1496            }
1497            if (ViewRoot.PROFILE_DRAWING) {
1498                EventLog.writeEvent(60003, hashCode());
1499            }
1500            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
1501        }
1502
1503        canvas.restoreToCount(restoreTo);
1504
1505        if (a != null && !more) {
1506            child.onSetAlpha(255);
1507            finishAnimatingView(child, a);
1508        }
1509
1510        return more;
1511    }
1512
1513    /**
1514     * By default, children are clipped to their bounds before drawing. This
1515     * allows view groups to override this behavior for animations, etc.
1516     *
1517     * @param clipChildren true to clip children to their bounds,
1518     *        false otherwise
1519     * @attr ref android.R.styleable#ViewGroup_clipChildren
1520     */
1521    public void setClipChildren(boolean clipChildren) {
1522        setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
1523    }
1524
1525    /**
1526     * By default, children are clipped to the padding of the ViewGroup. This
1527     * allows view groups to override this behavior
1528     *
1529     * @param clipToPadding true to clip children to the padding of the
1530     *        group, false otherwise
1531     * @attr ref android.R.styleable#ViewGroup_clipToPadding
1532     */
1533    public void setClipToPadding(boolean clipToPadding) {
1534        setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
1535    }
1536
1537    /**
1538     * {@inheritDoc}
1539     */
1540    @Override
1541    public void dispatchSetSelected(boolean selected) {
1542        final View[] children = mChildren;
1543        final int count = mChildrenCount;
1544        for (int i = 0; i < count; i++) {
1545            children[i].setSelected(selected);
1546        }
1547    }
1548
1549    @Override
1550    protected void dispatchSetPressed(boolean pressed) {
1551        final View[] children = mChildren;
1552        final int count = mChildrenCount;
1553        for (int i = 0; i < count; i++) {
1554            children[i].setPressed(pressed);
1555        }
1556    }
1557
1558    /**
1559     * When this property is set to true, this ViewGroup supports static transformations on
1560     * children; this causes
1561     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
1562     * invoked when a child is drawn.
1563     *
1564     * Any subclass overriding
1565     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
1566     * set this property to true.
1567     *
1568     * @param enabled True to enable static transformations on children, false otherwise.
1569     *
1570     * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
1571     */
1572    protected void setStaticTransformationsEnabled(boolean enabled) {
1573        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
1574    }
1575
1576    /**
1577     * {@inheritDoc}
1578     *
1579     * @see #setStaticTransformationsEnabled(boolean)
1580     */
1581    protected boolean getChildStaticTransformation(View child, Transformation t) {
1582        return false;
1583    }
1584
1585    /**
1586     * {@hide}
1587     */
1588    @Override
1589    protected View findViewTraversal(int id) {
1590        if (id == mID) {
1591            return this;
1592        }
1593
1594        final View[] where = mChildren;
1595        final int len = mChildrenCount;
1596
1597        for (int i = 0; i < len; i++) {
1598            View v = where[i];
1599
1600            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
1601                v = v.findViewById(id);
1602
1603                if (v != null) {
1604                    return v;
1605                }
1606            }
1607        }
1608
1609        return null;
1610    }
1611
1612    /**
1613     * {@hide}
1614     */
1615    @Override
1616    protected View findViewWithTagTraversal(Object tag) {
1617        if (tag != null && tag.equals(mTag)) {
1618            return this;
1619        }
1620
1621        final View[] where = mChildren;
1622        final int len = mChildrenCount;
1623
1624        for (int i = 0; i < len; i++) {
1625            View v = where[i];
1626
1627            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
1628                v = v.findViewWithTag(tag);
1629
1630                if (v != null) {
1631                    return v;
1632                }
1633            }
1634        }
1635
1636        return null;
1637    }
1638
1639    /**
1640     * Adds a child view. If no layout parameters are already set on the child, the
1641     * default parameters for this ViewGroup are set on the child.
1642     *
1643     * @param child the child view to add
1644     *
1645     * @see #generateDefaultLayoutParams()
1646     */
1647    public void addView(View child) {
1648        addView(child, -1);
1649    }
1650
1651    /**
1652     * Adds a child view. If no layout parameters are already set on the child, the
1653     * default parameters for this ViewGroup are set on the child.
1654     *
1655     * @param child the child view to add
1656     * @param index the position at which to add the child
1657     *
1658     * @see #generateDefaultLayoutParams()
1659     */
1660    public void addView(View child, int index) {
1661        LayoutParams params = child.getLayoutParams();
1662        if (params == null) {
1663            params = generateDefaultLayoutParams();
1664            if (params == null) {
1665                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
1666            }
1667        }
1668        addView(child, index, params);
1669    }
1670
1671    /**
1672     * Adds a child view with this ViewGroup's default layout parameters and the
1673     * specified width and height.
1674     *
1675     * @param child the child view to add
1676     */
1677    public void addView(View child, int width, int height) {
1678        final LayoutParams params = generateDefaultLayoutParams();
1679        params.width = width;
1680        params.height = height;
1681        addView(child, -1, params);
1682    }
1683
1684    /**
1685     * Adds a child view with the specified layout parameters.
1686     *
1687     * @param child the child view to add
1688     * @param params the layout parameters to set on the child
1689     */
1690    public void addView(View child, LayoutParams params) {
1691        addView(child, -1, params);
1692    }
1693
1694    /**
1695     * Adds a child view with the specified layout parameters.
1696     *
1697     * @param child the child view to add
1698     * @param index the position at which to add the child
1699     * @param params the layout parameters to set on the child
1700     */
1701    public void addView(View child, int index, LayoutParams params) {
1702        if (DBG) {
1703            System.out.println(this + " addView");
1704        }
1705
1706        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
1707        // therefore, we call requestLayout() on ourselves before, so that the child's request
1708        // will be blocked at our level
1709        requestLayout();
1710        invalidate();
1711        addViewInner(child, index, params, false);
1712    }
1713
1714    /**
1715     * {@inheritDoc}
1716     */
1717    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
1718        if (!checkLayoutParams(params)) {
1719            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
1720        }
1721        if (view.mParent != this) {
1722            throw new IllegalArgumentException("Given view not a child of " + this);
1723        }
1724        view.setLayoutParams(params);
1725    }
1726
1727    /**
1728     * {@inheritDoc}
1729     */
1730    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1731        return  p != null;
1732    }
1733
1734    /**
1735     * Interface definition for a callback to be invoked when the hierarchy
1736     * within this view changed. The hierarchy changes whenever a child is added
1737     * to or removed from this view.
1738     */
1739    public interface OnHierarchyChangeListener {
1740        /**
1741         * Called when a new child is added to a parent view.
1742         *
1743         * @param parent the view in which a child was added
1744         * @param child the new child view added in the hierarchy
1745         */
1746        void onChildViewAdded(View parent, View child);
1747
1748        /**
1749         * Called when a child is removed from a parent view.
1750         *
1751         * @param parent the view from which the child was removed
1752         * @param child the child removed from the hierarchy
1753         */
1754        void onChildViewRemoved(View parent, View child);
1755    }
1756
1757    /**
1758     * Register a callback to be invoked when a child is added to or removed
1759     * from this view.
1760     *
1761     * @param listener the callback to invoke on hierarchy change
1762     */
1763    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
1764        mOnHierarchyChangeListener = listener;
1765    }
1766
1767    /**
1768     * Adds a view during layout. This is useful if in your onLayout() method,
1769     * you need to add more views (as does the list view for example).
1770     *
1771     * If index is negative, it means put it at the end of the list.
1772     *
1773     * @param child the view to add to the group
1774     * @param index the index at which the child must be added
1775     * @param params the layout parameters to associate with the child
1776     * @return true if the child was added, false otherwise
1777     */
1778    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
1779        return addViewInLayout(child, index, params, false);
1780    }
1781
1782    /**
1783     * Adds a view during layout. This is useful if in your onLayout() method,
1784     * you need to add more views (as does the list view for example).
1785     *
1786     * If index is negative, it means put it at the end of the list.
1787     *
1788     * @param child the view to add to the group
1789     * @param index the index at which the child must be added
1790     * @param params the layout parameters to associate with the child
1791     * @param preventRequestLayout if true, calling this method will not trigger a
1792     *        layout request on child
1793     * @return true if the child was added, false otherwise
1794     */
1795    protected boolean addViewInLayout(View child, int index, LayoutParams params,
1796            boolean preventRequestLayout) {
1797        child.mParent = null;
1798        addViewInner(child, index, params, preventRequestLayout);
1799        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
1800        return true;
1801    }
1802
1803    /**
1804     * Prevents the specified child to be laid out during the next layout pass.
1805     *
1806     * @param child the child on which to perform the cleanup
1807     */
1808    protected void cleanupLayoutState(View child) {
1809        child.mPrivateFlags &= ~View.FORCE_LAYOUT;
1810    }
1811
1812    private void addViewInner(View child, int index, LayoutParams params,
1813            boolean preventRequestLayout) {
1814
1815        if (child.getParent() != null) {
1816            throw new IllegalStateException("The specified child already has a parent. " +
1817                    "You must call removeView() on the child's parent first.");
1818        }
1819
1820        if (!checkLayoutParams(params)) {
1821            params = generateLayoutParams(params);
1822        }
1823
1824        if (preventRequestLayout) {
1825            child.mLayoutParams = params;
1826        } else {
1827            child.setLayoutParams(params);
1828        }
1829
1830        if (index < 0) {
1831            index = mChildrenCount;
1832        }
1833
1834        addInArray(child, index);
1835
1836        // tell our children
1837        if (preventRequestLayout) {
1838            child.assignParent(this);
1839        } else {
1840            child.mParent = this;
1841        }
1842
1843        if (child.hasFocus()) {
1844            requestChildFocus(child, child.findFocus());
1845        }
1846
1847        AttachInfo ai = mAttachInfo;
1848        if (ai != null) {
1849            boolean lastKeepOn = ai.mKeepScreenOn;
1850            ai.mKeepScreenOn = false;
1851            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
1852            if (ai.mKeepScreenOn) {
1853                needGlobalAttributesUpdate(true);
1854            }
1855            ai.mKeepScreenOn = lastKeepOn;
1856        }
1857
1858        if (mOnHierarchyChangeListener != null) {
1859            mOnHierarchyChangeListener.onChildViewAdded(this, child);
1860        }
1861
1862        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
1863            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
1864        }
1865    }
1866
1867    private void addInArray(View child, int index) {
1868        View[] children = mChildren;
1869        final int count = mChildrenCount;
1870        final int size = children.length;
1871        if (index == count) {
1872            if (size == count) {
1873                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
1874                System.arraycopy(children, 0, mChildren, 0, size);
1875                children = mChildren;
1876            }
1877            children[mChildrenCount++] = child;
1878        } else if (index < count) {
1879            if (size == count) {
1880                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
1881                System.arraycopy(children, 0, mChildren, 0, index);
1882                System.arraycopy(children, index, mChildren, index + 1, count - index);
1883                children = mChildren;
1884            } else {
1885                System.arraycopy(children, index, children, index + 1, count - index);
1886            }
1887            children[index] = child;
1888            mChildrenCount++;
1889        } else {
1890            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
1891        }
1892    }
1893
1894    // This method also sets the child's mParent to null
1895    private void removeFromArray(int index) {
1896        final View[] children = mChildren;
1897        children[index].mParent = null;
1898        final int count = mChildrenCount;
1899        if (index == count - 1) {
1900            children[--mChildrenCount] = null;
1901        } else if (index >= 0 && index < count) {
1902            System.arraycopy(children, index + 1, children, index, count - index - 1);
1903            children[--mChildrenCount] = null;
1904        } else {
1905            throw new IndexOutOfBoundsException();
1906        }
1907    }
1908
1909    // This method also sets the children's mParent to null
1910    private void removeFromArray(int start, int count) {
1911        final View[] children = mChildren;
1912        final int childrenCount = mChildrenCount;
1913
1914        start = Math.max(0, start);
1915        final int end = Math.min(childrenCount, start + count);
1916
1917        if (start == end) {
1918            return;
1919        }
1920
1921        if (end == childrenCount) {
1922            for (int i = start; i < end; i++) {
1923                children[i].mParent = null;
1924                children[i] = null;
1925            }
1926        } else {
1927            for (int i = start; i < end; i++) {
1928                children[i].mParent = null;
1929            }
1930
1931            // Since we're looping above, we might as well do the copy, but is arraycopy()
1932            // faster than the extra 2 bounds checks we would do in the loop?
1933            System.arraycopy(children, end, children, start, childrenCount - end);
1934
1935            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
1936                children[i] = null;
1937            }
1938        }
1939
1940        mChildrenCount -= (end - start);
1941    }
1942
1943    private void bindLayoutAnimation(View child) {
1944        Animation a = mLayoutAnimationController.getAnimationForView(child);
1945        child.setAnimation(a);
1946    }
1947
1948    /**
1949     * Subclasses should override this method to set layout animation
1950     * parameters on the supplied child.
1951     *
1952     * @param child the child to associate with animation parameters
1953     * @param params the child's layout parameters which hold the animation
1954     *        parameters
1955     * @param index the index of the child in the view group
1956     * @param count the number of children in the view group
1957     */
1958    protected void attachLayoutAnimationParameters(View child,
1959            LayoutParams params, int index, int count) {
1960        LayoutAnimationController.AnimationParameters animationParams =
1961                    params.layoutAnimationParameters;
1962        if (animationParams == null) {
1963            animationParams = new LayoutAnimationController.AnimationParameters();
1964            params.layoutAnimationParameters = animationParams;
1965        }
1966
1967        animationParams.count = count;
1968        animationParams.index = index;
1969    }
1970
1971    /**
1972     * {@inheritDoc}
1973     */
1974    public void removeView(View view) {
1975        removeViewInternal(view);
1976        requestLayout();
1977        invalidate();
1978    }
1979
1980    /**
1981     * Removes a view during layout. This is useful if in your onLayout() method,
1982     * you need to remove more views.
1983     *
1984     * @param view the view to remove from the group
1985     */
1986    public void removeViewInLayout(View view) {
1987        removeViewInternal(view);
1988    }
1989
1990    /**
1991     * Removes a range of views during layout. This is useful if in your onLayout() method,
1992     * you need to remove more views.
1993     *
1994     * @param start the index of the first view to remove from the group
1995     * @param count the number of views to remove from the group
1996     */
1997    public void removeViewsInLayout(int start, int count) {
1998        removeViewsInternal(start, count);
1999    }
2000
2001    /**
2002     * Removes the view at the specified position in the group.
2003     *
2004     * @param index the position in the group of the view to remove
2005     */
2006    public void removeViewAt(int index) {
2007        removeViewInternal(index, getChildAt(index));
2008        requestLayout();
2009        invalidate();
2010    }
2011
2012    /**
2013     * Removes the specified range of views from the group.
2014     *
2015     * @param start the first position in the group of the range of views to remove
2016     * @param count the number of views to remove
2017     */
2018    public void removeViews(int start, int count) {
2019        removeViewsInternal(start, count);
2020        requestLayout();
2021        invalidate();
2022    }
2023
2024    private void removeViewInternal(View view) {
2025        final int index = indexOfChild(view);
2026        if (index >= 0) {
2027            removeViewInternal(index, view);
2028        }
2029    }
2030
2031    private void removeViewInternal(int index, View view) {
2032        boolean clearChildFocus = false;
2033        if (view == mFocused) {
2034            view.clearFocusForRemoval();
2035            clearChildFocus = true;
2036        }
2037
2038        if (view.getAnimation() != null) {
2039            addDisappearingView(view);
2040        } else if (view.mAttachInfo != null) {
2041           view.dispatchDetachedFromWindow();
2042        }
2043
2044        if (mOnHierarchyChangeListener != null) {
2045            mOnHierarchyChangeListener.onChildViewRemoved(this, view);
2046        }
2047
2048        needGlobalAttributesUpdate(false);
2049
2050        removeFromArray(index);
2051
2052        if (clearChildFocus) {
2053            clearChildFocus(view);
2054        }
2055    }
2056
2057    private void removeViewsInternal(int start, int count) {
2058        final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
2059        final boolean notifyListener = onHierarchyChangeListener != null;
2060        final View focused = mFocused;
2061        final boolean detach = mAttachInfo != null;
2062        View clearChildFocus = null;
2063
2064        final View[] children = mChildren;
2065        final int end = start + count;
2066
2067        for (int i = start; i < end; i++) {
2068            final View view = children[i];
2069
2070            if (view == focused) {
2071                view.clearFocusForRemoval();
2072                clearChildFocus = view;
2073            }
2074
2075            if (view.getAnimation() != null) {
2076                addDisappearingView(view);
2077            } else if (detach) {
2078               view.dispatchDetachedFromWindow();
2079            }
2080
2081            needGlobalAttributesUpdate(false);
2082
2083            if (notifyListener) {
2084                onHierarchyChangeListener.onChildViewRemoved(this, view);
2085            }
2086        }
2087
2088        removeFromArray(start, count);
2089
2090        if (clearChildFocus != null) {
2091            clearChildFocus(clearChildFocus);
2092        }
2093    }
2094
2095    /**
2096     * Call this method to remove all child views from the
2097     * ViewGroup.
2098     */
2099    public void removeAllViews() {
2100        removeAllViewsInLayout();
2101        requestLayout();
2102        invalidate();
2103    }
2104
2105    /**
2106     * Called by a ViewGroup subclass to remove child views from itself,
2107     * when it must first know its size on screen before it can calculate how many
2108     * child views it will render. An example is a Gallery or a ListView, which
2109     * may "have" 50 children, but actually only render the number of children
2110     * that can currently fit inside the object on screen. Do not call
2111     * this method unless you are extending ViewGroup and understand the
2112     * view measuring and layout pipeline.
2113     */
2114    public void removeAllViewsInLayout() {
2115        final int count = mChildrenCount;
2116        if (count <= 0) {
2117            return;
2118        }
2119
2120        final View[] children = mChildren;
2121        mChildrenCount = 0;
2122
2123        final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
2124        final boolean notify = listener != null;
2125        final View focused = mFocused;
2126        final boolean detach = mAttachInfo != null;
2127        View clearChildFocus = null;
2128
2129        needGlobalAttributesUpdate(false);
2130
2131        for (int i = count - 1; i >= 0; i--) {
2132            final View view = children[i];
2133
2134            if (view == focused) {
2135                view.clearFocusForRemoval();
2136                clearChildFocus = view;
2137            }
2138
2139            if (view.getAnimation() != null) {
2140                addDisappearingView(view);
2141            } else if (detach) {
2142               view.dispatchDetachedFromWindow();
2143            }
2144
2145            if (notify) {
2146                listener.onChildViewRemoved(this, view);
2147            }
2148
2149            view.mParent = null;
2150            children[i] = null;
2151        }
2152
2153        if (clearChildFocus != null) {
2154            clearChildFocus(clearChildFocus);
2155        }
2156    }
2157
2158    /**
2159     * Finishes the removal of a detached view. This method will dispatch the detached from
2160     * window event and notify the hierarchy change listener.
2161     *
2162     * @param child the child to be definitely removed from the view hierarchy
2163     * @param animate if true and the view has an animation, the view is placed in the
2164     *                disappearing views list, otherwise, it is detached from the window
2165     *
2166     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2167     * @see #detachAllViewsFromParent()
2168     * @see #detachViewFromParent(View)
2169     * @see #detachViewFromParent(int)
2170     */
2171    protected void removeDetachedView(View child, boolean animate) {
2172        if (child == mFocused) {
2173            child.clearFocus();
2174        }
2175
2176        if (animate && child.getAnimation() != null) {
2177            addDisappearingView(child);
2178        } else if (child.mAttachInfo != null) {
2179            child.dispatchDetachedFromWindow();
2180        }
2181
2182        if (mOnHierarchyChangeListener != null) {
2183            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
2184        }
2185    }
2186
2187    /**
2188     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
2189     * sets the layout parameters and puts the view in the list of children so it can be retrieved
2190     * by calling {@link #getChildAt(int)}.
2191     *
2192     * This method should be called only for view which were detached from their parent.
2193     *
2194     * @param child the child to attach
2195     * @param index the index at which the child should be attached
2196     * @param params the layout parameters of the child
2197     *
2198     * @see #removeDetachedView(View, boolean)
2199     * @see #detachAllViewsFromParent()
2200     * @see #detachViewFromParent(View)
2201     * @see #detachViewFromParent(int)
2202     */
2203    protected void attachViewToParent(View child, int index, LayoutParams params) {
2204        child.mLayoutParams = params;
2205
2206        if (index < 0) {
2207            index = mChildrenCount;
2208        }
2209
2210        addInArray(child, index);
2211
2212        child.mParent = this;
2213        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
2214
2215        if (child.hasFocus()) {
2216            requestChildFocus(child, child.findFocus());
2217        }
2218    }
2219
2220    /**
2221     * Detaches a view from its parent. Detaching a view should be temporary and followed
2222     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2223     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2224     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2225     *
2226     * @param child the child to detach
2227     *
2228     * @see #detachViewFromParent(int)
2229     * @see #detachViewsFromParent(int, int)
2230     * @see #detachAllViewsFromParent()
2231     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2232     * @see #removeDetachedView(View, boolean)
2233     */
2234    protected void detachViewFromParent(View child) {
2235        removeFromArray(indexOfChild(child));
2236    }
2237
2238    /**
2239     * Detaches a view from its parent. Detaching a view should be temporary and followed
2240     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2241     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2242     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2243     *
2244     * @param index the index of the child to detach
2245     *
2246     * @see #detachViewFromParent(View)
2247     * @see #detachAllViewsFromParent()
2248     * @see #detachViewsFromParent(int, int)
2249     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2250     * @see #removeDetachedView(View, boolean)
2251     */
2252    protected void detachViewFromParent(int index) {
2253        removeFromArray(index);
2254    }
2255
2256    /**
2257     * Detaches a range of view from their parent. Detaching a view should be temporary and followed
2258     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2259     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
2260     * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2261     *
2262     * @param start the first index of the childrend range to detach
2263     * @param count the number of children to detach
2264     *
2265     * @see #detachViewFromParent(View)
2266     * @see #detachViewFromParent(int)
2267     * @see #detachAllViewsFromParent()
2268     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2269     * @see #removeDetachedView(View, boolean)
2270     */
2271    protected void detachViewsFromParent(int start, int count) {
2272        removeFromArray(start, count);
2273    }
2274
2275    /**
2276     * Detaches all views from the parent. Detaching a view should be temporary and followed
2277     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2278     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2279     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2280     *
2281     * @see #detachViewFromParent(View)
2282     * @see #detachViewFromParent(int)
2283     * @see #detachViewsFromParent(int, int)
2284     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2285     * @see #removeDetachedView(View, boolean)
2286     */
2287    protected void detachAllViewsFromParent() {
2288        final int count = mChildrenCount;
2289        if (count <= 0) {
2290            return;
2291        }
2292
2293        final View[] children = mChildren;
2294        mChildrenCount = 0;
2295
2296        for (int i = count - 1; i >= 0; i--) {
2297            children[i].mParent = null;
2298            children[i] = null;
2299        }
2300    }
2301
2302    /**
2303     * Don't call or override this method. It is used for the implementation of
2304     * the view hierarchy.
2305     */
2306    public final void invalidateChild(View child, final Rect dirty) {
2307        if (ViewDebug.TRACE_HIERARCHY) {
2308            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
2309        }
2310
2311        ViewParent parent = this;
2312
2313        final AttachInfo attachInfo = mAttachInfo;
2314        if (attachInfo != null) {
2315            final int[] location = attachInfo.mInvalidateChildLocation;
2316            location[CHILD_LEFT_INDEX] = child.mLeft;
2317            location[CHILD_TOP_INDEX] = child.mTop;
2318
2319            // If the child is drawing an animation, we want to copy this flag onto
2320            // ourselves and the parent to make sure the invalidate request goes
2321            // through
2322            final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
2323
2324            // Check whether the child that requests the invalidate is fully opaque
2325            final boolean isOpaque = child.isOpaque();
2326            // Mark the child as dirty, using the appropriate flag
2327            // Make sure we do not set both flags at the same time
2328            final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
2329
2330            do {
2331                View view = null;
2332                if (parent instanceof View) {
2333                    view = (View) parent;
2334                }
2335
2336                if (drawAnimation) {
2337                    if (view != null) {
2338                        view.mPrivateFlags |= DRAW_ANIMATION;
2339                    } else if (parent instanceof ViewRoot) {
2340                        ((ViewRoot) parent).mIsAnimating = true;
2341                    }
2342                }
2343
2344                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
2345                // flag coming from the child that initiated the invalidate
2346                if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
2347                    view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
2348                }
2349
2350                parent = parent.invalidateChildInParent(location, dirty);
2351            } while (parent != null);
2352        }
2353    }
2354
2355    /**
2356     * Don't call or override this method. It is used for the implementation of
2357     * the view hierarchy.
2358     *
2359     * This implementation returns null if this ViewGroup does not have a parent,
2360     * if this ViewGroup is already fully invalidated or if the dirty rectangle
2361     * does not intersect with this ViewGroup's bounds.
2362     */
2363    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
2364        if (ViewDebug.TRACE_HIERARCHY) {
2365            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
2366        }
2367
2368        if ((mPrivateFlags & DRAWN) == DRAWN) {
2369            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
2370                        FLAG_OPTIMIZE_INVALIDATE) {
2371                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
2372                        location[CHILD_TOP_INDEX] - mScrollY);
2373
2374                final int left = mLeft;
2375                final int top = mTop;
2376
2377                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
2378                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
2379                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
2380
2381                    location[CHILD_LEFT_INDEX] = left;
2382                    location[CHILD_TOP_INDEX] = top;
2383
2384                    return mParent;
2385                }
2386            } else {
2387                mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
2388
2389                location[CHILD_LEFT_INDEX] = mLeft;
2390                location[CHILD_TOP_INDEX] = mTop;
2391
2392                dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
2393                        mBottom - location[CHILD_TOP_INDEX]);
2394
2395                return mParent;
2396            }
2397        }
2398
2399        return null;
2400    }
2401
2402    /**
2403     * Offset a rectangle that is in a descendant's coordinate
2404     * space into our coordinate space.
2405     * @param descendant A descendant of this view
2406     * @param rect A rectangle defined in descendant's coordinate space.
2407     */
2408    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
2409        offsetRectBetweenParentAndChild(descendant, rect, true, false);
2410    }
2411
2412    /**
2413     * Offset a rectangle that is in our coordinate space into an ancestor's
2414     * coordinate space.
2415     * @param descendant A descendant of this view
2416     * @param rect A rectangle defined in descendant's coordinate space.
2417     */
2418    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
2419        offsetRectBetweenParentAndChild(descendant, rect, false, false);
2420    }
2421
2422    /**
2423     * Helper method that offsets a rect either from parent to descendant or
2424     * descendant to parent.
2425     */
2426    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
2427            boolean offsetFromChildToParent, boolean clipToBounds) {
2428
2429        // already in the same coord system :)
2430        if (descendant == this) {
2431            return;
2432        }
2433
2434        ViewParent theParent = descendant.mParent;
2435
2436        // search and offset up to the parent
2437        while ((theParent != null)
2438                && (theParent instanceof View)
2439                && (theParent != this)) {
2440
2441            if (offsetFromChildToParent) {
2442                rect.offset(descendant.mLeft - descendant.mScrollX,
2443                        descendant.mTop - descendant.mScrollY);
2444                if (clipToBounds) {
2445                    View p = (View) theParent;
2446                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
2447                }
2448            } else {
2449                if (clipToBounds) {
2450                    View p = (View) theParent;
2451                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
2452                }
2453                rect.offset(descendant.mScrollX - descendant.mLeft,
2454                        descendant.mScrollY - descendant.mTop);
2455            }
2456
2457            descendant = (View) theParent;
2458            theParent = descendant.mParent;
2459        }
2460
2461        // now that we are up to this view, need to offset one more time
2462        // to get into our coordinate space
2463        if (theParent == this) {
2464            if (offsetFromChildToParent) {
2465                rect.offset(descendant.mLeft - descendant.mScrollX,
2466                        descendant.mTop - descendant.mScrollY);
2467            } else {
2468                rect.offset(descendant.mScrollX - descendant.mLeft,
2469                        descendant.mScrollY - descendant.mTop);
2470            }
2471        } else {
2472            throw new IllegalArgumentException("parameter must be a descendant of this view");
2473        }
2474    }
2475
2476    /**
2477     * Offset the vertical location of all children of this view by the specified number of pixels.
2478     *
2479     * @param offset the number of pixels to offset
2480     *
2481     * @hide
2482     */
2483    public void offsetChildrenTopAndBottom(int offset) {
2484        final int count = mChildrenCount;
2485        final View[] children = mChildren;
2486
2487        for (int i = 0; i < count; i++) {
2488            final View v = children[i];
2489            v.mTop += offset;
2490            v.mBottom += offset;
2491        }
2492    }
2493
2494    /**
2495     * {@inheritDoc}
2496     */
2497    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
2498        int dx = child.mLeft - mScrollX;
2499        int dy = child.mTop - mScrollY;
2500        if (offset != null) {
2501            offset.x += dx;
2502            offset.y += dy;
2503        }
2504        r.offset(dx, dy);
2505        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
2506               (mParent == null || mParent.getChildVisibleRect(this, r, offset));
2507    }
2508
2509    /**
2510     * {@inheritDoc}
2511     */
2512    @Override
2513    protected abstract void onLayout(boolean changed,
2514            int l, int t, int r, int b);
2515
2516    /**
2517     * Indicates whether the view group has the ability to animate its children
2518     * after the first layout.
2519     *
2520     * @return true if the children can be animated, false otherwise
2521     */
2522    protected boolean canAnimate() {
2523        return mLayoutAnimationController != null;
2524    }
2525
2526    /**
2527     * Runs the layout animation. Calling this method triggers a relayout of
2528     * this view group.
2529     */
2530    public void startLayoutAnimation() {
2531        if (mLayoutAnimationController != null) {
2532            mGroupFlags |= FLAG_RUN_ANIMATION;
2533            requestLayout();
2534        }
2535    }
2536
2537    /**
2538     * Schedules the layout animation to be played after the next layout pass
2539     * of this view group. This can be used to restart the layout animation
2540     * when the content of the view group changes or when the activity is
2541     * paused and resumed.
2542     */
2543    public void scheduleLayoutAnimation() {
2544        mGroupFlags |= FLAG_RUN_ANIMATION;
2545    }
2546
2547    /**
2548     * Sets the layout animation controller used to animate the group's
2549     * children after the first layout.
2550     *
2551     * @param controller the animation controller
2552     */
2553    public void setLayoutAnimation(LayoutAnimationController controller) {
2554        mLayoutAnimationController = controller;
2555        if (mLayoutAnimationController != null) {
2556            mGroupFlags |= FLAG_RUN_ANIMATION;
2557        }
2558    }
2559
2560    /**
2561     * Returns the layout animation controller used to animate the group's
2562     * children.
2563     *
2564     * @return the current animation controller
2565     */
2566    public LayoutAnimationController getLayoutAnimation() {
2567        return mLayoutAnimationController;
2568    }
2569
2570    /**
2571     * Indicates whether the children's drawing cache is used during a layout
2572     * animation. By default, the drawing cache is enabled but this will prevent
2573     * nested layout animations from working. To nest animations, you must disable
2574     * the cache.
2575     *
2576     * @return true if the animation cache is enabled, false otherwise
2577     *
2578     * @see #setAnimationCacheEnabled(boolean)
2579     * @see View#setDrawingCacheEnabled(boolean)
2580     */
2581    @ViewDebug.ExportedProperty
2582    public boolean isAnimationCacheEnabled() {
2583        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2584    }
2585
2586    /**
2587     * Enables or disables the children's drawing cache during a layout animation.
2588     * By default, the drawing cache is enabled but this will prevent nested
2589     * layout animations from working. To nest animations, you must disable the
2590     * cache.
2591     *
2592     * @param enabled true to enable the animation cache, false otherwise
2593     *
2594     * @see #isAnimationCacheEnabled()
2595     * @see View#setDrawingCacheEnabled(boolean)
2596     */
2597    public void setAnimationCacheEnabled(boolean enabled) {
2598        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
2599    }
2600
2601    /**
2602     * Indicates whether this ViewGroup will always try to draw its children using their
2603     * drawing cache. By default this property is enabled.
2604     *
2605     * @return true if the animation cache is enabled, false otherwise
2606     *
2607     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
2608     * @see #setChildrenDrawnWithCacheEnabled(boolean)
2609     * @see View#setDrawingCacheEnabled(boolean)
2610     */
2611    @ViewDebug.ExportedProperty
2612    public boolean isAlwaysDrawnWithCacheEnabled() {
2613        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
2614    }
2615
2616    /**
2617     * Indicates whether this ViewGroup will always try to draw its children using their
2618     * drawing cache. This property can be set to true when the cache rendering is
2619     * slightly different from the children's normal rendering. Renderings can be different,
2620     * for instance, when the cache's quality is set to low.
2621     *
2622     * When this property is disabled, the ViewGroup will use the drawing cache of its
2623     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
2624     * when to start using the drawing cache and when to stop using it.
2625     *
2626     * @param always true to always draw with the drawing cache, false otherwise
2627     *
2628     * @see #isAlwaysDrawnWithCacheEnabled()
2629     * @see #setChildrenDrawnWithCacheEnabled(boolean)
2630     * @see View#setDrawingCacheEnabled(boolean)
2631     * @see View#setDrawingCacheQuality(int)
2632     */
2633    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
2634        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
2635    }
2636
2637    /**
2638     * Indicates whether the ViewGroup is currently drawing its children using
2639     * their drawing cache.
2640     *
2641     * @return true if children should be drawn with their cache, false otherwise
2642     *
2643     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
2644     * @see #setChildrenDrawnWithCacheEnabled(boolean)
2645     */
2646    @ViewDebug.ExportedProperty
2647    protected boolean isChildrenDrawnWithCacheEnabled() {
2648        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
2649    }
2650
2651    /**
2652     * Tells the ViewGroup to draw its children using their drawing cache. This property
2653     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
2654     * will be used only if it has been enabled.
2655     *
2656     * Subclasses should call this method to start and stop using the drawing cache when
2657     * they perform performance sensitive operations, like scrolling or animating.
2658     *
2659     * @param enabled true if children should be drawn with their cache, false otherwise
2660     *
2661     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
2662     * @see #isChildrenDrawnWithCacheEnabled()
2663     */
2664    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
2665        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
2666    }
2667
2668    private void setBooleanFlag(int flag, boolean value) {
2669        if (value) {
2670            mGroupFlags |= flag;
2671        } else {
2672            mGroupFlags &= ~flag;
2673        }
2674    }
2675
2676    /**
2677     * Returns an integer indicating what types of drawing caches are kept in memory.
2678     *
2679     * @see #setPersistentDrawingCache(int)
2680     * @see #setAnimationCacheEnabled(boolean)
2681     *
2682     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
2683     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
2684     *         and {@link #PERSISTENT_ALL_CACHES}
2685     */
2686    @ViewDebug.ExportedProperty(mapping = {
2687        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
2688        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ANIMATION"),
2689        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
2690        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
2691    })
2692    public int getPersistentDrawingCache() {
2693        return mPersistentDrawingCache;
2694    }
2695
2696    /**
2697     * Indicates what types of drawing caches should be kept in memory after
2698     * they have been created.
2699     *
2700     * @see #getPersistentDrawingCache()
2701     * @see #setAnimationCacheEnabled(boolean)
2702     *
2703     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
2704     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
2705     *        and {@link #PERSISTENT_ALL_CACHES}
2706     */
2707    public void setPersistentDrawingCache(int drawingCacheToKeep) {
2708        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
2709    }
2710
2711    /**
2712     * Returns a new set of layout parameters based on the supplied attributes set.
2713     *
2714     * @param attrs the attributes to build the layout parameters from
2715     *
2716     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
2717     *         of its descendants
2718     */
2719    public LayoutParams generateLayoutParams(AttributeSet attrs) {
2720        return new LayoutParams(getContext(), attrs);
2721    }
2722
2723    /**
2724     * Returns a safe set of layout parameters based on the supplied layout params.
2725     * When a ViewGroup is passed a View whose layout params do not pass the test of
2726     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
2727     * is invoked. This method should return a new set of layout params suitable for
2728     * this ViewGroup, possibly by copying the appropriate attributes from the
2729     * specified set of layout params.
2730     *
2731     * @param p The layout parameters to convert into a suitable set of layout parameters
2732     *          for this ViewGroup.
2733     *
2734     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
2735     *         of its descendants
2736     */
2737    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
2738        return p;
2739    }
2740
2741    /**
2742     * Returns a set of default layout parameters. These parameters are requested
2743     * when the View passed to {@link #addView(View)} has no layout parameters
2744     * already set. If null is returned, an exception is thrown from addView.
2745     *
2746     * @return a set of default layout parameters or null
2747     */
2748    protected LayoutParams generateDefaultLayoutParams() {
2749        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
2750    }
2751
2752    /**
2753     * {@inheritDoc}
2754     */
2755    @Override
2756    protected void debug(int depth) {
2757        super.debug(depth);
2758        String output;
2759
2760        if (mFocused != null) {
2761            output = debugIndent(depth);
2762            output += "mFocused";
2763            Log.d(VIEW_LOG_TAG, output);
2764        }
2765        if (mChildrenCount != 0) {
2766            output = debugIndent(depth);
2767            output += "{";
2768            Log.d(VIEW_LOG_TAG, output);
2769        }
2770        int count = mChildrenCount;
2771        for (int i = 0; i < count; i++) {
2772            View child = mChildren[i];
2773            child.debug(depth + 1);
2774        }
2775
2776        if (mChildrenCount != 0) {
2777            output = debugIndent(depth);
2778            output += "}";
2779            Log.d(VIEW_LOG_TAG, output);
2780        }
2781    }
2782
2783    /**
2784     * Returns the position in the group of the specified child view.
2785     *
2786     * @param child the view for which to get the position
2787     * @return a positive integer representing the position of the view in the
2788     *         group, or -1 if the view does not exist in the group
2789     */
2790    public int indexOfChild(View child) {
2791        final int count = mChildrenCount;
2792        final View[] children = mChildren;
2793        for (int i = 0; i < count; i++) {
2794            if (children[i] == child) {
2795                return i;
2796            }
2797        }
2798        return -1;
2799    }
2800
2801    /**
2802     * Returns the number of children in the group.
2803     *
2804     * @return a positive integer representing the number of children in
2805     *         the group
2806     */
2807    public int getChildCount() {
2808        return mChildrenCount;
2809    }
2810
2811    /**
2812     * Returns the view at the specified position in the group.
2813     *
2814     * @param index the position at which to get the view from
2815     * @return the view at the specified position or null if the position
2816     *         does not exist within the group
2817     */
2818    public View getChildAt(int index) {
2819        try {
2820            return mChildren[index];
2821        } catch (IndexOutOfBoundsException ex) {
2822            return null;
2823        }
2824    }
2825
2826    /**
2827     * Ask all of the children of this view to measure themselves, taking into
2828     * account both the MeasureSpec requirements for this view and its padding.
2829     * We skip children that are in the GONE state The heavy lifting is done in
2830     * getChildMeasureSpec.
2831     *
2832     * @param widthMeasureSpec The width requirements for this view
2833     * @param heightMeasureSpec The height requirements for this view
2834     */
2835    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
2836        final int size = mChildrenCount;
2837        final View[] children = mChildren;
2838        for (int i = 0; i < size; ++i) {
2839            final View child = children[i];
2840            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
2841                measureChild(child, widthMeasureSpec, heightMeasureSpec);
2842            }
2843        }
2844    }
2845
2846    /**
2847     * Ask one of the children of this view to measure itself, taking into
2848     * account both the MeasureSpec requirements for this view and its padding.
2849     * The heavy lifting is done in getChildMeasureSpec.
2850     *
2851     * @param child The child to measure
2852     * @param parentWidthMeasureSpec The width requirements for this view
2853     * @param parentHeightMeasureSpec The height requirements for this view
2854     */
2855    protected void measureChild(View child, int parentWidthMeasureSpec,
2856            int parentHeightMeasureSpec) {
2857        final LayoutParams lp = child.getLayoutParams();
2858
2859        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
2860                mPaddingLeft + mPaddingRight, lp.width);
2861        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
2862                mPaddingTop + mPaddingBottom, lp.height);
2863
2864        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
2865    }
2866
2867    /**
2868     * Ask one of the children of this view to measure itself, taking into
2869     * account both the MeasureSpec requirements for this view and its padding
2870     * and margins. The child must have MarginLayoutParams The heavy lifting is
2871     * done in getChildMeasureSpec.
2872     *
2873     * @param child The child to measure
2874     * @param parentWidthMeasureSpec The width requirements for this view
2875     * @param widthUsed Extra space that has been used up by the parent
2876     *        horizontally (possibly by other children of the parent)
2877     * @param parentHeightMeasureSpec The height requirements for this view
2878     * @param heightUsed Extra space that has been used up by the parent
2879     *        vertically (possibly by other children of the parent)
2880     */
2881    protected void measureChildWithMargins(View child,
2882            int parentWidthMeasureSpec, int widthUsed,
2883            int parentHeightMeasureSpec, int heightUsed) {
2884        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
2885
2886        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
2887                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
2888                        + widthUsed, lp.width);
2889        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
2890                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
2891                        + heightUsed, lp.height);
2892
2893        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
2894    }
2895
2896    /**
2897     * Does the hard part of measureChildren: figuring out the MeasureSpec to
2898     * pass to a particular child. This method figures out the right MeasureSpec
2899     * for one dimension (height or width) of one child view.
2900     *
2901     * The goal is to combine information from our MeasureSpec with the
2902     * LayoutParams of the child to get the best possible results. For example,
2903     * if the this view knows its size (because its MeasureSpec has a mode of
2904     * EXACTLY), and the child has indicated in its LayoutParams that it wants
2905     * to be the same size as the parent, the parent should ask the child to
2906     * layout given an exact size.
2907     *
2908     * @param spec The requirements for this view
2909     * @param padding The padding of this view for the current dimension and
2910     *        margins, if applicable
2911     * @param childDimension How big the child wants to be in the current
2912     *        dimension
2913     * @return a MeasureSpec integer for the child
2914     */
2915    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
2916        int specMode = MeasureSpec.getMode(spec);
2917        int specSize = MeasureSpec.getSize(spec);
2918
2919        int size = Math.max(0, specSize - padding);
2920
2921        int resultSize = 0;
2922        int resultMode = 0;
2923
2924        switch (specMode) {
2925        // Parent has imposed an exact size on us
2926        case MeasureSpec.EXACTLY:
2927            if (childDimension >= 0) {
2928                resultSize = childDimension;
2929                resultMode = MeasureSpec.EXACTLY;
2930            } else if (childDimension == LayoutParams.FILL_PARENT) {
2931                // Child wants to be our size. So be it.
2932                resultSize = size;
2933                resultMode = MeasureSpec.EXACTLY;
2934            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
2935                // Child wants to determine its own size. It can't be
2936                // bigger than us.
2937                resultSize = size;
2938                resultMode = MeasureSpec.AT_MOST;
2939            }
2940            break;
2941
2942        // Parent has imposed a maximum size on us
2943        case MeasureSpec.AT_MOST:
2944            if (childDimension >= 0) {
2945                // Child wants a specific size... so be it
2946                resultSize = childDimension;
2947                resultMode = MeasureSpec.EXACTLY;
2948            } else if (childDimension == LayoutParams.FILL_PARENT) {
2949                // Child wants to be our size, but our size is not fixed.
2950                // Constrain child to not be bigger than us.
2951                resultSize = size;
2952                resultMode = MeasureSpec.AT_MOST;
2953            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
2954                // Child wants to determine its own size. It can't be
2955                // bigger than us.
2956                resultSize = size;
2957                resultMode = MeasureSpec.AT_MOST;
2958            }
2959            break;
2960
2961        // Parent asked to see how big we want to be
2962        case MeasureSpec.UNSPECIFIED:
2963            if (childDimension >= 0) {
2964                // Child wants a specific size... let him have it
2965                resultSize = childDimension;
2966                resultMode = MeasureSpec.EXACTLY;
2967            } else if (childDimension == LayoutParams.FILL_PARENT) {
2968                // Child wants to be our size... find out how big it should
2969                // be
2970                resultSize = 0;
2971                resultMode = MeasureSpec.UNSPECIFIED;
2972            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
2973                // Child wants to determine its own size.... find out how
2974                // big it should be
2975                resultSize = 0;
2976                resultMode = MeasureSpec.UNSPECIFIED;
2977            }
2978            break;
2979        }
2980        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
2981    }
2982
2983
2984    /**
2985     * Removes any pending animations for views that have been removed. Call
2986     * this if you don't want animations for exiting views to stack up.
2987     */
2988    public void clearDisappearingChildren() {
2989        if (mDisappearingChildren != null) {
2990            mDisappearingChildren.clear();
2991        }
2992    }
2993
2994    /**
2995     * Add a view which is removed from mChildren but still needs animation
2996     *
2997     * @param v View to add
2998     */
2999    private void addDisappearingView(View v) {
3000        ArrayList<View> disappearingChildren = mDisappearingChildren;
3001
3002        if (disappearingChildren == null) {
3003            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
3004        }
3005
3006        disappearingChildren.add(v);
3007    }
3008
3009    /**
3010     * Cleanup a view when its animation is done. This may mean removing it from
3011     * the list of disappearing views.
3012     *
3013     * @param view The view whose animation has finished
3014     * @param animation The animation, cannot be null
3015     */
3016    private void finishAnimatingView(final View view, Animation animation) {
3017        final ArrayList<View> disappearingChildren = mDisappearingChildren;
3018        if (disappearingChildren != null) {
3019            if (disappearingChildren.contains(view)) {
3020                disappearingChildren.remove(view);
3021
3022                if (view.mAttachInfo != null) {
3023                    view.dispatchDetachedFromWindow();
3024                }
3025
3026                view.clearAnimation();
3027                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
3028            }
3029        }
3030
3031        if (animation != null && !animation.getFillAfter()) {
3032            view.clearAnimation();
3033        }
3034
3035        if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
3036            view.onAnimationEnd();
3037            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
3038            // so we'd rather be safe than sorry
3039            view.mPrivateFlags &= ~ANIMATION_STARTED;
3040            // Draw one more frame after the animation is done
3041            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
3042        }
3043    }
3044
3045    /**
3046     * {@inheritDoc}
3047     */
3048    @Override
3049    public boolean gatherTransparentRegion(Region region) {
3050        // If no transparent regions requested, we are always opaque.
3051        final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
3052        if (meOpaque && region == null) {
3053            // The caller doesn't care about the region, so stop now.
3054            return true;
3055        }
3056        super.gatherTransparentRegion(region);
3057        final View[] children = mChildren;
3058        final int count = mChildrenCount;
3059        boolean noneOfTheChildrenAreTransparent = true;
3060        for (int i = 0; i < count; i++) {
3061            final View child = children[i];
3062            if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) {
3063                if (!child.gatherTransparentRegion(region)) {
3064                    noneOfTheChildrenAreTransparent = false;
3065                }
3066            }
3067        }
3068        return meOpaque || noneOfTheChildrenAreTransparent;
3069    }
3070
3071    /**
3072     * {@inheritDoc}
3073     */
3074    public void requestTransparentRegion(View child) {
3075        if (child != null) {
3076            child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
3077            if (mParent != null) {
3078                mParent.requestTransparentRegion(this);
3079            }
3080        }
3081    }
3082
3083
3084    @Override
3085    protected boolean fitSystemWindows(Rect insets) {
3086        boolean done = super.fitSystemWindows(insets);
3087        if (!done) {
3088            final int count = mChildrenCount;
3089            final View[] children = mChildren;
3090            for (int i = 0; i < count; i++) {
3091                done = children[i].fitSystemWindows(insets);
3092                if (done) {
3093                    break;
3094                }
3095            }
3096        }
3097        return done;
3098    }
3099
3100    /**
3101     * Returns the animation listener to which layout animation events are
3102     * sent.
3103     *
3104     * @return an {@link android.view.animation.Animation.AnimationListener}
3105     */
3106    public Animation.AnimationListener getLayoutAnimationListener() {
3107        return mAnimationListener;
3108    }
3109
3110    @Override
3111    protected void drawableStateChanged() {
3112        super.drawableStateChanged();
3113
3114        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
3115            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
3116                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
3117                        + " child has duplicateParentState set to true");
3118            }
3119
3120            final View[] children = mChildren;
3121            final int count = mChildrenCount;
3122
3123            for (int i = 0; i < count; i++) {
3124                final View child = children[i];
3125                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
3126                    child.refreshDrawableState();
3127                }
3128            }
3129        }
3130    }
3131
3132    @Override
3133    protected int[] onCreateDrawableState(int extraSpace) {
3134        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
3135            return super.onCreateDrawableState(extraSpace);
3136        }
3137
3138        int need = 0;
3139        int n = getChildCount();
3140        for (int i = 0; i < n; i++) {
3141            int[] childState = getChildAt(i).getDrawableState();
3142
3143            if (childState != null) {
3144                need += childState.length;
3145            }
3146        }
3147
3148        int[] state = super.onCreateDrawableState(extraSpace + need);
3149
3150        for (int i = 0; i < n; i++) {
3151            int[] childState = getChildAt(i).getDrawableState();
3152
3153            if (childState != null) {
3154                state = mergeDrawableStates(state, childState);
3155            }
3156        }
3157
3158        return state;
3159    }
3160
3161    /**
3162     * Sets whether this ViewGroup's drawable states also include
3163     * its children's drawable states.  This is used, for example, to
3164     * make a group appear to be focused when its child EditText or button
3165     * is focused.
3166     */
3167    public void setAddStatesFromChildren(boolean addsStates) {
3168        if (addsStates) {
3169            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
3170        } else {
3171            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
3172        }
3173
3174        refreshDrawableState();
3175    }
3176
3177    /**
3178     * Returns whether this ViewGroup's drawable states also include
3179     * its children's drawable states.  This is used, for example, to
3180     * make a group appear to be focused when its child EditText or button
3181     * is focused.
3182     */
3183    public boolean addStatesFromChildren() {
3184        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
3185    }
3186
3187    /**
3188     * If {link #addStatesFromChildren} is true, refreshes this group's
3189     * drawable state (to include the states from its children).
3190     */
3191    public void childDrawableStateChanged(View child) {
3192        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
3193            refreshDrawableState();
3194        }
3195    }
3196
3197    /**
3198     * Specifies the animation listener to which layout animation events must
3199     * be sent. Only
3200     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
3201     * and
3202     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
3203     * are invoked.
3204     *
3205     * @param animationListener the layout animation listener
3206     */
3207    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
3208        mAnimationListener = animationListener;
3209    }
3210
3211    /**
3212     * LayoutParams are used by views to tell their parents how they want to be
3213     * laid out. See
3214     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
3215     * for a list of all child view attributes that this class supports.
3216     *
3217     * <p>
3218     * The base LayoutParams class just describes how big the view wants to be
3219     * for both width and height. For each dimension, it can specify one of:
3220     * <ul>
3221     * <li> an exact number
3222     * <li>FILL_PARENT, which means the view wants to be as big as its parent
3223     * (minus padding)
3224     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
3225     * to enclose its content (plus padding)
3226     * </ul>
3227     * There are subclasses of LayoutParams for different subclasses of
3228     * ViewGroup. For example, AbsoluteLayout has its own subclass of
3229     * LayoutParams which adds an X and Y value.
3230     *
3231     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
3232     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
3233     */
3234    public static class LayoutParams {
3235        /**
3236         * Special value for the height or width requested by a View.
3237         * FILL_PARENT means that the view wants to fill the available space
3238         * within the parent, taking the parent's padding into account.
3239         */
3240        public static final int FILL_PARENT = -1;
3241
3242        /**
3243         * Special value for the height or width requested by a View.
3244         * WRAP_CONTENT means that the view wants to be just large enough to fit
3245         * its own internal content, taking its own padding into account.
3246         */
3247        public static final int WRAP_CONTENT = -2;
3248
3249        /**
3250         * Information about how wide the view wants to be. Can be an exact
3251         * size, or one of the constants FILL_PARENT or WRAP_CONTENT.
3252         */
3253        @ViewDebug.ExportedProperty(mapping = {
3254            @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"),
3255            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
3256        })
3257        public int width;
3258
3259        /**
3260         * Information about how tall the view wants to be. Can be an exact
3261         * size, or one of the constants FILL_PARENT or WRAP_CONTENT.
3262         */
3263        @ViewDebug.ExportedProperty(mapping = {
3264            @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"),
3265            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
3266        })
3267        public int height;
3268
3269        /**
3270         * Used to animate layouts.
3271         */
3272        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
3273
3274        /**
3275         * Creates a new set of layout parameters. The values are extracted from
3276         * the supplied attributes set and context. The XML attributes mapped
3277         * to this set of layout parameters are:
3278         *
3279         * <ul>
3280         *   <li><code>layout_width</code>: the width, either an exact value,
3281         *   {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li>
3282         *   <li><code>layout_height</code>: the height, either an exact value,
3283         *   {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li>
3284         * </ul>
3285         *
3286         * @param c the application environment
3287         * @param attrs the set of attributes from which to extract the layout
3288         *              parameters' values
3289         */
3290        public LayoutParams(Context c, AttributeSet attrs) {
3291            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
3292            setBaseAttributes(a,
3293                    R.styleable.ViewGroup_Layout_layout_width,
3294                    R.styleable.ViewGroup_Layout_layout_height);
3295            a.recycle();
3296        }
3297
3298        /**
3299         * Creates a new set of layout parameters with the specified width
3300         * and height.
3301         *
3302         * @param width the width, either {@link #FILL_PARENT},
3303         *        {@link #WRAP_CONTENT} or a fixed size in pixels
3304         * @param height the height, either {@link #FILL_PARENT},
3305         *        {@link #WRAP_CONTENT} or a fixed size in pixels
3306         */
3307        public LayoutParams(int width, int height) {
3308            this.width = width;
3309            this.height = height;
3310        }
3311
3312        /**
3313         * Copy constructor. Clones the width and height values of the source.
3314         *
3315         * @param source The layout params to copy from.
3316         */
3317        public LayoutParams(LayoutParams source) {
3318            this.width = source.width;
3319            this.height = source.height;
3320        }
3321
3322        /**
3323         * Used internally by MarginLayoutParams.
3324         * @hide
3325         */
3326        LayoutParams() {
3327        }
3328
3329        /**
3330         * Extracts the layout parameters from the supplied attributes.
3331         *
3332         * @param a the style attributes to extract the parameters from
3333         * @param widthAttr the identifier of the width attribute
3334         * @param heightAttr the identifier of the height attribute
3335         */
3336        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
3337            width = a.getLayoutDimension(widthAttr, "layout_width");
3338            height = a.getLayoutDimension(heightAttr, "layout_height");
3339        }
3340
3341        /**
3342         * Returns a String representation of this set of layout parameters.
3343         *
3344         * @param output the String to prepend to the internal representation
3345         * @return a String with the following format: output +
3346         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
3347         *
3348         * @hide
3349         */
3350        public String debug(String output) {
3351            return output + "ViewGroup.LayoutParams={ width="
3352                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
3353        }
3354
3355        /**
3356         * Converts the specified size to a readable String.
3357         *
3358         * @param size the size to convert
3359         * @return a String instance representing the supplied size
3360         *
3361         * @hide
3362         */
3363        protected static String sizeToString(int size) {
3364            if (size == WRAP_CONTENT) {
3365                return "wrap-content";
3366            }
3367            if (size == FILL_PARENT) {
3368                return "fill-parent";
3369            }
3370            return String.valueOf(size);
3371        }
3372    }
3373
3374    /**
3375     * Per-child layout information for layouts that support margins.
3376     * See
3377     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
3378     * for a list of all child view attributes that this class supports.
3379     */
3380    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
3381        /**
3382         * The left margin in pixels of the child.
3383         */
3384        @ViewDebug.ExportedProperty
3385        public int leftMargin;
3386
3387        /**
3388         * The top margin in pixels of the child.
3389         */
3390        @ViewDebug.ExportedProperty
3391        public int topMargin;
3392
3393        /**
3394         * The right margin in pixels of the child.
3395         */
3396        @ViewDebug.ExportedProperty
3397        public int rightMargin;
3398
3399        /**
3400         * The bottom margin in pixels of the child.
3401         */
3402        @ViewDebug.ExportedProperty
3403        public int bottomMargin;
3404
3405        /**
3406         * Creates a new set of layout parameters. The values are extracted from
3407         * the supplied attributes set and context.
3408         *
3409         * @param c the application environment
3410         * @param attrs the set of attributes from which to extract the layout
3411         *              parameters' values
3412         */
3413        public MarginLayoutParams(Context c, AttributeSet attrs) {
3414            super();
3415
3416            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
3417            setBaseAttributes(a,
3418                    R.styleable.ViewGroup_MarginLayout_layout_width,
3419                    R.styleable.ViewGroup_MarginLayout_layout_height);
3420
3421            int margin = a.getDimensionPixelSize(
3422                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
3423            if (margin >= 0) {
3424                leftMargin = margin;
3425                topMargin = margin;
3426                rightMargin= margin;
3427                bottomMargin = margin;
3428            } else {
3429                leftMargin = a.getDimensionPixelSize(
3430                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
3431                topMargin = a.getDimensionPixelSize(
3432                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
3433                rightMargin = a.getDimensionPixelSize(
3434                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
3435                bottomMargin = a.getDimensionPixelSize(
3436                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
3437            }
3438
3439            a.recycle();
3440        }
3441
3442        /**
3443         * {@inheritDoc}
3444         */
3445        public MarginLayoutParams(int width, int height) {
3446            super(width, height);
3447        }
3448
3449        /**
3450         * Copy constructor. Clones the width, height and margin values of the source.
3451         *
3452         * @param source The layout params to copy from.
3453         */
3454        public MarginLayoutParams(MarginLayoutParams source) {
3455            this.width = source.width;
3456            this.height = source.height;
3457
3458            this.leftMargin = source.leftMargin;
3459            this.topMargin = source.topMargin;
3460            this.rightMargin = source.rightMargin;
3461            this.bottomMargin = source.bottomMargin;
3462        }
3463
3464        /**
3465         * {@inheritDoc}
3466         */
3467        public MarginLayoutParams(LayoutParams source) {
3468            super(source);
3469        }
3470
3471        /**
3472         * Sets the margins, in pixels.
3473         *
3474         * @param left the left margin size
3475         * @param top the top margin size
3476         * @param right the right margin size
3477         * @param bottom the bottom margin size
3478         *
3479         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
3480         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
3481         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
3482         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
3483         */
3484        public void setMargins(int left, int top, int right, int bottom) {
3485            leftMargin = left;
3486            topMargin = top;
3487            rightMargin = right;
3488            bottomMargin = bottom;
3489        }
3490    }
3491}
3492