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