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