2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import android
.animation
.LayoutTransition
;
20 import android
.annotation
.IdRes
;
21 import android
.annotation
.NonNull
;
22 import android
.annotation
.UiThread
;
23 import android
.content
.Context
;
24 import android
.content
.Intent
;
25 import android
.content
.pm
.PackageManager
;
26 import android
.content
.res
.Configuration
;
27 import android
.content
.res
.TypedArray
;
28 import android
.graphics
.Bitmap
;
29 import android
.graphics
.Canvas
;
30 import android
.graphics
.Color
;
31 import android
.graphics
.Insets
;
32 import android
.graphics
.Matrix
;
33 import android
.graphics
.Paint
;
34 import android
.graphics
.PointF
;
35 import android
.graphics
.Rect
;
36 import android
.graphics
.RectF
;
37 import android
.graphics
.Region
;
38 import android
.os
.Build
;
39 import android
.os
.Bundle
;
40 import android
.os
.Parcelable
;
41 import android
.os
.SystemClock
;
42 import android
.util
.AttributeSet
;
43 import android
.util
.Log
;
44 import android
.util
.Pools
.SynchronizedPool
;
45 import android
.util
.SparseArray
;
46 import android
.util
.SparseBooleanArray
;
47 import android
.view
.accessibility
.AccessibilityEvent
;
48 import android
.view
.accessibility
.AccessibilityNodeInfo
;
49 import android
.view
.animation
.Animation
;
50 import android
.view
.animation
.AnimationUtils
;
51 import android
.view
.animation
.LayoutAnimationController
;
52 import android
.view
.animation
.Transformation
;
54 import com
.android
.internal
.R
;
55 import com
.android
.internal
.util
.Predicate
;
57 import java
.util
.ArrayList
;
58 import java
.util
.Collections
;
59 import java
.util
.HashSet
;
60 import java
.util
.List
;
62 import static android
.os
.Build
.VERSION_CODES
.JELLY_BEAN_MR1
;
66 * A <code>ViewGroup</code> is a special view that can contain other views
67 * (called children.) The view group is the base class for layouts and views
68 * containers. This class also defines the
69 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
70 * class for layouts parameters.
74 * Also see {@link LayoutParams} for layout attributes.
77 * <div class="special reference">
78 * <h3>Developer Guides</h3>
79 * <p>For more information about creating user interface layouts, read the
80 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
83 * <p>Here is a complete implementation of a custom ViewGroup that implements
84 * a simple {@link android.widget.FrameLayout} along with the ability to stack
85 * children in left and right gutters.</p>
87 * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
90 * <p>If you are implementing XML layout attributes as shown in the example, this is the
91 * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
93 * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
95 * <p>Finally the layout manager can be used in an XML layout like so:</p>
97 * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
99 * @attr ref android.R.styleable#ViewGroup_clipChildren
100 * @attr ref android.R.styleable#ViewGroup_clipToPadding
101 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
102 * @attr ref android.R.styleable#ViewGroup_animationCache
103 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
104 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
105 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
106 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
107 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
108 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
109 * @attr ref android.R.styleable#ViewGroup_layoutMode
112 public abstract class ViewGroup
extends View
implements ViewParent
, ViewManager
{
113 private static final String TAG
= "ViewGroup";
115 private static final boolean DBG
= false;
117 public static boolean DEBUG_DRAW
= false;
120 * Views which have been hidden or removed which need to be animated on
122 * This field should be made private, so it is hidden from the SDK.
125 protected ArrayList
<View
> mDisappearingChildren
;
128 * Listener used to propagate events indicating when children are added
129 * and/or removed from a view group.
130 * This field should be made private, so it is hidden from the SDK.
133 protected OnHierarchyChangeListener mOnHierarchyChangeListener
;
135 // The view contained within this ViewGroup that has or contains focus.
136 private View mFocused
;
139 * A Transformation used when drawing children, to
140 * apply on the child being drawn.
142 private Transformation mChildTransformation
;
145 * Used to track the current invalidation region.
147 RectF mInvalidateRegion
;
150 * A Transformation used to calculate a correct
151 * invalidation area when the application is autoscaled.
153 Transformation mInvalidationTransformation
;
155 // View currently under an ongoing drag
156 private View mCurrentDragView
;
158 // Metadata about the ongoing drag
159 private DragEvent mCurrentDrag
;
160 private HashSet
<View
> mDragNotifiedChildren
;
162 // Does this group have a child that can accept the current drag payload?
163 private boolean mChildAcceptsDrag
;
165 // Used during drag dispatch
166 private PointF mLocalPoint
;
168 // Lazily-created holder for point computations.
169 private float[] mTempPoint
;
172 private LayoutAnimationController mLayoutAnimationController
;
173 private Animation
.AnimationListener mAnimationListener
;
175 // First touch target in the linked list of touch targets.
176 private TouchTarget mFirstTouchTarget
;
178 // For debugging only. You can see these in hierarchyviewer.
179 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
180 @ViewDebug.ExportedProperty(category
= "events")
181 private long mLastTouchDownTime
;
182 @ViewDebug.ExportedProperty(category
= "events")
183 private int mLastTouchDownIndex
= -1;
184 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
185 @ViewDebug.ExportedProperty(category
= "events")
186 private float mLastTouchDownX
;
187 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
188 @ViewDebug.ExportedProperty(category
= "events")
189 private float mLastTouchDownY
;
191 // First hover target in the linked list of hover targets.
192 // The hover targets are children which have received ACTION_HOVER_ENTER.
193 // They might not have actually handled the hover event, but we will
194 // continue sending hover events to them as long as the pointer remains over
195 // their bounds and the view group does not intercept hover.
196 private HoverTarget mFirstHoverTarget
;
198 // True if the view group itself received a hover event.
199 // It might not have actually handled the hover event.
200 private boolean mHoveredSelf
;
205 * This field should be made private, so it is hidden from the SDK.
208 @ViewDebug.ExportedProperty(flagMapping
= {
209 @ViewDebug.FlagToString(mask
= FLAG_CLIP_CHILDREN
, equals
= FLAG_CLIP_CHILDREN
,
210 name
= "CLIP_CHILDREN"),
211 @ViewDebug.FlagToString(mask
= FLAG_CLIP_TO_PADDING
, equals
= FLAG_CLIP_TO_PADDING
,
212 name
= "CLIP_TO_PADDING"),
213 @ViewDebug.FlagToString(mask
= FLAG_PADDING_NOT_NULL
, equals
= FLAG_PADDING_NOT_NULL
,
214 name
= "PADDING_NOT_NULL")
215 }, formatToHexString
= true)
216 protected int mGroupFlags
;
219 * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
221 private int mLayoutMode
= LAYOUT_MODE_UNDEFINED
;
224 * NOTE: If you change the flags below make sure to reflect the changes
225 * the DisplayList class
228 // When set, ViewGroup invalidates only the child's rectangle
230 static final int FLAG_CLIP_CHILDREN
= 0x1;
232 // When set, ViewGroup excludes the padding area from the invalidate rectangle
234 private static final int FLAG_CLIP_TO_PADDING
= 0x2;
236 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
237 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
238 static final int FLAG_INVALIDATE_REQUIRED
= 0x4;
240 // When set, dispatchDraw() will run the layout animation and unset the flag
241 private static final int FLAG_RUN_ANIMATION
= 0x8;
243 // When set, there is either no layout animation on the ViewGroup or the layout
246 static final int FLAG_ANIMATION_DONE
= 0x10;
248 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
249 // to clip it, even if FLAG_CLIP_TO_PADDING is set
250 private static final int FLAG_PADDING_NOT_NULL
= 0x20;
252 /** @deprecated - functionality removed */
253 private static final int FLAG_ANIMATION_CACHE
= 0x40;
255 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
256 // layout animation; this avoid clobbering the hierarchy
257 // Automatically set when the layout animation starts, depending on the animation's
259 static final int FLAG_OPTIMIZE_INVALIDATE
= 0x80;
261 // When set, the next call to drawChild() will clear mChildTransformation's matrix
262 static final int FLAG_CLEAR_TRANSFORMATION
= 0x100;
264 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
265 // the children's Bitmap caches if necessary
266 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
267 private static final int FLAG_NOTIFY_ANIMATION_LISTENER
= 0x200;
270 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
271 * to get the index of the child to draw for that iteration.
275 protected static final int FLAG_USE_CHILD_DRAWING_ORDER
= 0x400;
278 * When set, this ViewGroup supports static transformations on children; this causes
279 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
280 * invoked when a child is drawn.
282 * Any subclass overriding
283 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
284 * set this flags in {@link #mGroupFlags}.
288 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS
= 0x800;
290 // UNUSED FLAG VALUE: 0x1000;
293 * When set, this ViewGroup's drawable states also include those
296 private static final int FLAG_ADD_STATES_FROM_CHILDREN
= 0x2000;
298 /** @deprecated functionality removed */
299 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE
= 0x4000;
301 /** @deprecated functionality removed */
302 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE
= 0x8000;
305 * When set, this group will go through its list of children to notify them of
306 * any drawable state change.
308 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE
= 0x10000;
310 private static final int FLAG_MASK_FOCUSABILITY
= 0x60000;
313 * This view will get focus before any of its descendants.
315 public static final int FOCUS_BEFORE_DESCENDANTS
= 0x20000;
318 * This view will get focus only if none of its descendants want it.
320 public static final int FOCUS_AFTER_DESCENDANTS
= 0x40000;
323 * This view will block any of its descendants from getting focus, even
324 * if they are focusable.
326 public static final int FOCUS_BLOCK_DESCENDANTS
= 0x60000;
329 * Used to map between enum in attrubutes and flag values.
331 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS
=
332 {FOCUS_BEFORE_DESCENDANTS
, FOCUS_AFTER_DESCENDANTS
,
333 FOCUS_BLOCK_DESCENDANTS
};
336 * When set, this ViewGroup should not intercept touch events.
339 protected static final int FLAG_DISALLOW_INTERCEPT
= 0x80000;
342 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
344 private static final int FLAG_SPLIT_MOTION_EVENTS
= 0x200000;
347 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
348 * to children when adding new views. This is used to prevent multiple
349 * onAttached calls when a ViewGroup adds children in its own onAttached method.
351 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW
= 0x400000;
354 * When true, indicates that a layoutMode has been explicitly set, either with
355 * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
356 * This distinguishes the situation in which a layout mode was inherited from
357 * one of the ViewGroup's ancestors and cached locally.
359 private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET
= 0x800000;
361 static final int FLAG_IS_TRANSITION_GROUP
= 0x1000000;
363 static final int FLAG_IS_TRANSITION_GROUP_SET
= 0x2000000;
366 * When set, focus will not be permitted to enter this group if a touchscreen is present.
368 static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS
= 0x4000000;
371 * When true, indicates that a call to startActionModeForChild was made with the type parameter
372 * and should not be ignored. This helps in backwards compatibility with the existing method
375 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
376 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
378 private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED
= 0x8000000;
381 * When true, indicates that a call to startActionModeForChild was made without the type
382 * parameter. This helps in backwards compatibility with the existing method
385 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
386 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
388 private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED
= 0x10000000;
391 * Indicates which types of drawing caches are to be kept in memory.
392 * This field should be made private, so it is hidden from the SDK.
395 protected int mPersistentDrawingCache
;
398 * Used to indicate that no drawing cache should be kept in memory.
400 public static final int PERSISTENT_NO_CACHE
= 0x0;
403 * Used to indicate that the animation drawing cache should be kept in memory.
405 public static final int PERSISTENT_ANIMATION_CACHE
= 0x1;
408 * Used to indicate that the scrolling drawing cache should be kept in memory.
410 public static final int PERSISTENT_SCROLLING_CACHE
= 0x2;
413 * Used to indicate that all drawing caches should be kept in memory.
415 public static final int PERSISTENT_ALL_CACHES
= 0x3;
419 private static final int LAYOUT_MODE_UNDEFINED
= -1;
422 * This constant is a {@link #setLayoutMode(int) layoutMode}.
423 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
424 * {@link #getRight() right} and {@link #getBottom() bottom}.
426 public static final int LAYOUT_MODE_CLIP_BOUNDS
= 0;
429 * This constant is a {@link #setLayoutMode(int) layoutMode}.
430 * Optical bounds describe where a widget appears to be. They sit inside the clip
431 * bounds which need to cover a larger area to allow other effects,
432 * such as shadows and glows, to be drawn.
434 public static final int LAYOUT_MODE_OPTICAL_BOUNDS
= 1;
437 public static int LAYOUT_MODE_DEFAULT
= LAYOUT_MODE_CLIP_BOUNDS
;
440 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
441 * are set at the same time.
443 protected static final int CLIP_TO_PADDING_MASK
= FLAG_CLIP_TO_PADDING
| FLAG_PADDING_NOT_NULL
;
445 // Index of the child's left position in the mLocation array
446 private static final int CHILD_LEFT_INDEX
= 0;
447 // Index of the child's top position in the mLocation array
448 private static final int CHILD_TOP_INDEX
= 1;
450 // Child views of this ViewGroup
451 private View
[] mChildren
;
452 // Number of valid children in the mChildren array, the rest should be null or not
453 // considered as children
454 private int mChildrenCount
;
456 // Whether layout calls are currently being suppressed, controlled by calls to
458 boolean mSuppressLayout
= false;
460 // Whether any layout calls have actually been suppressed while mSuppressLayout
461 // has been true. This tracks whether we need to issue a requestLayout() when
462 // layout is later re-enabled.
463 private boolean mLayoutCalledWhileSuppressed
= false;
465 private static final int ARRAY_INITIAL_CAPACITY
= 12;
466 private static final int ARRAY_CAPACITY_INCREMENT
= 12;
468 private static Paint sDebugPaint
;
469 private static float[] sDebugLines
;
471 // Used to draw cached views
474 // Used to animate add/remove changes in layout
475 private LayoutTransition mTransition
;
477 // The set of views that are currently being transitioned. This list is used to track views
478 // being removed that should not actually be removed from the parent yet because they are
480 private ArrayList
<View
> mTransitioningViews
;
482 // List of children changing visibility. This is used to potentially keep rendering
483 // views during a transition when they otherwise would have become gone/invisible
484 private ArrayList
<View
> mVisibilityChangingChildren
;
486 // Temporary holder of presorted children, only used for
487 // input/software draw dispatch for correctly Z ordering.
488 private ArrayList
<View
> mPreSortedChildren
;
490 // Indicates how many of this container's child subtrees contain transient state
491 @ViewDebug.ExportedProperty(category
= "layout")
492 private int mChildCountWithTransientState
= 0;
495 * Currently registered axes for nested scrolling. Flag set consisting of
496 * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
499 private int mNestedScrollAxes
;
501 // Used to manage the list of transient views, added by addTransientView()
502 private List
<Integer
> mTransientIndices
= null;
503 private List
<View
> mTransientViews
= null;
507 * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
509 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
510 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
512 private static final ActionMode SENTINEL_ACTION_MODE
= new ActionMode() {
514 public void setTitle(CharSequence title
) {}
517 public void setTitle(int resId
) {}
520 public void setSubtitle(CharSequence subtitle
) {}
523 public void setSubtitle(int resId
) {}
526 public void setCustomView(View view
) {}
529 public void invalidate() {}
532 public void finish() {}
535 public Menu
getMenu() {
540 public CharSequence
getTitle() {
545 public CharSequence
getSubtitle() {
550 public View
getCustomView() {
555 public MenuInflater
getMenuInflater() {
560 public ViewGroup(Context context
) {
564 public ViewGroup(Context context
, AttributeSet attrs
) {
565 this(context
, attrs
, 0);
568 public ViewGroup(Context context
, AttributeSet attrs
, int defStyleAttr
) {
569 this(context
, attrs
, defStyleAttr
, 0);
572 public ViewGroup(Context context
, AttributeSet attrs
, int defStyleAttr
, int defStyleRes
) {
573 super(context
, attrs
, defStyleAttr
, defStyleRes
);
575 initFromAttributes(context
, attrs
, defStyleAttr
, defStyleRes
);
578 private boolean debugDraw() {
579 return DEBUG_DRAW
|| mAttachInfo
!= null && mAttachInfo
.mDebugLayout
;
582 private void initViewGroup() {
583 // ViewGroup doesn't draw by default
585 setFlags(WILL_NOT_DRAW
, DRAW_MASK
);
587 mGroupFlags
|= FLAG_CLIP_CHILDREN
;
588 mGroupFlags
|= FLAG_CLIP_TO_PADDING
;
589 mGroupFlags
|= FLAG_ANIMATION_DONE
;
590 mGroupFlags
|= FLAG_ANIMATION_CACHE
;
591 mGroupFlags
|= FLAG_ALWAYS_DRAWN_WITH_CACHE
;
593 if (mContext
.getApplicationInfo().targetSdkVersion
>= Build
.VERSION_CODES
.HONEYCOMB
) {
594 mGroupFlags
|= FLAG_SPLIT_MOTION_EVENTS
;
597 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS
);
599 mChildren
= new View
[ARRAY_INITIAL_CAPACITY
];
602 mPersistentDrawingCache
= PERSISTENT_SCROLLING_CACHE
;
605 private void initFromAttributes(
606 Context context
, AttributeSet attrs
, int defStyleAttr
, int defStyleRes
) {
607 final TypedArray a
= context
.obtainStyledAttributes(attrs
, R
.styleable
.ViewGroup
, defStyleAttr
,
610 final int N
= a
.getIndexCount();
611 for (int i
= 0; i
< N
; i
++) {
612 int attr
= a
.getIndex(i
);
614 case R
.styleable
.ViewGroup_clipChildren
:
615 setClipChildren(a
.getBoolean(attr
, true));
617 case R
.styleable
.ViewGroup_clipToPadding
:
618 setClipToPadding(a
.getBoolean(attr
, true));
620 case R
.styleable
.ViewGroup_animationCache
:
621 setAnimationCacheEnabled(a
.getBoolean(attr
, true));
623 case R
.styleable
.ViewGroup_persistentDrawingCache
:
624 setPersistentDrawingCache(a
.getInt(attr
, PERSISTENT_SCROLLING_CACHE
));
626 case R
.styleable
.ViewGroup_addStatesFromChildren
:
627 setAddStatesFromChildren(a
.getBoolean(attr
, false));
629 case R
.styleable
.ViewGroup_alwaysDrawnWithCache
:
630 setAlwaysDrawnWithCacheEnabled(a
.getBoolean(attr
, true));
632 case R
.styleable
.ViewGroup_layoutAnimation
:
633 int id
= a
.getResourceId(attr
, -1);
635 setLayoutAnimation(AnimationUtils
.loadLayoutAnimation(mContext
, id
));
638 case R
.styleable
.ViewGroup_descendantFocusability
:
639 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS
[a
.getInt(attr
, 0)]);
641 case R
.styleable
.ViewGroup_splitMotionEvents
:
642 setMotionEventSplittingEnabled(a
.getBoolean(attr
, false));
644 case R
.styleable
.ViewGroup_animateLayoutChanges
:
645 boolean animateLayoutChanges
= a
.getBoolean(attr
, false);
646 if (animateLayoutChanges
) {
647 setLayoutTransition(new LayoutTransition());
650 case R
.styleable
.ViewGroup_layoutMode
:
651 setLayoutMode(a
.getInt(attr
, LAYOUT_MODE_UNDEFINED
));
653 case R
.styleable
.ViewGroup_transitionGroup
:
654 setTransitionGroup(a
.getBoolean(attr
, false));
656 case R
.styleable
.ViewGroup_touchscreenBlocksFocus
:
657 setTouchscreenBlocksFocus(a
.getBoolean(attr
, false));
666 * Gets the descendant focusability of this view group. The descendant
667 * focusability defines the relationship between this view group and its
668 * descendants when looking for a view to take focus in
669 * {@link #requestFocus(int, android.graphics.Rect)}.
671 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
672 * {@link #FOCUS_BLOCK_DESCENDANTS}.
674 @ViewDebug.ExportedProperty(category
= "focus", mapping
= {
675 @ViewDebug.IntToString(from
= FOCUS_BEFORE_DESCENDANTS
, to
= "FOCUS_BEFORE_DESCENDANTS"),
676 @ViewDebug.IntToString(from
= FOCUS_AFTER_DESCENDANTS
, to
= "FOCUS_AFTER_DESCENDANTS"),
677 @ViewDebug.IntToString(from
= FOCUS_BLOCK_DESCENDANTS
, to
= "FOCUS_BLOCK_DESCENDANTS")
679 public int getDescendantFocusability() {
680 return mGroupFlags
& FLAG_MASK_FOCUSABILITY
;
684 * Set the descendant focusability of this view group. This defines the relationship
685 * between this view group and its descendants when looking for a view to
686 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
688 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
689 * {@link #FOCUS_BLOCK_DESCENDANTS}.
691 public void setDescendantFocusability(int focusability
) {
692 switch (focusability
) {
693 case FOCUS_BEFORE_DESCENDANTS
:
694 case FOCUS_AFTER_DESCENDANTS
:
695 case FOCUS_BLOCK_DESCENDANTS
:
698 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
699 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
701 mGroupFlags
&= ~FLAG_MASK_FOCUSABILITY
;
702 mGroupFlags
|= (focusability
& FLAG_MASK_FOCUSABILITY
);
709 void handleFocusGainInternal(int direction
, Rect previouslyFocusedRect
) {
710 if (mFocused
!= null) {
711 mFocused
.unFocus(this);
714 super.handleFocusGainInternal(direction
, previouslyFocusedRect
);
720 public void requestChildFocus(View child
, View focused
) {
722 System
.out
.println(this + " requestChildFocus()");
724 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
) {
728 // Unfocus us, if necessary
729 super.unFocus(focused
);
731 // We had a previous notion of who had focus. Clear it.
732 if (mFocused
!= child
) {
733 if (mFocused
!= null) {
734 mFocused
.unFocus(focused
);
739 if (mParent
!= null) {
740 mParent
.requestChildFocus(this, focused
);
747 public void focusableViewAvailable(View v
) {
749 // shortcut: don't report a new focusable view if we block our descendants from
751 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
)
752 && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
753 // shortcut: don't report a new focusable view if we already are focused
754 // (and we don't prefer our descendants)
756 // note: knowing that mFocused is non-null is not a good enough reason
757 // to break the traversal since in that case we'd actually have to find
758 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
759 // an ancestor of v; this will get checked for at ViewAncestor
760 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS
)) {
761 mParent
.focusableViewAvailable(v
);
768 public boolean showContextMenuForChild(View originalView
) {
769 return mParent
!= null && mParent
.showContextMenuForChild(originalView
);
776 public ActionMode
startActionModeForChild(View originalView
, ActionMode
.Callback callback
) {
777 if ((mGroupFlags
& FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED
) == 0) {
778 // This is the original call.
780 mGroupFlags
|= FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED
;
781 return startActionModeForChild(originalView
, callback
, ActionMode
.TYPE_PRIMARY
);
783 mGroupFlags
&= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED
;
786 // We are being called from the new method with type.
787 return SENTINEL_ACTION_MODE
;
795 public ActionMode
startActionModeForChild(
796 View originalView
, ActionMode
.Callback callback
, int type
) {
797 if ((mGroupFlags
& FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED
) == 0
798 && type
== ActionMode
.TYPE_PRIMARY
) {
801 mGroupFlags
|= FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED
;
802 mode
= startActionModeForChild(originalView
, callback
);
804 mGroupFlags
&= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED
;
806 if (mode
!= SENTINEL_ACTION_MODE
) {
810 if (mParent
!= null) {
812 return mParent
.startActionModeForChild(originalView
, callback
, type
);
813 } catch (AbstractMethodError ame
) {
814 // Custom view parents might not implement this method.
815 return mParent
.startActionModeForChild(originalView
, callback
);
825 public boolean dispatchActivityResult(
826 String who
, int requestCode
, int resultCode
, Intent data
) {
827 if (super.dispatchActivityResult(who
, requestCode
, resultCode
, data
)) {
830 int childCount
= getChildCount();
831 for (int i
= 0; i
< childCount
; i
++) {
832 View child
= getChildAt(i
);
833 if (child
.dispatchActivityResult(who
, requestCode
, resultCode
, data
)) {
841 * Find the nearest view in the specified direction that wants to take
844 * @param focused The view that currently has focus
845 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
846 * FOCUS_RIGHT, or 0 for not applicable.
848 public View
focusSearch(View focused
, int direction
) {
849 if (isRootNamespace()) {
850 // root namespace means we should consider ourselves the top of the
851 // tree for focus searching; otherwise we could be focus searching
852 // into other tabs. see LocalActivityManager and TabHost for more info
853 return FocusFinder
.getInstance().findNextFocus(this, focused
, direction
);
854 } else if (mParent
!= null) {
855 return mParent
.focusSearch(focused
, direction
);
863 public boolean requestChildRectangleOnScreen(View child
, Rect rectangle
, boolean immediate
) {
871 public boolean requestSendAccessibilityEvent(View child
, AccessibilityEvent event
) {
872 ViewParent parent
= mParent
;
873 if (parent
== null) {
876 final boolean propagate
= onRequestSendAccessibilityEvent(child
, event
);
880 return parent
.requestSendAccessibilityEvent(this, event
);
884 * Called when a child has requested sending an {@link AccessibilityEvent} and
885 * gives an opportunity to its parent to augment the event.
887 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
888 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
889 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
890 * is responsible for handling this call.
893 * @param child The child which requests sending the event.
894 * @param event The event to be sent.
895 * @return True if the event should be sent.
897 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
899 public boolean onRequestSendAccessibilityEvent(View child
, AccessibilityEvent event
) {
900 if (mAccessibilityDelegate
!= null) {
901 return mAccessibilityDelegate
.onRequestSendAccessibilityEvent(this, child
, event
);
903 return onRequestSendAccessibilityEventInternal(child
, event
);
908 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
910 * Note: Called from the default {@link View.AccessibilityDelegate}.
914 public boolean onRequestSendAccessibilityEventInternal(View child
, AccessibilityEvent event
) {
919 * Called when a child view has changed whether or not it is tracking transient state.
921 public void childHasTransientStateChanged(View child
, boolean childHasTransientState
) {
922 final boolean oldHasTransientState
= hasTransientState();
923 if (childHasTransientState
) {
924 mChildCountWithTransientState
++;
926 mChildCountWithTransientState
--;
929 final boolean newHasTransientState
= hasTransientState();
930 if (mParent
!= null && oldHasTransientState
!= newHasTransientState
) {
932 mParent
.childHasTransientStateChanged(this, newHasTransientState
);
933 } catch (AbstractMethodError e
) {
934 Log
.e(TAG
, mParent
.getClass().getSimpleName() +
935 " does not fully implement ViewParent", e
);
941 public boolean hasTransientState() {
942 return mChildCountWithTransientState
> 0 || super.hasTransientState();
949 public boolean dispatchUnhandledMove(View focused
, int direction
) {
950 return mFocused
!= null &&
951 mFocused
.dispatchUnhandledMove(focused
, direction
);
957 public void clearChildFocus(View child
) {
959 System
.out
.println(this + " clearChildFocus()");
963 if (mParent
!= null) {
964 mParent
.clearChildFocus(this);
972 public void clearFocus() {
974 System
.out
.println(this + " clearFocus()");
976 if (mFocused
== null) {
979 View focused
= mFocused
;
981 focused
.clearFocus();
989 void unFocus(View focused
) {
991 System
.out
.println(this + " unFocus()");
993 if (mFocused
== null) {
994 super.unFocus(focused
);
996 mFocused
.unFocus(focused
);
1002 * Returns the focused child of this view, if any. The child may have focus
1005 * @return the focused child or null.
1007 public View
getFocusedChild() {
1011 View
getDeepestFocusedChild() {
1014 if (v
.isFocused()) {
1017 v
= v
instanceof ViewGroup ?
((ViewGroup
) v
).getFocusedChild() : null;
1023 * Returns true if this view has or contains focus
1025 * @return true if this view has or contains focus
1028 public boolean hasFocus() {
1029 return (mPrivateFlags
& PFLAG_FOCUSED
) != 0 || mFocused
!= null;
1035 * @see android.view.View#findFocus()
1038 public View
findFocus() {
1040 System
.out
.println("Find focus in " + this + ": flags="
1041 + isFocused() + ", child=" + mFocused
);
1048 if (mFocused
!= null) {
1049 return mFocused
.findFocus();
1058 public boolean hasFocusable() {
1059 if ((mViewFlags
& VISIBILITY_MASK
) != VISIBLE
) {
1063 if (isFocusable()) {
1067 final int descendantFocusability
= getDescendantFocusability();
1068 if (descendantFocusability
!= FOCUS_BLOCK_DESCENDANTS
) {
1069 final int count
= mChildrenCount
;
1070 final View
[] children
= mChildren
;
1072 for (int i
= 0; i
< count
; i
++) {
1073 final View child
= children
[i
];
1074 if (child
.hasFocusable()) {
1087 public void addFocusables(ArrayList
<View
> views
, int direction
, int focusableMode
) {
1088 final int focusableCount
= views
.size();
1090 final int descendantFocusability
= getDescendantFocusability();
1092 if (descendantFocusability
!= FOCUS_BLOCK_DESCENDANTS
) {
1093 if (shouldBlockFocusForTouchscreen()) {
1094 focusableMode
|= FOCUSABLES_TOUCH_MODE
;
1097 final int count
= mChildrenCount
;
1098 final View
[] children
= mChildren
;
1100 for (int i
= 0; i
< count
; i
++) {
1101 final View child
= children
[i
];
1102 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
) {
1103 child
.addFocusables(views
, direction
, focusableMode
);
1108 // we add ourselves (if focusable) in all cases except for when we are
1109 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
1110 // to avoid the focus search finding layouts when a more precise search
1111 // among the focusable children would be more interesting.
1112 if ((descendantFocusability
!= FOCUS_AFTER_DESCENDANTS
1113 // No focusable descendants
1114 || (focusableCount
== views
.size())) &&
1115 (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) {
1116 super.addFocusables(views
, direction
, focusableMode
);
1121 * Set whether this ViewGroup should ignore focus requests for itself and its children.
1122 * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
1123 * will proceed forward.
1125 * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
1127 public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus
) {
1128 if (touchscreenBlocksFocus
) {
1129 mGroupFlags
|= FLAG_TOUCHSCREEN_BLOCKS_FOCUS
;
1131 final View focusedChild
= getDeepestFocusedChild();
1132 if (!focusedChild
.isFocusableInTouchMode()) {
1133 final View newFocus
= focusSearch(FOCUS_FORWARD
);
1134 if (newFocus
!= null) {
1135 newFocus
.requestFocus();
1140 mGroupFlags
&= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS
;
1145 * Check whether this ViewGroup should ignore focus requests for itself and its children.
1147 public boolean getTouchscreenBlocksFocus() {
1148 return (mGroupFlags
& FLAG_TOUCHSCREEN_BLOCKS_FOCUS
) != 0;
1151 boolean shouldBlockFocusForTouchscreen() {
1152 return getTouchscreenBlocksFocus() &&
1153 mContext
.getPackageManager().hasSystemFeature(PackageManager
.FEATURE_TOUCHSCREEN
);
1157 public void findViewsWithText(ArrayList
<View
> outViews
, CharSequence text
, int flags
) {
1158 super.findViewsWithText(outViews
, text
, flags
);
1159 final int childrenCount
= mChildrenCount
;
1160 final View
[] children
= mChildren
;
1161 for (int i
= 0; i
< childrenCount
; i
++) {
1162 View child
= children
[i
];
1163 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
1164 && (child
.mPrivateFlags
& PFLAG_IS_ROOT_NAMESPACE
) == 0) {
1165 child
.findViewsWithText(outViews
, text
, flags
);
1172 public View
findViewByAccessibilityIdTraversal(int accessibilityId
) {
1173 View foundView
= super.findViewByAccessibilityIdTraversal(accessibilityId
);
1174 if (foundView
!= null) {
1178 if (getAccessibilityNodeProvider() != null) {
1182 final int childrenCount
= mChildrenCount
;
1183 final View
[] children
= mChildren
;
1184 for (int i
= 0; i
< childrenCount
; i
++) {
1185 View child
= children
[i
];
1186 foundView
= child
.findViewByAccessibilityIdTraversal(accessibilityId
);
1187 if (foundView
!= null) {
1199 public void dispatchWindowFocusChanged(boolean hasFocus
) {
1200 super.dispatchWindowFocusChanged(hasFocus
);
1201 final int count
= mChildrenCount
;
1202 final View
[] children
= mChildren
;
1203 for (int i
= 0; i
< count
; i
++) {
1204 children
[i
].dispatchWindowFocusChanged(hasFocus
);
1212 public void addTouchables(ArrayList
<View
> views
) {
1213 super.addTouchables(views
);
1215 final int count
= mChildrenCount
;
1216 final View
[] children
= mChildren
;
1218 for (int i
= 0; i
< count
; i
++) {
1219 final View child
= children
[i
];
1220 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
) {
1221 child
.addTouchables(views
);
1230 public void makeOptionalFitsSystemWindows() {
1231 super.makeOptionalFitsSystemWindows();
1232 final int count
= mChildrenCount
;
1233 final View
[] children
= mChildren
;
1234 for (int i
= 0; i
< count
; i
++) {
1235 children
[i
].makeOptionalFitsSystemWindows();
1243 public void dispatchDisplayHint(int hint
) {
1244 super.dispatchDisplayHint(hint
);
1245 final int count
= mChildrenCount
;
1246 final View
[] children
= mChildren
;
1247 for (int i
= 0; i
< count
; i
++) {
1248 children
[i
].dispatchDisplayHint(hint
);
1253 * Called when a view's visibility has changed. Notify the parent to take any appropriate
1256 * @param child The view whose visibility has changed
1257 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
1258 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
1261 protected void onChildVisibilityChanged(View child
, int oldVisibility
, int newVisibility
) {
1262 if (mTransition
!= null) {
1263 if (newVisibility
== VISIBLE
) {
1264 mTransition
.showChild(this, child
, oldVisibility
);
1266 mTransition
.hideChild(this, child
, newVisibility
);
1267 if (mTransitioningViews
!= null && mTransitioningViews
.contains(child
)) {
1268 // Only track this on disappearing views - appearing views are already visible
1269 // and don't need special handling during drawChild()
1270 if (mVisibilityChangingChildren
== null) {
1271 mVisibilityChangingChildren
= new ArrayList
<View
>();
1273 mVisibilityChangingChildren
.add(child
);
1274 addDisappearingView(child
);
1279 // in all cases, for drags
1280 if (mCurrentDrag
!= null) {
1281 if (newVisibility
== VISIBLE
) {
1282 notifyChildOfDrag(child
);
1291 protected void dispatchVisibilityChanged(View changedView
, int visibility
) {
1292 super.dispatchVisibilityChanged(changedView
, visibility
);
1293 final int count
= mChildrenCount
;
1294 final View
[] children
= mChildren
;
1295 for (int i
= 0; i
< count
; i
++) {
1296 children
[i
].dispatchVisibilityChanged(changedView
, visibility
);
1304 public void dispatchWindowVisibilityChanged(int visibility
) {
1305 super.dispatchWindowVisibilityChanged(visibility
);
1306 final int count
= mChildrenCount
;
1307 final View
[] children
= mChildren
;
1308 for (int i
= 0; i
< count
; i
++) {
1309 children
[i
].dispatchWindowVisibilityChanged(visibility
);
1317 public void dispatchConfigurationChanged(Configuration newConfig
) {
1318 super.dispatchConfigurationChanged(newConfig
);
1319 final int count
= mChildrenCount
;
1320 final View
[] children
= mChildren
;
1321 for (int i
= 0; i
< count
; i
++) {
1322 children
[i
].dispatchConfigurationChanged(newConfig
);
1329 public void recomputeViewAttributes(View child
) {
1330 if (mAttachInfo
!= null && !mAttachInfo
.mRecomputeGlobalAttributes
) {
1331 ViewParent parent
= mParent
;
1332 if (parent
!= null) parent
.recomputeViewAttributes(this);
1337 void dispatchCollectViewAttributes(AttachInfo attachInfo
, int visibility
) {
1338 if ((visibility
& VISIBILITY_MASK
) == VISIBLE
) {
1339 super.dispatchCollectViewAttributes(attachInfo
, visibility
);
1340 final int count
= mChildrenCount
;
1341 final View
[] children
= mChildren
;
1342 for (int i
= 0; i
< count
; i
++) {
1343 final View child
= children
[i
];
1344 child
.dispatchCollectViewAttributes(attachInfo
,
1345 visibility
| (child
.mViewFlags
&VISIBILITY_MASK
));
1353 public void bringChildToFront(View child
) {
1354 final int index
= indexOfChild(child
);
1356 removeFromArray(index
);
1357 addInArray(child
, mChildrenCount
);
1358 child
.mParent
= this;
1364 private PointF
getLocalPoint() {
1365 if (mLocalPoint
== null) mLocalPoint
= new PointF();
1372 // TODO: Write real docs
1374 public boolean dispatchDragEvent(DragEvent event
) {
1375 boolean retval
= false;
1376 final float tx
= event
.mX
;
1377 final float ty
= event
.mY
;
1379 ViewRootImpl root
= getViewRootImpl();
1381 // Dispatch down the view hierarchy
1382 final PointF localPoint
= getLocalPoint();
1384 switch (event
.mAction
) {
1385 case DragEvent
.ACTION_DRAG_STARTED
: {
1386 // clear state to recalculate which views we drag over
1387 mCurrentDragView
= null;
1389 // Set up our tracking of drag-started notifications
1390 mCurrentDrag
= DragEvent
.obtain(event
);
1391 if (mDragNotifiedChildren
== null) {
1392 mDragNotifiedChildren
= new HashSet
<View
>();
1394 mDragNotifiedChildren
.clear();
1397 // Now dispatch down to our children, caching the responses
1398 mChildAcceptsDrag
= false;
1399 final int count
= mChildrenCount
;
1400 final View
[] children
= mChildren
;
1401 for (int i
= 0; i
< count
; i
++) {
1402 final View child
= children
[i
];
1403 child
.mPrivateFlags2
&= ~View
.DRAG_MASK
;
1404 if (child
.getVisibility() == VISIBLE
) {
1405 final boolean handled
= notifyChildOfDrag(children
[i
]);
1407 mChildAcceptsDrag
= true;
1412 // Return HANDLED if one of our children can accept the drag
1413 if (mChildAcceptsDrag
) {
1418 case DragEvent
.ACTION_DRAG_ENDED
: {
1419 // Release the bookkeeping now that the drag lifecycle has ended
1420 if (mDragNotifiedChildren
!= null) {
1421 for (View child
: mDragNotifiedChildren
) {
1422 // If a child was notified about an ongoing drag, it's told that it's over
1423 child
.dispatchDragEvent(event
);
1424 child
.mPrivateFlags2
&= ~View
.DRAG_MASK
;
1425 child
.refreshDrawableState();
1428 mDragNotifiedChildren
.clear();
1429 if (mCurrentDrag
!= null) {
1430 mCurrentDrag
.recycle();
1431 mCurrentDrag
= null;
1435 // We consider drag-ended to have been handled if one of our children
1436 // had offered to handle the drag.
1437 if (mChildAcceptsDrag
) {
1442 case DragEvent
.ACTION_DRAG_LOCATION
: {
1443 // Find the [possibly new] drag target
1444 final View target
= findFrontmostDroppableChildAt(event
.mX
, event
.mY
, localPoint
);
1446 // If we've changed apparent drag target, tell the view root which view
1447 // we're over now [for purposes of the eventual drag-recipient-changed
1448 // notifications to the framework] and tell the new target that the drag
1449 // has entered its bounds. The root will see setDragFocus() calls all
1450 // the way down to the final leaf view that is handling the LOCATION event
1451 // before reporting the new potential recipient to the framework.
1452 if (mCurrentDragView
!= target
) {
1453 root
.setDragFocus(target
);
1455 final int action
= event
.mAction
;
1456 // If we've dragged off of a child view, send it the EXITED message
1457 if (mCurrentDragView
!= null) {
1458 final View view
= mCurrentDragView
;
1459 event
.mAction
= DragEvent
.ACTION_DRAG_EXITED
;
1460 view
.dispatchDragEvent(event
);
1461 view
.mPrivateFlags2
&= ~View
.PFLAG2_DRAG_HOVERED
;
1462 view
.refreshDrawableState();
1464 mCurrentDragView
= target
;
1466 // If we've dragged over a new child view, send it the ENTERED message
1467 if (target
!= null) {
1468 event
.mAction
= DragEvent
.ACTION_DRAG_ENTERED
;
1469 target
.dispatchDragEvent(event
);
1470 target
.mPrivateFlags2
|= View
.PFLAG2_DRAG_HOVERED
;
1471 target
.refreshDrawableState();
1473 event
.mAction
= action
; // restore the event's original state
1476 // Dispatch the actual drag location notice, localized into its coordinates
1477 if (target
!= null) {
1478 event
.mX
= localPoint
.x
;
1479 event
.mY
= localPoint
.y
;
1481 retval
= target
.dispatchDragEvent(event
);
1488 /* Entered / exited dispatch
1490 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1491 * that we're about to get the corresponding LOCATION event, which we will use to
1492 * determine which of our children is the new target; at that point we will
1493 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1495 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1496 * drag has left this ViewGroup, we know by definition that every contained subview
1497 * is also no longer under the drag point.
1500 case DragEvent
.ACTION_DRAG_EXITED
: {
1501 if (mCurrentDragView
!= null) {
1502 final View view
= mCurrentDragView
;
1503 view
.dispatchDragEvent(event
);
1504 view
.mPrivateFlags2
&= ~View
.PFLAG2_DRAG_HOVERED
;
1505 view
.refreshDrawableState();
1507 mCurrentDragView
= null;
1511 case DragEvent
.ACTION_DROP
: {
1512 if (ViewDebug
.DEBUG_DRAG
) Log
.d(View
.VIEW_LOG_TAG
, "Drop event: " + event
);
1513 View target
= findFrontmostDroppableChildAt(event
.mX
, event
.mY
, localPoint
);
1514 if (target
!= null) {
1515 if (ViewDebug
.DEBUG_DRAG
) Log
.d(View
.VIEW_LOG_TAG
, " dispatch drop to " + target
);
1516 event
.mX
= localPoint
.x
;
1517 event
.mY
= localPoint
.y
;
1518 retval
= target
.dispatchDragEvent(event
);
1522 if (ViewDebug
.DEBUG_DRAG
) {
1523 Log
.d(View
.VIEW_LOG_TAG
, " not dropped on an accepting view");
1529 // If none of our children could handle the event, try here
1531 // Call up to the View implementation that dispatches to installed listeners
1532 retval
= super.dispatchDragEvent(event
);
1537 // Find the frontmost child view that lies under the given point, and calculate
1538 // the position within its own local coordinate system.
1539 View
findFrontmostDroppableChildAt(float x
, float y
, PointF outLocalPoint
) {
1540 final int count
= mChildrenCount
;
1541 final View
[] children
= mChildren
;
1542 for (int i
= count
- 1; i
>= 0; i
--) {
1543 final View child
= children
[i
];
1544 if (!child
.canAcceptDrag()) {
1548 if (isTransformedTouchPointInView(x
, y
, child
, outLocalPoint
)) {
1555 boolean notifyChildOfDrag(View child
) {
1556 if (ViewDebug
.DEBUG_DRAG
) {
1557 Log
.d(View
.VIEW_LOG_TAG
, "Sending drag-started to view: " + child
);
1560 boolean canAccept
= false;
1561 if (! mDragNotifiedChildren
.contains(child
)) {
1562 mDragNotifiedChildren
.add(child
);
1563 canAccept
= child
.dispatchDragEvent(mCurrentDrag
);
1564 if (canAccept
&& !child
.canAcceptDrag()) {
1565 child
.mPrivateFlags2
|= View
.PFLAG2_DRAG_CAN_ACCEPT
;
1566 child
.refreshDrawableState();
1573 public void dispatchWindowSystemUiVisiblityChanged(int visible
) {
1574 super.dispatchWindowSystemUiVisiblityChanged(visible
);
1576 final int count
= mChildrenCount
;
1577 final View
[] children
= mChildren
;
1578 for (int i
=0; i
<count
; i
++) {
1579 final View child
= children
[i
];
1580 child
.dispatchWindowSystemUiVisiblityChanged(visible
);
1585 public void dispatchSystemUiVisibilityChanged(int visible
) {
1586 super.dispatchSystemUiVisibilityChanged(visible
);
1588 final int count
= mChildrenCount
;
1589 final View
[] children
= mChildren
;
1590 for (int i
=0; i
<count
; i
++) {
1591 final View child
= children
[i
];
1592 child
.dispatchSystemUiVisibilityChanged(visible
);
1597 boolean updateLocalSystemUiVisibility(int localValue
, int localChanges
) {
1598 boolean changed
= super.updateLocalSystemUiVisibility(localValue
, localChanges
);
1600 final int count
= mChildrenCount
;
1601 final View
[] children
= mChildren
;
1602 for (int i
=0; i
<count
; i
++) {
1603 final View child
= children
[i
];
1604 changed
|= child
.updateLocalSystemUiVisibility(localValue
, localChanges
);
1613 public boolean dispatchKeyEventPreIme(KeyEvent event
) {
1614 if ((mPrivateFlags
& (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
))
1615 == (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
)) {
1616 return super.dispatchKeyEventPreIme(event
);
1617 } else if (mFocused
!= null && (mFocused
.mPrivateFlags
& PFLAG_HAS_BOUNDS
)
1618 == PFLAG_HAS_BOUNDS
) {
1619 return mFocused
.dispatchKeyEventPreIme(event
);
1628 public boolean dispatchKeyEvent(KeyEvent event
) {
1629 if (mInputEventConsistencyVerifier
!= null) {
1630 mInputEventConsistencyVerifier
.onKeyEvent(event
, 1);
1633 if ((mPrivateFlags
& (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
))
1634 == (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
)) {
1635 if (super.dispatchKeyEvent(event
)) {
1638 } else if (mFocused
!= null && (mFocused
.mPrivateFlags
& PFLAG_HAS_BOUNDS
)
1639 == PFLAG_HAS_BOUNDS
) {
1640 if (mFocused
.dispatchKeyEvent(event
)) {
1645 if (mInputEventConsistencyVerifier
!= null) {
1646 mInputEventConsistencyVerifier
.onUnhandledEvent(event
, 1);
1655 public boolean dispatchKeyShortcutEvent(KeyEvent event
) {
1656 if ((mPrivateFlags
& (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
))
1657 == (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
)) {
1658 return super.dispatchKeyShortcutEvent(event
);
1659 } else if (mFocused
!= null && (mFocused
.mPrivateFlags
& PFLAG_HAS_BOUNDS
)
1660 == PFLAG_HAS_BOUNDS
) {
1661 return mFocused
.dispatchKeyShortcutEvent(event
);
1670 public boolean dispatchTrackballEvent(MotionEvent event
) {
1671 if (mInputEventConsistencyVerifier
!= null) {
1672 mInputEventConsistencyVerifier
.onTrackballEvent(event
, 1);
1675 if ((mPrivateFlags
& (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
))
1676 == (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
)) {
1677 if (super.dispatchTrackballEvent(event
)) {
1680 } else if (mFocused
!= null && (mFocused
.mPrivateFlags
& PFLAG_HAS_BOUNDS
)
1681 == PFLAG_HAS_BOUNDS
) {
1682 if (mFocused
.dispatchTrackballEvent(event
)) {
1687 if (mInputEventConsistencyVerifier
!= null) {
1688 mInputEventConsistencyVerifier
.onUnhandledEvent(event
, 1);
1696 @SuppressWarnings({"ConstantConditions"})
1698 protected boolean dispatchHoverEvent(MotionEvent event
) {
1699 final int action
= event
.getAction();
1701 // First check whether the view group wants to intercept the hover event.
1702 final boolean interceptHover
= onInterceptHoverEvent(event
);
1703 event
.setAction(action
); // restore action in case it was changed
1705 MotionEvent eventNoHistory
= event
;
1706 boolean handled
= false;
1708 // Send events to the hovered children and build a new list of hover targets until
1709 // one is found that handles the event.
1710 HoverTarget firstOldHoverTarget
= mFirstHoverTarget
;
1711 mFirstHoverTarget
= null;
1712 if (!interceptHover
&& action
!= MotionEvent
.ACTION_HOVER_EXIT
) {
1713 final float x
= event
.getX();
1714 final float y
= event
.getY();
1715 final int childrenCount
= mChildrenCount
;
1716 if (childrenCount
!= 0) {
1717 final ArrayList
<View
> preorderedList
= buildOrderedChildList();
1718 final boolean customOrder
= preorderedList
== null
1719 && isChildrenDrawingOrderEnabled();
1720 final View
[] children
= mChildren
;
1721 HoverTarget lastHoverTarget
= null;
1722 for (int i
= childrenCount
- 1; i
>= 0; i
--) {
1723 int childIndex
= customOrder ?
getChildDrawingOrder(childrenCount
, i
) : i
;
1724 final View child
= (preorderedList
== null)
1725 ? children
[childIndex
] : preorderedList
.get(childIndex
);
1726 if (!canViewReceivePointerEvents(child
)
1727 || !isTransformedTouchPointInView(x
, y
, child
, null)) {
1731 // Obtain a hover target for this child. Dequeue it from the
1732 // old hover target list if the child was previously hovered.
1733 HoverTarget hoverTarget
= firstOldHoverTarget
;
1734 final boolean wasHovered
;
1735 for (HoverTarget predecessor
= null; ;) {
1736 if (hoverTarget
== null) {
1737 hoverTarget
= HoverTarget
.obtain(child
);
1742 if (hoverTarget
.child
== child
) {
1743 if (predecessor
!= null) {
1744 predecessor
.next
= hoverTarget
.next
;
1746 firstOldHoverTarget
= hoverTarget
.next
;
1748 hoverTarget
.next
= null;
1753 predecessor
= hoverTarget
;
1754 hoverTarget
= hoverTarget
.next
;
1757 // Enqueue the hover target onto the new hover target list.
1758 if (lastHoverTarget
!= null) {
1759 lastHoverTarget
.next
= hoverTarget
;
1761 mFirstHoverTarget
= hoverTarget
;
1763 lastHoverTarget
= hoverTarget
;
1765 // Dispatch the event to the child.
1766 if (action
== MotionEvent
.ACTION_HOVER_ENTER
) {
1768 // Send the enter as is.
1769 handled
|= dispatchTransformedGenericPointerEvent(
1770 event
, child
); // enter
1772 } else if (action
== MotionEvent
.ACTION_HOVER_MOVE
) {
1774 // Synthesize an enter from a move.
1775 eventNoHistory
= obtainMotionEventNoHistoryOrSelf(eventNoHistory
);
1776 eventNoHistory
.setAction(MotionEvent
.ACTION_HOVER_ENTER
);
1777 handled
|= dispatchTransformedGenericPointerEvent(
1778 eventNoHistory
, child
); // enter
1779 eventNoHistory
.setAction(action
);
1781 handled
|= dispatchTransformedGenericPointerEvent(
1782 eventNoHistory
, child
); // move
1784 // Send the move as is.
1785 handled
|= dispatchTransformedGenericPointerEvent(event
, child
);
1792 if (preorderedList
!= null) preorderedList
.clear();
1796 // Send exit events to all previously hovered children that are no longer hovered.
1797 while (firstOldHoverTarget
!= null) {
1798 final View child
= firstOldHoverTarget
.child
;
1800 // Exit the old hovered child.
1801 if (action
== MotionEvent
.ACTION_HOVER_EXIT
) {
1802 // Send the exit as is.
1803 handled
|= dispatchTransformedGenericPointerEvent(
1804 event
, child
); // exit
1806 // Synthesize an exit from a move or enter.
1807 // Ignore the result because hover focus has moved to a different view.
1808 if (action
== MotionEvent
.ACTION_HOVER_MOVE
) {
1809 dispatchTransformedGenericPointerEvent(
1810 event
, child
); // move
1812 eventNoHistory
= obtainMotionEventNoHistoryOrSelf(eventNoHistory
);
1813 eventNoHistory
.setAction(MotionEvent
.ACTION_HOVER_EXIT
);
1814 dispatchTransformedGenericPointerEvent(
1815 eventNoHistory
, child
); // exit
1816 eventNoHistory
.setAction(action
);
1819 final HoverTarget nextOldHoverTarget
= firstOldHoverTarget
.next
;
1820 firstOldHoverTarget
.recycle();
1821 firstOldHoverTarget
= nextOldHoverTarget
;
1824 // Send events to the view group itself if no children have handled it.
1825 boolean newHoveredSelf
= !handled
;
1826 if (newHoveredSelf
== mHoveredSelf
) {
1827 if (newHoveredSelf
) {
1828 // Send event to the view group as before.
1829 handled
|= super.dispatchHoverEvent(event
);
1833 // Exit the view group.
1834 if (action
== MotionEvent
.ACTION_HOVER_EXIT
) {
1835 // Send the exit as is.
1836 handled
|= super.dispatchHoverEvent(event
); // exit
1838 // Synthesize an exit from a move or enter.
1839 // Ignore the result because hover focus is moving to a different view.
1840 if (action
== MotionEvent
.ACTION_HOVER_MOVE
) {
1841 super.dispatchHoverEvent(event
); // move
1843 eventNoHistory
= obtainMotionEventNoHistoryOrSelf(eventNoHistory
);
1844 eventNoHistory
.setAction(MotionEvent
.ACTION_HOVER_EXIT
);
1845 super.dispatchHoverEvent(eventNoHistory
); // exit
1846 eventNoHistory
.setAction(action
);
1848 mHoveredSelf
= false;
1851 if (newHoveredSelf
) {
1852 // Enter the view group.
1853 if (action
== MotionEvent
.ACTION_HOVER_ENTER
) {
1854 // Send the enter as is.
1855 handled
|= super.dispatchHoverEvent(event
); // enter
1856 mHoveredSelf
= true;
1857 } else if (action
== MotionEvent
.ACTION_HOVER_MOVE
) {
1858 // Synthesize an enter from a move.
1859 eventNoHistory
= obtainMotionEventNoHistoryOrSelf(eventNoHistory
);
1860 eventNoHistory
.setAction(MotionEvent
.ACTION_HOVER_ENTER
);
1861 handled
|= super.dispatchHoverEvent(eventNoHistory
); // enter
1862 eventNoHistory
.setAction(action
);
1864 handled
|= super.dispatchHoverEvent(eventNoHistory
); // move
1865 mHoveredSelf
= true;
1870 // Recycle the copy of the event that we made.
1871 if (eventNoHistory
!= event
) {
1872 eventNoHistory
.recycle();
1879 private void exitHoverTargets() {
1880 if (mHoveredSelf
|| mFirstHoverTarget
!= null) {
1881 final long now
= SystemClock
.uptimeMillis();
1882 MotionEvent event
= MotionEvent
.obtain(now
, now
,
1883 MotionEvent
.ACTION_HOVER_EXIT
, 0.0f
, 0.0f
, 0);
1884 event
.setSource(InputDevice
.SOURCE_TOUCHSCREEN
);
1885 dispatchHoverEvent(event
);
1890 private void cancelHoverTarget(View view
) {
1891 HoverTarget predecessor
= null;
1892 HoverTarget target
= mFirstHoverTarget
;
1893 while (target
!= null) {
1894 final HoverTarget next
= target
.next
;
1895 if (target
.child
== view
) {
1896 if (predecessor
== null) {
1897 mFirstHoverTarget
= next
;
1899 predecessor
.next
= next
;
1903 final long now
= SystemClock
.uptimeMillis();
1904 MotionEvent event
= MotionEvent
.obtain(now
, now
,
1905 MotionEvent
.ACTION_HOVER_EXIT
, 0.0f
, 0.0f
, 0);
1906 event
.setSource(InputDevice
.SOURCE_TOUCHSCREEN
);
1907 view
.dispatchHoverEvent(event
);
1911 predecessor
= target
;
1918 protected boolean hasHoveredChild() {
1919 return mFirstHoverTarget
!= null;
1923 public void addChildrenForAccessibility(ArrayList
<View
> outChildren
) {
1924 if (getAccessibilityNodeProvider() != null) {
1927 ChildListForAccessibility children
= ChildListForAccessibility
.obtain(this, true);
1929 final int childrenCount
= children
.getChildCount();
1930 for (int i
= 0; i
< childrenCount
; i
++) {
1931 View child
= children
.getChildAt(i
);
1932 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
) {
1933 if (child
.includeForAccessibility()) {
1934 outChildren
.add(child
);
1936 child
.addChildrenForAccessibility(outChildren
);
1946 * Implement this method to intercept hover events before they are handled
1949 * This method is called before dispatching a hover event to a child of
1950 * the view group or to the view group's own {@link #onHoverEvent} to allow
1951 * the view group a chance to intercept the hover event.
1952 * This method can also be used to watch all pointer motions that occur within
1953 * the bounds of the view group even when the pointer is hovering over
1954 * a child of the view group rather than over the view group itself.
1956 * The view group can prevent its children from receiving hover events by
1957 * implementing this method and returning <code>true</code> to indicate
1958 * that it would like to intercept hover events. The view group must
1959 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1960 * for as long as it wishes to continue intercepting hover events from
1963 * Interception preserves the invariant that at most one view can be
1964 * hovered at a time by transferring hover focus from the currently hovered
1965 * child to the view group or vice-versa as needed.
1967 * If this method returns <code>true</code> and a child is already hovered, then the
1968 * child view will first receive a hover exit event and then the view group
1969 * itself will receive a hover enter event in {@link #onHoverEvent}.
1970 * Likewise, if this method had previously returned <code>true</code> to intercept hover
1971 * events and instead returns <code>false</code> while the pointer is hovering
1972 * within the bounds of one of a child, then the view group will first receive a
1973 * hover exit event in {@link #onHoverEvent} and then the hovered child will
1974 * receive a hover enter event.
1976 * The default implementation always returns false.
1979 * @param event The motion event that describes the hover.
1980 * @return True if the view group would like to intercept the hover event
1981 * and prevent its children from receiving it.
1983 public boolean onInterceptHoverEvent(MotionEvent event
) {
1987 private static MotionEvent
obtainMotionEventNoHistoryOrSelf(MotionEvent event
) {
1988 if (event
.getHistorySize() == 0) {
1991 return MotionEvent
.obtainNoHistory(event
);
1998 protected boolean dispatchGenericPointerEvent(MotionEvent event
) {
1999 // Send the event to the child under the pointer.
2000 final int childrenCount
= mChildrenCount
;
2001 if (childrenCount
!= 0) {
2002 final float x
= event
.getX();
2003 final float y
= event
.getY();
2005 final ArrayList
<View
> preorderedList
= buildOrderedChildList();
2006 final boolean customOrder
= preorderedList
== null
2007 && isChildrenDrawingOrderEnabled();
2008 final View
[] children
= mChildren
;
2009 for (int i
= childrenCount
- 1; i
>= 0; i
--) {
2010 int childIndex
= customOrder ?
getChildDrawingOrder(childrenCount
, i
) : i
;
2011 final View child
= (preorderedList
== null)
2012 ? children
[childIndex
] : preorderedList
.get(childIndex
);
2013 if (!canViewReceivePointerEvents(child
)
2014 || !isTransformedTouchPointInView(x
, y
, child
, null)) {
2018 if (dispatchTransformedGenericPointerEvent(event
, child
)) {
2019 if (preorderedList
!= null) preorderedList
.clear();
2023 if (preorderedList
!= null) preorderedList
.clear();
2026 // No child handled the event. Send it to this view group.
2027 return super.dispatchGenericPointerEvent(event
);
2034 protected boolean dispatchGenericFocusedEvent(MotionEvent event
) {
2035 // Send the event to the focused child or to this view group if it has focus.
2036 if ((mPrivateFlags
& (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
))
2037 == (PFLAG_FOCUSED
| PFLAG_HAS_BOUNDS
)) {
2038 return super.dispatchGenericFocusedEvent(event
);
2039 } else if (mFocused
!= null && (mFocused
.mPrivateFlags
& PFLAG_HAS_BOUNDS
)
2040 == PFLAG_HAS_BOUNDS
) {
2041 return mFocused
.dispatchGenericMotionEvent(event
);
2047 * Dispatches a generic pointer event to a child, taking into account
2048 * transformations that apply to the child.
2050 * @param event The event to send.
2051 * @param child The view to send the event to.
2052 * @return {@code true} if the child handled the event.
2054 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event
, View child
) {
2055 final float offsetX
= mScrollX
- child
.mLeft
;
2056 final float offsetY
= mScrollY
- child
.mTop
;
2059 if (!child
.hasIdentityMatrix()) {
2060 MotionEvent transformedEvent
= MotionEvent
.obtain(event
);
2061 transformedEvent
.offsetLocation(offsetX
, offsetY
);
2062 transformedEvent
.transform(child
.getInverseMatrix());
2063 handled
= child
.dispatchGenericMotionEvent(transformedEvent
);
2064 transformedEvent
.recycle();
2066 event
.offsetLocation(offsetX
, offsetY
);
2067 handled
= child
.dispatchGenericMotionEvent(event
);
2068 event
.offsetLocation(-offsetX
, -offsetY
);
2077 public boolean dispatchTouchEvent(MotionEvent ev
) {
2078 if (mInputEventConsistencyVerifier
!= null) {
2079 mInputEventConsistencyVerifier
.onTouchEvent(ev
, 1);
2082 // If the event targets the accessibility focused view and this is it, start
2083 // normal event dispatch. Maybe a descendant is what will handle the click.
2084 if (ev
.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
2085 ev
.setTargetAccessibilityFocus(false);
2088 boolean handled
= false;
2089 if (onFilterTouchEventForSecurity(ev
)) {
2090 final int action
= ev
.getAction();
2091 final int actionMasked
= action
& MotionEvent
.ACTION_MASK
;
2093 // Handle an initial down.
2094 if (actionMasked
== MotionEvent
.ACTION_DOWN
) {
2095 // Throw away all previous state when starting a new touch gesture.
2096 // The framework may have dropped the up or cancel event for the previous gesture
2097 // due to an app switch, ANR, or some other state change.
2098 cancelAndClearTouchTargets(ev
);
2102 // Check for interception.
2103 final boolean intercepted
;
2104 if (actionMasked
== MotionEvent
.ACTION_DOWN
2105 || mFirstTouchTarget
!= null) {
2106 final boolean disallowIntercept
= (mGroupFlags
& FLAG_DISALLOW_INTERCEPT
) != 0;
2107 if (!disallowIntercept
) {
2108 intercepted
= onInterceptTouchEvent(ev
);
2109 ev
.setAction(action
); // restore action in case it was changed
2111 intercepted
= false;
2114 // There are no touch targets and this action is not an initial down
2115 // so this view group continues to intercept touches.
2119 // If intercepted, start normal event dispatch. Also if there is already
2120 // a view that is handling the gesture, do normal event dispatch.
2121 if (intercepted
|| mFirstTouchTarget
!= null) {
2122 ev
.setTargetAccessibilityFocus(false);
2125 // Check for cancelation.
2126 final boolean canceled
= resetCancelNextUpFlag(this)
2127 || actionMasked
== MotionEvent
.ACTION_CANCEL
;
2129 // Update list of touch targets for pointer down, if needed.
2130 final boolean split
= (mGroupFlags
& FLAG_SPLIT_MOTION_EVENTS
) != 0;
2131 TouchTarget newTouchTarget
= null;
2132 boolean alreadyDispatchedToNewTouchTarget
= false;
2133 if (!canceled
&& !intercepted
) {
2135 // If the event is targeting accessiiblity focus we give it to the
2136 // view that has accessibility focus and if it does not handle it
2137 // we clear the flag and dispatch the event to all children as usual.
2138 // We are looking up the accessibility focused host to avoid keeping
2139 // state since these events are very rare.
2140 View childWithAccessibilityFocus
= ev
.isTargetAccessibilityFocus()
2141 ?
findChildWithAccessibilityFocus() : null;
2143 if (actionMasked
== MotionEvent
.ACTION_DOWN
2144 || (split
&& actionMasked
== MotionEvent
.ACTION_POINTER_DOWN
)
2145 || actionMasked
== MotionEvent
.ACTION_HOVER_MOVE
) {
2146 final int actionIndex
= ev
.getActionIndex(); // always 0 for down
2147 final int idBitsToAssign
= split ?
1 << ev
.getPointerId(actionIndex
)
2148 : TouchTarget
.ALL_POINTER_IDS
;
2150 // Clean up earlier touch targets for this pointer id in case they
2151 // have become out of sync.
2152 removePointersFromTouchTargets(idBitsToAssign
);
2154 final int childrenCount
= mChildrenCount
;
2155 if (newTouchTarget
== null && childrenCount
!= 0) {
2156 final float x
= ev
.getX(actionIndex
);
2157 final float y
= ev
.getY(actionIndex
);
2158 // Find a child that can receive the event.
2159 // Scan children from front to back.
2160 final ArrayList
<View
> preorderedList
= buildOrderedChildList();
2161 final boolean customOrder
= preorderedList
== null
2162 && isChildrenDrawingOrderEnabled();
2163 final View
[] children
= mChildren
;
2164 for (int i
= childrenCount
- 1; i
>= 0; i
--) {
2165 final int childIndex
= customOrder
2166 ?
getChildDrawingOrder(childrenCount
, i
) : i
;
2167 final View child
= (preorderedList
== null)
2168 ? children
[childIndex
] : preorderedList
.get(childIndex
);
2170 // If there is a view that has accessibility focus we want it
2171 // to get the event first and if not handled we will perform a
2172 // normal dispatch. We may do a double iteration but this is
2173 // safer given the timeframe.
2174 if (childWithAccessibilityFocus
!= null) {
2175 if (childWithAccessibilityFocus
!= child
) {
2178 childWithAccessibilityFocus
= null;
2179 i
= childrenCount
- 1;
2182 if (!canViewReceivePointerEvents(child
)
2183 || !isTransformedTouchPointInView(x
, y
, child
, null)) {
2184 ev
.setTargetAccessibilityFocus(false);
2188 newTouchTarget
= getTouchTarget(child
);
2189 if (newTouchTarget
!= null) {
2190 // Child is already receiving touch within its bounds.
2191 // Give it the new pointer in addition to the ones it is handling.
2192 newTouchTarget
.pointerIdBits
|= idBitsToAssign
;
2196 resetCancelNextUpFlag(child
);
2197 if (dispatchTransformedTouchEvent(ev
, false, child
, idBitsToAssign
)) {
2198 // Child wants to receive touch within its bounds.
2199 mLastTouchDownTime
= ev
.getDownTime();
2200 if (preorderedList
!= null) {
2201 // childIndex points into presorted list, find original index
2202 for (int j
= 0; j
< childrenCount
; j
++) {
2203 if (children
[childIndex
] == mChildren
[j
]) {
2204 mLastTouchDownIndex
= j
;
2209 mLastTouchDownIndex
= childIndex
;
2211 mLastTouchDownX
= ev
.getX();
2212 mLastTouchDownY
= ev
.getY();
2213 newTouchTarget
= addTouchTarget(child
, idBitsToAssign
);
2214 alreadyDispatchedToNewTouchTarget
= true;
2218 // The accessibility focus didn't handle the event, so clear
2219 // the flag and do a normal dispatch to all children.
2220 ev
.setTargetAccessibilityFocus(false);
2222 if (preorderedList
!= null) preorderedList
.clear();
2225 if (newTouchTarget
== null && mFirstTouchTarget
!= null) {
2226 // Did not find a child to receive the event.
2227 // Assign the pointer to the least recently added target.
2228 newTouchTarget
= mFirstTouchTarget
;
2229 while (newTouchTarget
.next
!= null) {
2230 newTouchTarget
= newTouchTarget
.next
;
2232 newTouchTarget
.pointerIdBits
|= idBitsToAssign
;
2237 // Dispatch to touch targets.
2238 if (mFirstTouchTarget
== null) {
2239 // No touch targets so treat this as an ordinary view.
2240 handled
= dispatchTransformedTouchEvent(ev
, canceled
, null,
2241 TouchTarget
.ALL_POINTER_IDS
);
2243 // Dispatch to touch targets, excluding the new touch target if we already
2244 // dispatched to it. Cancel touch targets if necessary.
2245 TouchTarget predecessor
= null;
2246 TouchTarget target
= mFirstTouchTarget
;
2247 while (target
!= null) {
2248 final TouchTarget next
= target
.next
;
2249 if (alreadyDispatchedToNewTouchTarget
&& target
== newTouchTarget
) {
2252 final boolean cancelChild
= resetCancelNextUpFlag(target
.child
)
2254 if (dispatchTransformedTouchEvent(ev
, cancelChild
,
2255 target
.child
, target
.pointerIdBits
)) {
2259 if (predecessor
== null) {
2260 mFirstTouchTarget
= next
;
2262 predecessor
.next
= next
;
2269 predecessor
= target
;
2274 // Update list of touch targets for pointer up or cancel, if needed.
2276 || actionMasked
== MotionEvent
.ACTION_UP
2277 || actionMasked
== MotionEvent
.ACTION_HOVER_MOVE
) {
2279 } else if (split
&& actionMasked
== MotionEvent
.ACTION_POINTER_UP
) {
2280 final int actionIndex
= ev
.getActionIndex();
2281 final int idBitsToRemove
= 1 << ev
.getPointerId(actionIndex
);
2282 removePointersFromTouchTargets(idBitsToRemove
);
2286 if (!handled
&& mInputEventConsistencyVerifier
!= null) {
2287 mInputEventConsistencyVerifier
.onUnhandledEvent(ev
, 1);
2293 * Finds the child which has accessibility focus.
2295 * @return The child that has focus.
2297 private View
findChildWithAccessibilityFocus() {
2298 ViewRootImpl viewRoot
= getViewRootImpl();
2299 if (viewRoot
== null) {
2303 View current
= viewRoot
.getAccessibilityFocusedHost();
2304 if (current
== null) {
2308 ViewParent parent
= current
.getParent();
2309 while (parent
instanceof View
) {
2310 if (parent
== this) {
2313 current
= (View
) parent
;
2314 parent
= current
.getParent();
2321 * Resets all touch state in preparation for a new cycle.
2323 private void resetTouchState() {
2324 clearTouchTargets();
2325 resetCancelNextUpFlag(this);
2326 mGroupFlags
&= ~FLAG_DISALLOW_INTERCEPT
;
2327 mNestedScrollAxes
= SCROLL_AXIS_NONE
;
2331 * Resets the cancel next up flag.
2332 * Returns true if the flag was previously set.
2334 private static boolean resetCancelNextUpFlag(View view
) {
2335 if ((view
.mPrivateFlags
& PFLAG_CANCEL_NEXT_UP_EVENT
) != 0) {
2336 view
.mPrivateFlags
&= ~PFLAG_CANCEL_NEXT_UP_EVENT
;
2343 * Clears all touch targets.
2345 private void clearTouchTargets() {
2346 TouchTarget target
= mFirstTouchTarget
;
2347 if (target
!= null) {
2349 TouchTarget next
= target
.next
;
2352 } while (target
!= null);
2353 mFirstTouchTarget
= null;
2358 * Cancels and clears all touch targets.
2360 private void cancelAndClearTouchTargets(MotionEvent event
) {
2361 if (mFirstTouchTarget
!= null) {
2362 boolean syntheticEvent
= false;
2363 if (event
== null) {
2364 final long now
= SystemClock
.uptimeMillis();
2365 event
= MotionEvent
.obtain(now
, now
,
2366 MotionEvent
.ACTION_CANCEL
, 0.0f
, 0.0f
, 0);
2367 event
.setSource(InputDevice
.SOURCE_TOUCHSCREEN
);
2368 syntheticEvent
= true;
2371 for (TouchTarget target
= mFirstTouchTarget
; target
!= null; target
= target
.next
) {
2372 resetCancelNextUpFlag(target
.child
);
2373 dispatchTransformedTouchEvent(event
, true, target
.child
, target
.pointerIdBits
);
2375 clearTouchTargets();
2377 if (syntheticEvent
) {
2384 * Gets the touch target for specified child view.
2385 * Returns null if not found.
2387 private TouchTarget
getTouchTarget(View child
) {
2388 for (TouchTarget target
= mFirstTouchTarget
; target
!= null; target
= target
.next
) {
2389 if (target
.child
== child
) {
2397 * Adds a touch target for specified child to the beginning of the list.
2398 * Assumes the target child is not already present.
2400 private TouchTarget
addTouchTarget(View child
, int pointerIdBits
) {
2401 TouchTarget target
= TouchTarget
.obtain(child
, pointerIdBits
);
2402 target
.next
= mFirstTouchTarget
;
2403 mFirstTouchTarget
= target
;
2408 * Removes the pointer ids from consideration.
2410 private void removePointersFromTouchTargets(int pointerIdBits
) {
2411 TouchTarget predecessor
= null;
2412 TouchTarget target
= mFirstTouchTarget
;
2413 while (target
!= null) {
2414 final TouchTarget next
= target
.next
;
2415 if ((target
.pointerIdBits
& pointerIdBits
) != 0) {
2416 target
.pointerIdBits
&= ~pointerIdBits
;
2417 if (target
.pointerIdBits
== 0) {
2418 if (predecessor
== null) {
2419 mFirstTouchTarget
= next
;
2421 predecessor
.next
= next
;
2428 predecessor
= target
;
2433 private void cancelTouchTarget(View view
) {
2434 TouchTarget predecessor
= null;
2435 TouchTarget target
= mFirstTouchTarget
;
2436 while (target
!= null) {
2437 final TouchTarget next
= target
.next
;
2438 if (target
.child
== view
) {
2439 if (predecessor
== null) {
2440 mFirstTouchTarget
= next
;
2442 predecessor
.next
= next
;
2446 final long now
= SystemClock
.uptimeMillis();
2447 MotionEvent event
= MotionEvent
.obtain(now
, now
,
2448 MotionEvent
.ACTION_CANCEL
, 0.0f
, 0.0f
, 0);
2449 event
.setSource(InputDevice
.SOURCE_TOUCHSCREEN
);
2450 view
.dispatchTouchEvent(event
);
2454 predecessor
= target
;
2460 * Returns true if a child view can receive pointer events.
2463 private static boolean canViewReceivePointerEvents(View child
) {
2464 return (child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
2465 || child
.getAnimation() != null;
2468 private float[] getTempPoint() {
2469 if (mTempPoint
== null) {
2470 mTempPoint
= new float[2];
2476 * Returns true if a child view contains the specified point when transformed
2477 * into its coordinate space.
2478 * Child must not be null.
2481 protected boolean isTransformedTouchPointInView(float x
, float y
, View child
,
2482 PointF outLocalPoint
) {
2483 final float[] point
= getTempPoint();
2486 transformPointToViewLocal(point
, child
);
2487 final boolean isInView
= child
.pointInView(point
[0], point
[1]);
2488 if (isInView
&& outLocalPoint
!= null) {
2489 outLocalPoint
.set(point
[0], point
[1]);
2497 public void transformPointToViewLocal(float[] point
, View child
) {
2498 point
[0] += mScrollX
- child
.mLeft
;
2499 point
[1] += mScrollY
- child
.mTop
;
2501 if (!child
.hasIdentityMatrix()) {
2502 child
.getInverseMatrix().mapPoints(point
);
2507 * Transforms a motion event into the coordinate space of a particular child view,
2508 * filters out irrelevant pointer ids, and overrides its action if necessary.
2509 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2511 private boolean dispatchTransformedTouchEvent(MotionEvent event
, boolean cancel
,
2512 View child
, int desiredPointerIdBits
) {
2513 final boolean handled
;
2515 // Canceling motions is a special case. We don't need to perform any transformations
2516 // or filtering. The important part is the action, not the contents.
2517 final int oldAction
= event
.getAction();
2518 if (cancel
|| oldAction
== MotionEvent
.ACTION_CANCEL
) {
2519 event
.setAction(MotionEvent
.ACTION_CANCEL
);
2520 if (child
== null) {
2521 handled
= super.dispatchTouchEvent(event
);
2523 handled
= child
.dispatchTouchEvent(event
);
2525 event
.setAction(oldAction
);
2529 // Calculate the number of pointers to deliver.
2530 final int oldPointerIdBits
= event
.getPointerIdBits();
2531 final int newPointerIdBits
= oldPointerIdBits
& desiredPointerIdBits
;
2533 // If for some reason we ended up in an inconsistent state where it looks like we
2534 // might produce a motion event with no pointers in it, then drop the event.
2535 if (newPointerIdBits
== 0) {
2539 // If the number of pointers is the same and we don't need to perform any fancy
2540 // irreversible transformations, then we can reuse the motion event for this
2541 // dispatch as long as we are careful to revert any changes we make.
2542 // Otherwise we need to make a copy.
2543 final MotionEvent transformedEvent
;
2544 if (newPointerIdBits
== oldPointerIdBits
) {
2545 if (child
== null || child
.hasIdentityMatrix()) {
2546 if (child
== null) {
2547 handled
= super.dispatchTouchEvent(event
);
2549 final float offsetX
= mScrollX
- child
.mLeft
;
2550 final float offsetY
= mScrollY
- child
.mTop
;
2551 event
.offsetLocation(offsetX
, offsetY
);
2553 handled
= child
.dispatchTouchEvent(event
);
2555 event
.offsetLocation(-offsetX
, -offsetY
);
2559 transformedEvent
= MotionEvent
.obtain(event
);
2561 transformedEvent
= event
.split(newPointerIdBits
);
2564 // Perform any necessary transformations and dispatch.
2565 if (child
== null) {
2566 handled
= super.dispatchTouchEvent(transformedEvent
);
2568 final float offsetX
= mScrollX
- child
.mLeft
;
2569 final float offsetY
= mScrollY
- child
.mTop
;
2570 transformedEvent
.offsetLocation(offsetX
, offsetY
);
2571 if (! child
.hasIdentityMatrix()) {
2572 transformedEvent
.transform(child
.getInverseMatrix());
2575 handled
= child
.dispatchTouchEvent(transformedEvent
);
2579 transformedEvent
.recycle();
2584 * Enable or disable the splitting of MotionEvents to multiple children during touch event
2585 * dispatch. This behavior is enabled by default for applications that target an
2586 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
2588 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2589 * views depending on where each pointer initially went down. This allows for user interactions
2590 * such as scrolling two panes of content independently, chording of buttons, and performing
2591 * independent gestures on different pieces of content.
2593 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2594 * child views. <code>false</code> to only allow one child view to be the target of
2595 * any MotionEvent received by this ViewGroup.
2596 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
2598 public void setMotionEventSplittingEnabled(boolean split
) {
2599 // TODO Applications really shouldn't change this setting mid-touch event,
2600 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2601 // with gestures in progress when this is changed.
2603 mGroupFlags
|= FLAG_SPLIT_MOTION_EVENTS
;
2605 mGroupFlags
&= ~FLAG_SPLIT_MOTION_EVENTS
;
2610 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2611 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2613 public boolean isMotionEventSplittingEnabled() {
2614 return (mGroupFlags
& FLAG_SPLIT_MOTION_EVENTS
) == FLAG_SPLIT_MOTION_EVENTS
;
2618 * Returns true if this ViewGroup should be considered as a single entity for removal
2619 * when executing an Activity transition. If this is false, child elements will move
2620 * individually during the transition.
2622 * @return True if the ViewGroup should be acted on together during an Activity transition.
2623 * The default value is true when there is a non-null background or if
2624 * {@link #getTransitionName()} is not null or if a
2625 * non-null {@link android.view.ViewOutlineProvider} other than
2626 * {@link android.view.ViewOutlineProvider#BACKGROUND} was given to
2627 * {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise.
2629 public boolean isTransitionGroup() {
2630 if ((mGroupFlags
& FLAG_IS_TRANSITION_GROUP_SET
) != 0) {
2631 return ((mGroupFlags
& FLAG_IS_TRANSITION_GROUP
) != 0);
2633 final ViewOutlineProvider outlineProvider
= getOutlineProvider();
2634 return getBackground() != null || getTransitionName() != null ||
2635 (outlineProvider
!= null && outlineProvider
!= ViewOutlineProvider
.BACKGROUND
);
2640 * Changes whether or not this ViewGroup should be treated as a single entity during
2641 * Activity Transitions.
2642 * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
2643 * in Activity transitions. If false, the ViewGroup won't transition,
2644 * only its children. If true, the entire ViewGroup will transition
2646 * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
2647 * android.util.Pair[])
2649 public void setTransitionGroup(boolean isTransitionGroup
) {
2650 mGroupFlags
|= FLAG_IS_TRANSITION_GROUP_SET
;
2651 if (isTransitionGroup
) {
2652 mGroupFlags
|= FLAG_IS_TRANSITION_GROUP
;
2654 mGroupFlags
&= ~FLAG_IS_TRANSITION_GROUP
;
2661 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept
) {
2663 if (disallowIntercept
== ((mGroupFlags
& FLAG_DISALLOW_INTERCEPT
) != 0)) {
2664 // We're already in this state, assume our ancestors are too
2668 if (disallowIntercept
) {
2669 mGroupFlags
|= FLAG_DISALLOW_INTERCEPT
;
2671 mGroupFlags
&= ~FLAG_DISALLOW_INTERCEPT
;
2674 // Pass it up to our parent
2675 if (mParent
!= null) {
2676 mParent
.requestDisallowInterceptTouchEvent(disallowIntercept
);
2681 * Implement this method to intercept all touch screen motion events. This
2682 * allows you to watch events as they are dispatched to your children, and
2683 * take ownership of the current gesture at any point.
2685 * <p>Using this function takes some care, as it has a fairly complicated
2686 * interaction with {@link View#onTouchEvent(MotionEvent)
2687 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2688 * that method as well as this one in the correct way. Events will be
2689 * received in the following order:
2692 * <li> You will receive the down event here.
2693 * <li> The down event will be handled either by a child of this view
2694 * group, or given to your own onTouchEvent() method to handle; this means
2695 * you should implement onTouchEvent() to return true, so you will
2696 * continue to see the rest of the gesture (instead of looking for
2697 * a parent view to handle it). Also, by returning true from
2698 * onTouchEvent(), you will not receive any following
2699 * events in onInterceptTouchEvent() and all touch processing must
2700 * happen in onTouchEvent() like normal.
2701 * <li> For as long as you return false from this function, each following
2702 * event (up to and including the final up) will be delivered first here
2703 * and then to the target's onTouchEvent().
2704 * <li> If you return true from here, you will not receive any
2705 * following events: the target view will receive the same event but
2706 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2707 * events will be delivered to your onTouchEvent() method and no longer
2711 * @param ev The motion event being dispatched down the hierarchy.
2712 * @return Return true to steal motion events from the children and have
2713 * them dispatched to this ViewGroup through onTouchEvent().
2714 * The current target will receive an ACTION_CANCEL event, and no further
2715 * messages will be delivered here.
2717 public boolean onInterceptTouchEvent(MotionEvent ev
) {
2724 * Looks for a view to give focus to respecting the setting specified by
2725 * {@link #getDescendantFocusability()}.
2727 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2728 * find focus within the children of this group when appropriate.
2730 * @see #FOCUS_BEFORE_DESCENDANTS
2731 * @see #FOCUS_AFTER_DESCENDANTS
2732 * @see #FOCUS_BLOCK_DESCENDANTS
2733 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
2736 public boolean requestFocus(int direction
, Rect previouslyFocusedRect
) {
2738 System
.out
.println(this + " ViewGroup.requestFocus direction="
2741 int descendantFocusability
= getDescendantFocusability();
2743 switch (descendantFocusability
) {
2744 case FOCUS_BLOCK_DESCENDANTS
:
2745 return super.requestFocus(direction
, previouslyFocusedRect
);
2746 case FOCUS_BEFORE_DESCENDANTS
: {
2747 final boolean took
= super.requestFocus(direction
, previouslyFocusedRect
);
2748 return took ? took
: onRequestFocusInDescendants(direction
, previouslyFocusedRect
);
2750 case FOCUS_AFTER_DESCENDANTS
: {
2751 final boolean took
= onRequestFocusInDescendants(direction
, previouslyFocusedRect
);
2752 return took ? took
: super.requestFocus(direction
, previouslyFocusedRect
);
2755 throw new IllegalStateException("descendant focusability must be "
2756 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2757 + "but is " + descendantFocusability
);
2762 * Look for a descendant to call {@link View#requestFocus} on.
2763 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2764 * when it wants to request focus within its children. Override this to
2765 * customize how your {@link ViewGroup} requests focus within its children.
2766 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2767 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2768 * to give a finer grained hint about where focus is coming from. May be null
2769 * if there is no hint.
2770 * @return Whether focus was taken.
2772 @SuppressWarnings({"ConstantConditions"})
2773 protected boolean onRequestFocusInDescendants(int direction
,
2774 Rect previouslyFocusedRect
) {
2778 int count
= mChildrenCount
;
2779 if ((direction
& FOCUS_FORWARD
) != 0) {
2788 final View
[] children
= mChildren
;
2789 for (int i
= index
; i
!= end
; i
+= increment
) {
2790 View child
= children
[i
];
2791 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
) {
2792 if (child
.requestFocus(direction
, previouslyFocusedRect
)) {
2806 public void dispatchStartTemporaryDetach() {
2807 super.dispatchStartTemporaryDetach();
2808 final int count
= mChildrenCount
;
2809 final View
[] children
= mChildren
;
2810 for (int i
= 0; i
< count
; i
++) {
2811 children
[i
].dispatchStartTemporaryDetach();
2821 public void dispatchFinishTemporaryDetach() {
2822 super.dispatchFinishTemporaryDetach();
2823 final int count
= mChildrenCount
;
2824 final View
[] children
= mChildren
;
2825 for (int i
= 0; i
< count
; i
++) {
2826 children
[i
].dispatchFinishTemporaryDetach();
2834 void dispatchAttachedToWindow(AttachInfo info
, int visibility
) {
2835 mGroupFlags
|= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW
;
2836 super.dispatchAttachedToWindow(info
, visibility
);
2837 mGroupFlags
&= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW
;
2839 final int count
= mChildrenCount
;
2840 final View
[] children
= mChildren
;
2841 for (int i
= 0; i
< count
; i
++) {
2842 final View child
= children
[i
];
2843 child
.dispatchAttachedToWindow(info
,
2844 combineVisibility(visibility
, child
.getVisibility()));
2846 final int transientCount
= mTransientIndices
== null ?
0 : mTransientIndices
.size();
2847 for (int i
= 0; i
< transientCount
; ++i
) {
2848 View view
= mTransientViews
.get(i
);
2849 view
.dispatchAttachedToWindow(info
,
2850 combineVisibility(visibility
, view
.getVisibility()));
2855 void dispatchScreenStateChanged(int screenState
) {
2856 super.dispatchScreenStateChanged(screenState
);
2858 final int count
= mChildrenCount
;
2859 final View
[] children
= mChildren
;
2860 for (int i
= 0; i
< count
; i
++) {
2861 children
[i
].dispatchScreenStateChanged(screenState
);
2867 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event
) {
2868 boolean handled
= false;
2869 if (includeForAccessibility()) {
2870 handled
= super.dispatchPopulateAccessibilityEventInternal(event
);
2875 // Let our children have a shot in populating the event.
2876 ChildListForAccessibility children
= ChildListForAccessibility
.obtain(this, true);
2878 final int childCount
= children
.getChildCount();
2879 for (int i
= 0; i
< childCount
; i
++) {
2880 View child
= children
.getChildAt(i
);
2881 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
) {
2882 handled
= child
.dispatchPopulateAccessibilityEvent(event
);
2895 * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation
2896 * adds in all child views of the view group, in addition to calling the default View
2899 public void dispatchProvideStructure(ViewStructure structure
) {
2900 super.dispatchProvideStructure(structure
);
2901 if (!isAssistBlocked()) {
2902 if (structure
.getChildCount() == 0) {
2903 final int childrenCount
= getChildCount();
2904 if (childrenCount
> 0) {
2905 structure
.setChildCount(childrenCount
);
2906 ArrayList
<View
> preorderedList
= buildOrderedChildList();
2907 boolean customOrder
= preorderedList
== null
2908 && isChildrenDrawingOrderEnabled();
2909 final View
[] children
= mChildren
;
2910 for (int i
=0; i
<childrenCount
; i
++) {
2913 childIndex
= customOrder ?
getChildDrawingOrder(childrenCount
, i
) : i
;
2914 } catch (IndexOutOfBoundsException e
) {
2916 if (mContext
.getApplicationInfo().targetSdkVersion
2917 < Build
.VERSION_CODES
.M
) {
2918 Log
.w(TAG
, "Bad getChildDrawingOrder while collecting assist @ "
2919 + i
+ " of " + childrenCount
, e
);
2920 // At least one app is failing when we call getChildDrawingOrder
2921 // at this point, so deal semi-gracefully with it by falling back
2922 // on the basic order.
2923 customOrder
= false;
2925 // If we failed at the first index, there really isn't
2926 // anything to do -- we will just proceed with the simple
2928 // Otherwise, we failed in the middle, so need to come up
2929 // with an order for the remaining indices and use that.
2930 // Failed at the first one, easy peasy.
2931 int[] permutation
= new int[childrenCount
];
2932 SparseBooleanArray usedIndices
= new SparseBooleanArray();
2933 // Go back and collected the indices we have done so far.
2934 for (int j
=0; j
<i
; j
++) {
2935 permutation
[j
] = getChildDrawingOrder(childrenCount
, j
);
2936 usedIndices
.put(permutation
[j
], true);
2938 // Fill in the remaining indices with indices that have not
2941 for (int j
=i
; j
<childrenCount
; j
++) {
2942 while (usedIndices
.get(nextIndex
, false)) {
2945 permutation
[j
] = nextIndex
;
2948 // Build the final view list.
2949 preorderedList
= new ArrayList
<>(childrenCount
);
2950 for (int j
=0; j
<childrenCount
; j
++) {
2951 preorderedList
.add(children
[permutation
[j
]]);
2958 final View child
= (preorderedList
== null)
2959 ? children
[childIndex
] : preorderedList
.get(childIndex
);
2960 ViewStructure cstructure
= structure
.newChild(i
);
2961 child
.dispatchProvideStructure(cstructure
);
2970 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info
) {
2971 super.onInitializeAccessibilityNodeInfoInternal(info
);
2972 if (getAccessibilityNodeProvider() != null) {
2975 if (mAttachInfo
!= null) {
2976 final ArrayList
<View
> childrenForAccessibility
= mAttachInfo
.mTempArrayList
;
2977 childrenForAccessibility
.clear();
2978 addChildrenForAccessibility(childrenForAccessibility
);
2979 final int childrenForAccessibilityCount
= childrenForAccessibility
.size();
2980 for (int i
= 0; i
< childrenForAccessibilityCount
; i
++) {
2981 final View child
= childrenForAccessibility
.get(i
);
2982 info
.addChildUnchecked(child
);
2984 childrenForAccessibility
.clear();
2989 public CharSequence
getAccessibilityClassName() {
2990 return ViewGroup
.class.getName();
2994 public void notifySubtreeAccessibilityStateChanged(View child
, View source
, int changeType
) {
2995 // If this is a live region, we should send a subtree change event
2996 // from this view. Otherwise, we can let it propagate up.
2997 if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE
) {
2998 notifyViewAccessibilityStateChangedIfNeeded(changeType
);
2999 } else if (mParent
!= null) {
3001 mParent
.notifySubtreeAccessibilityStateChanged(this, source
, changeType
);
3002 } catch (AbstractMethodError e
) {
3003 Log
.e(VIEW_LOG_TAG
, mParent
.getClass().getSimpleName() +
3004 " does not fully implement ViewParent", e
);
3010 void resetSubtreeAccessibilityStateChanged() {
3011 super.resetSubtreeAccessibilityStateChanged();
3012 View
[] children
= mChildren
;
3013 final int childCount
= mChildrenCount
;
3014 for (int i
= 0; i
< childCount
; i
++) {
3015 children
[i
].resetSubtreeAccessibilityStateChanged();
3022 * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p>
3024 * @param target The target view dispatching this action
3025 * @param action Action being performed; see
3026 * {@link android.view.accessibility.AccessibilityNodeInfo}
3027 * @param args Optional action arguments
3028 * @return false by default. Subclasses should return true if they handle the event.
3031 public boolean onNestedPrePerformAccessibilityAction(View target
, int action
, Bundle args
) {
3039 void dispatchDetachedFromWindow() {
3040 // If we still have a touch target, we are still in the process of
3041 // dispatching motion events to a child; we need to get rid of that
3042 // child to avoid dispatching events to it after the window is torn
3043 // down. To make sure we keep the child in a consistent state, we
3044 // first send it an ACTION_CANCEL motion event.
3045 cancelAndClearTouchTargets(null);
3047 // Similarly, set ACTION_EXIT to all hover targets and clear them.
3050 // In case view is detached while transition is running
3051 mLayoutCalledWhileSuppressed
= false;
3053 // Tear down our drag tracking
3054 mDragNotifiedChildren
= null;
3055 if (mCurrentDrag
!= null) {
3056 mCurrentDrag
.recycle();
3057 mCurrentDrag
= null;
3060 final int count
= mChildrenCount
;
3061 final View
[] children
= mChildren
;
3062 for (int i
= 0; i
< count
; i
++) {
3063 children
[i
].dispatchDetachedFromWindow();
3065 clearDisappearingChildren();
3066 final int transientCount
= mTransientViews
== null ?
0 : mTransientIndices
.size();
3067 for (int i
= 0; i
< transientCount
; ++i
) {
3068 View view
= mTransientViews
.get(i
);
3069 view
.dispatchDetachedFromWindow();
3071 super.dispatchDetachedFromWindow();
3078 protected void internalSetPadding(int left
, int top
, int right
, int bottom
) {
3079 super.internalSetPadding(left
, top
, right
, bottom
);
3081 if ((mPaddingLeft
| mPaddingTop
| mPaddingRight
| mPaddingBottom
) != 0) {
3082 mGroupFlags
|= FLAG_PADDING_NOT_NULL
;
3084 mGroupFlags
&= ~FLAG_PADDING_NOT_NULL
;
3092 protected void dispatchSaveInstanceState(SparseArray
<Parcelable
> container
) {
3093 super.dispatchSaveInstanceState(container
);
3094 final int count
= mChildrenCount
;
3095 final View
[] children
= mChildren
;
3096 for (int i
= 0; i
< count
; i
++) {
3097 View c
= children
[i
];
3098 if ((c
.mViewFlags
& PARENT_SAVE_DISABLED_MASK
) != PARENT_SAVE_DISABLED
) {
3099 c
.dispatchSaveInstanceState(container
);
3105 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
3106 * to only this view, not to its children. For use when overriding
3107 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
3108 * subclasses to freeze their own state but not the state of their children.
3110 * @param container the container
3112 protected void dispatchFreezeSelfOnly(SparseArray
<Parcelable
> container
) {
3113 super.dispatchSaveInstanceState(container
);
3120 protected void dispatchRestoreInstanceState(SparseArray
<Parcelable
> container
) {
3121 super.dispatchRestoreInstanceState(container
);
3122 final int count
= mChildrenCount
;
3123 final View
[] children
= mChildren
;
3124 for (int i
= 0; i
< count
; i
++) {
3125 View c
= children
[i
];
3126 if ((c
.mViewFlags
& PARENT_SAVE_DISABLED_MASK
) != PARENT_SAVE_DISABLED
) {
3127 c
.dispatchRestoreInstanceState(container
);
3133 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
3134 * to only this view, not to its children. For use when overriding
3135 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
3136 * subclasses to thaw their own state but not the state of their children.
3138 * @param container the container
3140 protected void dispatchThawSelfOnly(SparseArray
<Parcelable
> container
) {
3141 super.dispatchRestoreInstanceState(container
);
3145 * Enables or disables the drawing cache for each child of this view group.
3147 * @param enabled true to enable the cache, false to dispose of it
3149 protected void setChildrenDrawingCacheEnabled(boolean enabled
) {
3150 if (enabled
|| (mPersistentDrawingCache
& PERSISTENT_ALL_CACHES
) != PERSISTENT_ALL_CACHES
) {
3151 final View
[] children
= mChildren
;
3152 final int count
= mChildrenCount
;
3153 for (int i
= 0; i
< count
; i
++) {
3154 children
[i
].setDrawingCacheEnabled(enabled
);
3160 Bitmap
createSnapshot(Bitmap
.Config quality
, int backgroundColor
, boolean skipChildren
) {
3161 int count
= mChildrenCount
;
3162 int[] visibilities
= null;
3165 visibilities
= new int[count
];
3166 for (int i
= 0; i
< count
; i
++) {
3167 View child
= getChildAt(i
);
3168 visibilities
[i
] = child
.getVisibility();
3169 if (visibilities
[i
] == View
.VISIBLE
) {
3170 child
.setVisibility(INVISIBLE
);
3175 Bitmap b
= super.createSnapshot(quality
, backgroundColor
, skipChildren
);
3178 for (int i
= 0; i
< count
; i
++) {
3179 getChildAt(i
).setVisibility(visibilities
[i
]);
3186 /** Return true if this ViewGroup is laying out using optical bounds. */
3187 boolean isLayoutModeOptical() {
3188 return mLayoutMode
== LAYOUT_MODE_OPTICAL_BOUNDS
;
3191 Insets
computeOpticalInsets() {
3192 if (isLayoutModeOptical()) {
3197 for (int i
= 0; i
< mChildrenCount
; i
++) {
3198 View child
= getChildAt(i
);
3199 if (child
.getVisibility() == VISIBLE
) {
3200 Insets insets
= child
.getOpticalInsets();
3201 left
= Math
.max(left
, insets
.left
);
3202 top
= Math
.max(top
, insets
.top
);
3203 right
= Math
.max(right
, insets
.right
);
3204 bottom
= Math
.max(bottom
, insets
.bottom
);
3207 return Insets
.of(left
, top
, right
, bottom
);
3213 private static void fillRect(Canvas canvas
, Paint paint
, int x1
, int y1
, int x2
, int y2
) {
3214 if (x1
!= x2
&& y1
!= y2
) {
3216 int tmp
= x1
; x1
= x2
; x2
= tmp
;
3219 int tmp
= y1
; y1
= y2
; y2
= tmp
;
3221 canvas
.drawRect(x1
, y1
, x2
, y2
, paint
);
3225 private static int sign(int x
) {
3226 return (x
>= 0) ?
1 : -1;
3229 private static void drawCorner(Canvas c
, Paint paint
, int x1
, int y1
, int dx
, int dy
, int lw
) {
3230 fillRect(c
, paint
, x1
, y1
, x1
+ dx
, y1
+ lw
* sign(dy
));
3231 fillRect(c
, paint
, x1
, y1
, x1
+ lw
* sign(dx
), y1
+ dy
);
3234 private int dipsToPixels(int dips
) {
3235 float scale
= getContext().getResources().getDisplayMetrics().density
;
3236 return (int) (dips
* scale
+ 0.5f
);
3239 private static void drawRectCorners(Canvas canvas
, int x1
, int y1
, int x2
, int y2
, Paint paint
,
3240 int lineLength
, int lineWidth
) {
3241 drawCorner(canvas
, paint
, x1
, y1
, lineLength
, lineLength
, lineWidth
);
3242 drawCorner(canvas
, paint
, x1
, y2
, lineLength
, -lineLength
, lineWidth
);
3243 drawCorner(canvas
, paint
, x2
, y1
, -lineLength
, lineLength
, lineWidth
);
3244 drawCorner(canvas
, paint
, x2
, y2
, -lineLength
, -lineLength
, lineWidth
);
3247 private static void fillDifference(Canvas canvas
,
3248 int x2
, int y2
, int x3
, int y3
,
3249 int dx1
, int dy1
, int dx2
, int dy2
, Paint paint
) {
3256 fillRect(canvas
, paint
, x1
, y1
, x4
, y2
);
3257 fillRect(canvas
, paint
, x1
, y2
, x2
, y3
);
3258 fillRect(canvas
, paint
, x3
, y2
, x4
, y3
);
3259 fillRect(canvas
, paint
, x1
, y3
, x4
, y4
);
3265 protected void onDebugDrawMargins(Canvas canvas
, Paint paint
) {
3266 for (int i
= 0; i
< getChildCount(); i
++) {
3267 View c
= getChildAt(i
);
3268 c
.getLayoutParams().onDebugDraw(c
, canvas
, paint
);
3275 protected void onDebugDraw(Canvas canvas
) {
3276 Paint paint
= getDebugPaint();
3278 // Draw optical bounds
3280 paint
.setColor(Color
.RED
);
3281 paint
.setStyle(Paint
.Style
.STROKE
);
3283 for (int i
= 0; i
< getChildCount(); i
++) {
3284 View c
= getChildAt(i
);
3285 if (c
.getVisibility() != View
.GONE
) {
3286 Insets insets
= c
.getOpticalInsets();
3288 drawRect(canvas
, paint
,
3289 c
.getLeft() + insets
.left
,
3290 c
.getTop() + insets
.top
,
3291 c
.getRight() - insets
.right
- 1,
3292 c
.getBottom() - insets
.bottom
- 1);
3299 paint
.setColor(Color
.argb(63, 255, 0, 255));
3300 paint
.setStyle(Paint
.Style
.FILL
);
3302 onDebugDrawMargins(canvas
, paint
);
3307 paint
.setColor(Color
.rgb(63, 127, 255));
3308 paint
.setStyle(Paint
.Style
.FILL
);
3310 int lineLength
= dipsToPixels(8);
3311 int lineWidth
= dipsToPixels(1);
3312 for (int i
= 0; i
< getChildCount(); i
++) {
3313 View c
= getChildAt(i
);
3314 if (c
.getVisibility() != View
.GONE
) {
3315 drawRectCorners(canvas
, c
.getLeft(), c
.getTop(), c
.getRight(), c
.getBottom(),
3316 paint
, lineLength
, lineWidth
);
3326 protected void dispatchDraw(Canvas canvas
) {
3327 boolean usingRenderNodeProperties
= canvas
.isRecordingFor(mRenderNode
);
3328 final int childrenCount
= mChildrenCount
;
3329 final View
[] children
= mChildren
;
3330 int flags
= mGroupFlags
;
3332 if ((flags
& FLAG_RUN_ANIMATION
) != 0 && canAnimate()) {
3333 final boolean buildCache
= !isHardwareAccelerated();
3334 for (int i
= 0; i
< childrenCount
; i
++) {
3335 final View child
= children
[i
];
3336 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
) {
3337 final LayoutParams params
= child
.getLayoutParams();
3338 attachLayoutAnimationParameters(child
, params
, i
, childrenCount
);
3339 bindLayoutAnimation(child
);
3343 final LayoutAnimationController controller
= mLayoutAnimationController
;
3344 if (controller
.willOverlap()) {
3345 mGroupFlags
|= FLAG_OPTIMIZE_INVALIDATE
;
3350 mGroupFlags
&= ~FLAG_RUN_ANIMATION
;
3351 mGroupFlags
&= ~FLAG_ANIMATION_DONE
;
3353 if (mAnimationListener
!= null) {
3354 mAnimationListener
.onAnimationStart(controller
.getAnimation());
3358 int clipSaveCount
= 0;
3359 final boolean clipToPadding
= (flags
& CLIP_TO_PADDING_MASK
) == CLIP_TO_PADDING_MASK
;
3360 if (clipToPadding
) {
3361 clipSaveCount
= canvas
.save();
3362 canvas
.clipRect(mScrollX
+ mPaddingLeft
, mScrollY
+ mPaddingTop
,
3363 mScrollX
+ mRight
- mLeft
- mPaddingRight
,
3364 mScrollY
+ mBottom
- mTop
- mPaddingBottom
);
3367 // We will draw our child's animation, let's reset the flag
3368 mPrivateFlags
&= ~PFLAG_DRAW_ANIMATION
;
3369 mGroupFlags
&= ~FLAG_INVALIDATE_REQUIRED
;
3371 boolean more
= false;
3372 final long drawingTime
= getDrawingTime();
3374 if (usingRenderNodeProperties
) canvas
.insertReorderBarrier();
3375 final int transientCount
= mTransientIndices
== null ?
0 : mTransientIndices
.size();
3376 int transientIndex
= transientCount
!= 0 ?
0 : -1;
3377 // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
3378 // draw reordering internally
3379 final ArrayList
<View
> preorderedList
= usingRenderNodeProperties
3380 ?
null : buildOrderedChildList();
3381 final boolean customOrder
= preorderedList
== null
3382 && isChildrenDrawingOrderEnabled();
3383 for (int i
= 0; i
< childrenCount
; i
++) {
3384 while (transientIndex
>= 0 && mTransientIndices
.get(transientIndex
) == i
) {
3385 final View transientChild
= mTransientViews
.get(transientIndex
);
3386 if ((transientChild
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
||
3387 transientChild
.getAnimation() != null) {
3388 more
|= drawChild(canvas
, transientChild
, drawingTime
);
3391 if (transientIndex
>= transientCount
) {
3392 transientIndex
= -1;
3395 int childIndex
= customOrder ?
getChildDrawingOrder(childrenCount
, i
) : i
;
3396 final View child
= (preorderedList
== null)
3397 ? children
[childIndex
] : preorderedList
.get(childIndex
);
3398 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
|| child
.getAnimation() != null) {
3399 more
|= drawChild(canvas
, child
, drawingTime
);
3402 while (transientIndex
>= 0) {
3403 // there may be additional transient views after the normal views
3404 final View transientChild
= mTransientViews
.get(transientIndex
);
3405 if ((transientChild
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
||
3406 transientChild
.getAnimation() != null) {
3407 more
|= drawChild(canvas
, transientChild
, drawingTime
);
3410 if (transientIndex
>= transientCount
) {
3414 if (preorderedList
!= null) preorderedList
.clear();
3416 // Draw any disappearing views that have animations
3417 if (mDisappearingChildren
!= null) {
3418 final ArrayList
<View
> disappearingChildren
= mDisappearingChildren
;
3419 final int disappearingCount
= disappearingChildren
.size() - 1;
3420 // Go backwards -- we may delete as animations finish
3421 for (int i
= disappearingCount
; i
>= 0; i
--) {
3422 final View child
= disappearingChildren
.get(i
);
3423 more
|= drawChild(canvas
, child
, drawingTime
);
3426 if (usingRenderNodeProperties
) canvas
.insertInorderBarrier();
3429 onDebugDraw(canvas
);
3432 if (clipToPadding
) {
3433 canvas
.restoreToCount(clipSaveCount
);
3436 // mGroupFlags might have been updated by drawChild()
3437 flags
= mGroupFlags
;
3439 if ((flags
& FLAG_INVALIDATE_REQUIRED
) == FLAG_INVALIDATE_REQUIRED
) {
3443 if ((flags
& FLAG_ANIMATION_DONE
) == 0 && (flags
& FLAG_NOTIFY_ANIMATION_LISTENER
) == 0 &&
3444 mLayoutAnimationController
.isDone() && !more
) {
3445 // We want to erase the drawing cache and notify the listener after the
3446 // next frame is drawn because one extra invalidate() is caused by
3447 // drawChild() after the animation is over
3448 mGroupFlags
|= FLAG_NOTIFY_ANIMATION_LISTENER
;
3449 final Runnable end
= new Runnable() {
3451 notifyAnimationListener();
3459 * Returns the ViewGroupOverlay for this view group, creating it if it does
3460 * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
3461 * {@link ViewGroupOverlay} allows views to be added to the overlay. These
3462 * views, like overlay drawables, are visual-only; they do not receive input
3463 * events and should not be used as anything other than a temporary
3464 * representation of a view in a parent container, such as might be used
3465 * by an animation effect.
3467 * <p>Note: Overlays do not currently work correctly with {@link
3468 * SurfaceView} or {@link TextureView}; contents in overlays for these
3469 * types of views may not display correctly.</p>
3471 * @return The ViewGroupOverlay object for this view.
3472 * @see ViewGroupOverlay
3475 public ViewGroupOverlay
getOverlay() {
3476 if (mOverlay
== null) {
3477 mOverlay
= new ViewGroupOverlay(mContext
, this);
3479 return (ViewGroupOverlay
) mOverlay
;
3483 * Returns the index of the child to draw for this iteration. Override this
3484 * if you want to change the drawing order of children. By default, it
3487 * NOTE: In order for this method to be called, you must enable child ordering
3488 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
3490 * @param i The current iteration.
3491 * @return The index of the child to draw this iteration.
3493 * @see #setChildrenDrawingOrderEnabled(boolean)
3494 * @see #isChildrenDrawingOrderEnabled()
3496 protected int getChildDrawingOrder(int childCount
, int i
) {
3500 private boolean hasChildWithZ() {
3501 for (int i
= 0; i
< mChildrenCount
; i
++) {
3502 if (mChildren
[i
].getZ() != 0) return true;
3508 * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
3509 * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
3510 * after use to avoid leaking child Views.
3512 * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
3515 ArrayList
<View
> buildOrderedChildList() {
3516 final int count
= mChildrenCount
;
3517 if (count
<= 1 || !hasChildWithZ()) return null;
3519 if (mPreSortedChildren
== null) {
3520 mPreSortedChildren
= new ArrayList
<View
>(count
);
3522 mPreSortedChildren
.ensureCapacity(count
);
3525 final boolean useCustomOrder
= isChildrenDrawingOrderEnabled();
3526 for (int i
= 0; i
< mChildrenCount
; i
++) {
3527 // add next child (in child order) to end of list
3528 int childIndex
= useCustomOrder ?
getChildDrawingOrder(mChildrenCount
, i
) : i
;
3529 View nextChild
= mChildren
[childIndex
];
3530 float currentZ
= nextChild
.getZ();
3532 // insert ahead of any Views with greater Z
3533 int insertIndex
= i
;
3534 while (insertIndex
> 0 && mPreSortedChildren
.get(insertIndex
- 1).getZ() > currentZ
) {
3537 mPreSortedChildren
.add(insertIndex
, nextChild
);
3539 return mPreSortedChildren
;
3542 private void notifyAnimationListener() {
3543 mGroupFlags
&= ~FLAG_NOTIFY_ANIMATION_LISTENER
;
3544 mGroupFlags
|= FLAG_ANIMATION_DONE
;
3546 if (mAnimationListener
!= null) {
3547 final Runnable end
= new Runnable() {
3549 mAnimationListener
.onAnimationEnd(mLayoutAnimationController
.getAnimation());
3559 * This method is used to cause children of this ViewGroup to restore or recreate their
3560 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
3561 * to recreate its own display list, which would happen if it went through the normal
3562 * draw/dispatchDraw mechanisms.
3567 protected void dispatchGetDisplayList() {
3568 final int count
= mChildrenCount
;
3569 final View
[] children
= mChildren
;
3570 for (int i
= 0; i
< count
; i
++) {
3571 final View child
= children
[i
];
3572 if (((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
|| child
.getAnimation() != null)) {
3573 recreateChildDisplayList(child
);
3576 if (mOverlay
!= null) {
3577 View overlayView
= mOverlay
.getOverlayView();
3578 recreateChildDisplayList(overlayView
);
3580 if (mDisappearingChildren
!= null) {
3581 final ArrayList
<View
> disappearingChildren
= mDisappearingChildren
;
3582 final int disappearingCount
= disappearingChildren
.size();
3583 for (int i
= 0; i
< disappearingCount
; ++i
) {
3584 final View child
= disappearingChildren
.get(i
);
3585 recreateChildDisplayList(child
);
3590 private void recreateChildDisplayList(View child
) {
3591 child
.mRecreateDisplayList
= (child
.mPrivateFlags
& PFLAG_INVALIDATED
) != 0;
3592 child
.mPrivateFlags
&= ~PFLAG_INVALIDATED
;
3593 child
.updateDisplayListIfDirty();
3594 child
.mRecreateDisplayList
= false;
3598 * Draw one child of this View Group. This method is responsible for getting
3599 * the canvas in the right state. This includes clipping, translating so
3600 * that the child's scrolled origin is at 0, 0, and applying any animation
3603 * @param canvas The canvas on which to draw the child
3604 * @param child Who to draw
3605 * @param drawingTime The time at which draw is occurring
3606 * @return True if an invalidate() was issued
3608 protected boolean drawChild(Canvas canvas
, View child
, long drawingTime
) {
3609 return child
.draw(canvas
, this, drawingTime
);
3613 void getScrollIndicatorBounds(@NonNull Rect out
) {
3614 super.getScrollIndicatorBounds(out
);
3616 // If we have padding and we're supposed to clip children to that
3617 // padding, offset the scroll indicators to match our clip bounds.
3618 final boolean clipToPadding
= (mGroupFlags
& CLIP_TO_PADDING_MASK
) == CLIP_TO_PADDING_MASK
;
3619 if (clipToPadding
) {
3620 out
.left
+= mPaddingLeft
;
3621 out
.right
-= mPaddingRight
;
3622 out
.top
+= mPaddingTop
;
3623 out
.bottom
-= mPaddingBottom
;
3628 * Returns whether this group's children are clipped to their bounds before drawing.
3629 * The default value is true.
3630 * @see #setClipChildren(boolean)
3632 * @return True if the group's children will be clipped to their bounds,
3635 @ViewDebug.ExportedProperty(category
= "drawing")
3636 public boolean getClipChildren() {
3637 return ((mGroupFlags
& FLAG_CLIP_CHILDREN
) != 0);
3641 * By default, children are clipped to their bounds before drawing. This
3642 * allows view groups to override this behavior for animations, etc.
3644 * @param clipChildren true to clip children to their bounds,
3646 * @attr ref android.R.styleable#ViewGroup_clipChildren
3648 public void setClipChildren(boolean clipChildren
) {
3649 boolean previousValue
= (mGroupFlags
& FLAG_CLIP_CHILDREN
) == FLAG_CLIP_CHILDREN
;
3650 if (clipChildren
!= previousValue
) {
3651 setBooleanFlag(FLAG_CLIP_CHILDREN
, clipChildren
);
3652 for (int i
= 0; i
< mChildrenCount
; ++i
) {
3653 View child
= getChildAt(i
);
3654 if (child
.mRenderNode
!= null) {
3655 child
.mRenderNode
.setClipToBounds(clipChildren
);
3663 * Sets whether this ViewGroup will clip its children to its padding and resize (but not
3664 * clip) any EdgeEffect to the padded region, if padding is present.
3666 * By default, children are clipped to the padding of their parent
3667 * ViewGroup. This clipping behavior is only enabled if padding is non-zero.
3669 * @param clipToPadding true to clip children to the padding of the group, and resize (but
3670 * not clip) any EdgeEffect to the padded region. False otherwise.
3671 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3673 public void setClipToPadding(boolean clipToPadding
) {
3674 if (hasBooleanFlag(FLAG_CLIP_TO_PADDING
) != clipToPadding
) {
3675 setBooleanFlag(FLAG_CLIP_TO_PADDING
, clipToPadding
);
3681 * Returns whether this ViewGroup will clip its children to its padding, and resize (but
3682 * not clip) any EdgeEffect to the padded region, if padding is present.
3684 * By default, children are clipped to the padding of their parent
3685 * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
3687 * @return true if this ViewGroup clips children to its padding and resizes (but doesn't
3688 * clip) any EdgeEffect to the padded region, false otherwise.
3690 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3692 @ViewDebug.ExportedProperty(category
= "drawing")
3693 public boolean getClipToPadding() {
3694 return hasBooleanFlag(FLAG_CLIP_TO_PADDING
);
3701 public void dispatchSetSelected(boolean selected
) {
3702 final View
[] children
= mChildren
;
3703 final int count
= mChildrenCount
;
3704 for (int i
= 0; i
< count
; i
++) {
3705 children
[i
].setSelected(selected
);
3713 public void dispatchSetActivated(boolean activated
) {
3714 final View
[] children
= mChildren
;
3715 final int count
= mChildrenCount
;
3716 for (int i
= 0; i
< count
; i
++) {
3717 children
[i
].setActivated(activated
);
3722 protected void dispatchSetPressed(boolean pressed
) {
3723 final View
[] children
= mChildren
;
3724 final int count
= mChildrenCount
;
3725 for (int i
= 0; i
< count
; i
++) {
3726 final View child
= children
[i
];
3727 // Children that are clickable on their own should not
3728 // show a pressed state when their parent view does.
3729 // Clearing a pressed state always propagates.
3730 if (!pressed
|| (!child
.isClickable() && !child
.isLongClickable())) {
3731 child
.setPressed(pressed
);
3737 * Dispatches drawable hotspot changes to child views that meet at least
3738 * one of the following criteria:
3740 * <li>Returns {@code false} from both {@link View#isClickable()} and
3741 * {@link View#isLongClickable()}</li>
3742 * <li>Requests duplication of parent state via
3743 * {@link View#setDuplicateParentStateEnabled(boolean)}</li>
3746 * @param x hotspot x coordinate
3747 * @param y hotspot y coordinate
3748 * @see #drawableHotspotChanged(float, float)
3751 public void dispatchDrawableHotspotChanged(float x
, float y
) {
3752 final int count
= mChildrenCount
;
3757 final View
[] children
= mChildren
;
3758 for (int i
= 0; i
< count
; i
++) {
3759 final View child
= children
[i
];
3760 // Children that are clickable on their own should not
3761 // receive hotspots when their parent view does.
3762 final boolean nonActionable
= !child
.isClickable() && !child
.isLongClickable();
3763 final boolean duplicatesState
= (child
.mViewFlags
& DUPLICATE_PARENT_STATE
) != 0;
3764 if (nonActionable
|| duplicatesState
) {
3765 final float[] point
= getTempPoint();
3768 transformPointToViewLocal(point
, child
);
3769 child
.drawableHotspotChanged(point
[0], point
[1]);
3775 void dispatchCancelPendingInputEvents() {
3776 super.dispatchCancelPendingInputEvents();
3778 final View
[] children
= mChildren
;
3779 final int count
= mChildrenCount
;
3780 for (int i
= 0; i
< count
; i
++) {
3781 children
[i
].dispatchCancelPendingInputEvents();
3786 * When this property is set to true, this ViewGroup supports static transformations on
3787 * children; this causes
3788 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3789 * invoked when a child is drawn.
3791 * Any subclass overriding
3792 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3793 * set this property to true.
3795 * @param enabled True to enable static transformations on children, false otherwise.
3797 * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
3799 protected void setStaticTransformationsEnabled(boolean enabled
) {
3800 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS
, enabled
);
3804 * Sets <code>t</code> to be the static transformation of the child, if set, returning a
3805 * boolean to indicate whether a static transform was set. The default implementation
3806 * simply returns <code>false</code>; subclasses may override this method for different
3807 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
3808 * for this method to be called.
3810 * @param child The child view whose static transform is being requested
3811 * @param t The Transformation which will hold the result
3812 * @return true if the transformation was set, false otherwise
3813 * @see #setStaticTransformationsEnabled(boolean)
3815 protected boolean getChildStaticTransformation(View child
, Transformation t
) {
3819 Transformation
getChildTransformation() {
3820 if (mChildTransformation
== null) {
3821 mChildTransformation
= new Transformation();
3823 return mChildTransformation
;
3830 protected View
findViewTraversal(@IdRes int id
) {
3835 final View
[] where
= mChildren
;
3836 final int len
= mChildrenCount
;
3838 for (int i
= 0; i
< len
; i
++) {
3841 if ((v
.mPrivateFlags
& PFLAG_IS_ROOT_NAMESPACE
) == 0) {
3842 v
= v
.findViewById(id
);
3857 protected View
findViewWithTagTraversal(Object tag
) {
3858 if (tag
!= null && tag
.equals(mTag
)) {
3862 final View
[] where
= mChildren
;
3863 final int len
= mChildrenCount
;
3865 for (int i
= 0; i
< len
; i
++) {
3868 if ((v
.mPrivateFlags
& PFLAG_IS_ROOT_NAMESPACE
) == 0) {
3869 v
= v
.findViewWithTag(tag
);
3884 protected View
findViewByPredicateTraversal(Predicate
<View
> predicate
, View childToSkip
) {
3885 if (predicate
.apply(this)) {
3889 final View
[] where
= mChildren
;
3890 final int len
= mChildrenCount
;
3892 for (int i
= 0; i
< len
; i
++) {
3895 if (v
!= childToSkip
&& (v
.mPrivateFlags
& PFLAG_IS_ROOT_NAMESPACE
) == 0) {
3896 v
= v
.findViewByPredicate(predicate
);
3908 * This method adds a view to this container at the specified index purely for the
3909 * purposes of allowing that view to draw even though it is not a normal child of
3910 * the container. That is, the view does not participate in layout, focus, accessibility,
3911 * input, or other normal view operations; it is purely an item to be drawn during the normal
3912 * rendering operation of this container. The index that it is added at is the order
3913 * in which it will be drawn, with respect to the other views in the container.
3914 * For example, a transient view added at index 0 will be drawn before all other views
3915 * in the container because it will be drawn first (including before any real view
3916 * at index 0). There can be more than one transient view at any particular index;
3917 * these views will be drawn in the order in which they were added to the list of
3918 * transient views. The index of transient views can also be greater than the number
3919 * of normal views in the container; that just means that they will be drawn after all
3920 * other views are drawn.
3922 * <p>Note that since transient views do not participate in layout, they must be sized
3923 * manually or, more typically, they should just use the size that they had before they
3924 * were removed from their container.</p>
3926 * <p>Transient views are useful for handling animations of views that have been removed
3927 * from the container, but which should be animated out after the removal. Adding these
3928 * views as transient views allows them to participate in drawing without side-effecting
3929 * the layout of the container.</p>
3931 * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed}
3932 * from the container when they are no longer needed. For example, a transient view
3933 * which is added in order to fade it out in its old location should be removed
3934 * once the animation is complete.</p>
3936 * @param view The view to be added
3937 * @param index The index at which this view should be drawn, must be >= 0.
3938 * This value is relative to the {@link #getChildAt(int) index} values in the normal
3939 * child list of this container, where any transient view at a particular index will
3940 * be drawn before any normal child at that same index.
3944 public void addTransientView(View view
, int index
) {
3948 if (mTransientIndices
== null) {
3949 mTransientIndices
= new ArrayList
<Integer
>();
3950 mTransientViews
= new ArrayList
<View
>();
3952 final int oldSize
= mTransientIndices
.size();
3955 for (insertionIndex
= 0; insertionIndex
< oldSize
; ++insertionIndex
) {
3956 if (index
< mTransientIndices
.get(insertionIndex
)) {
3960 mTransientIndices
.add(insertionIndex
, index
);
3961 mTransientViews
.add(insertionIndex
, view
);
3963 mTransientIndices
.add(index
);
3964 mTransientViews
.add(view
);
3966 view
.mParent
= this;
3967 view
.dispatchAttachedToWindow(mAttachInfo
, (mViewFlags
&VISIBILITY_MASK
));
3972 * Removes a view from the list of transient views in this container. If there is no
3973 * such transient view, this method does nothing.
3975 * @param view The transient view to be removed
3979 public void removeTransientView(View view
) {
3980 if (mTransientViews
== null) {
3983 final int size
= mTransientViews
.size();
3984 for (int i
= 0; i
< size
; ++i
) {
3985 if (view
== mTransientViews
.get(i
)) {
3986 mTransientViews
.remove(i
);
3987 mTransientIndices
.remove(i
);
3988 view
.mParent
= null;
3989 view
.dispatchDetachedFromWindow();
3997 * Returns the number of transient views in this container. Specific transient
3998 * views and the index at which they were added can be retrieved via
3999 * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}.
4001 * @see #addTransientView(View, int)
4002 * @return The number of transient views in this container
4006 public int getTransientViewCount() {
4007 return mTransientIndices
== null ?
0 : mTransientIndices
.size();
4011 * Given a valid position within the list of transient views, returns the index of
4012 * the transient view at that position.
4014 * @param position The position of the index being queried. Must be at least 0
4015 * and less than the value returned by {@link #getTransientViewCount()}.
4016 * @return The index of the transient view stored in the given position if the
4017 * position is valid, otherwise -1
4021 public int getTransientViewIndex(int position
) {
4022 if (position
< 0 || mTransientIndices
== null || position
>= mTransientIndices
.size()) {
4025 return mTransientIndices
.get(position
);
4029 * Given a valid position within the list of transient views, returns the
4030 * transient view at that position.
4032 * @param position The position of the view being queried. Must be at least 0
4033 * and less than the value returned by {@link #getTransientViewCount()}.
4034 * @return The transient view stored in the given position if the
4035 * position is valid, otherwise null
4039 public View
getTransientView(int position
) {
4040 if (mTransientViews
== null || position
>= mTransientViews
.size()) {
4043 return mTransientViews
.get(position
);
4047 * <p>Adds a child view. If no layout parameters are already set on the child, the
4048 * default parameters for this ViewGroup are set on the child.</p>
4050 * <p><strong>Note:</strong> do not invoke this method from
4051 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4052 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4054 * @param child the child view to add
4056 * @see #generateDefaultLayoutParams()
4058 public void addView(View child
) {
4063 * Adds a child view. If no layout parameters are already set on the child, the
4064 * default parameters for this ViewGroup are set on the child.
4066 * <p><strong>Note:</strong> do not invoke this method from
4067 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4068 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4070 * @param child the child view to add
4071 * @param index the position at which to add the child
4073 * @see #generateDefaultLayoutParams()
4075 public void addView(View child
, int index
) {
4076 if (child
== null) {
4077 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4079 LayoutParams params
= child
.getLayoutParams();
4080 if (params
== null) {
4081 params
= generateDefaultLayoutParams();
4082 if (params
== null) {
4083 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
4086 addView(child
, index
, params
);
4090 * Adds a child view with this ViewGroup's default layout parameters and the
4091 * specified width and height.
4093 * <p><strong>Note:</strong> do not invoke this method from
4094 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4095 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4097 * @param child the child view to add
4099 public void addView(View child
, int width
, int height
) {
4100 final LayoutParams params
= generateDefaultLayoutParams();
4101 params
.width
= width
;
4102 params
.height
= height
;
4103 addView(child
, -1, params
);
4107 * Adds a child view with the specified layout parameters.
4109 * <p><strong>Note:</strong> do not invoke this method from
4110 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4111 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4113 * @param child the child view to add
4114 * @param params the layout parameters to set on the child
4116 public void addView(View child
, LayoutParams params
) {
4117 addView(child
, -1, params
);
4121 * Adds a child view with the specified layout parameters.
4123 * <p><strong>Note:</strong> do not invoke this method from
4124 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4125 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4127 * @param child the child view to add
4128 * @param index the position at which to add the child or -1 to add last
4129 * @param params the layout parameters to set on the child
4131 public void addView(View child
, int index
, LayoutParams params
) {
4133 System
.out
.println(this + " addView");
4136 if (child
== null) {
4137 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4140 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
4141 // therefore, we call requestLayout() on ourselves before, so that the child's request
4142 // will be blocked at our level
4145 addViewInner(child
, index
, params
, false);
4151 public void updateViewLayout(View view
, ViewGroup
.LayoutParams params
) {
4152 if (!checkLayoutParams(params
)) {
4153 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
4155 if (view
.mParent
!= this) {
4156 throw new IllegalArgumentException("Given view not a child of " + this);
4158 view
.setLayoutParams(params
);
4164 protected boolean checkLayoutParams(ViewGroup
.LayoutParams p
) {
4169 * Interface definition for a callback to be invoked when the hierarchy
4170 * within this view changed. The hierarchy changes whenever a child is added
4171 * to or removed from this view.
4173 public interface OnHierarchyChangeListener
{
4175 * Called when a new child is added to a parent view.
4177 * @param parent the view in which a child was added
4178 * @param child the new child view added in the hierarchy
4180 void onChildViewAdded(View parent
, View child
);
4183 * Called when a child is removed from a parent view.
4185 * @param parent the view from which the child was removed
4186 * @param child the child removed from the hierarchy
4188 void onChildViewRemoved(View parent
, View child
);
4192 * Register a callback to be invoked when a child is added to or removed
4195 * @param listener the callback to invoke on hierarchy change
4197 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener
) {
4198 mOnHierarchyChangeListener
= listener
;
4201 void dispatchViewAdded(View child
) {
4203 if (mOnHierarchyChangeListener
!= null) {
4204 mOnHierarchyChangeListener
.onChildViewAdded(this, child
);
4209 * Called when a new child is added to this ViewGroup. Overrides should always
4210 * call super.onViewAdded.
4212 * @param child the added child view
4214 public void onViewAdded(View child
) {
4217 void dispatchViewRemoved(View child
) {
4218 onViewRemoved(child
);
4219 if (mOnHierarchyChangeListener
!= null) {
4220 mOnHierarchyChangeListener
.onChildViewRemoved(this, child
);
4225 * Called when a child view is removed from this ViewGroup. Overrides should always
4226 * call super.onViewRemoved.
4228 * @param child the removed child view
4230 public void onViewRemoved(View child
) {
4233 private void clearCachedLayoutMode() {
4234 if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET
)) {
4235 mLayoutMode
= LAYOUT_MODE_UNDEFINED
;
4240 protected void onAttachedToWindow() {
4241 super.onAttachedToWindow();
4242 clearCachedLayoutMode();
4246 protected void onDetachedFromWindow() {
4247 super.onDetachedFromWindow();
4248 clearCachedLayoutMode();
4252 * Adds a view during layout. This is useful if in your onLayout() method,
4253 * you need to add more views (as does the list view for example).
4255 * If index is negative, it means put it at the end of the list.
4257 * @param child the view to add to the group
4258 * @param index the index at which the child must be added or -1 to add last
4259 * @param params the layout parameters to associate with the child
4260 * @return true if the child was added, false otherwise
4262 protected boolean addViewInLayout(View child
, int index
, LayoutParams params
) {
4263 return addViewInLayout(child
, index
, params
, false);
4267 * Adds a view during layout. This is useful if in your onLayout() method,
4268 * you need to add more views (as does the list view for example).
4270 * If index is negative, it means put it at the end of the list.
4272 * @param child the view to add to the group
4273 * @param index the index at which the child must be added or -1 to add last
4274 * @param params the layout parameters to associate with the child
4275 * @param preventRequestLayout if true, calling this method will not trigger a
4276 * layout request on child
4277 * @return true if the child was added, false otherwise
4279 protected boolean addViewInLayout(View child
, int index
, LayoutParams params
,
4280 boolean preventRequestLayout
) {
4281 if (child
== null) {
4282 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4284 child
.mParent
= null;
4285 addViewInner(child
, index
, params
, preventRequestLayout
);
4286 child
.mPrivateFlags
= (child
.mPrivateFlags
& ~PFLAG_DIRTY_MASK
) | PFLAG_DRAWN
;
4291 * Prevents the specified child to be laid out during the next layout pass.
4293 * @param child the child on which to perform the cleanup
4295 protected void cleanupLayoutState(View child
) {
4296 child
.mPrivateFlags
&= ~View
.PFLAG_FORCE_LAYOUT
;
4299 private void addViewInner(View child
, int index
, LayoutParams params
,
4300 boolean preventRequestLayout
) {
4302 if (mTransition
!= null) {
4303 // Don't prevent other add transitions from completing, but cancel remove
4304 // transitions to let them complete the process before we add to the container
4305 mTransition
.cancel(LayoutTransition
.DISAPPEARING
);
4308 if (child
.getParent() != null) {
4309 throw new IllegalStateException("The specified child already has a parent. " +
4310 "You must call removeView() on the child's parent first.");
4313 if (mTransition
!= null) {
4314 mTransition
.addChild(this, child
);
4317 if (!checkLayoutParams(params
)) {
4318 params
= generateLayoutParams(params
);
4321 if (preventRequestLayout
) {
4322 child
.mLayoutParams
= params
;
4324 child
.setLayoutParams(params
);
4328 index
= mChildrenCount
;
4331 addInArray(child
, index
);
4333 // tell our children
4334 if (preventRequestLayout
) {
4335 child
.assignParent(this);
4337 child
.mParent
= this;
4340 if (child
.hasFocus()) {
4341 requestChildFocus(child
, child
.findFocus());
4344 AttachInfo ai
= mAttachInfo
;
4345 if (ai
!= null && (mGroupFlags
& FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW
) == 0) {
4346 boolean lastKeepOn
= ai
.mKeepScreenOn
;
4347 ai
.mKeepScreenOn
= false;
4348 child
.dispatchAttachedToWindow(mAttachInfo
, (mViewFlags
&VISIBILITY_MASK
));
4349 if (ai
.mKeepScreenOn
) {
4350 needGlobalAttributesUpdate(true);
4352 ai
.mKeepScreenOn
= lastKeepOn
;
4355 if (child
.isLayoutDirectionInherited()) {
4356 child
.resetRtlProperties();
4359 dispatchViewAdded(child
);
4361 if ((child
.mViewFlags
& DUPLICATE_PARENT_STATE
) == DUPLICATE_PARENT_STATE
) {
4362 mGroupFlags
|= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE
;
4365 if (child
.hasTransientState()) {
4366 childHasTransientStateChanged(child
, true);
4369 if (child
.getVisibility() != View
.GONE
) {
4370 notifySubtreeAccessibilityStateChangedIfNeeded();
4373 if (mTransientIndices
!= null) {
4374 final int transientCount
= mTransientIndices
.size();
4375 for (int i
= 0; i
< transientCount
; ++i
) {
4376 final int oldIndex
= mTransientIndices
.get(i
);
4377 if (index
<= oldIndex
) {
4378 mTransientIndices
.set(i
, oldIndex
+ 1);
4384 private void addInArray(View child
, int index
) {
4385 View
[] children
= mChildren
;
4386 final int count
= mChildrenCount
;
4387 final int size
= children
.length
;
4388 if (index
== count
) {
4389 if (size
== count
) {
4390 mChildren
= new View
[size
+ ARRAY_CAPACITY_INCREMENT
];
4391 System
.arraycopy(children
, 0, mChildren
, 0, size
);
4392 children
= mChildren
;
4394 children
[mChildrenCount
++] = child
;
4395 } else if (index
< count
) {
4396 if (size
== count
) {
4397 mChildren
= new View
[size
+ ARRAY_CAPACITY_INCREMENT
];
4398 System
.arraycopy(children
, 0, mChildren
, 0, index
);
4399 System
.arraycopy(children
, index
, mChildren
, index
+ 1, count
- index
);
4400 children
= mChildren
;
4402 System
.arraycopy(children
, index
, children
, index
+ 1, count
- index
);
4404 children
[index
] = child
;
4406 if (mLastTouchDownIndex
>= index
) {
4407 mLastTouchDownIndex
++;
4410 throw new IndexOutOfBoundsException("index=" + index
+ " count=" + count
);
4414 // This method also sets the child's mParent to null
4415 private void removeFromArray(int index
) {
4416 final View
[] children
= mChildren
;
4417 if (!(mTransitioningViews
!= null && mTransitioningViews
.contains(children
[index
]))) {
4418 children
[index
].mParent
= null;
4420 final int count
= mChildrenCount
;
4421 if (index
== count
- 1) {
4422 children
[--mChildrenCount
] = null;
4423 } else if (index
>= 0 && index
< count
) {
4424 System
.arraycopy(children
, index
+ 1, children
, index
, count
- index
- 1);
4425 children
[--mChildrenCount
] = null;
4427 throw new IndexOutOfBoundsException();
4429 if (mLastTouchDownIndex
== index
) {
4430 mLastTouchDownTime
= 0;
4431 mLastTouchDownIndex
= -1;
4432 } else if (mLastTouchDownIndex
> index
) {
4433 mLastTouchDownIndex
--;
4437 // This method also sets the children's mParent to null
4438 private void removeFromArray(int start
, int count
) {
4439 final View
[] children
= mChildren
;
4440 final int childrenCount
= mChildrenCount
;
4442 start
= Math
.max(0, start
);
4443 final int end
= Math
.min(childrenCount
, start
+ count
);
4449 if (end
== childrenCount
) {
4450 for (int i
= start
; i
< end
; i
++) {
4451 children
[i
].mParent
= null;
4455 for (int i
= start
; i
< end
; i
++) {
4456 children
[i
].mParent
= null;
4459 // Since we're looping above, we might as well do the copy, but is arraycopy()
4460 // faster than the extra 2 bounds checks we would do in the loop?
4461 System
.arraycopy(children
, end
, children
, start
, childrenCount
- end
);
4463 for (int i
= childrenCount
- (end
- start
); i
< childrenCount
; i
++) {
4468 mChildrenCount
-= (end
- start
);
4471 private void bindLayoutAnimation(View child
) {
4472 Animation a
= mLayoutAnimationController
.getAnimationForView(child
);
4473 child
.setAnimation(a
);
4477 * Subclasses should override this method to set layout animation
4478 * parameters on the supplied child.
4480 * @param child the child to associate with animation parameters
4481 * @param params the child's layout parameters which hold the animation
4483 * @param index the index of the child in the view group
4484 * @param count the number of children in the view group
4486 protected void attachLayoutAnimationParameters(View child
,
4487 LayoutParams params
, int index
, int count
) {
4488 LayoutAnimationController
.AnimationParameters animationParams
=
4489 params
.layoutAnimationParameters
;
4490 if (animationParams
== null) {
4491 animationParams
= new LayoutAnimationController
.AnimationParameters();
4492 params
.layoutAnimationParameters
= animationParams
;
4495 animationParams
.count
= count
;
4496 animationParams
.index
= index
;
4502 * <p><strong>Note:</strong> do not invoke this method from
4503 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4504 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4506 public void removeView(View view
) {
4507 if (removeViewInternal(view
)) {
4514 * Removes a view during layout. This is useful if in your onLayout() method,
4515 * you need to remove more views.
4517 * <p><strong>Note:</strong> do not invoke this method from
4518 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4519 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4521 * @param view the view to remove from the group
4523 public void removeViewInLayout(View view
) {
4524 removeViewInternal(view
);
4528 * Removes a range of views during layout. This is useful if in your onLayout() method,
4529 * you need to remove more views.
4531 * <p><strong>Note:</strong> do not invoke this method from
4532 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4533 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4535 * @param start the index of the first view to remove from the group
4536 * @param count the number of views to remove from the group
4538 public void removeViewsInLayout(int start
, int count
) {
4539 removeViewsInternal(start
, count
);
4543 * Removes the view at the specified position in the group.
4545 * <p><strong>Note:</strong> do not invoke this method from
4546 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4547 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4549 * @param index the position in the group of the view to remove
4551 public void removeViewAt(int index
) {
4552 removeViewInternal(index
, getChildAt(index
));
4558 * Removes the specified range of views from the group.
4560 * <p><strong>Note:</strong> do not invoke this method from
4561 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4562 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4564 * @param start the first position in the group of the range of views to remove
4565 * @param count the number of views to remove
4567 public void removeViews(int start
, int count
) {
4568 removeViewsInternal(start
, count
);
4573 private boolean removeViewInternal(View view
) {
4574 final int index
= indexOfChild(view
);
4576 removeViewInternal(index
, view
);
4582 private void removeViewInternal(int index
, View view
) {
4584 if (mTransition
!= null) {
4585 mTransition
.removeChild(this, view
);
4588 boolean clearChildFocus
= false;
4589 if (view
== mFocused
) {
4591 clearChildFocus
= true;
4594 view
.clearAccessibilityFocus();
4596 cancelTouchTarget(view
);
4597 cancelHoverTarget(view
);
4599 if (view
.getAnimation() != null ||
4600 (mTransitioningViews
!= null && mTransitioningViews
.contains(view
))) {
4601 addDisappearingView(view
);
4602 } else if (view
.mAttachInfo
!= null) {
4603 view
.dispatchDetachedFromWindow();
4606 if (view
.hasTransientState()) {
4607 childHasTransientStateChanged(view
, false);
4610 needGlobalAttributesUpdate(false);
4612 removeFromArray(index
);
4614 if (clearChildFocus
) {
4615 clearChildFocus(view
);
4616 if (!rootViewRequestFocus()) {
4617 notifyGlobalFocusCleared(this);
4621 dispatchViewRemoved(view
);
4623 if (view
.getVisibility() != View
.GONE
) {
4624 notifySubtreeAccessibilityStateChangedIfNeeded();
4627 int transientCount
= mTransientIndices
== null ?
0 : mTransientIndices
.size();
4628 for (int i
= 0; i
< transientCount
; ++i
) {
4629 final int oldIndex
= mTransientIndices
.get(i
);
4630 if (index
< oldIndex
) {
4631 mTransientIndices
.set(i
, oldIndex
- 1);
4637 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4638 * not null, changes in layout which occur because of children being added to or removed from
4639 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4640 * object. By default, the transition object is null (so layout changes are not animated).
4642 * <p>Replacing a non-null transition will cause that previous transition to be
4643 * canceled, if it is currently running, to restore this container to
4644 * its correct post-transition state.</p>
4646 * @param transition The LayoutTransition object that will animated changes in layout. A value
4647 * of <code>null</code> means no transition will run on layout changes.
4648 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
4650 public void setLayoutTransition(LayoutTransition transition
) {
4651 if (mTransition
!= null) {
4652 LayoutTransition previousTransition
= mTransition
;
4653 previousTransition
.cancel();
4654 previousTransition
.removeTransitionListener(mLayoutTransitionListener
);
4656 mTransition
= transition
;
4657 if (mTransition
!= null) {
4658 mTransition
.addTransitionListener(mLayoutTransitionListener
);
4663 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4664 * not null, changes in layout which occur because of children being added to or removed from
4665 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4666 * object. By default, the transition object is null (so layout changes are not animated).
4668 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
4669 * A value of <code>null</code> means no transition will run on layout changes.
4671 public LayoutTransition
getLayoutTransition() {
4675 private void removeViewsInternal(int start
, int count
) {
4676 final View focused
= mFocused
;
4677 final boolean detach
= mAttachInfo
!= null;
4678 boolean clearChildFocus
= false;
4680 final View
[] children
= mChildren
;
4681 final int end
= start
+ count
;
4683 for (int i
= start
; i
< end
; i
++) {
4684 final View view
= children
[i
];
4686 if (mTransition
!= null) {
4687 mTransition
.removeChild(this, view
);
4690 if (view
== focused
) {
4692 clearChildFocus
= true;
4695 view
.clearAccessibilityFocus();
4697 cancelTouchTarget(view
);
4698 cancelHoverTarget(view
);
4700 if (view
.getAnimation() != null ||
4701 (mTransitioningViews
!= null && mTransitioningViews
.contains(view
))) {
4702 addDisappearingView(view
);
4703 } else if (detach
) {
4704 view
.dispatchDetachedFromWindow();
4707 if (view
.hasTransientState()) {
4708 childHasTransientStateChanged(view
, false);
4711 needGlobalAttributesUpdate(false);
4713 dispatchViewRemoved(view
);
4716 removeFromArray(start
, count
);
4718 if (clearChildFocus
) {
4719 clearChildFocus(focused
);
4720 if (!rootViewRequestFocus()) {
4721 notifyGlobalFocusCleared(focused
);
4727 * Call this method to remove all child views from the
4730 * <p><strong>Note:</strong> do not invoke this method from
4731 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4732 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4734 public void removeAllViews() {
4735 removeAllViewsInLayout();
4741 * Called by a ViewGroup subclass to remove child views from itself,
4742 * when it must first know its size on screen before it can calculate how many
4743 * child views it will render. An example is a Gallery or a ListView, which
4744 * may "have" 50 children, but actually only render the number of children
4745 * that can currently fit inside the object on screen. Do not call
4746 * this method unless you are extending ViewGroup and understand the
4747 * view measuring and layout pipeline.
4749 * <p><strong>Note:</strong> do not invoke this method from
4750 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4751 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4753 public void removeAllViewsInLayout() {
4754 final int count
= mChildrenCount
;
4759 final View
[] children
= mChildren
;
4762 final View focused
= mFocused
;
4763 final boolean detach
= mAttachInfo
!= null;
4764 boolean clearChildFocus
= false;
4766 needGlobalAttributesUpdate(false);
4768 for (int i
= count
- 1; i
>= 0; i
--) {
4769 final View view
= children
[i
];
4771 if (mTransition
!= null) {
4772 mTransition
.removeChild(this, view
);
4775 if (view
== focused
) {
4777 clearChildFocus
= true;
4780 view
.clearAccessibilityFocus();
4782 cancelTouchTarget(view
);
4783 cancelHoverTarget(view
);
4785 if (view
.getAnimation() != null ||
4786 (mTransitioningViews
!= null && mTransitioningViews
.contains(view
))) {
4787 addDisappearingView(view
);
4788 } else if (detach
) {
4789 view
.dispatchDetachedFromWindow();
4792 if (view
.hasTransientState()) {
4793 childHasTransientStateChanged(view
, false);
4796 dispatchViewRemoved(view
);
4798 view
.mParent
= null;
4802 if (clearChildFocus
) {
4803 clearChildFocus(focused
);
4804 if (!rootViewRequestFocus()) {
4805 notifyGlobalFocusCleared(focused
);
4811 * Finishes the removal of a detached view. This method will dispatch the detached from
4812 * window event and notify the hierarchy change listener.
4814 * This method is intended to be lightweight and makes no assumptions about whether the
4815 * parent or child should be redrawn. Proper use of this method will include also making
4816 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4817 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4818 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
4819 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4821 * @param child the child to be definitely removed from the view hierarchy
4822 * @param animate if true and the view has an animation, the view is placed in the
4823 * disappearing views list, otherwise, it is detached from the window
4825 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4826 * @see #detachAllViewsFromParent()
4827 * @see #detachViewFromParent(View)
4828 * @see #detachViewFromParent(int)
4830 protected void removeDetachedView(View child
, boolean animate
) {
4831 if (mTransition
!= null) {
4832 mTransition
.removeChild(this, child
);
4835 if (child
== mFocused
) {
4839 child
.clearAccessibilityFocus();
4841 cancelTouchTarget(child
);
4842 cancelHoverTarget(child
);
4844 if ((animate
&& child
.getAnimation() != null) ||
4845 (mTransitioningViews
!= null && mTransitioningViews
.contains(child
))) {
4846 addDisappearingView(child
);
4847 } else if (child
.mAttachInfo
!= null) {
4848 child
.dispatchDetachedFromWindow();
4851 if (child
.hasTransientState()) {
4852 childHasTransientStateChanged(child
, false);
4855 dispatchViewRemoved(child
);
4859 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
4860 * sets the layout parameters and puts the view in the list of children so that
4861 * it can be retrieved by calling {@link #getChildAt(int)}.
4863 * This method is intended to be lightweight and makes no assumptions about whether the
4864 * parent or child should be redrawn. Proper use of this method will include also making
4865 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4866 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4867 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
4868 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4870 * This method should be called only for views which were detached from their parent.
4872 * @param child the child to attach
4873 * @param index the index at which the child should be attached
4874 * @param params the layout parameters of the child
4876 * @see #removeDetachedView(View, boolean)
4877 * @see #detachAllViewsFromParent()
4878 * @see #detachViewFromParent(View)
4879 * @see #detachViewFromParent(int)
4881 protected void attachViewToParent(View child
, int index
, LayoutParams params
) {
4882 child
.mLayoutParams
= params
;
4885 index
= mChildrenCount
;
4888 addInArray(child
, index
);
4890 child
.mParent
= this;
4891 child
.mPrivateFlags
= (child
.mPrivateFlags
& ~PFLAG_DIRTY_MASK
4892 & ~PFLAG_DRAWING_CACHE_VALID
)
4893 | PFLAG_DRAWN
| PFLAG_INVALIDATED
;
4894 this.mPrivateFlags
|= PFLAG_INVALIDATED
;
4896 if (child
.hasFocus()) {
4897 requestChildFocus(child
, child
.findFocus());
4902 * Detaches a view from its parent. Detaching a view should be followed
4903 * either by a call to
4904 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4905 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4906 * temporary; reattachment or removal should happen within the same drawing cycle as
4907 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4908 * call to {@link #getChildAt(int)}.
4910 * @param child the child to detach
4912 * @see #detachViewFromParent(int)
4913 * @see #detachViewsFromParent(int, int)
4914 * @see #detachAllViewsFromParent()
4915 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4916 * @see #removeDetachedView(View, boolean)
4918 protected void detachViewFromParent(View child
) {
4919 removeFromArray(indexOfChild(child
));
4923 * Detaches a view from its parent. Detaching a view should be followed
4924 * either by a call to
4925 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4926 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4927 * temporary; reattachment or removal should happen within the same drawing cycle as
4928 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4929 * call to {@link #getChildAt(int)}.
4931 * @param index the index of the child to detach
4933 * @see #detachViewFromParent(View)
4934 * @see #detachAllViewsFromParent()
4935 * @see #detachViewsFromParent(int, int)
4936 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4937 * @see #removeDetachedView(View, boolean)
4939 protected void detachViewFromParent(int index
) {
4940 removeFromArray(index
);
4944 * Detaches a range of views from their parents. Detaching a view should be followed
4945 * either by a call to
4946 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4947 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4948 * temporary; reattachment or removal should happen within the same drawing cycle as
4949 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4950 * call to {@link #getChildAt(int)}.
4952 * @param start the first index of the childrend range to detach
4953 * @param count the number of children to detach
4955 * @see #detachViewFromParent(View)
4956 * @see #detachViewFromParent(int)
4957 * @see #detachAllViewsFromParent()
4958 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4959 * @see #removeDetachedView(View, boolean)
4961 protected void detachViewsFromParent(int start
, int count
) {
4962 removeFromArray(start
, count
);
4966 * Detaches all views from the parent. Detaching a view should be followed
4967 * either by a call to
4968 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4969 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4970 * temporary; reattachment or removal should happen within the same drawing cycle as
4971 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4972 * call to {@link #getChildAt(int)}.
4974 * @see #detachViewFromParent(View)
4975 * @see #detachViewFromParent(int)
4976 * @see #detachViewsFromParent(int, int)
4977 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4978 * @see #removeDetachedView(View, boolean)
4980 protected void detachAllViewsFromParent() {
4981 final int count
= mChildrenCount
;
4986 final View
[] children
= mChildren
;
4989 for (int i
= count
- 1; i
>= 0; i
--) {
4990 children
[i
].mParent
= null;
4996 * Don't call or override this method. It is used for the implementation of
4997 * the view hierarchy.
4999 public final void invalidateChild(View child
, final Rect dirty
) {
5000 ViewParent parent
= this;
5002 final AttachInfo attachInfo
= mAttachInfo
;
5003 if (attachInfo
!= null) {
5004 // If the child is drawing an animation, we want to copy this flag onto
5005 // ourselves and the parent to make sure the invalidate request goes
5007 final boolean drawAnimation
= (child
.mPrivateFlags
& PFLAG_DRAW_ANIMATION
)
5008 == PFLAG_DRAW_ANIMATION
;
5010 // Check whether the child that requests the invalidate is fully opaque
5011 // Views being animated or transformed are not considered opaque because we may
5012 // be invalidating their old position and need the parent to paint behind them.
5013 Matrix childMatrix
= child
.getMatrix();
5014 final boolean isOpaque
= child
.isOpaque() && !drawAnimation
&&
5015 child
.getAnimation() == null && childMatrix
.isIdentity();
5016 // Mark the child as dirty, using the appropriate flag
5017 // Make sure we do not set both flags at the same time
5018 int opaqueFlag
= isOpaque ? PFLAG_DIRTY_OPAQUE
: PFLAG_DIRTY
;
5020 if (child
.mLayerType
!= LAYER_TYPE_NONE
) {
5021 mPrivateFlags
|= PFLAG_INVALIDATED
;
5022 mPrivateFlags
&= ~PFLAG_DRAWING_CACHE_VALID
;
5025 final int[] location
= attachInfo
.mInvalidateChildLocation
;
5026 location
[CHILD_LEFT_INDEX
] = child
.mLeft
;
5027 location
[CHILD_TOP_INDEX
] = child
.mTop
;
5028 if (!childMatrix
.isIdentity() ||
5029 (mGroupFlags
& ViewGroup
.FLAG_SUPPORT_STATIC_TRANSFORMATIONS
) != 0) {
5030 RectF boundingRect
= attachInfo
.mTmpTransformRect
;
5031 boundingRect
.set(dirty
);
5032 Matrix transformMatrix
;
5033 if ((mGroupFlags
& ViewGroup
.FLAG_SUPPORT_STATIC_TRANSFORMATIONS
) != 0) {
5034 Transformation t
= attachInfo
.mTmpTransformation
;
5035 boolean transformed
= getChildStaticTransformation(child
, t
);
5037 transformMatrix
= attachInfo
.mTmpMatrix
;
5038 transformMatrix
.set(t
.getMatrix());
5039 if (!childMatrix
.isIdentity()) {
5040 transformMatrix
.preConcat(childMatrix
);
5043 transformMatrix
= childMatrix
;
5046 transformMatrix
= childMatrix
;
5048 transformMatrix
.mapRect(boundingRect
);
5049 dirty
.set((int) (boundingRect
.left
- 0.5f
),
5050 (int) (boundingRect
.top
- 0.5f
),
5051 (int) (boundingRect
.right
+ 0.5f
),
5052 (int) (boundingRect
.bottom
+ 0.5f
));
5057 if (parent
instanceof View
) {
5058 view
= (View
) parent
;
5061 if (drawAnimation
) {
5063 view
.mPrivateFlags
|= PFLAG_DRAW_ANIMATION
;
5064 } else if (parent
instanceof ViewRootImpl
) {
5065 ((ViewRootImpl
) parent
).mIsAnimating
= true;
5069 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
5070 // flag coming from the child that initiated the invalidate
5072 if ((view
.mViewFlags
& FADING_EDGE_MASK
) != 0 &&
5073 view
.getSolidColor() == 0) {
5074 opaqueFlag
= PFLAG_DIRTY
;
5076 if ((view
.mPrivateFlags
& PFLAG_DIRTY_MASK
) != PFLAG_DIRTY
) {
5077 view
.mPrivateFlags
= (view
.mPrivateFlags
& ~PFLAG_DIRTY_MASK
) | opaqueFlag
;
5081 parent
= parent
.invalidateChildInParent(location
, dirty
);
5083 // Account for transform on current parent
5084 Matrix m
= view
.getMatrix();
5085 if (!m
.isIdentity()) {
5086 RectF boundingRect
= attachInfo
.mTmpTransformRect
;
5087 boundingRect
.set(dirty
);
5088 m
.mapRect(boundingRect
);
5089 dirty
.set((int) (boundingRect
.left
- 0.5f
),
5090 (int) (boundingRect
.top
- 0.5f
),
5091 (int) (boundingRect
.right
+ 0.5f
),
5092 (int) (boundingRect
.bottom
+ 0.5f
));
5095 } while (parent
!= null);
5100 * Don't call or override this method. It is used for the implementation of
5101 * the view hierarchy.
5103 * This implementation returns null if this ViewGroup does not have a parent,
5104 * if this ViewGroup is already fully invalidated or if the dirty rectangle
5105 * does not intersect with this ViewGroup's bounds.
5107 public ViewParent
invalidateChildInParent(final int[] location
, final Rect dirty
) {
5108 if ((mPrivateFlags
& PFLAG_DRAWN
) == PFLAG_DRAWN
||
5109 (mPrivateFlags
& PFLAG_DRAWING_CACHE_VALID
) == PFLAG_DRAWING_CACHE_VALID
) {
5110 if ((mGroupFlags
& (FLAG_OPTIMIZE_INVALIDATE
| FLAG_ANIMATION_DONE
)) !=
5111 FLAG_OPTIMIZE_INVALIDATE
) {
5112 dirty
.offset(location
[CHILD_LEFT_INDEX
] - mScrollX
,
5113 location
[CHILD_TOP_INDEX
] - mScrollY
);
5114 if ((mGroupFlags
& FLAG_CLIP_CHILDREN
) == 0) {
5115 dirty
.union(0, 0, mRight
- mLeft
, mBottom
- mTop
);
5118 final int left
= mLeft
;
5119 final int top
= mTop
;
5121 if ((mGroupFlags
& FLAG_CLIP_CHILDREN
) == FLAG_CLIP_CHILDREN
) {
5122 if (!dirty
.intersect(0, 0, mRight
- left
, mBottom
- top
)) {
5126 mPrivateFlags
&= ~PFLAG_DRAWING_CACHE_VALID
;
5128 location
[CHILD_LEFT_INDEX
] = left
;
5129 location
[CHILD_TOP_INDEX
] = top
;
5131 if (mLayerType
!= LAYER_TYPE_NONE
) {
5132 mPrivateFlags
|= PFLAG_INVALIDATED
;
5138 mPrivateFlags
&= ~PFLAG_DRAWN
& ~PFLAG_DRAWING_CACHE_VALID
;
5140 location
[CHILD_LEFT_INDEX
] = mLeft
;
5141 location
[CHILD_TOP_INDEX
] = mTop
;
5142 if ((mGroupFlags
& FLAG_CLIP_CHILDREN
) == FLAG_CLIP_CHILDREN
) {
5143 dirty
.set(0, 0, mRight
- mLeft
, mBottom
- mTop
);
5145 // in case the dirty rect extends outside the bounds of this container
5146 dirty
.union(0, 0, mRight
- mLeft
, mBottom
- mTop
);
5149 if (mLayerType
!= LAYER_TYPE_NONE
) {
5150 mPrivateFlags
|= PFLAG_INVALIDATED
;
5161 * Native-calculated damage path
5162 * Returns false if this path was unable to complete successfully. This means
5163 * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
5167 public boolean damageChildDeferred(View child
) {
5168 ViewParent parent
= getParent();
5169 while (parent
!= null) {
5170 if (parent
instanceof ViewGroup
) {
5171 parent
= parent
.getParent();
5172 } else if (parent
instanceof ViewRootImpl
) {
5173 ((ViewRootImpl
) parent
).invalidate();
5183 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
5184 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
5185 * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
5189 public void damageChild(View child
, final Rect dirty
) {
5190 if (damageChildDeferred(child
)) {
5194 ViewParent parent
= this;
5196 final AttachInfo attachInfo
= mAttachInfo
;
5197 if (attachInfo
!= null) {
5198 int left
= child
.mLeft
;
5199 int top
= child
.mTop
;
5200 if (!child
.getMatrix().isIdentity()) {
5201 child
.transformRect(dirty
);
5205 if (parent
instanceof ViewGroup
) {
5206 ViewGroup parentVG
= (ViewGroup
) parent
;
5207 if (parentVG
.mLayerType
!= LAYER_TYPE_NONE
) {
5208 // Layered parents should be recreated, not just re-issued
5209 parentVG
.invalidate();
5212 parent
= parentVG
.damageChildInParent(left
, top
, dirty
);
5213 left
= parentVG
.mLeft
;
5214 top
= parentVG
.mTop
;
5217 // Reached the top; this calls into the usual invalidate method in
5218 // ViewRootImpl, which schedules a traversal
5219 final int[] location
= attachInfo
.mInvalidateChildLocation
;
5222 parent
= parent
.invalidateChildInParent(location
, dirty
);
5224 } while (parent
!= null);
5229 * Quick invalidation method that simply transforms the dirty rect into the parent's
5230 * coordinate system, pruning the invalidation if the parent has already been invalidated.
5234 protected ViewParent
damageChildInParent(int left
, int top
, final Rect dirty
) {
5235 if ((mPrivateFlags
& PFLAG_DRAWN
) != 0
5236 || (mPrivateFlags
& PFLAG_DRAWING_CACHE_VALID
) != 0) {
5237 dirty
.offset(left
- mScrollX
, top
- mScrollY
);
5238 if ((mGroupFlags
& FLAG_CLIP_CHILDREN
) == 0) {
5239 dirty
.union(0, 0, mRight
- mLeft
, mBottom
- mTop
);
5242 if ((mGroupFlags
& FLAG_CLIP_CHILDREN
) == 0 ||
5243 dirty
.intersect(0, 0, mRight
- mLeft
, mBottom
- mTop
)) {
5245 if (!getMatrix().isIdentity()) {
5246 transformRect(dirty
);
5257 * Offset a rectangle that is in a descendant's coordinate
5258 * space into our coordinate space.
5259 * @param descendant A descendant of this view
5260 * @param rect A rectangle defined in descendant's coordinate space.
5262 public final void offsetDescendantRectToMyCoords(View descendant
, Rect rect
) {
5263 offsetRectBetweenParentAndChild(descendant
, rect
, true, false);
5267 * Offset a rectangle that is in our coordinate space into an ancestor's
5269 * @param descendant A descendant of this view
5270 * @param rect A rectangle defined in descendant's coordinate space.
5272 public final void offsetRectIntoDescendantCoords(View descendant
, Rect rect
) {
5273 offsetRectBetweenParentAndChild(descendant
, rect
, false, false);
5277 * Helper method that offsets a rect either from parent to descendant or
5278 * descendant to parent.
5280 void offsetRectBetweenParentAndChild(View descendant
, Rect rect
,
5281 boolean offsetFromChildToParent
, boolean clipToBounds
) {
5283 // already in the same coord system :)
5284 if (descendant
== this) {
5288 ViewParent theParent
= descendant
.mParent
;
5290 // search and offset up to the parent
5291 while ((theParent
!= null)
5292 && (theParent
instanceof View
)
5293 && (theParent
!= this)) {
5295 if (offsetFromChildToParent
) {
5296 rect
.offset(descendant
.mLeft
- descendant
.mScrollX
,
5297 descendant
.mTop
- descendant
.mScrollY
);
5299 View p
= (View
) theParent
;
5300 boolean intersected
= rect
.intersect(0, 0, p
.mRight
- p
.mLeft
,
5301 p
.mBottom
- p
.mTop
);
5308 View p
= (View
) theParent
;
5309 boolean intersected
= rect
.intersect(0, 0, p
.mRight
- p
.mLeft
,
5310 p
.mBottom
- p
.mTop
);
5315 rect
.offset(descendant
.mScrollX
- descendant
.mLeft
,
5316 descendant
.mScrollY
- descendant
.mTop
);
5319 descendant
= (View
) theParent
;
5320 theParent
= descendant
.mParent
;
5323 // now that we are up to this view, need to offset one more time
5324 // to get into our coordinate space
5325 if (theParent
== this) {
5326 if (offsetFromChildToParent
) {
5327 rect
.offset(descendant
.mLeft
- descendant
.mScrollX
,
5328 descendant
.mTop
- descendant
.mScrollY
);
5330 rect
.offset(descendant
.mScrollX
- descendant
.mLeft
,
5331 descendant
.mScrollY
- descendant
.mTop
);
5334 throw new IllegalArgumentException("parameter must be a descendant of this view");
5339 * Offset the vertical location of all children of this view by the specified number of pixels.
5341 * @param offset the number of pixels to offset
5345 public void offsetChildrenTopAndBottom(int offset
) {
5346 final int count
= mChildrenCount
;
5347 final View
[] children
= mChildren
;
5348 boolean invalidate
= false;
5350 for (int i
= 0; i
< count
; i
++) {
5351 final View v
= children
[i
];
5353 v
.mBottom
+= offset
;
5354 if (v
.mRenderNode
!= null) {
5356 v
.mRenderNode
.offsetTopAndBottom(offset
);
5361 invalidateViewProperty(false, false);
5363 notifySubtreeAccessibilityStateChangedIfNeeded();
5369 public boolean getChildVisibleRect(View child
, Rect r
, android
.graphics
.Point offset
) {
5370 // It doesn't make a whole lot of sense to call this on a view that isn't attached,
5371 // but for some simple tests it can be useful. If we don't have attach info this
5372 // will allocate memory.
5373 final RectF rect
= mAttachInfo
!= null ? mAttachInfo
.mTmpTransformRect
: new RectF();
5376 if (!child
.hasIdentityMatrix()) {
5377 child
.getMatrix().mapRect(rect
);
5380 final int dx
= child
.mLeft
- mScrollX
;
5381 final int dy
= child
.mTop
- mScrollY
;
5383 rect
.offset(dx
, dy
);
5385 if (offset
!= null) {
5386 if (!child
.hasIdentityMatrix()) {
5387 float[] position
= mAttachInfo
!= null ? mAttachInfo
.mTmpTransformLocation
5389 position
[0] = offset
.x
;
5390 position
[1] = offset
.y
;
5391 child
.getMatrix().mapPoints(position
);
5392 offset
.x
= (int) (position
[0] + 0.5f
);
5393 offset
.y
= (int) (position
[1] + 0.5f
);
5399 final int width
= mRight
- mLeft
;
5400 final int height
= mBottom
- mTop
;
5402 boolean rectIsVisible
= true;
5403 if (mParent
== null ||
5404 (mParent
instanceof ViewGroup
&& ((ViewGroup
) mParent
).getClipChildren())) {
5406 rectIsVisible
= rect
.intersect(0, 0, width
, height
);
5409 if (rectIsVisible
&& (mGroupFlags
& CLIP_TO_PADDING_MASK
) == CLIP_TO_PADDING_MASK
) {
5411 rectIsVisible
= rect
.intersect(mPaddingLeft
, mPaddingTop
,
5412 width
- mPaddingRight
, height
- mPaddingBottom
);
5415 if (rectIsVisible
&& mClipBounds
!= null) {
5416 // Clip to clipBounds.
5417 rectIsVisible
= rect
.intersect(mClipBounds
.left
, mClipBounds
.top
, mClipBounds
.right
,
5418 mClipBounds
.bottom
);
5420 r
.set((int) (rect
.left
+ 0.5f
), (int) (rect
.top
+ 0.5f
), (int) (rect
.right
+ 0.5f
),
5421 (int) (rect
.bottom
+ 0.5f
));
5422 if (rectIsVisible
&& mParent
!= null) {
5423 rectIsVisible
= mParent
.getChildVisibleRect(this, r
, offset
);
5425 return rectIsVisible
;
5432 public final void layout(int l
, int t
, int r
, int b
) {
5433 if (!mSuppressLayout
&& (mTransition
== null || !mTransition
.isChangingLayout())) {
5434 if (mTransition
!= null) {
5435 mTransition
.layoutChange(this);
5437 super.layout(l
, t
, r
, b
);
5439 // record the fact that we noop'd it; request layout when transition finishes
5440 mLayoutCalledWhileSuppressed
= true;
5448 protected abstract void onLayout(boolean changed
,
5449 int l
, int t
, int r
, int b
);
5452 * Indicates whether the view group has the ability to animate its children
5453 * after the first layout.
5455 * @return true if the children can be animated, false otherwise
5457 protected boolean canAnimate() {
5458 return mLayoutAnimationController
!= null;
5462 * Runs the layout animation. Calling this method triggers a relayout of
5465 public void startLayoutAnimation() {
5466 if (mLayoutAnimationController
!= null) {
5467 mGroupFlags
|= FLAG_RUN_ANIMATION
;
5473 * Schedules the layout animation to be played after the next layout pass
5474 * of this view group. This can be used to restart the layout animation
5475 * when the content of the view group changes or when the activity is
5476 * paused and resumed.
5478 public void scheduleLayoutAnimation() {
5479 mGroupFlags
|= FLAG_RUN_ANIMATION
;
5483 * Sets the layout animation controller used to animate the group's
5484 * children after the first layout.
5486 * @param controller the animation controller
5488 public void setLayoutAnimation(LayoutAnimationController controller
) {
5489 mLayoutAnimationController
= controller
;
5490 if (mLayoutAnimationController
!= null) {
5491 mGroupFlags
|= FLAG_RUN_ANIMATION
;
5496 * Returns the layout animation controller used to animate the group's
5499 * @return the current animation controller
5501 public LayoutAnimationController
getLayoutAnimation() {
5502 return mLayoutAnimationController
;
5506 * Indicates whether the children's drawing cache is used during a layout
5507 * animation. By default, the drawing cache is enabled but this will prevent
5508 * nested layout animations from working. To nest animations, you must disable
5511 * @return true if the animation cache is enabled, false otherwise
5513 * @see #setAnimationCacheEnabled(boolean)
5514 * @see View#setDrawingCacheEnabled(boolean)
5516 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5517 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
5519 public boolean isAnimationCacheEnabled() {
5520 return (mGroupFlags
& FLAG_ANIMATION_CACHE
) == FLAG_ANIMATION_CACHE
;
5524 * Enables or disables the children's drawing cache during a layout animation.
5525 * By default, the drawing cache is enabled but this will prevent nested
5526 * layout animations from working. To nest animations, you must disable the
5529 * @param enabled true to enable the animation cache, false otherwise
5531 * @see #isAnimationCacheEnabled()
5532 * @see View#setDrawingCacheEnabled(boolean)
5534 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5535 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
5537 public void setAnimationCacheEnabled(boolean enabled
) {
5538 setBooleanFlag(FLAG_ANIMATION_CACHE
, enabled
);
5542 * Indicates whether this ViewGroup will always try to draw its children using their
5543 * drawing cache. By default this property is enabled.
5545 * @return true if the animation cache is enabled, false otherwise
5547 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5548 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5549 * @see View#setDrawingCacheEnabled(boolean)
5551 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5552 * Child views may no longer have their caching behavior disabled by parents.
5554 public boolean isAlwaysDrawnWithCacheEnabled() {
5555 return (mGroupFlags
& FLAG_ALWAYS_DRAWN_WITH_CACHE
) == FLAG_ALWAYS_DRAWN_WITH_CACHE
;
5559 * Indicates whether this ViewGroup will always try to draw its children using their
5560 * drawing cache. This property can be set to true when the cache rendering is
5561 * slightly different from the children's normal rendering. Renderings can be different,
5562 * for instance, when the cache's quality is set to low.
5564 * When this property is disabled, the ViewGroup will use the drawing cache of its
5565 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
5566 * when to start using the drawing cache and when to stop using it.
5568 * @param always true to always draw with the drawing cache, false otherwise
5570 * @see #isAlwaysDrawnWithCacheEnabled()
5571 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5572 * @see View#setDrawingCacheEnabled(boolean)
5573 * @see View#setDrawingCacheQuality(int)
5575 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5576 * Child views may no longer have their caching behavior disabled by parents.
5578 public void setAlwaysDrawnWithCacheEnabled(boolean always
) {
5579 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE
, always
);
5583 * Indicates whether the ViewGroup is currently drawing its children using
5584 * their drawing cache.
5586 * @return true if children should be drawn with their cache, false otherwise
5588 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5589 * @see #setChildrenDrawnWithCacheEnabled(boolean)
5591 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5592 * Child views may no longer be forced to cache their rendering state by their parents.
5593 * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
5595 protected boolean isChildrenDrawnWithCacheEnabled() {
5596 return (mGroupFlags
& FLAG_CHILDREN_DRAWN_WITH_CACHE
) == FLAG_CHILDREN_DRAWN_WITH_CACHE
;
5600 * Tells the ViewGroup to draw its children using their drawing cache. This property
5601 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
5602 * will be used only if it has been enabled.
5604 * Subclasses should call this method to start and stop using the drawing cache when
5605 * they perform performance sensitive operations, like scrolling or animating.
5607 * @param enabled true if children should be drawn with their cache, false otherwise
5609 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5610 * @see #isChildrenDrawnWithCacheEnabled()
5612 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
5613 * Child views may no longer be forced to cache their rendering state by their parents.
5614 * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
5616 protected void setChildrenDrawnWithCacheEnabled(boolean enabled
) {
5617 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE
, enabled
);
5621 * Indicates whether the ViewGroup is drawing its children in the order defined by
5622 * {@link #getChildDrawingOrder(int, int)}.
5624 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
5627 * @see #setChildrenDrawingOrderEnabled(boolean)
5628 * @see #getChildDrawingOrder(int, int)
5630 @ViewDebug.ExportedProperty(category
= "drawing")
5631 protected boolean isChildrenDrawingOrderEnabled() {
5632 return (mGroupFlags
& FLAG_USE_CHILD_DRAWING_ORDER
) == FLAG_USE_CHILD_DRAWING_ORDER
;
5636 * Tells the ViewGroup whether to draw its children in the order defined by the method
5637 * {@link #getChildDrawingOrder(int, int)}.
5639 * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
5640 * will override custom child ordering done via this method.
5642 * @param enabled true if the order of the children when drawing is determined by
5643 * {@link #getChildDrawingOrder(int, int)}, false otherwise
5645 * @see #isChildrenDrawingOrderEnabled()
5646 * @see #getChildDrawingOrder(int, int)
5648 protected void setChildrenDrawingOrderEnabled(boolean enabled
) {
5649 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER
, enabled
);
5652 private boolean hasBooleanFlag(int flag
) {
5653 return (mGroupFlags
& flag
) == flag
;
5656 private void setBooleanFlag(int flag
, boolean value
) {
5658 mGroupFlags
|= flag
;
5660 mGroupFlags
&= ~flag
;
5665 * Returns an integer indicating what types of drawing caches are kept in memory.
5667 * @see #setPersistentDrawingCache(int)
5668 * @see #setAnimationCacheEnabled(boolean)
5670 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
5671 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5672 * and {@link #PERSISTENT_ALL_CACHES}
5674 @ViewDebug.ExportedProperty(category
= "drawing", mapping
= {
5675 @ViewDebug.IntToString(from
= PERSISTENT_NO_CACHE
, to
= "NONE"),
5676 @ViewDebug.IntToString(from
= PERSISTENT_ANIMATION_CACHE
, to
= "ANIMATION"),
5677 @ViewDebug.IntToString(from
= PERSISTENT_SCROLLING_CACHE
, to
= "SCROLLING"),
5678 @ViewDebug.IntToString(from
= PERSISTENT_ALL_CACHES
, to
= "ALL")
5680 public int getPersistentDrawingCache() {
5681 return mPersistentDrawingCache
;
5685 * Indicates what types of drawing caches should be kept in memory after
5686 * they have been created.
5688 * @see #getPersistentDrawingCache()
5689 * @see #setAnimationCacheEnabled(boolean)
5691 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
5692 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5693 * and {@link #PERSISTENT_ALL_CACHES}
5695 public void setPersistentDrawingCache(int drawingCacheToKeep
) {
5696 mPersistentDrawingCache
= drawingCacheToKeep
& PERSISTENT_ALL_CACHES
;
5699 private void setLayoutMode(int layoutMode
, boolean explicitly
) {
5700 mLayoutMode
= layoutMode
;
5701 setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET
, explicitly
);
5705 * Recursively traverse the view hierarchy, resetting the layoutMode of any
5706 * descendants that had inherited a different layoutMode from a previous parent.
5707 * Recursion terminates when a descendant's mode is:
5709 * <li>Undefined</li>
5710 * <li>The same as the root node's</li>
5711 * <li>A mode that had been explicitly set</li>
5713 * The first two clauses are optimizations.
5714 * @param layoutModeOfRoot
5717 void invalidateInheritedLayoutMode(int layoutModeOfRoot
) {
5718 if (mLayoutMode
== LAYOUT_MODE_UNDEFINED
||
5719 mLayoutMode
== layoutModeOfRoot
||
5720 hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET
)) {
5723 setLayoutMode(LAYOUT_MODE_UNDEFINED
, false);
5725 // apply recursively
5726 for (int i
= 0, N
= getChildCount(); i
< N
; i
++) {
5727 getChildAt(i
).invalidateInheritedLayoutMode(layoutModeOfRoot
);
5732 * Returns the basis of alignment during layout operations on this ViewGroup:
5733 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5735 * If no layoutMode was explicitly set, either programmatically or in an XML resource,
5736 * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
5737 * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
5739 * @return the layout mode to use during layout operations
5741 * @see #setLayoutMode(int)
5743 public int getLayoutMode() {
5744 if (mLayoutMode
== LAYOUT_MODE_UNDEFINED
) {
5745 int inheritedLayoutMode
= (mParent
instanceof ViewGroup
) ?
5746 ((ViewGroup
) mParent
).getLayoutMode() : LAYOUT_MODE_DEFAULT
;
5747 setLayoutMode(inheritedLayoutMode
, false);
5753 * Sets the basis of alignment during the layout of this ViewGroup.
5754 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
5755 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5757 * @param layoutMode the layout mode to use during layout operations
5759 * @see #getLayoutMode()
5760 * @attr ref android.R.styleable#ViewGroup_layoutMode
5762 public void setLayoutMode(int layoutMode
) {
5763 if (mLayoutMode
!= layoutMode
) {
5764 invalidateInheritedLayoutMode(layoutMode
);
5765 setLayoutMode(layoutMode
, layoutMode
!= LAYOUT_MODE_UNDEFINED
);
5771 * Returns a new set of layout parameters based on the supplied attributes set.
5773 * @param attrs the attributes to build the layout parameters from
5775 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5776 * of its descendants
5778 public LayoutParams
generateLayoutParams(AttributeSet attrs
) {
5779 return new LayoutParams(getContext(), attrs
);
5783 * Returns a safe set of layout parameters based on the supplied layout params.
5784 * When a ViewGroup is passed a View whose layout params do not pass the test of
5785 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
5786 * is invoked. This method should return a new set of layout params suitable for
5787 * this ViewGroup, possibly by copying the appropriate attributes from the
5788 * specified set of layout params.
5790 * @param p The layout parameters to convert into a suitable set of layout parameters
5791 * for this ViewGroup.
5793 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5794 * of its descendants
5796 protected LayoutParams
generateLayoutParams(ViewGroup
.LayoutParams p
) {
5801 * Returns a set of default layout parameters. These parameters are requested
5802 * when the View passed to {@link #addView(View)} has no layout parameters
5803 * already set. If null is returned, an exception is thrown from addView.
5805 * @return a set of default layout parameters or null
5807 protected LayoutParams
generateDefaultLayoutParams() {
5808 return new LayoutParams(LayoutParams
.WRAP_CONTENT
, LayoutParams
.WRAP_CONTENT
);
5815 protected void debug(int depth
) {
5819 if (mFocused
!= null) {
5820 output
= debugIndent(depth
);
5821 output
+= "mFocused";
5822 Log
.d(VIEW_LOG_TAG
, output
);
5824 if (mChildrenCount
!= 0) {
5825 output
= debugIndent(depth
);
5827 Log
.d(VIEW_LOG_TAG
, output
);
5829 int count
= mChildrenCount
;
5830 for (int i
= 0; i
< count
; i
++) {
5831 View child
= mChildren
[i
];
5832 child
.debug(depth
+ 1);
5835 if (mChildrenCount
!= 0) {
5836 output
= debugIndent(depth
);
5838 Log
.d(VIEW_LOG_TAG
, output
);
5843 * Returns the position in the group of the specified child view.
5845 * @param child the view for which to get the position
5846 * @return a positive integer representing the position of the view in the
5847 * group, or -1 if the view does not exist in the group
5849 public int indexOfChild(View child
) {
5850 final int count
= mChildrenCount
;
5851 final View
[] children
= mChildren
;
5852 for (int i
= 0; i
< count
; i
++) {
5853 if (children
[i
] == child
) {
5861 * Returns the number of children in the group.
5863 * @return a positive integer representing the number of children in
5866 public int getChildCount() {
5867 return mChildrenCount
;
5871 * Returns the view at the specified position in the group.
5873 * @param index the position at which to get the view from
5874 * @return the view at the specified position or null if the position
5875 * does not exist within the group
5877 public View
getChildAt(int index
) {
5878 if (index
< 0 || index
>= mChildrenCount
) {
5881 return mChildren
[index
];
5885 * Ask all of the children of this view to measure themselves, taking into
5886 * account both the MeasureSpec requirements for this view and its padding.
5887 * We skip children that are in the GONE state The heavy lifting is done in
5888 * getChildMeasureSpec.
5890 * @param widthMeasureSpec The width requirements for this view
5891 * @param heightMeasureSpec The height requirements for this view
5893 protected void measureChildren(int widthMeasureSpec
, int heightMeasureSpec
) {
5894 final int size
= mChildrenCount
;
5895 final View
[] children
= mChildren
;
5896 for (int i
= 0; i
< size
; ++i
) {
5897 final View child
= children
[i
];
5898 if ((child
.mViewFlags
& VISIBILITY_MASK
) != GONE
) {
5899 measureChild(child
, widthMeasureSpec
, heightMeasureSpec
);
5905 * Ask one of the children of this view to measure itself, taking into
5906 * account both the MeasureSpec requirements for this view and its padding.
5907 * The heavy lifting is done in getChildMeasureSpec.
5909 * @param child The child to measure
5910 * @param parentWidthMeasureSpec The width requirements for this view
5911 * @param parentHeightMeasureSpec The height requirements for this view
5913 protected void measureChild(View child
, int parentWidthMeasureSpec
,
5914 int parentHeightMeasureSpec
) {
5915 final LayoutParams lp
= child
.getLayoutParams();
5917 final int childWidthMeasureSpec
= getChildMeasureSpec(parentWidthMeasureSpec
,
5918 mPaddingLeft
+ mPaddingRight
, lp
.width
);
5919 final int childHeightMeasureSpec
= getChildMeasureSpec(parentHeightMeasureSpec
,
5920 mPaddingTop
+ mPaddingBottom
, lp
.height
);
5922 child
.measure(childWidthMeasureSpec
, childHeightMeasureSpec
);
5926 * Ask one of the children of this view to measure itself, taking into
5927 * account both the MeasureSpec requirements for this view and its padding
5928 * and margins. The child must have MarginLayoutParams The heavy lifting is
5929 * done in getChildMeasureSpec.
5931 * @param child The child to measure
5932 * @param parentWidthMeasureSpec The width requirements for this view
5933 * @param widthUsed Extra space that has been used up by the parent
5934 * horizontally (possibly by other children of the parent)
5935 * @param parentHeightMeasureSpec The height requirements for this view
5936 * @param heightUsed Extra space that has been used up by the parent
5937 * vertically (possibly by other children of the parent)
5939 protected void measureChildWithMargins(View child
,
5940 int parentWidthMeasureSpec
, int widthUsed
,
5941 int parentHeightMeasureSpec
, int heightUsed
) {
5942 final MarginLayoutParams lp
= (MarginLayoutParams
) child
.getLayoutParams();
5944 final int childWidthMeasureSpec
= getChildMeasureSpec(parentWidthMeasureSpec
,
5945 mPaddingLeft
+ mPaddingRight
+ lp
.leftMargin
+ lp
.rightMargin
5946 + widthUsed
, lp
.width
);
5947 final int childHeightMeasureSpec
= getChildMeasureSpec(parentHeightMeasureSpec
,
5948 mPaddingTop
+ mPaddingBottom
+ lp
.topMargin
+ lp
.bottomMargin
5949 + heightUsed
, lp
.height
);
5951 child
.measure(childWidthMeasureSpec
, childHeightMeasureSpec
);
5955 * Does the hard part of measureChildren: figuring out the MeasureSpec to
5956 * pass to a particular child. This method figures out the right MeasureSpec
5957 * for one dimension (height or width) of one child view.
5959 * The goal is to combine information from our MeasureSpec with the
5960 * LayoutParams of the child to get the best possible results. For example,
5961 * if the this view knows its size (because its MeasureSpec has a mode of
5962 * EXACTLY), and the child has indicated in its LayoutParams that it wants
5963 * to be the same size as the parent, the parent should ask the child to
5964 * layout given an exact size.
5966 * @param spec The requirements for this view
5967 * @param padding The padding of this view for the current dimension and
5968 * margins, if applicable
5969 * @param childDimension How big the child wants to be in the current
5971 * @return a MeasureSpec integer for the child
5973 public static int getChildMeasureSpec(int spec
, int padding
, int childDimension
) {
5974 int specMode
= MeasureSpec
.getMode(spec
);
5975 int specSize
= MeasureSpec
.getSize(spec
);
5977 int size
= Math
.max(0, specSize
- padding
);
5983 // Parent has imposed an exact size on us
5984 case MeasureSpec
.EXACTLY
:
5985 if (childDimension
>= 0) {
5986 resultSize
= childDimension
;
5987 resultMode
= MeasureSpec
.EXACTLY
;
5988 } else if (childDimension
== LayoutParams
.MATCH_PARENT
) {
5989 // Child wants to be our size. So be it.
5991 resultMode
= MeasureSpec
.EXACTLY
;
5992 } else if (childDimension
== LayoutParams
.WRAP_CONTENT
) {
5993 // Child wants to determine its own size. It can't be
5996 resultMode
= MeasureSpec
.AT_MOST
;
6000 // Parent has imposed a maximum size on us
6001 case MeasureSpec
.AT_MOST
:
6002 if (childDimension
>= 0) {
6003 // Child wants a specific size... so be it
6004 resultSize
= childDimension
;
6005 resultMode
= MeasureSpec
.EXACTLY
;
6006 } else if (childDimension
== LayoutParams
.MATCH_PARENT
) {
6007 // Child wants to be our size, but our size is not fixed.
6008 // Constrain child to not be bigger than us.
6010 resultMode
= MeasureSpec
.AT_MOST
;
6011 } else if (childDimension
== LayoutParams
.WRAP_CONTENT
) {
6012 // Child wants to determine its own size. It can't be
6015 resultMode
= MeasureSpec
.AT_MOST
;
6019 // Parent asked to see how big we want to be
6020 case MeasureSpec
.UNSPECIFIED
:
6021 if (childDimension
>= 0) {
6022 // Child wants a specific size... let him have it
6023 resultSize
= childDimension
;
6024 resultMode
= MeasureSpec
.EXACTLY
;
6025 } else if (childDimension
== LayoutParams
.MATCH_PARENT
) {
6026 // Child wants to be our size... find out how big it should
6028 resultSize
= View
.sUseZeroUnspecifiedMeasureSpec ?
0 : size
;
6029 resultMode
= MeasureSpec
.UNSPECIFIED
;
6030 } else if (childDimension
== LayoutParams
.WRAP_CONTENT
) {
6031 // Child wants to determine its own size.... find out how
6033 resultSize
= View
.sUseZeroUnspecifiedMeasureSpec ?
0 : size
;
6034 resultMode
= MeasureSpec
.UNSPECIFIED
;
6038 return MeasureSpec
.makeMeasureSpec(resultSize
, resultMode
);
6043 * Removes any pending animations for views that have been removed. Call
6044 * this if you don't want animations for exiting views to stack up.
6046 public void clearDisappearingChildren() {
6047 final ArrayList
<View
> disappearingChildren
= mDisappearingChildren
;
6048 if (disappearingChildren
!= null) {
6049 final int count
= disappearingChildren
.size();
6050 for (int i
= 0; i
< count
; i
++) {
6051 final View view
= disappearingChildren
.get(i
);
6052 if (view
.mAttachInfo
!= null) {
6053 view
.dispatchDetachedFromWindow();
6055 view
.clearAnimation();
6057 disappearingChildren
.clear();
6063 * Add a view which is removed from mChildren but still needs animation
6065 * @param v View to add
6067 private void addDisappearingView(View v
) {
6068 ArrayList
<View
> disappearingChildren
= mDisappearingChildren
;
6070 if (disappearingChildren
== null) {
6071 disappearingChildren
= mDisappearingChildren
= new ArrayList
<View
>();
6074 disappearingChildren
.add(v
);
6078 * Cleanup a view when its animation is done. This may mean removing it from
6079 * the list of disappearing views.
6081 * @param view The view whose animation has finished
6082 * @param animation The animation, cannot be null
6084 void finishAnimatingView(final View view
, Animation animation
) {
6085 final ArrayList
<View
> disappearingChildren
= mDisappearingChildren
;
6086 if (disappearingChildren
!= null) {
6087 if (disappearingChildren
.contains(view
)) {
6088 disappearingChildren
.remove(view
);
6090 if (view
.mAttachInfo
!= null) {
6091 view
.dispatchDetachedFromWindow();
6094 view
.clearAnimation();
6095 mGroupFlags
|= FLAG_INVALIDATE_REQUIRED
;
6099 if (animation
!= null && !animation
.getFillAfter()) {
6100 view
.clearAnimation();
6103 if ((view
.mPrivateFlags
& PFLAG_ANIMATION_STARTED
) == PFLAG_ANIMATION_STARTED
) {
6104 view
.onAnimationEnd();
6105 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
6106 // so we'd rather be safe than sorry
6107 view
.mPrivateFlags
&= ~PFLAG_ANIMATION_STARTED
;
6108 // Draw one more frame after the animation is done
6109 mGroupFlags
|= FLAG_INVALIDATE_REQUIRED
;
6114 * Utility function called by View during invalidation to determine whether a view that
6115 * is invisible or gone should still be invalidated because it is being transitioned (and
6116 * therefore still needs to be drawn).
6118 boolean isViewTransitioning(View view
) {
6119 return (mTransitioningViews
!= null && mTransitioningViews
.contains(view
));
6123 * This method tells the ViewGroup that the given View object, which should have this
6124 * ViewGroup as its parent,
6125 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
6126 * is removed from its parent. This allows animations, such as those used by
6127 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
6128 * the removal of views. A call to this method should always be accompanied by a later call
6129 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
6130 * so that the View finally gets removed.
6132 * @param view The View object to be kept visible even if it gets removed from its parent.
6134 public void startViewTransition(View view
) {
6135 if (view
.mParent
== this) {
6136 if (mTransitioningViews
== null) {
6137 mTransitioningViews
= new ArrayList
<View
>();
6139 mTransitioningViews
.add(view
);
6144 * This method should always be called following an earlier call to
6145 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
6146 * and will no longer be displayed. Note that this method does not perform the functionality
6147 * of removing a view from its parent; it just discontinues the display of a View that
6148 * has previously been removed.
6150 * @return view The View object that has been removed but is being kept around in the visible
6151 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
6153 public void endViewTransition(View view
) {
6154 if (mTransitioningViews
!= null) {
6155 mTransitioningViews
.remove(view
);
6156 final ArrayList
<View
> disappearingChildren
= mDisappearingChildren
;
6157 if (disappearingChildren
!= null && disappearingChildren
.contains(view
)) {
6158 disappearingChildren
.remove(view
);
6159 if (mVisibilityChangingChildren
!= null &&
6160 mVisibilityChangingChildren
.contains(view
)) {
6161 mVisibilityChangingChildren
.remove(view
);
6163 if (view
.mAttachInfo
!= null) {
6164 view
.dispatchDetachedFromWindow();
6166 if (view
.mParent
!= null) {
6167 view
.mParent
= null;
6175 private LayoutTransition
.TransitionListener mLayoutTransitionListener
=
6176 new LayoutTransition
.TransitionListener() {
6178 public void startTransition(LayoutTransition transition
, ViewGroup container
,
6179 View view
, int transitionType
) {
6180 // We only care about disappearing items, since we need special logic to keep
6181 // those items visible after they've been 'removed'
6182 if (transitionType
== LayoutTransition
.DISAPPEARING
) {
6183 startViewTransition(view
);
6188 public void endTransition(LayoutTransition transition
, ViewGroup container
,
6189 View view
, int transitionType
) {
6190 if (mLayoutCalledWhileSuppressed
&& !transition
.isChangingLayout()) {
6192 mLayoutCalledWhileSuppressed
= false;
6194 if (transitionType
== LayoutTransition
.DISAPPEARING
&& mTransitioningViews
!= null) {
6195 endViewTransition(view
);
6201 * Tells this ViewGroup to suppress all layout() calls until layout
6202 * suppression is disabled with a later call to suppressLayout(false).
6203 * When layout suppression is disabled, a requestLayout() call is sent
6204 * if layout() was attempted while layout was being suppressed.
6208 public void suppressLayout(boolean suppress
) {
6209 mSuppressLayout
= suppress
;
6211 if (mLayoutCalledWhileSuppressed
) {
6213 mLayoutCalledWhileSuppressed
= false;
6219 * Returns whether layout calls on this container are currently being
6220 * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
6222 * @return true if layout calls are currently suppressed, false otherwise.
6226 public boolean isLayoutSuppressed() {
6227 return mSuppressLayout
;
6234 public boolean gatherTransparentRegion(Region region
) {
6235 // If no transparent regions requested, we are always opaque.
6236 final boolean meOpaque
= (mPrivateFlags
& View
.PFLAG_REQUEST_TRANSPARENT_REGIONS
) == 0;
6237 if (meOpaque
&& region
== null) {
6238 // The caller doesn't care about the region, so stop now.
6241 super.gatherTransparentRegion(region
);
6242 final View
[] children
= mChildren
;
6243 final int count
= mChildrenCount
;
6244 boolean noneOfTheChildrenAreTransparent
= true;
6245 for (int i
= 0; i
< count
; i
++) {
6246 final View child
= children
[i
];
6247 if ((child
.mViewFlags
& VISIBILITY_MASK
) == VISIBLE
|| child
.getAnimation() != null) {
6248 if (!child
.gatherTransparentRegion(region
)) {
6249 noneOfTheChildrenAreTransparent
= false;
6253 return meOpaque
|| noneOfTheChildrenAreTransparent
;
6259 public void requestTransparentRegion(View child
) {
6260 if (child
!= null) {
6261 child
.mPrivateFlags
|= View
.PFLAG_REQUEST_TRANSPARENT_REGIONS
;
6262 if (mParent
!= null) {
6263 mParent
.requestTransparentRegion(this);
6269 public WindowInsets
dispatchApplyWindowInsets(WindowInsets insets
) {
6270 insets
= super.dispatchApplyWindowInsets(insets
);
6271 if (!insets
.isConsumed()) {
6272 final int count
= getChildCount();
6273 for (int i
= 0; i
< count
; i
++) {
6274 insets
= getChildAt(i
).dispatchApplyWindowInsets(insets
);
6275 if (insets
.isConsumed()) {
6284 * Returns the animation listener to which layout animation events are
6287 * @return an {@link android.view.animation.Animation.AnimationListener}
6289 public Animation
.AnimationListener
getLayoutAnimationListener() {
6290 return mAnimationListener
;
6294 protected void drawableStateChanged() {
6295 super.drawableStateChanged();
6297 if ((mGroupFlags
& FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE
) != 0) {
6298 if ((mGroupFlags
& FLAG_ADD_STATES_FROM_CHILDREN
) != 0) {
6299 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
6300 + " child has duplicateParentState set to true");
6303 final View
[] children
= mChildren
;
6304 final int count
= mChildrenCount
;
6306 for (int i
= 0; i
< count
; i
++) {
6307 final View child
= children
[i
];
6308 if ((child
.mViewFlags
& DUPLICATE_PARENT_STATE
) != 0) {
6309 child
.refreshDrawableState();
6316 public void jumpDrawablesToCurrentState() {
6317 super.jumpDrawablesToCurrentState();
6318 final View
[] children
= mChildren
;
6319 final int count
= mChildrenCount
;
6320 for (int i
= 0; i
< count
; i
++) {
6321 children
[i
].jumpDrawablesToCurrentState();
6326 protected int[] onCreateDrawableState(int extraSpace
) {
6327 if ((mGroupFlags
& FLAG_ADD_STATES_FROM_CHILDREN
) == 0) {
6328 return super.onCreateDrawableState(extraSpace
);
6332 int n
= getChildCount();
6333 for (int i
= 0; i
< n
; i
++) {
6334 int[] childState
= getChildAt(i
).getDrawableState();
6336 if (childState
!= null) {
6337 need
+= childState
.length
;
6341 int[] state
= super.onCreateDrawableState(extraSpace
+ need
);
6343 for (int i
= 0; i
< n
; i
++) {
6344 int[] childState
= getChildAt(i
).getDrawableState();
6346 if (childState
!= null) {
6347 state
= mergeDrawableStates(state
, childState
);
6355 * Sets whether this ViewGroup's drawable states also include
6356 * its children's drawable states. This is used, for example, to
6357 * make a group appear to be focused when its child EditText or button
6360 public void setAddStatesFromChildren(boolean addsStates
) {
6362 mGroupFlags
|= FLAG_ADD_STATES_FROM_CHILDREN
;
6364 mGroupFlags
&= ~FLAG_ADD_STATES_FROM_CHILDREN
;
6367 refreshDrawableState();
6371 * Returns whether this ViewGroup's drawable states also include
6372 * its children's drawable states. This is used, for example, to
6373 * make a group appear to be focused when its child EditText or button
6376 public boolean addStatesFromChildren() {
6377 return (mGroupFlags
& FLAG_ADD_STATES_FROM_CHILDREN
) != 0;
6381 * If {@link #addStatesFromChildren} is true, refreshes this group's
6382 * drawable state (to include the states from its children).
6384 public void childDrawableStateChanged(View child
) {
6385 if ((mGroupFlags
& FLAG_ADD_STATES_FROM_CHILDREN
) != 0) {
6386 refreshDrawableState();
6391 * Specifies the animation listener to which layout animation events must
6393 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
6395 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
6398 * @param animationListener the layout animation listener
6400 public void setLayoutAnimationListener(Animation
.AnimationListener animationListener
) {
6401 mAnimationListener
= animationListener
;
6405 * This method is called by LayoutTransition when there are 'changing' animations that need
6406 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
6407 * starts all pending transitions prior to the drawing phase in the current traversal.
6409 * @param transition The LayoutTransition to be started on the next traversal.
6413 public void requestTransitionStart(LayoutTransition transition
) {
6414 ViewRootImpl viewAncestor
= getViewRootImpl();
6415 if (viewAncestor
!= null) {
6416 viewAncestor
.requestTransitionStart(transition
);
6424 public boolean resolveRtlPropertiesIfNeeded() {
6425 final boolean result
= super.resolveRtlPropertiesIfNeeded();
6426 // We dont need to resolve the children RTL properties if nothing has changed for the parent
6428 int count
= getChildCount();
6429 for (int i
= 0; i
< count
; i
++) {
6430 final View child
= getChildAt(i
);
6431 if (child
.isLayoutDirectionInherited()) {
6432 child
.resolveRtlPropertiesIfNeeded();
6443 public boolean resolveLayoutDirection() {
6444 final boolean result
= super.resolveLayoutDirection();
6446 int count
= getChildCount();
6447 for (int i
= 0; i
< count
; i
++) {
6448 final View child
= getChildAt(i
);
6449 if (child
.isLayoutDirectionInherited()) {
6450 child
.resolveLayoutDirection();
6461 public boolean resolveTextDirection() {
6462 final boolean result
= super.resolveTextDirection();
6464 int count
= getChildCount();
6465 for (int i
= 0; i
< count
; i
++) {
6466 final View child
= getChildAt(i
);
6467 if (child
.isTextDirectionInherited()) {
6468 child
.resolveTextDirection();
6479 public boolean resolveTextAlignment() {
6480 final boolean result
= super.resolveTextAlignment();
6482 int count
= getChildCount();
6483 for (int i
= 0; i
< count
; i
++) {
6484 final View child
= getChildAt(i
);
6485 if (child
.isTextAlignmentInherited()) {
6486 child
.resolveTextAlignment();
6497 public void resolvePadding() {
6498 super.resolvePadding();
6499 int count
= getChildCount();
6500 for (int i
= 0; i
< count
; i
++) {
6501 final View child
= getChildAt(i
);
6502 if (child
.isLayoutDirectionInherited() && !child
.isPaddingResolved()) {
6503 child
.resolvePadding();
6512 protected void resolveDrawables() {
6513 super.resolveDrawables();
6514 int count
= getChildCount();
6515 for (int i
= 0; i
< count
; i
++) {
6516 final View child
= getChildAt(i
);
6517 if (child
.isLayoutDirectionInherited() && !child
.areDrawablesResolved()) {
6518 child
.resolveDrawables();
6527 public void resolveLayoutParams() {
6528 super.resolveLayoutParams();
6529 int count
= getChildCount();
6530 for (int i
= 0; i
< count
; i
++) {
6531 final View child
= getChildAt(i
);
6532 child
.resolveLayoutParams();
6540 public void resetResolvedLayoutDirection() {
6541 super.resetResolvedLayoutDirection();
6543 int count
= getChildCount();
6544 for (int i
= 0; i
< count
; i
++) {
6545 final View child
= getChildAt(i
);
6546 if (child
.isLayoutDirectionInherited()) {
6547 child
.resetResolvedLayoutDirection();
6556 public void resetResolvedTextDirection() {
6557 super.resetResolvedTextDirection();
6559 int count
= getChildCount();
6560 for (int i
= 0; i
< count
; i
++) {
6561 final View child
= getChildAt(i
);
6562 if (child
.isTextDirectionInherited()) {
6563 child
.resetResolvedTextDirection();
6572 public void resetResolvedTextAlignment() {
6573 super.resetResolvedTextAlignment();
6575 int count
= getChildCount();
6576 for (int i
= 0; i
< count
; i
++) {
6577 final View child
= getChildAt(i
);
6578 if (child
.isTextAlignmentInherited()) {
6579 child
.resetResolvedTextAlignment();
6588 public void resetResolvedPadding() {
6589 super.resetResolvedPadding();
6591 int count
= getChildCount();
6592 for (int i
= 0; i
< count
; i
++) {
6593 final View child
= getChildAt(i
);
6594 if (child
.isLayoutDirectionInherited()) {
6595 child
.resetResolvedPadding();
6604 protected void resetResolvedDrawables() {
6605 super.resetResolvedDrawables();
6607 int count
= getChildCount();
6608 for (int i
= 0; i
< count
; i
++) {
6609 final View child
= getChildAt(i
);
6610 if (child
.isLayoutDirectionInherited()) {
6611 child
.resetResolvedDrawables();
6617 * Return true if the pressed state should be delayed for children or descendants of this
6618 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
6619 * This prevents the pressed state from appearing when the user is actually trying to scroll
6622 * The default implementation returns true for compatibility reasons. Subclasses that do
6623 * not scroll should generally override this method and return false.
6625 public boolean shouldDelayChildPressedState() {
6633 public boolean onStartNestedScroll(View child
, View target
, int nestedScrollAxes
) {
6641 public void onNestedScrollAccepted(View child
, View target
, int axes
) {
6642 mNestedScrollAxes
= axes
;
6648 * <p>The default implementation of onStopNestedScroll calls
6649 * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
6652 public void onStopNestedScroll(View child
) {
6653 // Stop any recursive nested scrolling.
6655 mNestedScrollAxes
= 0;
6662 public void onNestedScroll(View target
, int dxConsumed
, int dyConsumed
,
6663 int dxUnconsumed
, int dyUnconsumed
) {
6671 public void onNestedPreScroll(View target
, int dx
, int dy
, int[] consumed
) {
6679 public boolean onNestedFling(View target
, float velocityX
, float velocityY
, boolean consumed
) {
6687 public boolean onNestedPreFling(View target
, float velocityX
, float velocityY
) {
6692 * Return the current axes of nested scrolling for this ViewGroup.
6694 * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
6695 * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p>
6697 * @return Flags indicating the current axes of nested scrolling
6698 * @see #SCROLL_AXIS_HORIZONTAL
6699 * @see #SCROLL_AXIS_VERTICAL
6700 * @see #SCROLL_AXIS_NONE
6702 public int getNestedScrollAxes() {
6703 return mNestedScrollAxes
;
6707 protected void onSetLayoutParams(View child
, LayoutParams layoutParams
) {
6712 public void captureTransitioningViews(List
<View
> transitioningViews
) {
6713 if (getVisibility() != View
.VISIBLE
) {
6716 if (isTransitionGroup()) {
6717 transitioningViews
.add(this);
6719 int count
= getChildCount();
6720 for (int i
= 0; i
< count
; i
++) {
6721 View child
= getChildAt(i
);
6722 child
.captureTransitioningViews(transitioningViews
);
6729 public void findNamedViews(Map
<String
, View
> namedElements
) {
6730 if (getVisibility() != VISIBLE
&& mGhostView
== null) {
6733 super.findNamedViews(namedElements
);
6734 int count
= getChildCount();
6735 for (int i
= 0; i
< count
; i
++) {
6736 View child
= getChildAt(i
);
6737 child
.findNamedViews(namedElements
);
6742 * LayoutParams are used by views to tell their parents how they want to be
6744 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
6745 * for a list of all child view attributes that this class supports.
6748 * The base LayoutParams class just describes how big the view wants to be
6749 * for both width and height. For each dimension, it can specify one of:
6751 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
6752 * means that the view wants to be as big as its parent (minus padding)
6753 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
6754 * to enclose its content (plus padding)
6755 * <li> an exact number
6757 * There are subclasses of LayoutParams for different subclasses of
6758 * ViewGroup. For example, AbsoluteLayout has its own subclass of
6759 * LayoutParams which adds an X and Y value.</p>
6761 * <div class="special reference">
6762 * <h3>Developer Guides</h3>
6763 * <p>For more information about creating user interface layouts, read the
6764 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
6767 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
6768 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
6770 public static class LayoutParams
{
6772 * Special value for the height or width requested by a View.
6773 * FILL_PARENT means that the view wants to be as big as its parent,
6774 * minus the parent's padding, if any. This value is deprecated
6775 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
6777 @SuppressWarnings({"UnusedDeclaration"})
6779 public static final int FILL_PARENT
= -1;
6782 * Special value for the height or width requested by a View.
6783 * MATCH_PARENT means that the view wants to be as big as its parent,
6784 * minus the parent's padding, if any. Introduced in API Level 8.
6786 public static final int MATCH_PARENT
= -1;
6789 * Special value for the height or width requested by a View.
6790 * WRAP_CONTENT means that the view wants to be just large enough to fit
6791 * its own internal content, taking its own padding into account.
6793 public static final int WRAP_CONTENT
= -2;
6796 * Information about how wide the view wants to be. Can be one of the
6797 * constants FILL_PARENT (replaced by MATCH_PARENT
6798 * in API Level 8) or WRAP_CONTENT, or an exact size.
6800 @ViewDebug.ExportedProperty(category
= "layout", mapping
= {
6801 @ViewDebug.IntToString(from
= MATCH_PARENT
, to
= "MATCH_PARENT"),
6802 @ViewDebug.IntToString(from
= WRAP_CONTENT
, to
= "WRAP_CONTENT")
6807 * Information about how tall the view wants to be. Can be one of the
6808 * constants FILL_PARENT (replaced by MATCH_PARENT
6809 * in API Level 8) or WRAP_CONTENT, or an exact size.
6811 @ViewDebug.ExportedProperty(category
= "layout", mapping
= {
6812 @ViewDebug.IntToString(from
= MATCH_PARENT
, to
= "MATCH_PARENT"),
6813 @ViewDebug.IntToString(from
= WRAP_CONTENT
, to
= "WRAP_CONTENT")
6818 * Used to animate layouts.
6820 public LayoutAnimationController
.AnimationParameters layoutAnimationParameters
;
6823 * Creates a new set of layout parameters. The values are extracted from
6824 * the supplied attributes set and context. The XML attributes mapped
6825 * to this set of layout parameters are:
6828 * <li><code>layout_width</code>: the width, either an exact value,
6829 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6830 * {@link #MATCH_PARENT} in API Level 8)</li>
6831 * <li><code>layout_height</code>: the height, either an exact value,
6832 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6833 * {@link #MATCH_PARENT} in API Level 8)</li>
6836 * @param c the application environment
6837 * @param attrs the set of attributes from which to extract the layout
6838 * parameters' values
6840 public LayoutParams(Context c
, AttributeSet attrs
) {
6841 TypedArray a
= c
.obtainStyledAttributes(attrs
, R
.styleable
.ViewGroup_Layout
);
6842 setBaseAttributes(a
,
6843 R
.styleable
.ViewGroup_Layout_layout_width
,
6844 R
.styleable
.ViewGroup_Layout_layout_height
);
6849 * Creates a new set of layout parameters with the specified width
6852 * @param width the width, either {@link #WRAP_CONTENT},
6853 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6854 * API Level 8), or a fixed size in pixels
6855 * @param height the height, either {@link #WRAP_CONTENT},
6856 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6857 * API Level 8), or a fixed size in pixels
6859 public LayoutParams(int width
, int height
) {
6861 this.height
= height
;
6865 * Copy constructor. Clones the width and height values of the source.
6867 * @param source The layout params to copy from.
6869 public LayoutParams(LayoutParams source
) {
6870 this.width
= source
.width
;
6871 this.height
= source
.height
;
6875 * Used internally by MarginLayoutParams.
6882 * Extracts the layout parameters from the supplied attributes.
6884 * @param a the style attributes to extract the parameters from
6885 * @param widthAttr the identifier of the width attribute
6886 * @param heightAttr the identifier of the height attribute
6888 protected void setBaseAttributes(TypedArray a
, int widthAttr
, int heightAttr
) {
6889 width
= a
.getLayoutDimension(widthAttr
, "layout_width");
6890 height
= a
.getLayoutDimension(heightAttr
, "layout_height");
6894 * Resolve layout parameters depending on the layout direction. Subclasses that care about
6895 * layoutDirection changes should override this method. The default implementation does
6898 * @param layoutDirection the direction of the layout
6900 * {@link View#LAYOUT_DIRECTION_LTR}
6901 * {@link View#LAYOUT_DIRECTION_RTL}
6903 public void resolveLayoutDirection(int layoutDirection
) {
6907 * Returns a String representation of this set of layout parameters.
6909 * @param output the String to prepend to the internal representation
6910 * @return a String with the following format: output +
6911 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
6915 public String
debug(String output
) {
6916 return output
+ "ViewGroup.LayoutParams={ width="
6917 + sizeToString(width
) + ", height=" + sizeToString(height
) + " }";
6921 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
6923 * @param view the view that contains these layout parameters
6924 * @param canvas the canvas on which to draw
6928 public void onDebugDraw(View view
, Canvas canvas
, Paint paint
) {
6932 * Converts the specified size to a readable String.
6934 * @param size the size to convert
6935 * @return a String instance representing the supplied size
6939 protected static String
sizeToString(int size
) {
6940 if (size
== WRAP_CONTENT
) {
6941 return "wrap-content";
6943 if (size
== MATCH_PARENT
) {
6944 return "match-parent";
6946 return String
.valueOf(size
);
6950 void encode(@NonNull ViewHierarchyEncoder encoder
) {
6951 encoder
.beginObject(this);
6952 encodeProperties(encoder
);
6953 encoder
.endObject();
6957 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder
) {
6958 encoder
.addProperty("width", width
);
6959 encoder
.addProperty("height", height
);
6964 * Per-child layout information for layouts that support margins.
6966 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
6967 * for a list of all child view attributes that this class supports.
6969 public static class MarginLayoutParams
extends ViewGroup
.LayoutParams
{
6971 * The left margin in pixels of the child. Margin values should be positive.
6972 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6975 @ViewDebug.ExportedProperty(category
= "layout")
6976 public int leftMargin
;
6979 * The top margin in pixels of the child. Margin values should be positive.
6980 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6983 @ViewDebug.ExportedProperty(category
= "layout")
6984 public int topMargin
;
6987 * The right margin in pixels of the child. Margin values should be positive.
6988 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6991 @ViewDebug.ExportedProperty(category
= "layout")
6992 public int rightMargin
;
6995 * The bottom margin in pixels of the child. Margin values should be positive.
6996 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6999 @ViewDebug.ExportedProperty(category
= "layout")
7000 public int bottomMargin
;
7003 * The start margin in pixels of the child. Margin values should be positive.
7004 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7007 @ViewDebug.ExportedProperty(category
= "layout")
7008 private int startMargin
= DEFAULT_MARGIN_RELATIVE
;
7011 * The end margin in pixels of the child. Margin values should be positive.
7012 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7015 @ViewDebug.ExportedProperty(category
= "layout")
7016 private int endMargin
= DEFAULT_MARGIN_RELATIVE
;
7019 * The default start and end margin.
7022 public static final int DEFAULT_MARGIN_RELATIVE
= Integer
.MIN_VALUE
;
7025 * Bit 0: layout direction
7026 * Bit 1: layout direction
7027 * Bit 2: left margin undefined
7028 * Bit 3: right margin undefined
7029 * Bit 4: is RTL compatibility mode
7030 * Bit 5: need resolution
7032 * Bit 6 to 7 not used
7036 @ViewDebug.ExportedProperty(category
= "layout", flagMapping
= {
7037 @ViewDebug.FlagToString(mask
= LAYOUT_DIRECTION_MASK
,
7038 equals
= LAYOUT_DIRECTION_MASK
, name
= "LAYOUT_DIRECTION"),
7039 @ViewDebug.FlagToString(mask
= LEFT_MARGIN_UNDEFINED_MASK
,
7040 equals
= LEFT_MARGIN_UNDEFINED_MASK
, name
= "LEFT_MARGIN_UNDEFINED_MASK"),
7041 @ViewDebug.FlagToString(mask
= RIGHT_MARGIN_UNDEFINED_MASK
,
7042 equals
= RIGHT_MARGIN_UNDEFINED_MASK
, name
= "RIGHT_MARGIN_UNDEFINED_MASK"),
7043 @ViewDebug.FlagToString(mask
= RTL_COMPATIBILITY_MODE_MASK
,
7044 equals
= RTL_COMPATIBILITY_MODE_MASK
, name
= "RTL_COMPATIBILITY_MODE_MASK"),
7045 @ViewDebug.FlagToString(mask
= NEED_RESOLUTION_MASK
,
7046 equals
= NEED_RESOLUTION_MASK
, name
= "NEED_RESOLUTION_MASK")
7047 }, formatToHexString
= true)
7050 private static final int LAYOUT_DIRECTION_MASK
= 0x00000003;
7051 private static final int LEFT_MARGIN_UNDEFINED_MASK
= 0x00000004;
7052 private static final int RIGHT_MARGIN_UNDEFINED_MASK
= 0x00000008;
7053 private static final int RTL_COMPATIBILITY_MODE_MASK
= 0x00000010;
7054 private static final int NEED_RESOLUTION_MASK
= 0x00000020;
7056 private static final int DEFAULT_MARGIN_RESOLVED
= 0;
7057 private static final int UNDEFINED_MARGIN
= DEFAULT_MARGIN_RELATIVE
;
7060 * Creates a new set of layout parameters. The values are extracted from
7061 * the supplied attributes set and context.
7063 * @param c the application environment
7064 * @param attrs the set of attributes from which to extract the layout
7065 * parameters' values
7067 public MarginLayoutParams(Context c
, AttributeSet attrs
) {
7070 TypedArray a
= c
.obtainStyledAttributes(attrs
, R
.styleable
.ViewGroup_MarginLayout
);
7071 setBaseAttributes(a
,
7072 R
.styleable
.ViewGroup_MarginLayout_layout_width
,
7073 R
.styleable
.ViewGroup_MarginLayout_layout_height
);
7075 int margin
= a
.getDimensionPixelSize(
7076 com
.android
.internal
.R
.styleable
.ViewGroup_MarginLayout_layout_margin
, -1);
7078 leftMargin
= margin
;
7080 rightMargin
= margin
;
7081 bottomMargin
= margin
;
7083 leftMargin
= a
.getDimensionPixelSize(
7084 R
.styleable
.ViewGroup_MarginLayout_layout_marginLeft
,
7086 if (leftMargin
== UNDEFINED_MARGIN
) {
7087 mMarginFlags
|= LEFT_MARGIN_UNDEFINED_MASK
;
7088 leftMargin
= DEFAULT_MARGIN_RESOLVED
;
7090 rightMargin
= a
.getDimensionPixelSize(
7091 R
.styleable
.ViewGroup_MarginLayout_layout_marginRight
,
7093 if (rightMargin
== UNDEFINED_MARGIN
) {
7094 mMarginFlags
|= RIGHT_MARGIN_UNDEFINED_MASK
;
7095 rightMargin
= DEFAULT_MARGIN_RESOLVED
;
7098 topMargin
= a
.getDimensionPixelSize(
7099 R
.styleable
.ViewGroup_MarginLayout_layout_marginTop
,
7100 DEFAULT_MARGIN_RESOLVED
);
7101 bottomMargin
= a
.getDimensionPixelSize(
7102 R
.styleable
.ViewGroup_MarginLayout_layout_marginBottom
,
7103 DEFAULT_MARGIN_RESOLVED
);
7105 startMargin
= a
.getDimensionPixelSize(
7106 R
.styleable
.ViewGroup_MarginLayout_layout_marginStart
,
7107 DEFAULT_MARGIN_RELATIVE
);
7108 endMargin
= a
.getDimensionPixelSize(
7109 R
.styleable
.ViewGroup_MarginLayout_layout_marginEnd
,
7110 DEFAULT_MARGIN_RELATIVE
);
7112 if (isMarginRelative()) {
7113 mMarginFlags
|= NEED_RESOLUTION_MASK
;
7117 final boolean hasRtlSupport
= c
.getApplicationInfo().hasRtlSupport();
7118 final int targetSdkVersion
= c
.getApplicationInfo().targetSdkVersion
;
7119 if (targetSdkVersion
< JELLY_BEAN_MR1
|| !hasRtlSupport
) {
7120 mMarginFlags
|= RTL_COMPATIBILITY_MODE_MASK
;
7123 // Layout direction is LTR by default
7124 mMarginFlags
|= LAYOUT_DIRECTION_LTR
;
7132 public MarginLayoutParams(int width
, int height
) {
7133 super(width
, height
);
7135 mMarginFlags
|= LEFT_MARGIN_UNDEFINED_MASK
;
7136 mMarginFlags
|= RIGHT_MARGIN_UNDEFINED_MASK
;
7138 mMarginFlags
&= ~NEED_RESOLUTION_MASK
;
7139 mMarginFlags
&= ~RTL_COMPATIBILITY_MODE_MASK
;
7143 * Copy constructor. Clones the width, height and margin values of the source.
7145 * @param source The layout params to copy from.
7147 public MarginLayoutParams(MarginLayoutParams source
) {
7148 this.width
= source
.width
;
7149 this.height
= source
.height
;
7151 this.leftMargin
= source
.leftMargin
;
7152 this.topMargin
= source
.topMargin
;
7153 this.rightMargin
= source
.rightMargin
;
7154 this.bottomMargin
= source
.bottomMargin
;
7155 this.startMargin
= source
.startMargin
;
7156 this.endMargin
= source
.endMargin
;
7158 this.mMarginFlags
= source
.mMarginFlags
;
7164 public MarginLayoutParams(LayoutParams source
) {
7167 mMarginFlags
|= LEFT_MARGIN_UNDEFINED_MASK
;
7168 mMarginFlags
|= RIGHT_MARGIN_UNDEFINED_MASK
;
7170 mMarginFlags
&= ~NEED_RESOLUTION_MASK
;
7171 mMarginFlags
&= ~RTL_COMPATIBILITY_MODE_MASK
;
7175 * @hide Used internally.
7177 public final void copyMarginsFrom(MarginLayoutParams source
) {
7178 this.leftMargin
= source
.leftMargin
;
7179 this.topMargin
= source
.topMargin
;
7180 this.rightMargin
= source
.rightMargin
;
7181 this.bottomMargin
= source
.bottomMargin
;
7182 this.startMargin
= source
.startMargin
;
7183 this.endMargin
= source
.endMargin
;
7185 this.mMarginFlags
= source
.mMarginFlags
;
7189 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
7190 * to be done so that the new margins are taken into account. Left and right margins may be
7191 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
7192 * Margin values should be positive.
7194 * @param left the left margin size
7195 * @param top the top margin size
7196 * @param right the right margin size
7197 * @param bottom the bottom margin size
7199 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
7200 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7201 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
7202 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7204 public void setMargins(int left
, int top
, int right
, int bottom
) {
7207 rightMargin
= right
;
7208 bottomMargin
= bottom
;
7209 mMarginFlags
&= ~LEFT_MARGIN_UNDEFINED_MASK
;
7210 mMarginFlags
&= ~RIGHT_MARGIN_UNDEFINED_MASK
;
7211 if (isMarginRelative()) {
7212 mMarginFlags
|= NEED_RESOLUTION_MASK
;
7214 mMarginFlags
&= ~NEED_RESOLUTION_MASK
;
7219 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
7220 * needs to be done so that the new relative margins are taken into account. Left and right
7221 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
7222 * direction. Margin values should be positive.
7224 * @param start the start margin size
7225 * @param top the top margin size
7226 * @param end the right margin size
7227 * @param bottom the bottom margin size
7229 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7230 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7231 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7232 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7236 public void setMarginsRelative(int start
, int top
, int end
, int bottom
) {
7237 startMargin
= start
;
7240 bottomMargin
= bottom
;
7241 mMarginFlags
|= NEED_RESOLUTION_MASK
;
7245 * Sets the relative start margin. Margin values should be positive.
7247 * @param start the start margin size
7249 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7251 public void setMarginStart(int start
) {
7252 startMargin
= start
;
7253 mMarginFlags
|= NEED_RESOLUTION_MASK
;
7257 * Returns the start margin in pixels.
7259 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7261 * @return the start margin in pixels.
7263 public int getMarginStart() {
7264 if (startMargin
!= DEFAULT_MARGIN_RELATIVE
) return startMargin
;
7265 if ((mMarginFlags
& NEED_RESOLUTION_MASK
) == NEED_RESOLUTION_MASK
) {
7268 switch(mMarginFlags
& LAYOUT_DIRECTION_MASK
) {
7269 case View
.LAYOUT_DIRECTION_RTL
:
7271 case View
.LAYOUT_DIRECTION_LTR
:
7278 * Sets the relative end margin. Margin values should be positive.
7280 * @param end the end margin size
7282 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7284 public void setMarginEnd(int end
) {
7286 mMarginFlags
|= NEED_RESOLUTION_MASK
;
7290 * Returns the end margin in pixels.
7292 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7294 * @return the end margin in pixels.
7296 public int getMarginEnd() {
7297 if (endMargin
!= DEFAULT_MARGIN_RELATIVE
) return endMargin
;
7298 if ((mMarginFlags
& NEED_RESOLUTION_MASK
) == NEED_RESOLUTION_MASK
) {
7301 switch(mMarginFlags
& LAYOUT_DIRECTION_MASK
) {
7302 case View
.LAYOUT_DIRECTION_RTL
:
7304 case View
.LAYOUT_DIRECTION_LTR
:
7311 * Check if margins are relative.
7313 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7314 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7316 * @return true if either marginStart or marginEnd has been set.
7318 public boolean isMarginRelative() {
7319 return (startMargin
!= DEFAULT_MARGIN_RELATIVE
|| endMargin
!= DEFAULT_MARGIN_RELATIVE
);
7323 * Set the layout direction
7324 * @param layoutDirection the layout direction.
7325 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
7326 * or {@link View#LAYOUT_DIRECTION_RTL}.
7328 public void setLayoutDirection(int layoutDirection
) {
7329 if (layoutDirection
!= View
.LAYOUT_DIRECTION_LTR
&&
7330 layoutDirection
!= View
.LAYOUT_DIRECTION_RTL
) return;
7331 if (layoutDirection
!= (mMarginFlags
& LAYOUT_DIRECTION_MASK
)) {
7332 mMarginFlags
&= ~LAYOUT_DIRECTION_MASK
;
7333 mMarginFlags
|= (layoutDirection
& LAYOUT_DIRECTION_MASK
);
7334 if (isMarginRelative()) {
7335 mMarginFlags
|= NEED_RESOLUTION_MASK
;
7337 mMarginFlags
&= ~NEED_RESOLUTION_MASK
;
7343 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
7344 * {@link View#LAYOUT_DIRECTION_RTL}.
7346 * @return the layout direction.
7348 public int getLayoutDirection() {
7349 return (mMarginFlags
& LAYOUT_DIRECTION_MASK
);
7353 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
7354 * may be overridden depending on layout direction.
7357 public void resolveLayoutDirection(int layoutDirection
) {
7358 setLayoutDirection(layoutDirection
);
7360 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
7361 // Will use the left and right margins if no relative margin is defined.
7362 if (!isMarginRelative() ||
7363 (mMarginFlags
& NEED_RESOLUTION_MASK
) != NEED_RESOLUTION_MASK
) return;
7365 // Proceed with resolution
7369 private void doResolveMargins() {
7370 if ((mMarginFlags
& RTL_COMPATIBILITY_MODE_MASK
) == RTL_COMPATIBILITY_MODE_MASK
) {
7371 // if left or right margins are not defined and if we have some start or end margin
7372 // defined then use those start and end margins.
7373 if ((mMarginFlags
& LEFT_MARGIN_UNDEFINED_MASK
) == LEFT_MARGIN_UNDEFINED_MASK
7374 && startMargin
> DEFAULT_MARGIN_RELATIVE
) {
7375 leftMargin
= startMargin
;
7377 if ((mMarginFlags
& RIGHT_MARGIN_UNDEFINED_MASK
) == RIGHT_MARGIN_UNDEFINED_MASK
7378 && endMargin
> DEFAULT_MARGIN_RELATIVE
) {
7379 rightMargin
= endMargin
;
7382 // We have some relative margins (either the start one or the end one or both). So use
7383 // them and override what has been defined for left and right margins. If either start
7384 // or end margin is not defined, just set it to default "0".
7385 switch(mMarginFlags
& LAYOUT_DIRECTION_MASK
) {
7386 case View
.LAYOUT_DIRECTION_RTL
:
7387 leftMargin
= (endMargin
> DEFAULT_MARGIN_RELATIVE
) ?
7388 endMargin
: DEFAULT_MARGIN_RESOLVED
;
7389 rightMargin
= (startMargin
> DEFAULT_MARGIN_RELATIVE
) ?
7390 startMargin
: DEFAULT_MARGIN_RESOLVED
;
7392 case View
.LAYOUT_DIRECTION_LTR
:
7394 leftMargin
= (startMargin
> DEFAULT_MARGIN_RELATIVE
) ?
7395 startMargin
: DEFAULT_MARGIN_RESOLVED
;
7396 rightMargin
= (endMargin
> DEFAULT_MARGIN_RELATIVE
) ?
7397 endMargin
: DEFAULT_MARGIN_RESOLVED
;
7401 mMarginFlags
&= ~NEED_RESOLUTION_MASK
;
7407 public boolean isLayoutRtl() {
7408 return ((mMarginFlags
& LAYOUT_DIRECTION_MASK
) == View
.LAYOUT_DIRECTION_RTL
);
7415 public void onDebugDraw(View view
, Canvas canvas
, Paint paint
) {
7416 Insets oi
= isLayoutModeOptical(view
.mParent
) ? view
.getOpticalInsets() : Insets
.NONE
;
7418 fillDifference(canvas
,
7419 view
.getLeft() + oi
.left
,
7420 view
.getTop() + oi
.top
,
7421 view
.getRight() - oi
.right
,
7422 view
.getBottom() - oi
.bottom
,
7432 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder
) {
7433 super.encodeProperties(encoder
);
7434 encoder
.addProperty("leftMargin", leftMargin
);
7435 encoder
.addProperty("topMargin", topMargin
);
7436 encoder
.addProperty("rightMargin", rightMargin
);
7437 encoder
.addProperty("bottomMargin", bottomMargin
);
7438 encoder
.addProperty("startMargin", startMargin
);
7439 encoder
.addProperty("endMargin", endMargin
);
7443 /* Describes a touched view and the ids of the pointers that it has captured.
7445 * This code assumes that pointer ids are always in the range 0..31 such that
7446 * it can use a bitfield to track which pointer ids are present.
7447 * As it happens, the lower layers of the input dispatch pipeline also use the
7448 * same trick so the assumption should be safe here...
7450 private static final class TouchTarget
{
7451 private static final int MAX_RECYCLED
= 32;
7452 private static final Object sRecycleLock
= new Object
[0];
7453 private static TouchTarget sRecycleBin
;
7454 private static int sRecycledCount
;
7456 public static final int ALL_POINTER_IDS
= -1; // all ones
7458 // The touched child view.
7461 // The combined bit mask of pointer ids for all pointers captured by the target.
7462 public int pointerIdBits
;
7464 // The next target in the target list.
7465 public TouchTarget next
;
7467 private TouchTarget() {
7470 public static TouchTarget
obtain(View child
, int pointerIdBits
) {
7471 final TouchTarget target
;
7472 synchronized (sRecycleLock
) {
7473 if (sRecycleBin
== null) {
7474 target
= new TouchTarget();
7476 target
= sRecycleBin
;
7477 sRecycleBin
= target
.next
;
7482 target
.child
= child
;
7483 target
.pointerIdBits
= pointerIdBits
;
7487 public void recycle() {
7488 synchronized (sRecycleLock
) {
7489 if (sRecycledCount
< MAX_RECYCLED
) {
7492 sRecycledCount
+= 1;
7501 /* Describes a hovered view. */
7502 private static final class HoverTarget
{
7503 private static final int MAX_RECYCLED
= 32;
7504 private static final Object sRecycleLock
= new Object
[0];
7505 private static HoverTarget sRecycleBin
;
7506 private static int sRecycledCount
;
7508 // The hovered child view.
7511 // The next target in the target list.
7512 public HoverTarget next
;
7514 private HoverTarget() {
7517 public static HoverTarget
obtain(View child
) {
7518 final HoverTarget target
;
7519 synchronized (sRecycleLock
) {
7520 if (sRecycleBin
== null) {
7521 target
= new HoverTarget();
7523 target
= sRecycleBin
;
7524 sRecycleBin
= target
.next
;
7529 target
.child
= child
;
7533 public void recycle() {
7534 synchronized (sRecycleLock
) {
7535 if (sRecycledCount
< MAX_RECYCLED
) {
7538 sRecycledCount
+= 1;
7548 * Pooled class that orderes the children of a ViewGroup from start
7549 * to end based on how they are laid out and the layout direction.
7551 static class ChildListForAccessibility
{
7553 private static final int MAX_POOL_SIZE
= 32;
7555 private static final SynchronizedPool
<ChildListForAccessibility
> sPool
=
7556 new SynchronizedPool
<ChildListForAccessibility
>(MAX_POOL_SIZE
);
7558 private final ArrayList
<View
> mChildren
= new ArrayList
<View
>();
7560 private final ArrayList
<ViewLocationHolder
> mHolders
= new ArrayList
<ViewLocationHolder
>();
7562 public static ChildListForAccessibility
obtain(ViewGroup parent
, boolean sort
) {
7563 ChildListForAccessibility list
= sPool
.acquire();
7565 list
= new ChildListForAccessibility();
7567 list
.init(parent
, sort
);
7571 public void recycle() {
7573 sPool
.release(this);
7576 public int getChildCount() {
7577 return mChildren
.size();
7580 public View
getChildAt(int index
) {
7581 return mChildren
.get(index
);
7584 public int getChildIndex(View child
) {
7585 return mChildren
.indexOf(child
);
7588 private void init(ViewGroup parent
, boolean sort
) {
7589 ArrayList
<View
> children
= mChildren
;
7590 final int childCount
= parent
.getChildCount();
7591 for (int i
= 0; i
< childCount
; i
++) {
7592 View child
= parent
.getChildAt(i
);
7593 children
.add(child
);
7596 ArrayList
<ViewLocationHolder
> holders
= mHolders
;
7597 for (int i
= 0; i
< childCount
; i
++) {
7598 View child
= children
.get(i
);
7599 ViewLocationHolder holder
= ViewLocationHolder
.obtain(parent
, child
);
7600 holders
.add(holder
);
7603 for (int i
= 0; i
< childCount
; i
++) {
7604 ViewLocationHolder holder
= holders
.get(i
);
7605 children
.set(i
, holder
.mView
);
7612 private void sort(ArrayList
<ViewLocationHolder
> holders
) {
7613 // This is gross but the least risky solution. The current comparison
7614 // strategy breaks transitivity but produces very good results. Coming
7615 // up with a new strategy requires time which we do not have, so ...
7617 ViewLocationHolder
.setComparisonStrategy(
7618 ViewLocationHolder
.COMPARISON_STRATEGY_STRIPE
);
7619 Collections
.sort(holders
);
7620 } catch (IllegalArgumentException iae
) {
7621 // Note that in practice this occurs extremely rarely in a couple
7622 // of pathological cases.
7623 ViewLocationHolder
.setComparisonStrategy(
7624 ViewLocationHolder
.COMPARISON_STRATEGY_LOCATION
);
7625 Collections
.sort(holders
);
7629 private void clear() {
7635 * Pooled class that holds a View and its location with respect to
7636 * a specified root. This enables sorting of views based on their
7637 * coordinates without recomputing the position relative to the root
7638 * on every comparison.
7640 static class ViewLocationHolder
implements Comparable
<ViewLocationHolder
> {
7642 private static final int MAX_POOL_SIZE
= 32;
7644 private static final SynchronizedPool
<ViewLocationHolder
> sPool
=
7645 new SynchronizedPool
<ViewLocationHolder
>(MAX_POOL_SIZE
);
7647 public static final int COMPARISON_STRATEGY_STRIPE
= 1;
7649 public static final int COMPARISON_STRATEGY_LOCATION
= 2;
7651 private static int sComparisonStrategy
= COMPARISON_STRATEGY_STRIPE
;
7653 private final Rect mLocation
= new Rect();
7657 private int mLayoutDirection
;
7659 public static ViewLocationHolder
obtain(ViewGroup root
, View view
) {
7660 ViewLocationHolder holder
= sPool
.acquire();
7661 if (holder
== null) {
7662 holder
= new ViewLocationHolder();
7664 holder
.init(root
, view
);
7668 public static void setComparisonStrategy(int strategy
) {
7669 sComparisonStrategy
= strategy
;
7672 public void recycle() {
7674 sPool
.release(this);
7678 public int compareTo(ViewLocationHolder another
) {
7679 // This instance is greater than an invalid argument.
7680 if (another
== null) {
7684 if (sComparisonStrategy
== COMPARISON_STRATEGY_STRIPE
) {
7685 // First is above second.
7686 if (mLocation
.bottom
- another
.mLocation
.top
<= 0) {
7689 // First is below second.
7690 if (mLocation
.top
- another
.mLocation
.bottom
>= 0) {
7695 // We are ordering left-to-right, top-to-bottom.
7696 if (mLayoutDirection
== LAYOUT_DIRECTION_LTR
) {
7697 final int leftDifference
= mLocation
.left
- another
.mLocation
.left
;
7698 if (leftDifference
!= 0) {
7699 return leftDifference
;
7702 final int rightDifference
= mLocation
.right
- another
.mLocation
.right
;
7703 if (rightDifference
!= 0) {
7704 return -rightDifference
;
7707 // We are ordering left-to-right, top-to-bottom.
7708 final int topDifference
= mLocation
.top
- another
.mLocation
.top
;
7709 if (topDifference
!= 0) {
7710 return topDifference
;
7712 // Break tie by height.
7713 final int heightDiference
= mLocation
.height() - another
.mLocation
.height();
7714 if (heightDiference
!= 0) {
7715 return -heightDiference
;
7717 // Break tie by width.
7718 final int widthDiference
= mLocation
.width() - another
.mLocation
.width();
7719 if (widthDiference
!= 0) {
7720 return -widthDiference
;
7722 // Just break the tie somehow. The accessibliity ids are unique
7723 // and stable, hence this is deterministic tie breaking.
7724 return mView
.getAccessibilityViewId() - another
.mView
.getAccessibilityViewId();
7727 private void init(ViewGroup root
, View view
) {
7728 Rect viewLocation
= mLocation
;
7729 view
.getDrawingRect(viewLocation
);
7730 root
.offsetDescendantRectToMyCoords(view
, viewLocation
);
7732 mLayoutDirection
= root
.getLayoutDirection();
7735 private void clear() {
7737 mLocation
.set(0, 0, 0, 0);
7741 private static Paint
getDebugPaint() {
7742 if (sDebugPaint
== null) {
7743 sDebugPaint
= new Paint();
7744 sDebugPaint
.setAntiAlias(false);
7749 private static void drawRect(Canvas canvas
, Paint paint
, int x1
, int y1
, int x2
, int y2
) {
7750 if (sDebugLines
== null) {
7751 // TODO: This won't work with multiple UI threads in a single process
7752 sDebugLines
= new float[16];
7755 sDebugLines
[0] = x1
;
7756 sDebugLines
[1] = y1
;
7757 sDebugLines
[2] = x2
;
7758 sDebugLines
[3] = y1
;
7760 sDebugLines
[4] = x2
;
7761 sDebugLines
[5] = y1
;
7762 sDebugLines
[6] = x2
;
7763 sDebugLines
[7] = y2
;
7765 sDebugLines
[8] = x2
;
7766 sDebugLines
[9] = y2
;
7767 sDebugLines
[10] = x1
;
7768 sDebugLines
[11] = y2
;
7770 sDebugLines
[12] = x1
;
7771 sDebugLines
[13] = y2
;
7772 sDebugLines
[14] = x1
;
7773 sDebugLines
[15] = y1
;
7775 canvas
.drawLines(sDebugLines
, paint
);
7780 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder
) {
7781 super.encodeProperties(encoder
);
7783 encoder
.addProperty("focus:descendantFocusability", getDescendantFocusability());
7784 encoder
.addProperty("drawing:clipChildren", getClipChildren());
7785 encoder
.addProperty("drawing:clipToPadding", getClipToPadding());
7786 encoder
.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled());
7787 encoder
.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache());
7789 int n
= getChildCount();
7790 encoder
.addProperty("meta:__childCount__", (short)n
);
7791 for (int i
= 0; i
< n
; i
++) {
7792 encoder
.addPropertyKey("meta:__child__" + i
);
7793 getChildAt(i
).encode(encoder
);