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