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