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