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