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