blob: 7935eb1ffc396da520ba743d392e278befb71158 [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
20import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
21import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
22
23import android.animation.LayoutTransition;
24import android.annotation.CallSuper;
25import android.annotation.IdRes;
26import android.annotation.NonNull;
27import android.annotation.Nullable;
28import android.annotation.TestApi;
29import android.annotation.UiThread;
30import android.compat.annotation.UnsupportedAppUsage;
31import android.content.ClipData;
32import android.content.Context;
33import android.content.Intent;
34import android.content.pm.PackageManager;
35import android.content.res.Configuration;
36import android.content.res.TypedArray;
37import android.graphics.Bitmap;
38import android.graphics.Canvas;
39import android.graphics.Color;
40import android.graphics.Insets;
41import android.graphics.Matrix;
42import android.graphics.Paint;
43import android.graphics.Point;
44import android.graphics.PointF;
45import android.graphics.Rect;
46import android.graphics.RectF;
47import android.graphics.Region;
48import android.os.Build;
49import android.os.Bundle;
50import android.os.Parcelable;
51import android.os.SystemClock;
52import android.util.AttributeSet;
53import android.util.Log;
54import android.util.Pools;
55import android.util.Pools.SynchronizedPool;
56import android.util.SparseArray;
57import android.util.SparseBooleanArray;
58import android.view.WindowInsetsAnimation.Bounds;
59import android.view.WindowInsetsAnimation.Callback.DispatchMode;
60import android.view.accessibility.AccessibilityEvent;
61import android.view.accessibility.AccessibilityManager;
62import android.view.accessibility.AccessibilityNodeInfo;
63import android.view.animation.Animation;
64import android.view.animation.AnimationUtils;
65import android.view.animation.LayoutAnimationController;
66import android.view.animation.Transformation;
67import android.view.autofill.Helper;
68import android.view.inspector.InspectableProperty;
69import android.view.inspector.InspectableProperty.EnumEntry;
70
71import com.android.internal.R;
72
73import java.util.ArrayList;
74import java.util.Collection;
75import java.util.Collections;
76import java.util.HashSet;
77import java.util.List;
78import java.util.Map;
79import java.util.Queue;
80import java.util.function.Predicate;
81
82/**
83 * <p>
84 * A <code>ViewGroup</code> is a special view that can contain other views
85 * (called children.) The view group is the base class for layouts and views
86 * containers. This class also defines the
87 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
88 * class for layouts parameters.
89 * </p>
90 *
91 * <p>
92 * Also see {@link LayoutParams} for layout attributes.
93 * </p>
94 *
95 * <div class="special reference">
96 * <h3>Developer Guides</h3>
97 * <p>For more information about creating user interface layouts, read the
98 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
99 * guide.</p></div>
100 *
101 * <p>Here is a complete implementation of a custom ViewGroup that implements
102 * a simple {@link android.widget.FrameLayout} along with the ability to stack
103 * children in left and right gutters.</p>
104 *
105 * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
106 * Complete}
107 *
108 * <p>If you are implementing XML layout attributes as shown in the example, this is the
109 * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
110 *
111 * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
112 *
113 * <p>Finally the layout manager can be used in an XML layout like so:</p>
114 *
115 * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
116 *
117 * @attr ref android.R.styleable#ViewGroup_clipChildren
118 * @attr ref android.R.styleable#ViewGroup_clipToPadding
119 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
120 * @attr ref android.R.styleable#ViewGroup_animationCache
121 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
122 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
123 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
124 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
125 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
126 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
127 * @attr ref android.R.styleable#ViewGroup_layoutMode
128 */
129@UiThread
130public abstract class ViewGroup extends View implements ViewParent, ViewManager {
131 private static final String TAG = "ViewGroup";
132
133 @UnsupportedAppUsage
134 private static final boolean DBG = false;
135
136 /**
137 * Views which have been hidden or removed which need to be animated on
138 * their way out.
139 * This field should be made private, so it is hidden from the SDK.
140 * {@hide}
141 */
142 @UnsupportedAppUsage
143 protected ArrayList<View> mDisappearingChildren;
144
145 /**
146 * Listener used to propagate events indicating when children are added
147 * and/or removed from a view group.
148 * This field should be made private, so it is hidden from the SDK.
149 * {@hide}
150 */
151 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768704)
152 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
153
154 // The view contained within this ViewGroup that has or contains focus.
155 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
156 private View mFocused;
157 // The view contained within this ViewGroup (excluding nested keyboard navigation clusters)
158 // that is or contains a default-focus view.
159 private View mDefaultFocus;
160 // The last child of this ViewGroup which held focus within the current cluster
161 View mFocusedInCluster;
162
163 /**
164 * A Transformation used when drawing children, to
165 * apply on the child being drawn.
166 */
167 private Transformation mChildTransformation;
168
169 /**
170 * Used to track the current invalidation region.
171 */
172 RectF mInvalidateRegion;
173
174 /**
175 * A Transformation used to calculate a correct
176 * invalidation area when the application is autoscaled.
177 */
178 Transformation mInvalidationTransformation;
179
180 // Current frontmost child that can accept drag and lies under the drag location.
181 // Used only to generate ENTER/EXIT events for pre-Nougat aps.
182 private View mCurrentDragChild;
183
184 // Metadata about the ongoing drag
185 private DragEvent mCurrentDragStartEvent;
186 private boolean mIsInterestedInDrag;
187 private HashSet<View> mChildrenInterestedInDrag;
188
189 // Used during drag dispatch
190 private PointF mLocalPoint;
191
192 // Lazily-created holder for point computations.
193 private float[] mTempPosition;
194
195 // Lazily-created holder for point computations.
196 private Point mTempPoint;
197
198 // Lazily created Rect for dispatch to children
199 private Rect mTempRect;
200
201 // Lazily created int[2] for dispatch to children
202 private int[] mTempLocation;
203
204 // Layout animation
205 private LayoutAnimationController mLayoutAnimationController;
206 private Animation.AnimationListener mAnimationListener;
207
208 // First touch target in the linked list of touch targets.
209 @UnsupportedAppUsage
210 private TouchTarget mFirstTouchTarget;
211
212 // For debugging only. You can see these in hierarchyviewer.
213 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
214 @ViewDebug.ExportedProperty(category = "events")
215 private long mLastTouchDownTime;
216 @ViewDebug.ExportedProperty(category = "events")
217 private int mLastTouchDownIndex = -1;
218 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
219 @ViewDebug.ExportedProperty(category = "events")
220 private float mLastTouchDownX;
221 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
222 @ViewDebug.ExportedProperty(category = "events")
223 private float mLastTouchDownY;
224
225 // First hover target in the linked list of hover targets.
226 // The hover targets are children which have received ACTION_HOVER_ENTER.
227 // They might not have actually handled the hover event, but we will
228 // continue sending hover events to them as long as the pointer remains over
229 // their bounds and the view group does not intercept hover.
230 private HoverTarget mFirstHoverTarget;
231
232 // True if the view group itself received a hover event.
233 // It might not have actually handled the hover event.
234 private boolean mHoveredSelf;
235
236 // The child capable of showing a tooltip and currently under the pointer.
237 private View mTooltipHoverTarget;
238
239 // True if the view group is capable of showing a tooltip and the pointer is directly
240 // over the view group but not one of its child views.
241 private boolean mTooltipHoveredSelf;
242
243 /**
244 * Internal flags.
245 *
246 * This field should be made private, so it is hidden from the SDK.
247 * {@hide}
248 */
249 @ViewDebug.ExportedProperty(flagMapping = {
250 @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN,
251 name = "CLIP_CHILDREN"),
252 @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING,
253 name = "CLIP_TO_PADDING"),
254 @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL,
255 name = "PADDING_NOT_NULL")
256 }, formatToHexString = true)
257 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769411)
258 protected int mGroupFlags;
259
260 /**
261 * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
262 */
263 private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
264
265 /**
266 * NOTE: If you change the flags below make sure to reflect the changes
267 * the DisplayList class
268 */
269
270 // When set, ViewGroup invalidates only the child's rectangle
271 // Set by default
272 static final int FLAG_CLIP_CHILDREN = 0x1;
273
274 // When set, ViewGroup excludes the padding area from the invalidate rectangle
275 // Set by default
276 private static final int FLAG_CLIP_TO_PADDING = 0x2;
277
278 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
279 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
280 static final int FLAG_INVALIDATE_REQUIRED = 0x4;
281
282 // When set, dispatchDraw() will run the layout animation and unset the flag
283 private static final int FLAG_RUN_ANIMATION = 0x8;
284
285 // When set, there is either no layout animation on the ViewGroup or the layout
286 // animation is over
287 // Set by default
288 static final int FLAG_ANIMATION_DONE = 0x10;
289
290 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
291 // to clip it, even if FLAG_CLIP_TO_PADDING is set
292 private static final int FLAG_PADDING_NOT_NULL = 0x20;
293
294 /** @deprecated - functionality removed */
295 @Deprecated
296 private static final int FLAG_ANIMATION_CACHE = 0x40;
297
298 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
299 // layout animation; this avoid clobbering the hierarchy
300 // Automatically set when the layout animation starts, depending on the animation's
301 // characteristics
302 static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
303
304 // When set, the next call to drawChild() will clear mChildTransformation's matrix
305 static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
306
307 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
308 // the children's Bitmap caches if necessary
309 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
310 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
311
312 /**
313 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
314 * to get the index of the child to draw for that iteration.
315 *
316 * @hide
317 */
318 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769377)
319 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
320
321 /**
322 * When set, this ViewGroup supports static transformations on children; this causes
323 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
324 * invoked when a child is drawn.
325 *
326 * Any subclass overriding
327 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
328 * set this flags in {@link #mGroupFlags}.
329 *
330 * {@hide}
331 */
332 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769647)
333 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
334
335 // UNUSED FLAG VALUE: 0x1000;
336
337 /**
338 * When set, this ViewGroup's drawable states also include those
339 * of its children.
340 */
341 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
342
343 /** @deprecated functionality removed */
344 @Deprecated
345 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
346
347 /** @deprecated functionality removed */
348 @Deprecated
349 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
350
351 /**
352 * When set, this group will go through its list of children to notify them of
353 * any drawable state change.
354 */
355 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
356
357 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
358
359 /**
360 * This view will get focus before any of its descendants.
361 */
362 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
363
364 /**
365 * This view will get focus only if none of its descendants want it.
366 */
367 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
368
369 /**
370 * This view will block any of its descendants from getting focus, even
371 * if they are focusable.
372 */
373 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
374
375 /**
376 * Used to map between enum in attrubutes and flag values.
377 */
378 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
379 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
380 FOCUS_BLOCK_DESCENDANTS};
381
382 /**
383 * When set, this ViewGroup should not intercept touch events.
384 * {@hide}
385 */
386 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123983692)
387 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
388
389 /**
390 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
391 */
392 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
393
394 /**
395 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
396 * to children when adding new views. This is used to prevent multiple
397 * onAttached calls when a ViewGroup adds children in its own onAttached method.
398 */
399 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
400
401 /**
402 * When true, indicates that a layoutMode has been explicitly set, either with
403 * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
404 * This distinguishes the situation in which a layout mode was inherited from
405 * one of the ViewGroup's ancestors and cached locally.
406 */
407 private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
408
409 static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
410
411 static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
412
413 /**
414 * When set, focus will not be permitted to enter this group if a touchscreen is present.
415 */
416 static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000;
417
418 /**
419 * When true, indicates that a call to startActionModeForChild was made with the type parameter
420 * and should not be ignored. This helps in backwards compatibility with the existing method
421 * without a type.
422 *
423 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
424 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
425 */
426 private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED = 0x8000000;
427
428 /**
429 * When true, indicates that a call to startActionModeForChild was made without the type
430 * parameter. This helps in backwards compatibility with the existing method
431 * without a type.
432 *
433 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
434 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
435 */
436 private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED = 0x10000000;
437
438 /**
439 * When set, indicates that a call to showContextMenuForChild was made with explicit
440 * coordinates within the initiating child view.
441 */
442 private static final int FLAG_SHOW_CONTEXT_MENU_WITH_COORDS = 0x20000000;
443
444 /**
445 * Indicates which types of drawing caches are to be kept in memory.
446 * This field should be made private, so it is hidden from the SDK.
447 * {@hide}
448 */
449 @UnsupportedAppUsage
450 protected int mPersistentDrawingCache;
451
452 /**
453 * Used to indicate that no drawing cache should be kept in memory.
454 *
455 * @deprecated The view drawing cache was largely made obsolete with the introduction of
456 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
457 * layers are largely unnecessary and can easily result in a net loss in performance due to the
458 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
459 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
460 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
461 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
462 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
463 * software-rendered usages are discouraged and have compatibility issues with hardware-only
464 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
465 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
466 * reports or unit testing the {@link PixelCopy} API is recommended.
467 */
468 @Deprecated
469 public static final int PERSISTENT_NO_CACHE = 0x0;
470
471 /**
472 * Used to indicate that the animation drawing cache should be kept in memory.
473 *
474 * @deprecated The view drawing cache was largely made obsolete with the introduction of
475 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
476 * layers are largely unnecessary and can easily result in a net loss in performance due to the
477 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
478 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
479 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
480 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
481 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
482 * software-rendered usages are discouraged and have compatibility issues with hardware-only
483 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
484 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
485 * reports or unit testing the {@link PixelCopy} API is recommended.
486 */
487 @Deprecated
488 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
489
490 /**
491 * Used to indicate that the scrolling drawing cache should be kept in memory.
492 *
493 * @deprecated The view drawing cache was largely made obsolete with the introduction of
494 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
495 * layers are largely unnecessary and can easily result in a net loss in performance due to the
496 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
497 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
498 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
499 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
500 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
501 * software-rendered usages are discouraged and have compatibility issues with hardware-only
502 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
503 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
504 * reports or unit testing the {@link PixelCopy} API is recommended.
505 */
506 @Deprecated
507 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
508
509 /**
510 * Used to indicate that all drawing caches should be kept in memory.
511 *
512 * @deprecated The view drawing cache was largely made obsolete with the introduction of
513 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
514 * layers are largely unnecessary and can easily result in a net loss in performance due to the
515 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
516 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
517 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
518 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
519 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
520 * software-rendered usages are discouraged and have compatibility issues with hardware-only
521 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
522 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
523 * reports or unit testing the {@link PixelCopy} API is recommended.
524 */
525 @Deprecated
526 public static final int PERSISTENT_ALL_CACHES = 0x3;
527
528 // Layout Modes
529
530 private static final int LAYOUT_MODE_UNDEFINED = -1;
531
532 /**
533 * This constant is a {@link #setLayoutMode(int) layoutMode}.
534 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
535 * {@link #getRight() right} and {@link #getBottom() bottom}.
536 */
537 public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
538
539 /**
540 * This constant is a {@link #setLayoutMode(int) layoutMode}.
541 * Optical bounds describe where a widget appears to be. They sit inside the clip
542 * bounds which need to cover a larger area to allow other effects,
543 * such as shadows and glows, to be drawn.
544 */
545 public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
546
547 /** @hide */
548 public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
549
550 /**
551 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
552 * are set at the same time.
553 */
554 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
555
556 // Index of the child's left position in the mLocation array
557 private static final int CHILD_LEFT_INDEX = 0;
558 // Index of the child's top position in the mLocation array
559 private static final int CHILD_TOP_INDEX = 1;
560
561 // Child views of this ViewGroup
562 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
563 private View[] mChildren;
564 // Number of valid children in the mChildren array, the rest should be null or not
565 // considered as children
566 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
567 private int mChildrenCount;
568
569 // Whether layout calls are currently being suppressed, controlled by calls to
570 // suppressLayout()
571 boolean mSuppressLayout = false;
572
573 // Whether any layout calls have actually been suppressed while mSuppressLayout
574 // has been true. This tracks whether we need to issue a requestLayout() when
575 // layout is later re-enabled.
576 private boolean mLayoutCalledWhileSuppressed = false;
577
578 private static final int ARRAY_INITIAL_CAPACITY = 12;
579 private static final int ARRAY_CAPACITY_INCREMENT = 12;
580
581 private static float[] sDebugLines;
582
583 // Used to draw cached views
584 Paint mCachePaint;
585
586 // Used to animate add/remove changes in layout
587 private LayoutTransition mTransition;
588
589 // The set of views that are currently being transitioned. This list is used to track views
590 // being removed that should not actually be removed from the parent yet because they are
591 // being animated.
592 private ArrayList<View> mTransitioningViews;
593
594 // List of children changing visibility. This is used to potentially keep rendering
595 // views during a transition when they otherwise would have become gone/invisible
596 private ArrayList<View> mVisibilityChangingChildren;
597
598 // Temporary holder of presorted children, only used for
599 // input/software draw dispatch for correctly Z ordering.
600 private ArrayList<View> mPreSortedChildren;
601
602 // Indicates how many of this container's child subtrees contain transient state
603 @ViewDebug.ExportedProperty(category = "layout")
604 private int mChildCountWithTransientState = 0;
605
606 /**
607 * Currently registered axes for nested scrolling. Flag set consisting of
608 * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
609 * for null.
610 */
611 private int mNestedScrollAxes;
612
613 // Used to manage the list of transient views, added by addTransientView()
614 private List<Integer> mTransientIndices = null;
615 private List<View> mTransientViews = null;
616
617 /**
618 * Keeps track of how many child views have UnhandledKeyEventListeners. This should only be
619 * updated on the UI thread so shouldn't require explicit synchronization.
620 */
621 int mChildUnhandledKeyListeners = 0;
622
623 /**
624 * Current dispatch mode of animation events
625 *
626 * @see WindowInsetsAnimation.Callback#getDispatchMode()
627 */
628 private @DispatchMode int mInsetsAnimationDispatchMode = DISPATCH_MODE_CONTINUE_ON_SUBTREE;
629
630 /**
631 * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
632 *
633 * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
634 * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
635 */
636 private static final ActionMode SENTINEL_ACTION_MODE = new ActionMode() {
637 @Override
638 public void setTitle(CharSequence title) {}
639
640 @Override
641 public void setTitle(int resId) {}
642
643 @Override
644 public void setSubtitle(CharSequence subtitle) {}
645
646 @Override
647 public void setSubtitle(int resId) {}
648
649 @Override
650 public void setCustomView(View view) {}
651
652 @Override
653 public void invalidate() {}
654
655 @Override
656 public void finish() {}
657
658 @Override
659 public Menu getMenu() {
660 return null;
661 }
662
663 @Override
664 public CharSequence getTitle() {
665 return null;
666 }
667
668 @Override
669 public CharSequence getSubtitle() {
670 return null;
671 }
672
673 @Override
674 public View getCustomView() {
675 return null;
676 }
677
678 @Override
679 public MenuInflater getMenuInflater() {
680 return null;
681 }
682 };
683
684 public ViewGroup(Context context) {
685 this(context, null);
686 }
687
688 public ViewGroup(Context context, AttributeSet attrs) {
689 this(context, attrs, 0);
690 }
691
692 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
693 this(context, attrs, defStyleAttr, 0);
694 }
695
696 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
697 super(context, attrs, defStyleAttr, defStyleRes);
698
699 initViewGroup();
700 initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
701 }
702
703 private void initViewGroup() {
704 // ViewGroup doesn't draw by default
705 if (!isShowingLayoutBounds()) {
706 setFlags(WILL_NOT_DRAW, DRAW_MASK);
707 }
708 mGroupFlags |= FLAG_CLIP_CHILDREN;
709 mGroupFlags |= FLAG_CLIP_TO_PADDING;
710 mGroupFlags |= FLAG_ANIMATION_DONE;
711 mGroupFlags |= FLAG_ANIMATION_CACHE;
712 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
713
714 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
715 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
716 }
717
718 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
719
720 mChildren = new View[ARRAY_INITIAL_CAPACITY];
721 mChildrenCount = 0;
722
723 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
724 }
725
726 private void initFromAttributes(
727 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
728 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup,
729 defStyleAttr, defStyleRes);
730 saveAttributeDataForStyleable(context, R.styleable.ViewGroup, attrs, a, defStyleAttr,
731 defStyleRes);
732
733 final int N = a.getIndexCount();
734 for (int i = 0; i < N; i++) {
735 int attr = a.getIndex(i);
736 switch (attr) {
737 case R.styleable.ViewGroup_clipChildren:
738 setClipChildren(a.getBoolean(attr, true));
739 break;
740 case R.styleable.ViewGroup_clipToPadding:
741 setClipToPadding(a.getBoolean(attr, true));
742 break;
743 case R.styleable.ViewGroup_animationCache:
744 setAnimationCacheEnabled(a.getBoolean(attr, true));
745 break;
746 case R.styleable.ViewGroup_persistentDrawingCache:
747 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
748 break;
749 case R.styleable.ViewGroup_addStatesFromChildren:
750 setAddStatesFromChildren(a.getBoolean(attr, false));
751 break;
752 case R.styleable.ViewGroup_alwaysDrawnWithCache:
753 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
754 break;
755 case R.styleable.ViewGroup_layoutAnimation:
756 int id = a.getResourceId(attr, -1);
757 if (id > 0) {
758 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
759 }
760 break;
761 case R.styleable.ViewGroup_descendantFocusability:
762 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
763 break;
764 case R.styleable.ViewGroup_splitMotionEvents:
765 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
766 break;
767 case R.styleable.ViewGroup_animateLayoutChanges:
768 boolean animateLayoutChanges = a.getBoolean(attr, false);
769 if (animateLayoutChanges) {
770 setLayoutTransition(new LayoutTransition());
771 }
772 break;
773 case R.styleable.ViewGroup_layoutMode:
774 setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
775 break;
776 case R.styleable.ViewGroup_transitionGroup:
777 setTransitionGroup(a.getBoolean(attr, false));
778 break;
779 case R.styleable.ViewGroup_touchscreenBlocksFocus:
780 setTouchscreenBlocksFocus(a.getBoolean(attr, false));
781 break;
782 }
783 }
784
785 a.recycle();
786 }
787
788 /**
789 * Gets the descendant focusability of this view group. The descendant
790 * focusability defines the relationship between this view group and its
791 * descendants when looking for a view to take focus in
792 * {@link #requestFocus(int, android.graphics.Rect)}.
793 *
794 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
795 * {@link #FOCUS_BLOCK_DESCENDANTS}.
796 */
797 @ViewDebug.ExportedProperty(category = "focus", mapping = {
798 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
799 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
800 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
801 })
802 @InspectableProperty(enumMapping = {
803 @EnumEntry(value = FOCUS_BEFORE_DESCENDANTS, name = "beforeDescendants"),
804 @EnumEntry(value = FOCUS_AFTER_DESCENDANTS, name = "afterDescendants"),
805 @EnumEntry(value = FOCUS_BLOCK_DESCENDANTS, name = "blocksDescendants")
806 })
807 public int getDescendantFocusability() {
808 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
809 }
810
811 /**
812 * Set the descendant focusability of this view group. This defines the relationship
813 * between this view group and its descendants when looking for a view to
814 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
815 *
816 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
817 * {@link #FOCUS_BLOCK_DESCENDANTS}.
818 */
819 public void setDescendantFocusability(int focusability) {
820 switch (focusability) {
821 case FOCUS_BEFORE_DESCENDANTS:
822 case FOCUS_AFTER_DESCENDANTS:
823 case FOCUS_BLOCK_DESCENDANTS:
824 break;
825 default:
826 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
827 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
828 }
829 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
830 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
831 }
832
833 @Override
834 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
835 if (mFocused != null) {
836 mFocused.unFocus(this);
837 mFocused = null;
838 mFocusedInCluster = null;
839 }
840 super.handleFocusGainInternal(direction, previouslyFocusedRect);
841 }
842
843 @Override
844 public void requestChildFocus(View child, View focused) {
845 if (DBG) {
846 System.out.println(this + " requestChildFocus()");
847 }
848 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
849 return;
850 }
851
852 // Unfocus us, if necessary
853 super.unFocus(focused);
854
855 // We had a previous notion of who had focus. Clear it.
856 if (mFocused != child) {
857 if (mFocused != null) {
858 mFocused.unFocus(focused);
859 }
860
861 mFocused = child;
862 }
863 if (mParent != null) {
864 mParent.requestChildFocus(this, focused);
865 }
866 }
867
868 void setDefaultFocus(View child) {
869 // Stop at any higher view which is explicitly focused-by-default
870 if (mDefaultFocus != null && mDefaultFocus.isFocusedByDefault()) {
871 return;
872 }
873
874 mDefaultFocus = child;
875
876 if (mParent instanceof ViewGroup) {
877 ((ViewGroup) mParent).setDefaultFocus(this);
878 }
879 }
880
881 /**
882 * Clears the default-focus chain from {@param child} up to the first parent which has another
883 * default-focusable branch below it or until there is no default-focus chain.
884 *
885 * @param child
886 */
887 void clearDefaultFocus(View child) {
888 // Stop at any higher view which is explicitly focused-by-default
889 if (mDefaultFocus != child && mDefaultFocus != null
890 && mDefaultFocus.isFocusedByDefault()) {
891 return;
892 }
893
894 mDefaultFocus = null;
895
896 // Search child siblings for default focusables.
897 for (int i = 0; i < mChildrenCount; ++i) {
898 View sibling = mChildren[i];
899 if (sibling.isFocusedByDefault()) {
900 mDefaultFocus = sibling;
901 return;
902 } else if (mDefaultFocus == null && sibling.hasDefaultFocus()) {
903 mDefaultFocus = sibling;
904 }
905 }
906
907 if (mParent instanceof ViewGroup) {
908 ((ViewGroup) mParent).clearDefaultFocus(this);
909 }
910 }
911
912 @Override
913 boolean hasDefaultFocus() {
914 return mDefaultFocus != null || super.hasDefaultFocus();
915 }
916
917 /**
918 * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing
919 * it.
920 * <br>
921 * This is intended to be run on {@code child}'s immediate parent. This is necessary because
922 * the chain is sometimes cleared after {@code child} has been detached.
923 */
924 void clearFocusedInCluster(View child) {
925 if (mFocusedInCluster != child) {
926 return;
927 }
928 clearFocusedInCluster();
929 }
930
931 /**
932 * Removes the focusedInCluster chain from this up to the cluster containing it.
933 */
934 void clearFocusedInCluster() {
935 View top = findKeyboardNavigationCluster();
936 ViewParent parent = this;
937 do {
938 ((ViewGroup) parent).mFocusedInCluster = null;
939 if (parent == top) {
940 break;
941 }
942 parent = parent.getParent();
943 } while (parent instanceof ViewGroup);
944 }
945
946 @Override
947 public void focusableViewAvailable(View v) {
948 if (mParent != null
949 // shortcut: don't report a new focusable view if we block our descendants from
950 // getting focus or if we're not visible
951 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
952 && ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
953 && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
954 // shortcut: don't report a new focusable view if we already are focused
955 // (and we don't prefer our descendants)
956 //
957 // note: knowing that mFocused is non-null is not a good enough reason
958 // to break the traversal since in that case we'd actually have to find
959 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
960 // an ancestor of v; this will get checked for at ViewAncestor
961 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
962 mParent.focusableViewAvailable(v);
963 }
964 }
965
966 @Override
967 public boolean showContextMenuForChild(View originalView) {
968 if (isShowingContextMenuWithCoords()) {
969 // We're being called for compatibility. Return false and let the version
970 // with coordinates recurse up.
971 return false;
972 }
973 return mParent != null && mParent.showContextMenuForChild(originalView);
974 }
975
976 /**
977 * @hide used internally for compatibility with existing app code only
978 */
979 public final boolean isShowingContextMenuWithCoords() {
980 return (mGroupFlags & FLAG_SHOW_CONTEXT_MENU_WITH_COORDS) != 0;
981 }
982
983 @Override
984 public boolean showContextMenuForChild(View originalView, float x, float y) {
985 try {
986 mGroupFlags |= FLAG_SHOW_CONTEXT_MENU_WITH_COORDS;
987 if (showContextMenuForChild(originalView)) {
988 return true;
989 }
990 } finally {
991 mGroupFlags &= ~FLAG_SHOW_CONTEXT_MENU_WITH_COORDS;
992 }
993 return mParent != null && mParent.showContextMenuForChild(originalView, x, y);
994 }
995
996 @Override
997 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
998 if ((mGroupFlags & FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED) == 0) {
999 // This is the original call.
1000 try {
1001 mGroupFlags |= FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED;
1002 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
1003 } finally {
1004 mGroupFlags &= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED;
1005 }
1006 } else {
1007 // We are being called from the new method with type.
1008 return SENTINEL_ACTION_MODE;
1009 }
1010 }
1011
1012 @Override
1013 public ActionMode startActionModeForChild(
1014 View originalView, ActionMode.Callback callback, int type) {
1015 if ((mGroupFlags & FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED) == 0
1016 && type == ActionMode.TYPE_PRIMARY) {
1017 ActionMode mode;
1018 try {
1019 mGroupFlags |= FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED;
1020 mode = startActionModeForChild(originalView, callback);
1021 } finally {
1022 mGroupFlags &= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED;
1023 }
1024 if (mode != SENTINEL_ACTION_MODE) {
1025 return mode;
1026 }
1027 }
1028 if (mParent != null) {
1029 try {
1030 return mParent.startActionModeForChild(originalView, callback, type);
1031 } catch (AbstractMethodError ame) {
1032 // Custom view parents might not implement this method.
1033 return mParent.startActionModeForChild(originalView, callback);
1034 }
1035 }
1036 return null;
1037 }
1038
1039 /**
1040 * @hide
1041 */
1042 @Override
1043 public boolean dispatchActivityResult(
1044 String who, int requestCode, int resultCode, Intent data) {
1045 if (super.dispatchActivityResult(who, requestCode, resultCode, data)) {
1046 return true;
1047 }
1048 int childCount = getChildCount();
1049 for (int i = 0; i < childCount; i++) {
1050 View child = getChildAt(i);
1051 if (child.dispatchActivityResult(who, requestCode, resultCode, data)) {
1052 return true;
1053 }
1054 }
1055 return false;
1056 }
1057
1058 /**
1059 * Find the nearest view in the specified direction that wants to take
1060 * focus.
1061 *
1062 * @param focused The view that currently has focus
1063 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
1064 * FOCUS_RIGHT, or 0 for not applicable.
1065 */
1066 @Override
1067 public View focusSearch(View focused, int direction) {
1068 if (isRootNamespace()) {
1069 // root namespace means we should consider ourselves the top of the
1070 // tree for focus searching; otherwise we could be focus searching
1071 // into other tabs. see LocalActivityManager and TabHost for more info.
1072 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
1073 } else if (mParent != null) {
1074 return mParent.focusSearch(focused, direction);
1075 }
1076 return null;
1077 }
1078
1079 @Override
1080 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
1081 return false;
1082 }
1083
1084 @Override
1085 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
1086 ViewParent parent = mParent;
1087 if (parent == null) {
1088 return false;
1089 }
1090 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
1091 if (!propagate) {
1092 return false;
1093 }
1094 return parent.requestSendAccessibilityEvent(this, event);
1095 }
1096
1097 /**
1098 * Called when a child has requested sending an {@link AccessibilityEvent} and
1099 * gives an opportunity to its parent to augment the event.
1100 * <p>
1101 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
1102 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
1103 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
1104 * is responsible for handling this call.
1105 * </p>
1106 *
1107 * @param child The child which requests sending the event.
1108 * @param event The event to be sent.
1109 * @return True if the event should be sent.
1110 *
1111 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
1112 */
1113 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
1114 if (mAccessibilityDelegate != null) {
1115 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
1116 } else {
1117 return onRequestSendAccessibilityEventInternal(child, event);
1118 }
1119 }
1120
1121 /**
1122 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
1123 *
1124 * Note: Called from the default {@link View.AccessibilityDelegate}.
1125 *
1126 * @hide
1127 */
1128 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1129 return true;
1130 }
1131
1132 /**
1133 * Called when a child view has changed whether or not it is tracking transient state.
1134 */
1135 @Override
1136 public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
1137 final boolean oldHasTransientState = hasTransientState();
1138 if (childHasTransientState) {
1139 mChildCountWithTransientState++;
1140 } else {
1141 mChildCountWithTransientState--;
1142 }
1143
1144 final boolean newHasTransientState = hasTransientState();
1145 if (mParent != null && oldHasTransientState != newHasTransientState) {
1146 try {
1147 mParent.childHasTransientStateChanged(this, newHasTransientState);
1148 } catch (AbstractMethodError e) {
1149 Log.e(TAG, mParent.getClass().getSimpleName() +
1150 " does not fully implement ViewParent", e);
1151 }
1152 }
1153 }
1154
1155 @Override
1156 public boolean hasTransientState() {
1157 return mChildCountWithTransientState > 0 || super.hasTransientState();
1158 }
1159
1160 @Override
1161 public boolean dispatchUnhandledMove(View focused, int direction) {
1162 return mFocused != null &&
1163 mFocused.dispatchUnhandledMove(focused, direction);
1164 }
1165
1166 @Override
1167 public void clearChildFocus(View child) {
1168 if (DBG) {
1169 System.out.println(this + " clearChildFocus()");
1170 }
1171
1172 mFocused = null;
1173 if (mParent != null) {
1174 mParent.clearChildFocus(this);
1175 }
1176 }
1177
1178 @Override
1179 public void clearFocus() {
1180 if (DBG) {
1181 System.out.println(this + " clearFocus()");
1182 }
1183 if (mFocused == null) {
1184 super.clearFocus();
1185 } else {
1186 View focused = mFocused;
1187 mFocused = null;
1188 focused.clearFocus();
1189 }
1190 }
1191
1192 @Override
1193 void unFocus(View focused) {
1194 if (DBG) {
1195 System.out.println(this + " unFocus()");
1196 }
1197 if (mFocused == null) {
1198 super.unFocus(focused);
1199 } else {
1200 mFocused.unFocus(focused);
1201 mFocused = null;
1202 }
1203 }
1204
1205 /**
1206 * Returns the focused child of this view, if any. The child may have focus
1207 * or contain focus.
1208 *
1209 * @return the focused child or null.
1210 */
1211 public View getFocusedChild() {
1212 return mFocused;
1213 }
1214
1215 View getDeepestFocusedChild() {
1216 View v = this;
1217 while (v != null) {
1218 if (v.isFocused()) {
1219 return v;
1220 }
1221 v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
1222 }
1223 return null;
1224 }
1225
1226 /**
1227 * Returns true if this view has or contains focus
1228 *
1229 * @return true if this view has or contains focus
1230 */
1231 @Override
1232 public boolean hasFocus() {
1233 return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
1234 }
1235
1236 /*
1237 * (non-Javadoc)
1238 *
1239 * @see android.view.View#findFocus()
1240 */
1241 @Override
1242 public View findFocus() {
1243 if (DBG) {
1244 System.out.println("Find focus in " + this + ": flags="
1245 + isFocused() + ", child=" + mFocused);
1246 }
1247
1248 if (isFocused()) {
1249 return this;
1250 }
1251
1252 if (mFocused != null) {
1253 return mFocused.findFocus();
1254 }
1255 return null;
1256 }
1257
1258 @Override
1259 boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) {
1260 // This should probably be super.hasFocusable, but that would change
1261 // behavior. Historically, we have not checked the ancestor views for
1262 // shouldBlockFocusForTouchscreen() in ViewGroup.hasFocusable.
1263
1264 // Invisible and gone views are never focusable.
1265 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
1266 return false;
1267 }
1268
1269 // Only use effective focusable value when allowed.
1270 if ((allowAutoFocus || getFocusable() != FOCUSABLE_AUTO) && isFocusable()) {
1271 return true;
1272 }
1273
1274 // Determine whether we have a focused descendant.
1275 final int descendantFocusability = getDescendantFocusability();
1276 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1277 return hasFocusableChild(dispatchExplicit);
1278 }
1279
1280 return false;
1281 }
1282
1283 boolean hasFocusableChild(boolean dispatchExplicit) {
1284 // Determine whether we have a focusable descendant.
1285 final int count = mChildrenCount;
1286 final View[] children = mChildren;
1287
1288 for (int i = 0; i < count; i++) {
1289 final View child = children[i];
1290
1291 // In case the subclass has overridden has[Explicit]Focusable, dispatch
1292 // to the expected one for each child even though we share logic here.
1293 if ((dispatchExplicit && child.hasExplicitFocusable())
1294 || (!dispatchExplicit && child.hasFocusable())) {
1295 return true;
1296 }
1297 }
1298
1299 return false;
1300 }
1301
1302 @Override
1303 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1304 final int focusableCount = views.size();
1305
1306 final int descendantFocusability = getDescendantFocusability();
1307 final boolean blockFocusForTouchscreen = shouldBlockFocusForTouchscreen();
1308 final boolean focusSelf = (isFocusableInTouchMode() || !blockFocusForTouchscreen);
1309
1310 if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {
1311 if (focusSelf) {
1312 super.addFocusables(views, direction, focusableMode);
1313 }
1314 return;
1315 }
1316
1317 if (blockFocusForTouchscreen) {
1318 focusableMode |= FOCUSABLES_TOUCH_MODE;
1319 }
1320
1321 if ((descendantFocusability == FOCUS_BEFORE_DESCENDANTS) && focusSelf) {
1322 super.addFocusables(views, direction, focusableMode);
1323 }
1324
1325 int count = 0;
1326 final View[] children = new View[mChildrenCount];
1327 for (int i = 0; i < mChildrenCount; ++i) {
1328 View child = mChildren[i];
1329 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1330 children[count++] = child;
1331 }
1332 }
1333 FocusFinder.sort(children, 0, count, this, isLayoutRtl());
1334 for (int i = 0; i < count; ++i) {
1335 children[i].addFocusables(views, direction, focusableMode);
1336 }
1337
1338 // When set to FOCUS_AFTER_DESCENDANTS, we only add ourselves if
1339 // there aren't any focusable descendants. this is
1340 // to avoid the focus search finding layouts when a more precise search
1341 // among the focusable children would be more interesting.
1342 if ((descendantFocusability == FOCUS_AFTER_DESCENDANTS) && focusSelf
1343 && focusableCount == views.size()) {
1344 super.addFocusables(views, direction, focusableMode);
1345 }
1346 }
1347
1348 @Override
1349 public void addKeyboardNavigationClusters(Collection<View> views, int direction) {
1350 final int focusableCount = views.size();
1351
1352 if (isKeyboardNavigationCluster()) {
1353 // Cluster-navigation can enter a touchscreenBlocksFocus cluster, so temporarily
1354 // disable touchscreenBlocksFocus to evaluate whether it contains focusables.
1355 final boolean blockedFocus = getTouchscreenBlocksFocus();
1356 try {
1357 setTouchscreenBlocksFocusNoRefocus(false);
1358 super.addKeyboardNavigationClusters(views, direction);
1359 } finally {
1360 setTouchscreenBlocksFocusNoRefocus(blockedFocus);
1361 }
1362 } else {
1363 super.addKeyboardNavigationClusters(views, direction);
1364 }
1365
1366 if (focusableCount != views.size()) {
1367 // No need to look for groups inside a group.
1368 return;
1369 }
1370
1371 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
1372 return;
1373 }
1374
1375 int count = 0;
1376 final View[] visibleChildren = new View[mChildrenCount];
1377 for (int i = 0; i < mChildrenCount; ++i) {
1378 final View child = mChildren[i];
1379 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1380 visibleChildren[count++] = child;
1381 }
1382 }
1383 FocusFinder.sort(visibleChildren, 0, count, this, isLayoutRtl());
1384 for (int i = 0; i < count; ++i) {
1385 visibleChildren[i].addKeyboardNavigationClusters(views, direction);
1386 }
1387 }
1388
1389 /**
1390 * Set whether this ViewGroup should ignore focus requests for itself and its children.
1391 * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
1392 * will proceed forward.
1393 *
1394 * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
1395 */
1396 public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
1397 if (touchscreenBlocksFocus) {
1398 mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1399 if (hasFocus() && !isKeyboardNavigationCluster()) {
1400 final View focusedChild = getDeepestFocusedChild();
1401 if (!focusedChild.isFocusableInTouchMode()) {
1402 final View newFocus = focusSearch(FOCUS_FORWARD);
1403 if (newFocus != null) {
1404 newFocus.requestFocus();
1405 }
1406 }
1407 }
1408 } else {
1409 mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1410 }
1411 }
1412
1413 private void setTouchscreenBlocksFocusNoRefocus(boolean touchscreenBlocksFocus) {
1414 if (touchscreenBlocksFocus) {
1415 mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1416 } else {
1417 mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1418 }
1419 }
1420
1421 /**
1422 * Check whether this ViewGroup should ignore focus requests for itself and its children.
1423 */
1424 @ViewDebug.ExportedProperty(category = "focus")
1425 @InspectableProperty
1426 public boolean getTouchscreenBlocksFocus() {
1427 return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
1428 }
1429
1430 boolean shouldBlockFocusForTouchscreen() {
1431 // There is a special case for keyboard-navigation clusters. We allow cluster navigation
1432 // to jump into blockFocusForTouchscreen ViewGroups which are clusters. Once in the
1433 // cluster, focus is free to move around within it.
1434 return getTouchscreenBlocksFocus() &&
1435 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
1436 && !(isKeyboardNavigationCluster()
1437 && (hasFocus() || (findKeyboardNavigationCluster() != this)));
1438 }
1439
1440 @Override
1441 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
1442 super.findViewsWithText(outViews, text, flags);
1443 final int childrenCount = mChildrenCount;
1444 final View[] children = mChildren;
1445 for (int i = 0; i < childrenCount; i++) {
1446 View child = children[i];
1447 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1448 && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
1449 child.findViewsWithText(outViews, text, flags);
1450 }
1451 }
1452 }
1453
1454 /** @hide */
1455 @Override
1456 public View findViewByAccessibilityIdTraversal(int accessibilityId) {
1457 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
1458 if (foundView != null) {
1459 return foundView;
1460 }
1461
1462 if (getAccessibilityNodeProvider() != null) {
1463 return null;
1464 }
1465
1466 final int childrenCount = mChildrenCount;
1467 final View[] children = mChildren;
1468 for (int i = 0; i < childrenCount; i++) {
1469 View child = children[i];
1470 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
1471 if (foundView != null) {
1472 return foundView;
1473 }
1474 }
1475
1476 return null;
1477 }
1478
1479 /** @hide */
1480 @Override
1481 public View findViewByAutofillIdTraversal(int autofillId) {
1482 View foundView = super.findViewByAutofillIdTraversal(autofillId);
1483 if (foundView != null) {
1484 return foundView;
1485 }
1486
1487 final int childrenCount = mChildrenCount;
1488 final View[] children = mChildren;
1489 for (int i = 0; i < childrenCount; i++) {
1490 View child = children[i];
1491 foundView = child.findViewByAutofillIdTraversal(autofillId);
1492 if (foundView != null) {
1493 return foundView;
1494 }
1495 }
1496
1497 return null;
1498 }
1499
1500 @Override
1501 public void dispatchWindowFocusChanged(boolean hasFocus) {
1502 super.dispatchWindowFocusChanged(hasFocus);
1503 final int count = mChildrenCount;
1504 final View[] children = mChildren;
1505 for (int i = 0; i < count; i++) {
1506 children[i].dispatchWindowFocusChanged(hasFocus);
1507 }
1508 }
1509
1510 @Override
1511 public void addTouchables(ArrayList<View> views) {
1512 super.addTouchables(views);
1513
1514 final int count = mChildrenCount;
1515 final View[] children = mChildren;
1516
1517 for (int i = 0; i < count; i++) {
1518 final View child = children[i];
1519 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1520 child.addTouchables(views);
1521 }
1522 }
1523 }
1524
1525 /**
1526 * @hide
1527 */
1528 @Override
1529 @UnsupportedAppUsage
1530 public void makeOptionalFitsSystemWindows() {
1531 super.makeOptionalFitsSystemWindows();
1532 final int count = mChildrenCount;
1533 final View[] children = mChildren;
1534 for (int i = 0; i < count; i++) {
1535 children[i].makeOptionalFitsSystemWindows();
1536 }
1537 }
1538
1539 /**
1540 * @hide
1541 */
1542 @Override
1543 public void makeFrameworkOptionalFitsSystemWindows() {
1544 super.makeFrameworkOptionalFitsSystemWindows();
1545 final int count = mChildrenCount;
1546 final View[] children = mChildren;
1547 for (int i = 0; i < count; i++) {
1548 children[i].makeFrameworkOptionalFitsSystemWindows();
1549 }
1550 }
1551
1552 @Override
1553 public void dispatchDisplayHint(int hint) {
1554 super.dispatchDisplayHint(hint);
1555 final int count = mChildrenCount;
1556 final View[] children = mChildren;
1557 for (int i = 0; i < count; i++) {
1558 children[i].dispatchDisplayHint(hint);
1559 }
1560 }
1561
1562 /**
1563 * Called when a view's visibility has changed. Notify the parent to take any appropriate
1564 * action.
1565 *
1566 * @param child The view whose visibility has changed
1567 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
1568 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
1569 * @hide
1570 */
1571 @UnsupportedAppUsage
1572 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1573 if (mTransition != null) {
1574 if (newVisibility == VISIBLE) {
1575 mTransition.showChild(this, child, oldVisibility);
1576 } else {
1577 mTransition.hideChild(this, child, newVisibility);
1578 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
1579 // Only track this on disappearing views - appearing views are already visible
1580 // and don't need special handling during drawChild()
1581 if (mVisibilityChangingChildren == null) {
1582 mVisibilityChangingChildren = new ArrayList<View>();
1583 }
1584 mVisibilityChangingChildren.add(child);
1585 addDisappearingView(child);
1586 }
1587 }
1588 }
1589
1590 // in all cases, for drags
1591 if (newVisibility == VISIBLE && mCurrentDragStartEvent != null) {
1592 if (!mChildrenInterestedInDrag.contains(child)) {
1593 notifyChildOfDragStart(child);
1594 }
1595 }
1596 }
1597
1598 @Override
1599 protected void dispatchVisibilityChanged(View changedView, int visibility) {
1600 super.dispatchVisibilityChanged(changedView, visibility);
1601 final int count = mChildrenCount;
1602 final View[] children = mChildren;
1603 for (int i = 0; i < count; i++) {
1604 children[i].dispatchVisibilityChanged(changedView, visibility);
1605 }
1606 }
1607
1608 @Override
1609 public void dispatchWindowVisibilityChanged(int visibility) {
1610 super.dispatchWindowVisibilityChanged(visibility);
1611 final int count = mChildrenCount;
1612 final View[] children = mChildren;
1613 for (int i = 0; i < count; i++) {
1614 children[i].dispatchWindowVisibilityChanged(visibility);
1615 }
1616 }
1617
1618 @Override
1619 boolean dispatchVisibilityAggregated(boolean isVisible) {
1620 isVisible = super.dispatchVisibilityAggregated(isVisible);
1621 final int count = mChildrenCount;
1622 final View[] children = mChildren;
1623 for (int i = 0; i < count; i++) {
1624 // Only dispatch to visible children. Not visible children and their subtrees already
1625 // know that they aren't visible and that's not going to change as a result of
1626 // whatever triggered this dispatch.
1627 if (children[i].getVisibility() == VISIBLE) {
1628 children[i].dispatchVisibilityAggregated(isVisible);
1629 }
1630 }
1631 return isVisible;
1632 }
1633
1634 @Override
1635 public void dispatchConfigurationChanged(Configuration newConfig) {
1636 super.dispatchConfigurationChanged(newConfig);
1637 final int count = mChildrenCount;
1638 final View[] children = mChildren;
1639 for (int i = 0; i < count; i++) {
1640 children[i].dispatchConfigurationChanged(newConfig);
1641 }
1642 }
1643
1644 @Override
1645 public void recomputeViewAttributes(View child) {
1646 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1647 ViewParent parent = mParent;
1648 if (parent != null) parent.recomputeViewAttributes(this);
1649 }
1650 }
1651
1652 @Override
1653 void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
1654 if ((visibility & VISIBILITY_MASK) == VISIBLE) {
1655 super.dispatchCollectViewAttributes(attachInfo, visibility);
1656 final int count = mChildrenCount;
1657 final View[] children = mChildren;
1658 for (int i = 0; i < count; i++) {
1659 final View child = children[i];
1660 child.dispatchCollectViewAttributes(attachInfo,
1661 visibility | (child.mViewFlags&VISIBILITY_MASK));
1662 }
1663 }
1664 }
1665
1666 @Override
1667 public void bringChildToFront(View child) {
1668 final int index = indexOfChild(child);
1669 if (index >= 0) {
1670 removeFromArray(index);
1671 addInArray(child, mChildrenCount);
1672 child.mParent = this;
1673 requestLayout();
1674 invalidate();
1675 }
1676 }
1677
1678 private PointF getLocalPoint() {
1679 if (mLocalPoint == null) mLocalPoint = new PointF();
1680 return mLocalPoint;
1681 }
1682
1683 @Override
1684 boolean dispatchDragEnterExitInPreN(DragEvent event) {
1685 if (event.mAction == DragEvent.ACTION_DRAG_EXITED && mCurrentDragChild != null) {
1686 // The drag exited a sub-tree of views; notify of the exit all descendants that are in
1687 // entered state.
1688 // We don't need this recursive delivery for ENTERED events because they get generated
1689 // from the recursive delivery of LOCATION/DROP events, and hence, don't need their own
1690 // recursion.
1691 mCurrentDragChild.dispatchDragEnterExitInPreN(event);
1692 mCurrentDragChild = null;
1693 }
1694 return mIsInterestedInDrag && super.dispatchDragEnterExitInPreN(event);
1695 }
1696
1697 // TODO: Write real docs
1698 @Override
1699 public boolean dispatchDragEvent(DragEvent event) {
1700 boolean retval = false;
1701 final float tx = event.mX;
1702 final float ty = event.mY;
1703 final ClipData td = event.mClipData;
1704
1705 // Dispatch down the view hierarchy
1706 final PointF localPoint = getLocalPoint();
1707
1708 switch (event.mAction) {
1709 case DragEvent.ACTION_DRAG_STARTED: {
1710 // Clear the state to recalculate which views we drag over.
1711 mCurrentDragChild = null;
1712
1713 // Set up our tracking of drag-started notifications
1714 mCurrentDragStartEvent = DragEvent.obtain(event);
1715 if (mChildrenInterestedInDrag == null) {
1716 mChildrenInterestedInDrag = new HashSet<View>();
1717 } else {
1718 mChildrenInterestedInDrag.clear();
1719 }
1720
1721 // Now dispatch down to our children, caching the responses
1722 final int count = mChildrenCount;
1723 final View[] children = mChildren;
1724 for (int i = 0; i < count; i++) {
1725 final View child = children[i];
1726 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1727 if (child.getVisibility() == VISIBLE) {
1728 if (notifyChildOfDragStart(children[i])) {
1729 retval = true;
1730 }
1731 }
1732 }
1733
1734 // Notify itself of the drag start.
1735 mIsInterestedInDrag = super.dispatchDragEvent(event);
1736 if (mIsInterestedInDrag) {
1737 retval = true;
1738 }
1739
1740 if (!retval) {
1741 // Neither us nor any of our children are interested in this drag, so stop tracking
1742 // the current drag event.
1743 mCurrentDragStartEvent.recycle();
1744 mCurrentDragStartEvent = null;
1745 }
1746 } break;
1747
1748 case DragEvent.ACTION_DRAG_ENDED: {
1749 // Release the bookkeeping now that the drag lifecycle has ended
1750 final HashSet<View> childrenInterestedInDrag = mChildrenInterestedInDrag;
1751 if (childrenInterestedInDrag != null) {
1752 for (View child : childrenInterestedInDrag) {
1753 // If a child was interested in the ongoing drag, it's told that it's over
1754 if (child.dispatchDragEvent(event)) {
1755 retval = true;
1756 }
1757 }
1758 childrenInterestedInDrag.clear();
1759 }
1760 if (mCurrentDragStartEvent != null) {
1761 mCurrentDragStartEvent.recycle();
1762 mCurrentDragStartEvent = null;
1763 }
1764
1765 if (mIsInterestedInDrag) {
1766 if (super.dispatchDragEvent(event)) {
1767 retval = true;
1768 }
1769 mIsInterestedInDrag = false;
1770 }
1771 } break;
1772
1773 case DragEvent.ACTION_DRAG_LOCATION:
1774 case DragEvent.ACTION_DROP: {
1775 // Find the [possibly new] drag target
1776 View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1777
1778 if (target != mCurrentDragChild) {
1779 if (sCascadedDragDrop) {
1780 // For pre-Nougat apps, make sure that the whole hierarchy of views that contain
1781 // the drag location is kept in the state between ENTERED and EXITED events.
1782 // (Starting with N, only the innermost view will be in that state).
1783
1784 final int action = event.mAction;
1785 // Position should not be available for ACTION_DRAG_ENTERED and
1786 // ACTION_DRAG_EXITED.
1787 event.mX = 0;
1788 event.mY = 0;
1789 event.mClipData = null;
1790
1791 if (mCurrentDragChild != null) {
1792 event.mAction = DragEvent.ACTION_DRAG_EXITED;
1793 mCurrentDragChild.dispatchDragEnterExitInPreN(event);
1794 }
1795
1796 if (target != null) {
1797 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1798 target.dispatchDragEnterExitInPreN(event);
1799 }
1800
1801 event.mAction = action;
1802 event.mX = tx;
1803 event.mY = ty;
1804 event.mClipData = td;
1805 }
1806 mCurrentDragChild = target;
1807 }
1808
1809 if (target == null && mIsInterestedInDrag) {
1810 target = this;
1811 }
1812
1813 // Dispatch the actual drag notice, localized into the target coordinates.
1814 if (target != null) {
1815 if (target != this) {
1816 event.mX = localPoint.x;
1817 event.mY = localPoint.y;
1818
1819 retval = target.dispatchDragEvent(event);
1820
1821 event.mX = tx;
1822 event.mY = ty;
1823
1824 if (mIsInterestedInDrag) {
1825 final boolean eventWasConsumed;
1826 if (sCascadedDragDrop) {
1827 eventWasConsumed = retval;
1828 } else {
1829 eventWasConsumed = event.mEventHandlerWasCalled;
1830 }
1831
1832 if (!eventWasConsumed) {
1833 retval = super.dispatchDragEvent(event);
1834 }
1835 }
1836 } else {
1837 retval = super.dispatchDragEvent(event);
1838 }
1839 }
1840 } break;
1841 }
1842
1843 return retval;
1844 }
1845
1846 // Find the frontmost child view that lies under the given point, and calculate
1847 // the position within its own local coordinate system.
1848 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
1849 final int count = mChildrenCount;
1850 final View[] children = mChildren;
1851 for (int i = count - 1; i >= 0; i--) {
1852 final View child = children[i];
1853 if (!child.canAcceptDrag()) {
1854 continue;
1855 }
1856
1857 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1858 return child;
1859 }
1860 }
1861 return null;
1862 }
1863
1864 boolean notifyChildOfDragStart(View child) {
1865 // The caller guarantees that the child is not in mChildrenInterestedInDrag yet.
1866
1867 if (ViewDebug.DEBUG_DRAG) {
1868 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1869 }
1870
1871 final float tx = mCurrentDragStartEvent.mX;
1872 final float ty = mCurrentDragStartEvent.mY;
1873
1874 final float[] point = getTempLocationF();
1875 point[0] = tx;
1876 point[1] = ty;
1877 transformPointToViewLocal(point, child);
1878
1879 mCurrentDragStartEvent.mX = point[0];
1880 mCurrentDragStartEvent.mY = point[1];
1881 final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent);
1882 mCurrentDragStartEvent.mX = tx;
1883 mCurrentDragStartEvent.mY = ty;
1884 mCurrentDragStartEvent.mEventHandlerWasCalled = false;
1885 if (canAccept) {
1886 mChildrenInterestedInDrag.add(child);
1887 if (!child.canAcceptDrag()) {
1888 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
1889 child.refreshDrawableState();
1890 }
1891 }
1892 return canAccept;
1893 }
1894
1895 @Override
1896 @Deprecated
1897 public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1898 super.dispatchWindowSystemUiVisiblityChanged(visible);
1899
1900 final int count = mChildrenCount;
1901 final View[] children = mChildren;
1902 for (int i=0; i <count; i++) {
1903 final View child = children[i];
1904 child.dispatchWindowSystemUiVisiblityChanged(visible);
1905 }
1906 }
1907
1908 @Override
1909 @Deprecated
1910 public void dispatchSystemUiVisibilityChanged(int visible) {
1911 super.dispatchSystemUiVisibilityChanged(visible);
1912
1913 final int count = mChildrenCount;
1914 final View[] children = mChildren;
1915 for (int i=0; i <count; i++) {
1916 final View child = children[i];
1917 child.dispatchSystemUiVisibilityChanged(visible);
1918 }
1919 }
1920
1921 @Override
1922 boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1923 boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
1924
1925 final int count = mChildrenCount;
1926 final View[] children = mChildren;
1927 for (int i=0; i <count; i++) {
1928 final View child = children[i];
1929 changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
1930 }
1931 return changed;
1932 }
1933
1934 @Override
1935 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1936 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1937 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1938 return super.dispatchKeyEventPreIme(event);
1939 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1940 == PFLAG_HAS_BOUNDS) {
1941 return mFocused.dispatchKeyEventPreIme(event);
1942 }
1943 return false;
1944 }
1945
1946 @Override
1947 public boolean dispatchKeyEvent(KeyEvent event) {
1948 if (mInputEventConsistencyVerifier != null) {
1949 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1950 }
1951
1952 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1953 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1954 if (super.dispatchKeyEvent(event)) {
1955 return true;
1956 }
1957 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1958 == PFLAG_HAS_BOUNDS) {
1959 if (mFocused.dispatchKeyEvent(event)) {
1960 return true;
1961 }
1962 }
1963
1964 if (mInputEventConsistencyVerifier != null) {
1965 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1966 }
1967 return false;
1968 }
1969
1970 @Override
1971 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1972 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1973 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1974 return super.dispatchKeyShortcutEvent(event);
1975 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1976 == PFLAG_HAS_BOUNDS) {
1977 return mFocused.dispatchKeyShortcutEvent(event);
1978 }
1979 return false;
1980 }
1981
1982 @Override
1983 public boolean dispatchTrackballEvent(MotionEvent event) {
1984 if (mInputEventConsistencyVerifier != null) {
1985 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1986 }
1987
1988 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1989 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1990 if (super.dispatchTrackballEvent(event)) {
1991 return true;
1992 }
1993 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1994 == PFLAG_HAS_BOUNDS) {
1995 if (mFocused.dispatchTrackballEvent(event)) {
1996 return true;
1997 }
1998 }
1999
2000 if (mInputEventConsistencyVerifier != null) {
2001 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
2002 }
2003 return false;
2004 }
2005
2006 @Override
2007 public boolean dispatchCapturedPointerEvent(MotionEvent event) {
2008 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
2009 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
2010 if (super.dispatchCapturedPointerEvent(event)) {
2011 return true;
2012 }
2013 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
2014 == PFLAG_HAS_BOUNDS) {
2015 if (mFocused.dispatchCapturedPointerEvent(event)) {
2016 return true;
2017 }
2018 }
2019 return false;
2020 }
2021
2022 @Override
2023 public void dispatchPointerCaptureChanged(boolean hasCapture) {
2024 exitHoverTargets();
2025
2026 super.dispatchPointerCaptureChanged(hasCapture);
2027 final int count = mChildrenCount;
2028 final View[] children = mChildren;
2029 for (int i = 0; i < count; i++) {
2030 children[i].dispatchPointerCaptureChanged(hasCapture);
2031 }
2032 }
2033
2034 @Override
2035 public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
2036 final float x = event.getX(pointerIndex);
2037 final float y = event.getY(pointerIndex);
2038 if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) {
2039 return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW);
2040 }
2041 // Check what the child under the pointer says about the pointer.
2042 final int childrenCount = mChildrenCount;
2043 if (childrenCount != 0) {
2044 final ArrayList<View> preorderedList = buildOrderedChildList();
2045 final boolean customOrder = preorderedList == null
2046 && isChildrenDrawingOrderEnabled();
2047 final View[] children = mChildren;
2048 for (int i = childrenCount - 1; i >= 0; i--) {
2049 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
2050 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
2051 if (!child.canReceivePointerEvents()
2052 || !isTransformedTouchPointInView(x, y, child, null)) {
2053 continue;
2054 }
2055 final PointerIcon pointerIcon =
2056 dispatchResolvePointerIcon(event, pointerIndex, child);
2057 if (pointerIcon != null) {
2058 if (preorderedList != null) preorderedList.clear();
2059 return pointerIcon;
2060 }
2061 }
2062 if (preorderedList != null) preorderedList.clear();
2063 }
2064
2065 // The pointer is not a child or the child has no preferences, returning the default
2066 // implementation.
2067 return super.onResolvePointerIcon(event, pointerIndex);
2068 }
2069
2070 private PointerIcon dispatchResolvePointerIcon(MotionEvent event, int pointerIndex,
2071 View child) {
2072 final PointerIcon pointerIcon;
2073 if (!child.hasIdentityMatrix()) {
2074 MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
2075 pointerIcon = child.onResolvePointerIcon(transformedEvent, pointerIndex);
2076 transformedEvent.recycle();
2077 } else {
2078 final float offsetX = mScrollX - child.mLeft;
2079 final float offsetY = mScrollY - child.mTop;
2080 event.offsetLocation(offsetX, offsetY);
2081 pointerIcon = child.onResolvePointerIcon(event, pointerIndex);
2082 event.offsetLocation(-offsetX, -offsetY);
2083 }
2084 return pointerIcon;
2085 }
2086
2087 private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
2088 final int childIndex;
2089 if (customOrder) {
2090 final int childIndex1 = getChildDrawingOrder(childrenCount, i);
2091 if (childIndex1 >= childrenCount) {
2092 throw new IndexOutOfBoundsException("getChildDrawingOrder() "
2093 + "returned invalid index " + childIndex1
2094 + " (child count is " + childrenCount + ")");
2095 }
2096 childIndex = childIndex1;
2097 } else {
2098 childIndex = i;
2099 }
2100 return childIndex;
2101 }
2102
2103 @SuppressWarnings({"ConstantConditions"})
2104 @Override
2105 protected boolean dispatchHoverEvent(MotionEvent event) {
2106 final int action = event.getAction();
2107
2108 // First check whether the view group wants to intercept the hover event.
2109 final boolean interceptHover = onInterceptHoverEvent(event);
2110 event.setAction(action); // restore action in case it was changed
2111
2112 MotionEvent eventNoHistory = event;
2113 boolean handled = false;
2114
2115 // Send events to the hovered children and build a new list of hover targets until
2116 // one is found that handles the event.
2117 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
2118 mFirstHoverTarget = null;
2119 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
2120 final float x = event.getX();
2121 final float y = event.getY();
2122 final int childrenCount = mChildrenCount;
2123 if (childrenCount != 0) {
2124 final ArrayList<View> preorderedList = buildOrderedChildList();
2125 final boolean customOrder = preorderedList == null
2126 && isChildrenDrawingOrderEnabled();
2127 final View[] children = mChildren;
2128 HoverTarget lastHoverTarget = null;
2129 for (int i = childrenCount - 1; i >= 0; i--) {
2130 final int childIndex = getAndVerifyPreorderedIndex(
2131 childrenCount, i, customOrder);
2132 final View child = getAndVerifyPreorderedView(
2133 preorderedList, children, childIndex);
2134 if (!child.canReceivePointerEvents()
2135 || !isTransformedTouchPointInView(x, y, child, null)) {
2136 continue;
2137 }
2138
2139 // Obtain a hover target for this child. Dequeue it from the
2140 // old hover target list if the child was previously hovered.
2141 HoverTarget hoverTarget = firstOldHoverTarget;
2142 final boolean wasHovered;
2143 for (HoverTarget predecessor = null; ;) {
2144 if (hoverTarget == null) {
2145 hoverTarget = HoverTarget.obtain(child);
2146 wasHovered = false;
2147 break;
2148 }
2149
2150 if (hoverTarget.child == child) {
2151 if (predecessor != null) {
2152 predecessor.next = hoverTarget.next;
2153 } else {
2154 firstOldHoverTarget = hoverTarget.next;
2155 }
2156 hoverTarget.next = null;
2157 wasHovered = true;
2158 break;
2159 }
2160
2161 predecessor = hoverTarget;
2162 hoverTarget = hoverTarget.next;
2163 }
2164
2165 // Enqueue the hover target onto the new hover target list.
2166 if (lastHoverTarget != null) {
2167 lastHoverTarget.next = hoverTarget;
2168 } else {
2169 mFirstHoverTarget = hoverTarget;
2170 }
2171 lastHoverTarget = hoverTarget;
2172
2173 // Dispatch the event to the child.
2174 if (action == MotionEvent.ACTION_HOVER_ENTER) {
2175 if (!wasHovered) {
2176 // Send the enter as is.
2177 handled |= dispatchTransformedGenericPointerEvent(
2178 event, child); // enter
2179 }
2180 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
2181 if (!wasHovered) {
2182 // Synthesize an enter from a move.
2183 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2184 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
2185 handled |= dispatchTransformedGenericPointerEvent(
2186 eventNoHistory, child); // enter
2187 eventNoHistory.setAction(action);
2188
2189 handled |= dispatchTransformedGenericPointerEvent(
2190 eventNoHistory, child); // move
2191 } else {
2192 // Send the move as is.
2193 handled |= dispatchTransformedGenericPointerEvent(event, child);
2194 }
2195 }
2196 if (handled) {
2197 break;
2198 }
2199 }
2200 if (preorderedList != null) preorderedList.clear();
2201 }
2202 }
2203
2204 // Send exit events to all previously hovered children that are no longer hovered.
2205 while (firstOldHoverTarget != null) {
2206 final View child = firstOldHoverTarget.child;
2207
2208 // Exit the old hovered child.
2209 if (action == MotionEvent.ACTION_HOVER_EXIT) {
2210 // Send the exit as is.
2211 handled |= dispatchTransformedGenericPointerEvent(
2212 event, child); // exit
2213 } else {
2214 // Synthesize an exit from a move or enter.
2215 // Ignore the result because hover focus has moved to a different view.
2216 if (action == MotionEvent.ACTION_HOVER_MOVE) {
2217 final boolean hoverExitPending = event.isHoverExitPending();
2218 event.setHoverExitPending(true);
2219 dispatchTransformedGenericPointerEvent(
2220 event, child); // move
2221 event.setHoverExitPending(hoverExitPending);
2222 }
2223 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2224 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
2225 dispatchTransformedGenericPointerEvent(
2226 eventNoHistory, child); // exit
2227 eventNoHistory.setAction(action);
2228 }
2229
2230 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
2231 firstOldHoverTarget.recycle();
2232 firstOldHoverTarget = nextOldHoverTarget;
2233 }
2234
2235 // Send events to the view group itself if no children have handled it and the view group
2236 // itself is not currently being hover-exited.
2237 boolean newHoveredSelf = !handled &&
2238 (action != MotionEvent.ACTION_HOVER_EXIT) && !event.isHoverExitPending();
2239 if (newHoveredSelf == mHoveredSelf) {
2240 if (newHoveredSelf) {
2241 // Send event to the view group as before.
2242 handled |= super.dispatchHoverEvent(event);
2243 }
2244 } else {
2245 if (mHoveredSelf) {
2246 // Exit the view group.
2247 if (action == MotionEvent.ACTION_HOVER_EXIT) {
2248 // Send the exit as is.
2249 handled |= super.dispatchHoverEvent(event); // exit
2250 } else {
2251 // Synthesize an exit from a move or enter.
2252 // Ignore the result because hover focus is moving to a different view.
2253 if (action == MotionEvent.ACTION_HOVER_MOVE) {
2254 super.dispatchHoverEvent(event); // move
2255 }
2256 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2257 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
2258 super.dispatchHoverEvent(eventNoHistory); // exit
2259 eventNoHistory.setAction(action);
2260 }
2261 mHoveredSelf = false;
2262 }
2263
2264 if (newHoveredSelf) {
2265 // Enter the view group.
2266 if (action == MotionEvent.ACTION_HOVER_ENTER) {
2267 // Send the enter as is.
2268 handled |= super.dispatchHoverEvent(event); // enter
2269 mHoveredSelf = true;
2270 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
2271 // Synthesize an enter from a move.
2272 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2273 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
2274 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
2275 eventNoHistory.setAction(action);
2276
2277 handled |= super.dispatchHoverEvent(eventNoHistory); // move
2278 mHoveredSelf = true;
2279 }
2280 }
2281 }
2282
2283 // Recycle the copy of the event that we made.
2284 if (eventNoHistory != event) {
2285 eventNoHistory.recycle();
2286 }
2287
2288 // Done.
2289 return handled;
2290 }
2291
2292 private void exitHoverTargets() {
2293 if (mHoveredSelf || mFirstHoverTarget != null) {
2294 final long now = SystemClock.uptimeMillis();
2295 MotionEvent event = MotionEvent.obtain(now, now,
2296 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
2297 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2298 dispatchHoverEvent(event);
2299 event.recycle();
2300 }
2301 }
2302
2303 private void cancelHoverTarget(View view) {
2304 HoverTarget predecessor = null;
2305 HoverTarget target = mFirstHoverTarget;
2306 while (target != null) {
2307 final HoverTarget next = target.next;
2308 if (target.child == view) {
2309 if (predecessor == null) {
2310 mFirstHoverTarget = next;
2311 } else {
2312 predecessor.next = next;
2313 }
2314 target.recycle();
2315
2316 final long now = SystemClock.uptimeMillis();
2317 MotionEvent event = MotionEvent.obtain(now, now,
2318 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
2319 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2320 view.dispatchHoverEvent(event);
2321 event.recycle();
2322 return;
2323 }
2324 predecessor = target;
2325 target = next;
2326 }
2327 }
2328
2329 @Override
2330 boolean dispatchTooltipHoverEvent(MotionEvent event) {
2331 final int action = event.getAction();
2332 switch (action) {
2333 case MotionEvent.ACTION_HOVER_ENTER:
2334 break;
2335
2336 case MotionEvent.ACTION_HOVER_MOVE:
2337 View newTarget = null;
2338
2339 // Check what the child under the pointer says about the tooltip.
2340 final int childrenCount = mChildrenCount;
2341 if (childrenCount != 0) {
2342 final float x = event.getX();
2343 final float y = event.getY();
2344
2345 final ArrayList<View> preorderedList = buildOrderedChildList();
2346 final boolean customOrder = preorderedList == null
2347 && isChildrenDrawingOrderEnabled();
2348 final View[] children = mChildren;
2349 for (int i = childrenCount - 1; i >= 0; i--) {
2350 final int childIndex =
2351 getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
2352 final View child =
2353 getAndVerifyPreorderedView(preorderedList, children, childIndex);
2354 if (!child.canReceivePointerEvents()
2355 || !isTransformedTouchPointInView(x, y, child, null)) {
2356 continue;
2357 }
2358 if (dispatchTooltipHoverEvent(event, child)) {
2359 newTarget = child;
2360 break;
2361 }
2362 }
2363 if (preorderedList != null) preorderedList.clear();
2364 }
2365
2366 if (mTooltipHoverTarget != newTarget) {
2367 if (mTooltipHoverTarget != null) {
2368 event.setAction(MotionEvent.ACTION_HOVER_EXIT);
2369 mTooltipHoverTarget.dispatchTooltipHoverEvent(event);
2370 event.setAction(action);
2371 }
2372 mTooltipHoverTarget = newTarget;
2373 }
2374
2375 if (mTooltipHoverTarget != null) {
2376 if (mTooltipHoveredSelf) {
2377 mTooltipHoveredSelf = false;
2378 event.setAction(MotionEvent.ACTION_HOVER_EXIT);
2379 super.dispatchTooltipHoverEvent(event);
2380 event.setAction(action);
2381 }
2382 return true;
2383 }
2384
2385 mTooltipHoveredSelf = super.dispatchTooltipHoverEvent(event);
2386 return mTooltipHoveredSelf;
2387
2388 case MotionEvent.ACTION_HOVER_EXIT:
2389 if (mTooltipHoverTarget != null) {
2390 mTooltipHoverTarget.dispatchTooltipHoverEvent(event);
2391 mTooltipHoverTarget = null;
2392 } else if (mTooltipHoveredSelf) {
2393 super.dispatchTooltipHoverEvent(event);
2394 mTooltipHoveredSelf = false;
2395 }
2396 break;
2397 }
2398 return false;
2399 }
2400
2401 private boolean dispatchTooltipHoverEvent(MotionEvent event, View child) {
2402 final boolean result;
2403 if (!child.hasIdentityMatrix()) {
2404 MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
2405 result = child.dispatchTooltipHoverEvent(transformedEvent);
2406 transformedEvent.recycle();
2407 } else {
2408 final float offsetX = mScrollX - child.mLeft;
2409 final float offsetY = mScrollY - child.mTop;
2410 event.offsetLocation(offsetX, offsetY);
2411 result = child.dispatchTooltipHoverEvent(event);
2412 event.offsetLocation(-offsetX, -offsetY);
2413 }
2414 return result;
2415 }
2416
2417 private void exitTooltipHoverTargets() {
2418 if (mTooltipHoveredSelf || mTooltipHoverTarget != null) {
2419 final long now = SystemClock.uptimeMillis();
2420 MotionEvent event = MotionEvent.obtain(now, now,
2421 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
2422 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2423 dispatchTooltipHoverEvent(event);
2424 event.recycle();
2425 }
2426 }
2427
2428 /** @hide */
2429 @Override
2430 protected boolean hasHoveredChild() {
2431 return mFirstHoverTarget != null;
2432 }
2433
2434 /** @hide */
2435 @Override
2436 protected boolean pointInHoveredChild(MotionEvent event) {
2437 if (mFirstHoverTarget != null) {
2438 return isTransformedTouchPointInView(event.getX(), event.getY(),
2439 mFirstHoverTarget.child, null);
2440 }
2441 return false;
2442 }
2443
2444 @Override
2445 public void addChildrenForAccessibility(ArrayList<View> outChildren) {
2446 if (getAccessibilityNodeProvider() != null) {
2447 return;
2448 }
2449 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
2450 try {
2451 final int childrenCount = children.getChildCount();
2452 for (int i = 0; i < childrenCount; i++) {
2453 View child = children.getChildAt(i);
2454 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2455 if (child.includeForAccessibility()) {
2456 outChildren.add(child);
2457 } else {
2458 child.addChildrenForAccessibility(outChildren);
2459 }
2460 }
2461 }
2462 } finally {
2463 children.recycle();
2464 }
2465 }
2466
2467 /**
2468 * Implement this method to intercept hover events before they are handled
2469 * by child views.
2470 * <p>
2471 * This method is called before dispatching a hover event to a child of
2472 * the view group or to the view group's own {@link #onHoverEvent} to allow
2473 * the view group a chance to intercept the hover event.
2474 * This method can also be used to watch all pointer motions that occur within
2475 * the bounds of the view group even when the pointer is hovering over
2476 * a child of the view group rather than over the view group itself.
2477 * </p><p>
2478 * The view group can prevent its children from receiving hover events by
2479 * implementing this method and returning <code>true</code> to indicate
2480 * that it would like to intercept hover events. The view group must
2481 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
2482 * for as long as it wishes to continue intercepting hover events from
2483 * its children.
2484 * </p><p>
2485 * Interception preserves the invariant that at most one view can be
2486 * hovered at a time by transferring hover focus from the currently hovered
2487 * child to the view group or vice-versa as needed.
2488 * </p><p>
2489 * If this method returns <code>true</code> and a child is already hovered, then the
2490 * child view will first receive a hover exit event and then the view group
2491 * itself will receive a hover enter event in {@link #onHoverEvent}.
2492 * Likewise, if this method had previously returned <code>true</code> to intercept hover
2493 * events and instead returns <code>false</code> while the pointer is hovering
2494 * within the bounds of one of a child, then the view group will first receive a
2495 * hover exit event in {@link #onHoverEvent} and then the hovered child will
2496 * receive a hover enter event.
2497 * </p><p>
2498 * The default implementation handles mouse hover on the scroll bars.
2499 * </p>
2500 *
2501 * @param event The motion event that describes the hover.
2502 * @return True if the view group would like to intercept the hover event
2503 * and prevent its children from receiving it.
2504 */
2505 public boolean onInterceptHoverEvent(MotionEvent event) {
2506 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
2507 final int action = event.getAction();
2508 final float x = event.getX();
2509 final float y = event.getY();
2510 if ((action == MotionEvent.ACTION_HOVER_MOVE
2511 || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) {
2512 return true;
2513 }
2514 }
2515 return false;
2516 }
2517
2518 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
2519 if (event.getHistorySize() == 0) {
2520 return event;
2521 }
2522 return MotionEvent.obtainNoHistory(event);
2523 }
2524
2525 @Override
2526 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
2527 // Send the event to the child under the pointer.
2528 final int childrenCount = mChildrenCount;
2529 if (childrenCount != 0) {
2530 final float x = event.getX();
2531 final float y = event.getY();
2532
2533 final ArrayList<View> preorderedList = buildOrderedChildList();
2534 final boolean customOrder = preorderedList == null
2535 && isChildrenDrawingOrderEnabled();
2536 final View[] children = mChildren;
2537 for (int i = childrenCount - 1; i >= 0; i--) {
2538 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
2539 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
2540 if (!child.canReceivePointerEvents()
2541 || !isTransformedTouchPointInView(x, y, child, null)) {
2542 continue;
2543 }
2544
2545 if (dispatchTransformedGenericPointerEvent(event, child)) {
2546 if (preorderedList != null) preorderedList.clear();
2547 return true;
2548 }
2549 }
2550 if (preorderedList != null) preorderedList.clear();
2551 }
2552
2553 // No child handled the event. Send it to this view group.
2554 return super.dispatchGenericPointerEvent(event);
2555 }
2556
2557 @Override
2558 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
2559 // Send the event to the focused child or to this view group if it has focus.
2560 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
2561 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
2562 return super.dispatchGenericFocusedEvent(event);
2563 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
2564 == PFLAG_HAS_BOUNDS) {
2565 return mFocused.dispatchGenericMotionEvent(event);
2566 }
2567 return false;
2568 }
2569
2570 /**
2571 * Dispatches a generic pointer event to a child, taking into account
2572 * transformations that apply to the child.
2573 *
2574 * @param event The event to send.
2575 * @param child The view to send the event to.
2576 * @return {@code true} if the child handled the event.
2577 */
2578 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
2579 boolean handled;
2580 if (!child.hasIdentityMatrix()) {
2581 MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
2582 handled = child.dispatchGenericMotionEvent(transformedEvent);
2583 transformedEvent.recycle();
2584 } else {
2585 final float offsetX = mScrollX - child.mLeft;
2586 final float offsetY = mScrollY - child.mTop;
2587 event.offsetLocation(offsetX, offsetY);
2588 handled = child.dispatchGenericMotionEvent(event);
2589 event.offsetLocation(-offsetX, -offsetY);
2590 }
2591 return handled;
2592 }
2593
2594 /**
2595 * Returns a MotionEvent that's been transformed into the child's local coordinates.
2596 *
2597 * It's the responsibility of the caller to recycle it once they're finished with it.
2598 * @param event The event to transform.
2599 * @param child The view whose coordinate space is to be used.
2600 * @return A copy of the the given MotionEvent, transformed into the given View's coordinate
2601 * space.
2602 */
2603 private MotionEvent getTransformedMotionEvent(MotionEvent event, View child) {
2604 final float offsetX = mScrollX - child.mLeft;
2605 final float offsetY = mScrollY - child.mTop;
2606 final MotionEvent transformedEvent = MotionEvent.obtain(event);
2607 transformedEvent.offsetLocation(offsetX, offsetY);
2608 if (!child.hasIdentityMatrix()) {
2609 transformedEvent.transform(child.getInverseMatrix());
2610 }
2611 return transformedEvent;
2612 }
2613
2614 @Override
2615 public boolean dispatchTouchEvent(MotionEvent ev) {
2616 if (mInputEventConsistencyVerifier != null) {
2617 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
2618 }
2619
2620 boolean handled = false;
2621 if (onFilterTouchEventForSecurity(ev)) {
2622 final int action = ev.getAction();
2623 final int actionMasked = action & MotionEvent.ACTION_MASK;
2624
2625 // Handle an initial down.
2626 if (actionMasked == MotionEvent.ACTION_DOWN) {
2627 // Throw away all previous state when starting a new touch gesture.
2628 // The framework may have dropped the up or cancel event for the previous gesture
2629 // due to an app switch, ANR, or some other state change.
2630 cancelAndClearTouchTargets(ev);
2631 resetTouchState();
2632 }
2633
2634 // Check for interception.
2635 final boolean intercepted;
2636 if (actionMasked == MotionEvent.ACTION_DOWN
2637 || mFirstTouchTarget != null) {
2638 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
2639 if (!disallowIntercept) {
2640 intercepted = onInterceptTouchEvent(ev);
2641 ev.setAction(action); // restore action in case it was changed
2642 } else {
2643 intercepted = false;
2644 }
2645 } else {
2646 // There are no touch targets and this action is not an initial down
2647 // so this view group continues to intercept touches.
2648 intercepted = true;
2649 }
2650 // Check for cancelation.
2651 final boolean canceled = resetCancelNextUpFlag(this)
2652 || actionMasked == MotionEvent.ACTION_CANCEL;
2653
2654 // Update list of touch targets for pointer down, if needed.
2655 final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
2656 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0
2657 && !isMouseEvent;
2658 TouchTarget newTouchTarget = null;
2659 boolean alreadyDispatchedToNewTouchTarget = false;
2660 if (!canceled && !intercepted) {
2661 if (actionMasked == MotionEvent.ACTION_DOWN
2662 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
2663 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2664 final int actionIndex = ev.getActionIndex(); // always 0 for down
2665 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
2666 : TouchTarget.ALL_POINTER_IDS;
2667
2668 // Clean up earlier touch targets for this pointer id in case they
2669 // have become out of sync.
2670 removePointersFromTouchTargets(idBitsToAssign);
2671
2672 final int childrenCount = mChildrenCount;
2673 if (newTouchTarget == null && childrenCount != 0) {
2674 final float x =
2675 isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
2676 final float y =
2677 isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
2678 // Find a child that can receive the event.
2679 // Scan children from front to back.
2680 final ArrayList<View> preorderedList = buildTouchDispatchChildList();
2681 final boolean customOrder = preorderedList == null
2682 && isChildrenDrawingOrderEnabled();
2683 final View[] children = mChildren;
2684 for (int i = childrenCount - 1; i >= 0; i--) {
2685 final int childIndex = getAndVerifyPreorderedIndex(
2686 childrenCount, i, customOrder);
2687 final View child = getAndVerifyPreorderedView(
2688 preorderedList, children, childIndex);
2689 if (!child.canReceivePointerEvents()
2690 || !isTransformedTouchPointInView(x, y, child, null)) {
2691 continue;
2692 }
2693
2694 newTouchTarget = getTouchTarget(child);
2695 if (newTouchTarget != null) {
2696 // Child is already receiving touch within its bounds.
2697 // Give it the new pointer in addition to the ones it is handling.
2698 newTouchTarget.pointerIdBits |= idBitsToAssign;
2699 break;
2700 }
2701
2702 resetCancelNextUpFlag(child);
2703 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
2704 // Child wants to receive touch within its bounds.
2705 mLastTouchDownTime = ev.getDownTime();
2706 if (preorderedList != null) {
2707 // childIndex points into presorted list, find original index
2708 for (int j = 0; j < childrenCount; j++) {
2709 if (children[childIndex] == mChildren[j]) {
2710 mLastTouchDownIndex = j;
2711 break;
2712 }
2713 }
2714 } else {
2715 mLastTouchDownIndex = childIndex;
2716 }
2717 mLastTouchDownX = ev.getX();
2718 mLastTouchDownY = ev.getY();
2719 newTouchTarget = addTouchTarget(child, idBitsToAssign);
2720 alreadyDispatchedToNewTouchTarget = true;
2721 break;
2722 }
2723 }
2724 if (preorderedList != null) preorderedList.clear();
2725 }
2726
2727 if (newTouchTarget == null && mFirstTouchTarget != null) {
2728 // Did not find a child to receive the event.
2729 // Assign the pointer to the least recently added target.
2730 newTouchTarget = mFirstTouchTarget;
2731 while (newTouchTarget.next != null) {
2732 newTouchTarget = newTouchTarget.next;
2733 }
2734 newTouchTarget.pointerIdBits |= idBitsToAssign;
2735 }
2736 }
2737 }
2738
2739 // Dispatch to touch targets.
2740 if (mFirstTouchTarget == null) {
2741 // No touch targets so treat this as an ordinary view.
2742 handled = dispatchTransformedTouchEvent(ev, canceled, null,
2743 TouchTarget.ALL_POINTER_IDS);
2744 } else {
2745 // Dispatch to touch targets, excluding the new touch target if we already
2746 // dispatched to it. Cancel touch targets if necessary.
2747 TouchTarget predecessor = null;
2748 TouchTarget target = mFirstTouchTarget;
2749 while (target != null) {
2750 final TouchTarget next = target.next;
2751 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
2752 handled = true;
2753 } else {
2754 final boolean cancelChild = resetCancelNextUpFlag(target.child)
2755 || intercepted;
2756 if (dispatchTransformedTouchEvent(ev, cancelChild,
2757 target.child, target.pointerIdBits)) {
2758 handled = true;
2759 }
2760 if (cancelChild) {
2761 if (predecessor == null) {
2762 mFirstTouchTarget = next;
2763 } else {
2764 predecessor.next = next;
2765 }
2766 target.recycle();
2767 target = next;
2768 continue;
2769 }
2770 }
2771 predecessor = target;
2772 target = next;
2773 }
2774 }
2775
2776 // Update list of touch targets for pointer up or cancel, if needed.
2777 if (canceled
2778 || actionMasked == MotionEvent.ACTION_UP
2779 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2780 resetTouchState();
2781 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
2782 final int actionIndex = ev.getActionIndex();
2783 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
2784 removePointersFromTouchTargets(idBitsToRemove);
2785 }
2786 }
2787
2788 if (!handled && mInputEventConsistencyVerifier != null) {
2789 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
2790 }
2791 return handled;
2792 }
2793
2794 /**
2795 * Provide custom ordering of views in which the touch will be dispatched.
2796 *
2797 * This is called within a tight loop, so you are not allowed to allocate objects, including
2798 * the return array. Instead, you should return a pre-allocated list that will be cleared
2799 * after the dispatch is finished.
2800 * @hide
2801 */
2802 public ArrayList<View> buildTouchDispatchChildList() {
2803 return buildOrderedChildList();
2804 }
2805
2806 /**
2807 * Resets all touch state in preparation for a new cycle.
2808 */
2809 private void resetTouchState() {
2810 clearTouchTargets();
2811 resetCancelNextUpFlag(this);
2812 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2813 mNestedScrollAxes = SCROLL_AXIS_NONE;
2814 }
2815
2816 /**
2817 * Resets the cancel next up flag.
2818 * Returns true if the flag was previously set.
2819 */
2820 private static boolean resetCancelNextUpFlag(@NonNull View view) {
2821 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
2822 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
2823 return true;
2824 }
2825 return false;
2826 }
2827
2828 /**
2829 * Clears all touch targets.
2830 */
2831 private void clearTouchTargets() {
2832 TouchTarget target = mFirstTouchTarget;
2833 if (target != null) {
2834 do {
2835 TouchTarget next = target.next;
2836 target.recycle();
2837 target = next;
2838 } while (target != null);
2839 mFirstTouchTarget = null;
2840 }
2841 }
2842
2843 /**
2844 * Cancels and clears all touch targets.
2845 */
2846 private void cancelAndClearTouchTargets(MotionEvent event) {
2847 if (mFirstTouchTarget != null) {
2848 boolean syntheticEvent = false;
2849 if (event == null) {
2850 final long now = SystemClock.uptimeMillis();
2851 event = MotionEvent.obtain(now, now,
2852 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2853 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2854 syntheticEvent = true;
2855 }
2856
2857 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2858 resetCancelNextUpFlag(target.child);
2859 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2860 }
2861 clearTouchTargets();
2862
2863 if (syntheticEvent) {
2864 event.recycle();
2865 }
2866 }
2867 }
2868
2869 /**
2870 * Gets the touch target for specified child view.
2871 * Returns null if not found.
2872 */
2873 private TouchTarget getTouchTarget(@NonNull View child) {
2874 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2875 if (target.child == child) {
2876 return target;
2877 }
2878 }
2879 return null;
2880 }
2881
2882 /**
2883 * Adds a touch target for specified child to the beginning of the list.
2884 * Assumes the target child is not already present.
2885 */
2886 private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
2887 final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2888 target.next = mFirstTouchTarget;
2889 mFirstTouchTarget = target;
2890 return target;
2891 }
2892
2893 /**
2894 * Removes the pointer ids from consideration.
2895 */
2896 private void removePointersFromTouchTargets(int pointerIdBits) {
2897 TouchTarget predecessor = null;
2898 TouchTarget target = mFirstTouchTarget;
2899 while (target != null) {
2900 final TouchTarget next = target.next;
2901 if ((target.pointerIdBits & pointerIdBits) != 0) {
2902 target.pointerIdBits &= ~pointerIdBits;
2903 if (target.pointerIdBits == 0) {
2904 if (predecessor == null) {
2905 mFirstTouchTarget = next;
2906 } else {
2907 predecessor.next = next;
2908 }
2909 target.recycle();
2910 target = next;
2911 continue;
2912 }
2913 }
2914 predecessor = target;
2915 target = next;
2916 }
2917 }
2918
2919 @UnsupportedAppUsage
2920 private void cancelTouchTarget(View view) {
2921 TouchTarget predecessor = null;
2922 TouchTarget target = mFirstTouchTarget;
2923 while (target != null) {
2924 final TouchTarget next = target.next;
2925 if (target.child == view) {
2926 if (predecessor == null) {
2927 mFirstTouchTarget = next;
2928 } else {
2929 predecessor.next = next;
2930 }
2931 target.recycle();
2932
2933 final long now = SystemClock.uptimeMillis();
2934 MotionEvent event = MotionEvent.obtain(now, now,
2935 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2936 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2937 view.dispatchTouchEvent(event);
2938 event.recycle();
2939 return;
2940 }
2941 predecessor = target;
2942 target = next;
2943 }
2944 }
2945
2946 private Rect getTempRect() {
2947 if (mTempRect == null) {
2948 mTempRect = new Rect();
2949 }
2950 return mTempRect;
2951 }
2952
2953 private float[] getTempLocationF() {
2954 if (mTempPosition == null) {
2955 mTempPosition = new float[2];
2956 }
2957 return mTempPosition;
2958 }
2959
2960 private Point getTempPoint() {
2961 if (mTempPoint == null) {
2962 mTempPoint = new Point();
2963 }
2964 return mTempPoint;
2965 }
2966
2967 /**
2968 * Returns true if a child view contains the specified point when transformed
2969 * into its coordinate space.
2970 * Child must not be null.
2971 * @hide
2972 */
2973 @UnsupportedAppUsage
2974 protected boolean isTransformedTouchPointInView(float x, float y, View child,
2975 PointF outLocalPoint) {
2976 final float[] point = getTempLocationF();
2977 point[0] = x;
2978 point[1] = y;
2979 transformPointToViewLocal(point, child);
2980 final boolean isInView = child.pointInView(point[0], point[1]);
2981 if (isInView && outLocalPoint != null) {
2982 outLocalPoint.set(point[0], point[1]);
2983 }
2984 return isInView;
2985 }
2986
2987 /**
2988 * @hide
2989 */
2990 @UnsupportedAppUsage
2991 public void transformPointToViewLocal(float[] point, View child) {
2992 point[0] += mScrollX - child.mLeft;
2993 point[1] += mScrollY - child.mTop;
2994
2995 if (!child.hasIdentityMatrix()) {
2996 child.getInverseMatrix().mapPoints(point);
2997 }
2998 }
2999
3000 /**
3001 * Transforms a motion event into the coordinate space of a particular child view,
3002 * filters out irrelevant pointer ids, and overrides its action if necessary.
3003 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
3004 */
3005 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
3006 View child, int desiredPointerIdBits) {
3007 final boolean handled;
3008
3009 // Canceling motions is a special case. We don't need to perform any transformations
3010 // or filtering. The important part is the action, not the contents.
3011 final int oldAction = event.getAction();
3012 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
3013 event.setAction(MotionEvent.ACTION_CANCEL);
3014 if (child == null) {
3015 handled = super.dispatchTouchEvent(event);
3016 } else {
3017 handled = child.dispatchTouchEvent(event);
3018 }
3019 event.setAction(oldAction);
3020 return handled;
3021 }
3022
3023 // Calculate the number of pointers to deliver.
3024 final int oldPointerIdBits = event.getPointerIdBits();
3025 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
3026
3027 // If for some reason we ended up in an inconsistent state where it looks like we
3028 // might produce a motion event with no pointers in it, then drop the event.
3029 if (newPointerIdBits == 0) {
3030 return false;
3031 }
3032
3033 // If the number of pointers is the same and we don't need to perform any fancy
3034 // irreversible transformations, then we can reuse the motion event for this
3035 // dispatch as long as we are careful to revert any changes we make.
3036 // Otherwise we need to make a copy.
3037 final MotionEvent transformedEvent;
3038 if (newPointerIdBits == oldPointerIdBits) {
3039 if (child == null || child.hasIdentityMatrix()) {
3040 if (child == null) {
3041 handled = super.dispatchTouchEvent(event);
3042 } else {
3043 final float offsetX = mScrollX - child.mLeft;
3044 final float offsetY = mScrollY - child.mTop;
3045 event.offsetLocation(offsetX, offsetY);
3046
3047 handled = child.dispatchTouchEvent(event);
3048
3049 event.offsetLocation(-offsetX, -offsetY);
3050 }
3051 return handled;
3052 }
3053 transformedEvent = MotionEvent.obtain(event);
3054 } else {
3055 transformedEvent = event.split(newPointerIdBits);
3056 }
3057
3058 // Perform any necessary transformations and dispatch.
3059 if (child == null) {
3060 handled = super.dispatchTouchEvent(transformedEvent);
3061 } else {
3062 final float offsetX = mScrollX - child.mLeft;
3063 final float offsetY = mScrollY - child.mTop;
3064 transformedEvent.offsetLocation(offsetX, offsetY);
3065 if (! child.hasIdentityMatrix()) {
3066 transformedEvent.transform(child.getInverseMatrix());
3067 }
3068
3069 handled = child.dispatchTouchEvent(transformedEvent);
3070 }
3071
3072 // Done.
3073 transformedEvent.recycle();
3074 return handled;
3075 }
3076
3077 /**
3078 * Enable or disable the splitting of MotionEvents to multiple children during touch event
3079 * dispatch. This behavior is enabled by default for applications that target an
3080 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
3081 *
3082 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
3083 * views depending on where each pointer initially went down. This allows for user interactions
3084 * such as scrolling two panes of content independently, chording of buttons, and performing
3085 * independent gestures on different pieces of content.
3086 *
3087 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
3088 * child views. <code>false</code> to only allow one child view to be the target of
3089 * any MotionEvent received by this ViewGroup.
3090 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
3091 */
3092 public void setMotionEventSplittingEnabled(boolean split) {
3093 // TODO Applications really shouldn't change this setting mid-touch event,
3094 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
3095 // with gestures in progress when this is changed.
3096 if (split) {
3097 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
3098 } else {
3099 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
3100 }
3101 }
3102
3103 /**
3104 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
3105 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
3106 */
3107 @InspectableProperty(name = "splitMotionEvents")
3108 public boolean isMotionEventSplittingEnabled() {
3109 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
3110 }
3111
3112 /**
3113 * Returns true if this ViewGroup should be considered as a single entity for removal
3114 * when executing an Activity transition. If this is false, child elements will move
3115 * individually during the transition.
3116 *
3117 * @return True if the ViewGroup should be acted on together during an Activity transition.
3118 * The default value is true when there is a non-null background or if
3119 * {@link #getTransitionName()} is not null or if a
3120 * non-null {@link android.view.ViewOutlineProvider} other than
3121 * {@link android.view.ViewOutlineProvider#BACKGROUND} was given to
3122 * {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise.
3123 */
3124 @InspectableProperty
3125 public boolean isTransitionGroup() {
3126 if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
3127 return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
3128 } else {
3129 final ViewOutlineProvider outlineProvider = getOutlineProvider();
3130 return getBackground() != null || getTransitionName() != null ||
3131 (outlineProvider != null && outlineProvider != ViewOutlineProvider.BACKGROUND);
3132 }
3133 }
3134
3135 /**
3136 * Changes whether or not this ViewGroup should be treated as a single entity during
3137 * Activity Transitions.
3138 * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
3139 * in Activity transitions. If false, the ViewGroup won't transition,
3140 * only its children. If true, the entire ViewGroup will transition
3141 * together.
3142 * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
3143 * android.util.Pair[])
3144 */
3145 public void setTransitionGroup(boolean isTransitionGroup) {
3146 mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
3147 if (isTransitionGroup) {
3148 mGroupFlags |= FLAG_IS_TRANSITION_GROUP;
3149 } else {
3150 mGroupFlags &= ~FLAG_IS_TRANSITION_GROUP;
3151 }
3152 }
3153
3154 @Override
3155 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3156
3157 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
3158 // We're already in this state, assume our ancestors are too
3159 return;
3160 }
3161
3162 if (disallowIntercept) {
3163 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
3164 } else {
3165 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
3166 }
3167
3168 // Pass it up to our parent
3169 if (mParent != null) {
3170 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
3171 }
3172 }
3173
3174 /**
3175 * Implement this method to intercept all touch screen motion events. This
3176 * allows you to watch events as they are dispatched to your children, and
3177 * take ownership of the current gesture at any point.
3178 *
3179 * <p>Using this function takes some care, as it has a fairly complicated
3180 * interaction with {@link View#onTouchEvent(MotionEvent)
3181 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
3182 * that method as well as this one in the correct way. Events will be
3183 * received in the following order:
3184 *
3185 * <ol>
3186 * <li> You will receive the down event here.
3187 * <li> The down event will be handled either by a child of this view
3188 * group, or given to your own onTouchEvent() method to handle; this means
3189 * you should implement onTouchEvent() to return true, so you will
3190 * continue to see the rest of the gesture (instead of looking for
3191 * a parent view to handle it). Also, by returning true from
3192 * onTouchEvent(), you will not receive any following
3193 * events in onInterceptTouchEvent() and all touch processing must
3194 * happen in onTouchEvent() like normal.
3195 * <li> For as long as you return false from this function, each following
3196 * event (up to and including the final up) will be delivered first here
3197 * and then to the target's onTouchEvent().
3198 * <li> If you return true from here, you will not receive any
3199 * following events: the target view will receive the same event but
3200 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
3201 * events will be delivered to your onTouchEvent() method and no longer
3202 * appear here.
3203 * </ol>
3204 *
3205 * @param ev The motion event being dispatched down the hierarchy.
3206 * @return Return true to steal motion events from the children and have
3207 * them dispatched to this ViewGroup through onTouchEvent().
3208 * The current target will receive an ACTION_CANCEL event, and no further
3209 * messages will be delivered here.
3210 */
3211 public boolean onInterceptTouchEvent(MotionEvent ev) {
3212 if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
3213 && ev.getAction() == MotionEvent.ACTION_DOWN
3214 && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
3215 && isOnScrollbarThumb(ev.getX(), ev.getY())) {
3216 return true;
3217 }
3218 return false;
3219 }
3220
3221 /**
3222 * {@inheritDoc}
3223 *
3224 * Looks for a view to give focus to respecting the setting specified by
3225 * {@link #getDescendantFocusability()}.
3226 *
3227 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
3228 * find focus within the children of this group when appropriate.
3229 *
3230 * @see #FOCUS_BEFORE_DESCENDANTS
3231 * @see #FOCUS_AFTER_DESCENDANTS
3232 * @see #FOCUS_BLOCK_DESCENDANTS
3233 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
3234 */
3235 @Override
3236 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
3237 if (DBG) {
3238 System.out.println(this + " ViewGroup.requestFocus direction="
3239 + direction);
3240 }
3241 int descendantFocusability = getDescendantFocusability();
3242
3243 boolean result;
3244 switch (descendantFocusability) {
3245 case FOCUS_BLOCK_DESCENDANTS:
3246 result = super.requestFocus(direction, previouslyFocusedRect);
3247 break;
3248 case FOCUS_BEFORE_DESCENDANTS: {
3249 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
3250 result = took ? took : onRequestFocusInDescendants(direction,
3251 previouslyFocusedRect);
3252 break;
3253 }
3254 case FOCUS_AFTER_DESCENDANTS: {
3255 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
3256 result = took ? took : super.requestFocus(direction, previouslyFocusedRect);
3257 break;
3258 }
3259 default:
3260 throw new IllegalStateException("descendant focusability must be "
3261 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
3262 + "but is " + descendantFocusability);
3263 }
3264 if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
3265 mPrivateFlags |= PFLAG_WANTS_FOCUS;
3266 }
3267 return result;
3268 }
3269
3270 /**
3271 * Look for a descendant to call {@link View#requestFocus} on.
3272 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
3273 * when it wants to request focus within its children. Override this to
3274 * customize how your {@link ViewGroup} requests focus within its children.
3275 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
3276 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
3277 * to give a finer grained hint about where focus is coming from. May be null
3278 * if there is no hint.
3279 * @return Whether focus was taken.
3280 */
3281 @SuppressWarnings({"ConstantConditions"})
3282 protected boolean onRequestFocusInDescendants(int direction,
3283 Rect previouslyFocusedRect) {
3284 int index;
3285 int increment;
3286 int end;
3287 int count = mChildrenCount;
3288 if ((direction & FOCUS_FORWARD) != 0) {
3289 index = 0;
3290 increment = 1;
3291 end = count;
3292 } else {
3293 index = count - 1;
3294 increment = -1;
3295 end = -1;
3296 }
3297 final View[] children = mChildren;
3298 for (int i = index; i != end; i += increment) {
3299 View child = children[i];
3300 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3301 if (child.requestFocus(direction, previouslyFocusedRect)) {
3302 return true;
3303 }
3304 }
3305 }
3306 return false;
3307 }
3308
3309 @Override
3310 public boolean restoreDefaultFocus() {
3311 if (mDefaultFocus != null
3312 && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
3313 && (mDefaultFocus.mViewFlags & VISIBILITY_MASK) == VISIBLE
3314 && mDefaultFocus.restoreDefaultFocus()) {
3315 return true;
3316 }
3317 return super.restoreDefaultFocus();
3318 }
3319
3320 /**
3321 * @hide
3322 */
3323 @TestApi
3324 @Override
3325 public boolean restoreFocusInCluster(@FocusRealDirection int direction) {
3326 // Allow cluster-navigation to enter touchscreenBlocksFocus ViewGroups.
3327 if (isKeyboardNavigationCluster()) {
3328 final boolean blockedFocus = getTouchscreenBlocksFocus();
3329 try {
3330 setTouchscreenBlocksFocusNoRefocus(false);
3331 return restoreFocusInClusterInternal(direction);
3332 } finally {
3333 setTouchscreenBlocksFocusNoRefocus(blockedFocus);
3334 }
3335 } else {
3336 return restoreFocusInClusterInternal(direction);
3337 }
3338 }
3339
3340 private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) {
3341 if (mFocusedInCluster != null && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
3342 && (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE
3343 && mFocusedInCluster.restoreFocusInCluster(direction)) {
3344 return true;
3345 }
3346 return super.restoreFocusInCluster(direction);
3347 }
3348
3349 /**
3350 * @hide
3351 */
3352 @Override
3353 public boolean restoreFocusNotInCluster() {
3354 if (mFocusedInCluster != null) {
3355 // since clusters don't nest; we can assume that a non-null mFocusedInCluster
3356 // will refer to a view not-in a cluster.
3357 return restoreFocusInCluster(View.FOCUS_DOWN);
3358 }
3359 if (isKeyboardNavigationCluster() || (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
3360 return false;
3361 }
3362 int descendentFocusability = getDescendantFocusability();
3363 if (descendentFocusability == FOCUS_BLOCK_DESCENDANTS) {
3364 return super.requestFocus(FOCUS_DOWN, null);
3365 }
3366 if (descendentFocusability == FOCUS_BEFORE_DESCENDANTS
3367 && super.requestFocus(FOCUS_DOWN, null)) {
3368 return true;
3369 }
3370 for (int i = 0; i < mChildrenCount; ++i) {
3371 View child = mChildren[i];
3372 if (!child.isKeyboardNavigationCluster()
3373 && child.restoreFocusNotInCluster()) {
3374 return true;
3375 }
3376 }
3377 if (descendentFocusability == FOCUS_AFTER_DESCENDANTS && !hasFocusableChild(false)) {
3378 return super.requestFocus(FOCUS_DOWN, null);
3379 }
3380 return false;
3381 }
3382
3383 /**
3384 * {@inheritDoc}
3385 *
3386 * @hide
3387 */
3388 @Override
3389 public void dispatchStartTemporaryDetach() {
3390 super.dispatchStartTemporaryDetach();
3391 final int count = mChildrenCount;
3392 final View[] children = mChildren;
3393 for (int i = 0; i < count; i++) {
3394 children[i].dispatchStartTemporaryDetach();
3395 }
3396 }
3397
3398 /**
3399 * {@inheritDoc}
3400 *
3401 * @hide
3402 */
3403 @Override
3404 public void dispatchFinishTemporaryDetach() {
3405 super.dispatchFinishTemporaryDetach();
3406 final int count = mChildrenCount;
3407 final View[] children = mChildren;
3408 for (int i = 0; i < count; i++) {
3409 children[i].dispatchFinishTemporaryDetach();
3410 }
3411 }
3412
3413 @Override
3414 @UnsupportedAppUsage
3415 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
3416 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
3417 super.dispatchAttachedToWindow(info, visibility);
3418 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
3419
3420 final int count = mChildrenCount;
3421 final View[] children = mChildren;
3422 for (int i = 0; i < count; i++) {
3423 final View child = children[i];
3424 child.dispatchAttachedToWindow(info,
3425 combineVisibility(visibility, child.getVisibility()));
3426 }
3427 final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
3428 for (int i = 0; i < transientCount; ++i) {
3429 View view = mTransientViews.get(i);
3430 view.dispatchAttachedToWindow(info,
3431 combineVisibility(visibility, view.getVisibility()));
3432 }
3433 }
3434
3435 @Override
3436 void dispatchScreenStateChanged(int screenState) {
3437 super.dispatchScreenStateChanged(screenState);
3438
3439 final int count = mChildrenCount;
3440 final View[] children = mChildren;
3441 for (int i = 0; i < count; i++) {
3442 children[i].dispatchScreenStateChanged(screenState);
3443 }
3444 }
3445
3446 @Override
3447 void dispatchMovedToDisplay(Display display, Configuration config) {
3448 super.dispatchMovedToDisplay(display, config);
3449
3450 final int count = mChildrenCount;
3451 final View[] children = mChildren;
3452 for (int i = 0; i < count; i++) {
3453 children[i].dispatchMovedToDisplay(display, config);
3454 }
3455 }
3456
3457 /** @hide */
3458 @Override
3459 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
3460 boolean handled = false;
3461 if (includeForAccessibility()) {
3462 handled = super.dispatchPopulateAccessibilityEventInternal(event);
3463 if (handled) {
3464 return handled;
3465 }
3466 }
3467 // Let our children have a shot in populating the event.
3468 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
3469 try {
3470 final int childCount = children.getChildCount();
3471 for (int i = 0; i < childCount; i++) {
3472 View child = children.getChildAt(i);
3473 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3474 handled = child.dispatchPopulateAccessibilityEvent(event);
3475 if (handled) {
3476 return handled;
3477 }
3478 }
3479 }
3480 } finally {
3481 children.recycle();
3482 }
3483 return false;
3484 }
3485
3486 /**
3487 * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation
3488 * adds in all child views of the view group, in addition to calling the default View
3489 * implementation.
3490 */
3491 @Override
3492 public void dispatchProvideStructure(ViewStructure structure) {
3493 super.dispatchProvideStructure(structure);
3494 if (isAssistBlocked() || structure.getChildCount() != 0) {
3495 return;
3496 }
3497 final int childrenCount = mChildrenCount;
3498 if (childrenCount <= 0) {
3499 return;
3500 }
3501
3502 if (!isLaidOut()) {
3503 if (Helper.sVerbose) {
3504 Log.v(VIEW_LOG_TAG, "dispatchProvideStructure(): not laid out, ignoring "
3505 + childrenCount + " children of " + getAccessibilityViewId());
3506 }
3507 return;
3508 }
3509
3510 structure.setChildCount(childrenCount);
3511 ArrayList<View> preorderedList = buildOrderedChildList();
3512 boolean customOrder = preorderedList == null
3513 && isChildrenDrawingOrderEnabled();
3514 for (int i = 0; i < childrenCount; i++) {
3515 int childIndex;
3516 try {
3517 childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
3518 } catch (IndexOutOfBoundsException e) {
3519 childIndex = i;
3520 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
3521 Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
3522 + i + " of " + childrenCount, e);
3523 // At least one app is failing when we call getChildDrawingOrder
3524 // at this point, so deal semi-gracefully with it by falling back
3525 // on the basic order.
3526 customOrder = false;
3527 if (i > 0) {
3528 // If we failed at the first index, there really isn't
3529 // anything to do -- we will just proceed with the simple
3530 // sequence order.
3531 // Otherwise, we failed in the middle, so need to come up
3532 // with an order for the remaining indices and use that.
3533 // Failed at the first one, easy peasy.
3534 int[] permutation = new int[childrenCount];
3535 SparseBooleanArray usedIndices = new SparseBooleanArray();
3536 // Go back and collected the indices we have done so far.
3537 for (int j = 0; j < i; j++) {
3538 permutation[j] = getChildDrawingOrder(childrenCount, j);
3539 usedIndices.put(permutation[j], true);
3540 }
3541 // Fill in the remaining indices with indices that have not
3542 // yet been used.
3543 int nextIndex = 0;
3544 for (int j = i; j < childrenCount; j++) {
3545 while (usedIndices.get(nextIndex, false)) {
3546 nextIndex++;
3547 }
3548 permutation[j] = nextIndex;
3549 nextIndex++;
3550 }
3551 // Build the final view list.
3552 preorderedList = new ArrayList<>(childrenCount);
3553 for (int j = 0; j < childrenCount; j++) {
3554 final int index = permutation[j];
3555 final View child = mChildren[index];
3556 preorderedList.add(child);
3557 }
3558 }
3559 } else {
3560 throw e;
3561 }
3562 }
3563 final View child = getAndVerifyPreorderedView(preorderedList, mChildren,
3564 childIndex);
3565 final ViewStructure cstructure = structure.newChild(i);
3566 child.dispatchProvideStructure(cstructure);
3567 }
3568 if (preorderedList != null) {
3569 preorderedList.clear();
3570 }
3571 }
3572
3573 /**
3574 * {@inheritDoc}
3575 *
3576 * <p>This implementation adds in all child views of the view group, in addition to calling the
3577 * default {@link View} implementation.
3578 */
3579 @Override
3580 public void dispatchProvideAutofillStructure(ViewStructure structure,
3581 @AutofillFlags int flags) {
3582 super.dispatchProvideAutofillStructure(structure, flags);
3583
3584 if (structure.getChildCount() != 0) {
3585 return;
3586 }
3587
3588 if (!isLaidOut()) {
3589 if (Helper.sVerbose) {
3590 Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
3591 + mChildrenCount + " children of " + getAutofillId());
3592 }
3593 return;
3594 }
3595
3596 final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags);
3597 final int childrenCount = children.size();
3598 structure.setChildCount(childrenCount);
3599 for (int i = 0; i < childrenCount; i++) {
3600 final View child = children.get(i);
3601 final ViewStructure cstructure = structure.newChild(i);
3602 child.dispatchProvideAutofillStructure(cstructure, flags);
3603 }
3604 children.recycle();
3605 }
3606
3607 /** @hide */
3608 @Override
3609 public void dispatchProvideContentCaptureStructure() {
3610 super.dispatchProvideContentCaptureStructure();
3611
3612 if (!isLaidOut()) return;
3613
3614 final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture();
3615 final int childrenCount = children.size();
3616 for (int i = 0; i < childrenCount; i++) {
3617 final View child = children.get(i);
3618 child.dispatchProvideContentCaptureStructure();
3619 }
3620 children.recycle();
3621 }
3622
3623 /**
3624 * Gets the children for autofill. Children for autofill are the first
3625 * level descendants that are important for autofill. The returned
3626 * child list object is pooled and the caller must recycle it once done.
3627 * @hide */
3628 private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill(
3629 @AutofillFlags int flags) {
3630 final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
3631 .obtain();
3632 populateChildrenForAutofill(children, flags);
3633 return children;
3634 }
3635
3636 /** @hide */
3637 private void populateChildrenForAutofill(ArrayList<View> list, @AutofillFlags int flags) {
3638 final int childrenCount = mChildrenCount;
3639 if (childrenCount <= 0) {
3640 return;
3641 }
3642 final ArrayList<View> preorderedList = buildOrderedChildList();
3643 final boolean customOrder = preorderedList == null
3644 && isChildrenDrawingOrderEnabled();
3645 for (int i = 0; i < childrenCount; i++) {
3646 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
3647 final View child = (preorderedList == null)
3648 ? mChildren[childIndex] : preorderedList.get(childIndex);
3649 if ((flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
3650 || child.isImportantForAutofill()) {
3651 list.add(child);
3652 } else if (child instanceof ViewGroup) {
3653 ((ViewGroup) child).populateChildrenForAutofill(list, flags);
3654 }
3655 }
3656 }
3657
3658 private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() {
3659 final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
3660 .obtain();
3661 populateChildrenForContentCapture(children);
3662 return children;
3663 }
3664
3665 /** @hide */
3666 private void populateChildrenForContentCapture(ArrayList<View> list) {
3667 final int childrenCount = mChildrenCount;
3668 if (childrenCount <= 0) {
3669 return;
3670 }
3671 final ArrayList<View> preorderedList = buildOrderedChildList();
3672 final boolean customOrder = preorderedList == null
3673 && isChildrenDrawingOrderEnabled();
3674 for (int i = 0; i < childrenCount; i++) {
3675 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
3676 final View child = (preorderedList == null)
3677 ? mChildren[childIndex] : preorderedList.get(childIndex);
3678 if (child.isImportantForContentCapture()) {
3679 list.add(child);
3680 } else if (child instanceof ViewGroup) {
3681 ((ViewGroup) child).populateChildrenForContentCapture(list);
3682 }
3683 }
3684 }
3685
3686 private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
3687 int childIndex) {
3688 final View child;
3689 if (preorderedList != null) {
3690 child = preorderedList.get(childIndex);
3691 if (child == null) {
3692 throw new RuntimeException("Invalid preorderedList contained null child at index "
3693 + childIndex);
3694 }
3695 } else {
3696 child = children[childIndex];
3697 }
3698 return child;
3699 }
3700
3701 /** @hide */
3702 @Override
3703 @UnsupportedAppUsage
3704 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
3705 super.onInitializeAccessibilityNodeInfoInternal(info);
3706 if (getAccessibilityNodeProvider() != null) {
3707 return;
3708 }
3709 if (mAttachInfo != null) {
3710 final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
3711 childrenForAccessibility.clear();
3712 addChildrenForAccessibility(childrenForAccessibility);
3713 final int childrenForAccessibilityCount = childrenForAccessibility.size();
3714 for (int i = 0; i < childrenForAccessibilityCount; i++) {
3715 final View child = childrenForAccessibility.get(i);
3716 info.addChildUnchecked(child);
3717 }
3718 childrenForAccessibility.clear();
3719 }
3720 info.setAvailableExtraData(Collections.singletonList(
3721 AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY));
3722 }
3723
3724 /**
3725 * {@inheritDoc}
3726 *
3727 * @param info The info to which to add the extra data. Never {@code null}.
3728 * @param extraDataKey A key specifying the type of extra data to add to the info. The
3729 * extra data should be added to the {@link Bundle} returned by
3730 * the info's {@link AccessibilityNodeInfo#getExtras} method. Never
3731 * {@code null}.
3732 * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
3733 * {@code null} if the service provided no arguments.
3734 *
3735 */
3736 @Override
3737 public void addExtraDataToAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info,
3738 @NonNull String extraDataKey, @Nullable Bundle arguments) {
3739 if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
3740 final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
3741 AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
3742 extraRenderingInfo.setLayoutSize(getLayoutParams().width, getLayoutParams().height);
3743 info.setExtraRenderingInfo(extraRenderingInfo);
3744 }
3745 }
3746
3747 @Override
3748 public CharSequence getAccessibilityClassName() {
3749 return ViewGroup.class.getName();
3750 }
3751
3752 @Override
3753 public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
3754 // If this is a live region, we should send a subtree change event
3755 // from this view. Otherwise, we can let it propagate up.
3756 if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
3757 notifyViewAccessibilityStateChangedIfNeeded(
3758 AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
3759 } else if (mParent != null) {
3760 try {
3761 mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
3762 } catch (AbstractMethodError e) {
3763 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
3764 " does not fully implement ViewParent", e);
3765 }
3766 }
3767 }
3768
3769 /** @hide */
3770 @Override
3771 public void notifySubtreeAccessibilityStateChangedIfNeeded() {
3772 if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
3773 return;
3774 }
3775 // If something important for a11y is happening in this subtree, make sure it's dispatched
3776 // from a view that is important for a11y so it doesn't get lost.
3777 if ((getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
3778 && !isImportantForAccessibility() && (getChildCount() > 0)) {
3779 ViewParent a11yParent = getParentForAccessibility();
3780 if (a11yParent instanceof View) {
3781 ((View) a11yParent).notifySubtreeAccessibilityStateChangedIfNeeded();
3782 return;
3783 }
3784 }
3785 super.notifySubtreeAccessibilityStateChangedIfNeeded();
3786 }
3787
3788 @Override
3789 void resetSubtreeAccessibilityStateChanged() {
3790 super.resetSubtreeAccessibilityStateChanged();
3791 View[] children = mChildren;
3792 final int childCount = mChildrenCount;
3793 for (int i = 0; i < childCount; i++) {
3794 children[i].resetSubtreeAccessibilityStateChanged();
3795 }
3796 }
3797
3798 /**
3799 * Counts the number of children of this View that will be sent to an accessibility service.
3800 *
3801 * @return The number of children an {@code AccessibilityNodeInfo} rooted at this View
3802 * would have.
3803 */
3804 int getNumChildrenForAccessibility() {
3805 int numChildrenForAccessibility = 0;
3806 for (int i = 0; i < getChildCount(); i++) {
3807 View child = getChildAt(i);
3808 if (child.includeForAccessibility()) {
3809 numChildrenForAccessibility++;
3810 } else if (child instanceof ViewGroup) {
3811 numChildrenForAccessibility += ((ViewGroup) child)
3812 .getNumChildrenForAccessibility();
3813 }
3814 }
3815 return numChildrenForAccessibility;
3816 }
3817
3818 /**
3819 * {@inheritDoc}
3820 *
3821 * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p>
3822 *
3823 * @param target The target view dispatching this action
3824 * @param action Action being performed; see
3825 * {@link android.view.accessibility.AccessibilityNodeInfo}
3826 * @param args Optional action arguments
3827 * @return false by default. Subclasses should return true if they handle the event.
3828 */
3829 @Override
3830 public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
3831 return false;
3832 }
3833
3834 @Override
3835 @UnsupportedAppUsage
3836 void dispatchDetachedFromWindow() {
3837 // If we still have a touch target, we are still in the process of
3838 // dispatching motion events to a child; we need to get rid of that
3839 // child to avoid dispatching events to it after the window is torn
3840 // down. To make sure we keep the child in a consistent state, we
3841 // first send it an ACTION_CANCEL motion event.
3842 cancelAndClearTouchTargets(null);
3843
3844 // Similarly, set ACTION_EXIT to all hover targets and clear them.
3845 exitHoverTargets();
3846 exitTooltipHoverTargets();
3847
3848 // In case view is detached while transition is running
3849 mLayoutCalledWhileSuppressed = false;
3850
3851 // Tear down our drag tracking
3852 mChildrenInterestedInDrag = null;
3853 mIsInterestedInDrag = false;
3854 if (mCurrentDragStartEvent != null) {
3855 mCurrentDragStartEvent.recycle();
3856 mCurrentDragStartEvent = null;
3857 }
3858
3859 final int count = mChildrenCount;
3860 final View[] children = mChildren;
3861 for (int i = 0; i < count; i++) {
3862 children[i].dispatchDetachedFromWindow();
3863 }
3864 clearDisappearingChildren();
3865 final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
3866 for (int i = 0; i < transientCount; ++i) {
3867 View view = mTransientViews.get(i);
3868 view.dispatchDetachedFromWindow();
3869 }
3870 super.dispatchDetachedFromWindow();
3871 }
3872
3873 /**
3874 * @hide
3875 */
3876 @Override
3877 protected void internalSetPadding(int left, int top, int right, int bottom) {
3878 super.internalSetPadding(left, top, right, bottom);
3879
3880 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
3881 mGroupFlags |= FLAG_PADDING_NOT_NULL;
3882 } else {
3883 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
3884 }
3885 }
3886
3887 @Override
3888 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
3889 super.dispatchSaveInstanceState(container);
3890 final int count = mChildrenCount;
3891 final View[] children = mChildren;
3892 for (int i = 0; i < count; i++) {
3893 View c = children[i];
3894 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
3895 c.dispatchSaveInstanceState(container);
3896 }
3897 }
3898 }
3899
3900 /**
3901 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
3902 * to only this view, not to its children. For use when overriding
3903 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
3904 * subclasses to freeze their own state but not the state of their children.
3905 *
3906 * @param container the container
3907 */
3908 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
3909 super.dispatchSaveInstanceState(container);
3910 }
3911
3912 @Override
3913 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
3914 super.dispatchRestoreInstanceState(container);
3915 final int count = mChildrenCount;
3916 final View[] children = mChildren;
3917 for (int i = 0; i < count; i++) {
3918 View c = children[i];
3919 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
3920 c.dispatchRestoreInstanceState(container);
3921 }
3922 }
3923 }
3924
3925 /**
3926 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
3927 * to only this view, not to its children. For use when overriding
3928 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
3929 * subclasses to thaw their own state but not the state of their children.
3930 *
3931 * @param container the container
3932 */
3933 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
3934 super.dispatchRestoreInstanceState(container);
3935 }
3936
3937 /**
3938 * Enables or disables the drawing cache for each child of this view group.
3939 *
3940 * @param enabled true to enable the cache, false to dispose of it
3941 *
3942 * @deprecated The view drawing cache was largely made obsolete with the introduction of
3943 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
3944 * layers are largely unnecessary and can easily result in a net loss in performance due to the
3945 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
3946 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
3947 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
3948 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
3949 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
3950 * software-rendered usages are discouraged and have compatibility issues with hardware-only
3951 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
3952 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
3953 * reports or unit testing the {@link PixelCopy} API is recommended.
3954 */
3955 @Deprecated
3956 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
3957 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
3958 final View[] children = mChildren;
3959 final int count = mChildrenCount;
3960 for (int i = 0; i < count; i++) {
3961 children[i].setDrawingCacheEnabled(enabled);
3962 }
3963 }
3964 }
3965
3966 /**
3967 * @hide
3968 */
3969 @Override
3970 public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) {
3971 int count = mChildrenCount;
3972 int[] visibilities = null;
3973
3974 if (skipChildren) {
3975 visibilities = new int[count];
3976 for (int i = 0; i < count; i++) {
3977 View child = getChildAt(i);
3978 visibilities[i] = child.getVisibility();
3979 if (visibilities[i] == View.VISIBLE) {
3980 child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
3981 | (View.INVISIBLE & View.VISIBILITY_MASK);
3982 }
3983 }
3984 }
3985
3986 try {
3987 return super.createSnapshot(canvasProvider, skipChildren);
3988 } finally {
3989 if (skipChildren) {
3990 for (int i = 0; i < count; i++) {
3991 View child = getChildAt(i);
3992 child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
3993 | (visibilities[i] & View.VISIBILITY_MASK);
3994 }
3995 }
3996 }
3997 }
3998
3999 /** Return true if this ViewGroup is laying out using optical bounds. */
4000 boolean isLayoutModeOptical() {
4001 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
4002 }
4003
4004 @Override
4005 Insets computeOpticalInsets() {
4006 if (isLayoutModeOptical()) {
4007 int left = 0;
4008 int top = 0;
4009 int right = 0;
4010 int bottom = 0;
4011 for (int i = 0; i < mChildrenCount; i++) {
4012 View child = getChildAt(i);
4013 if (child.getVisibility() == VISIBLE) {
4014 Insets insets = child.getOpticalInsets();
4015 left = Math.max(left, insets.left);
4016 top = Math.max(top, insets.top);
4017 right = Math.max(right, insets.right);
4018 bottom = Math.max(bottom, insets.bottom);
4019 }
4020 }
4021 return Insets.of(left, top, right, bottom);
4022 } else {
4023 return Insets.NONE;
4024 }
4025 }
4026
4027 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
4028 if (x1 != x2 && y1 != y2) {
4029 if (x1 > x2) {
4030 int tmp = x1; x1 = x2; x2 = tmp;
4031 }
4032 if (y1 > y2) {
4033 int tmp = y1; y1 = y2; y2 = tmp;
4034 }
4035 canvas.drawRect(x1, y1, x2, y2, paint);
4036 }
4037 }
4038
4039 private static int sign(int x) {
4040 return (x >= 0) ? 1 : -1;
4041 }
4042
4043 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
4044 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
4045 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
4046 }
4047
4048 private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
4049 int lineLength, int lineWidth) {
4050 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
4051 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
4052 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
4053 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
4054 }
4055
4056 private static void fillDifference(Canvas canvas,
4057 int x2, int y2, int x3, int y3,
4058 int dx1, int dy1, int dx2, int dy2, Paint paint) {
4059 int x1 = x2 - dx1;
4060 int y1 = y2 - dy1;
4061
4062 int x4 = x3 + dx2;
4063 int y4 = y3 + dy2;
4064
4065 fillRect(canvas, paint, x1, y1, x4, y2);
4066 fillRect(canvas, paint, x1, y2, x2, y3);
4067 fillRect(canvas, paint, x3, y2, x4, y3);
4068 fillRect(canvas, paint, x1, y3, x4, y4);
4069 }
4070
4071 /**
4072 * @hide
4073 */
4074 protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
4075 for (int i = 0; i < getChildCount(); i++) {
4076 View c = getChildAt(i);
4077 c.getLayoutParams().onDebugDraw(c, canvas, paint);
4078 }
4079 }
4080
4081 /**
4082 * @hide
4083 */
4084 protected void onDebugDraw(Canvas canvas) {
4085 Paint paint = getDebugPaint();
4086
4087 // Draw optical bounds
4088 {
4089 paint.setColor(Color.RED);
4090 paint.setStyle(Paint.Style.STROKE);
4091
4092 for (int i = 0; i < getChildCount(); i++) {
4093 View c = getChildAt(i);
4094 if (c.getVisibility() != View.GONE) {
4095 Insets insets = c.getOpticalInsets();
4096
4097 drawRect(canvas, paint,
4098 c.getLeft() + insets.left,
4099 c.getTop() + insets.top,
4100 c.getRight() - insets.right - 1,
4101 c.getBottom() - insets.bottom - 1);
4102 }
4103 }
4104 }
4105
4106 // Draw margins
4107 {
4108 paint.setColor(Color.argb(63, 255, 0, 255));
4109 paint.setStyle(Paint.Style.FILL);
4110
4111 onDebugDrawMargins(canvas, paint);
4112 }
4113
4114 // Draw clip bounds
4115 {
4116 paint.setColor(DEBUG_CORNERS_COLOR);
4117 paint.setStyle(Paint.Style.FILL);
4118
4119 int lineLength = dipsToPixels(DEBUG_CORNERS_SIZE_DIP);
4120 int lineWidth = dipsToPixels(1);
4121 for (int i = 0; i < getChildCount(); i++) {
4122 View c = getChildAt(i);
4123 if (c.getVisibility() != View.GONE) {
4124 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
4125 paint, lineLength, lineWidth);
4126 }
4127 }
4128 }
4129 }
4130
4131 @Override
4132 protected void dispatchDraw(Canvas canvas) {
4133 boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
4134 final int childrenCount = mChildrenCount;
4135 final View[] children = mChildren;
4136 int flags = mGroupFlags;
4137
4138 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
4139 final boolean buildCache = !isHardwareAccelerated();
4140 for (int i = 0; i < childrenCount; i++) {
4141 final View child = children[i];
4142 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
4143 final LayoutParams params = child.getLayoutParams();
4144 attachLayoutAnimationParameters(child, params, i, childrenCount);
4145 bindLayoutAnimation(child);
4146 }
4147 }
4148
4149 final LayoutAnimationController controller = mLayoutAnimationController;
4150 if (controller.willOverlap()) {
4151 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
4152 }
4153
4154 controller.start();
4155
4156 mGroupFlags &= ~FLAG_RUN_ANIMATION;
4157 mGroupFlags &= ~FLAG_ANIMATION_DONE;
4158
4159 if (mAnimationListener != null) {
4160 mAnimationListener.onAnimationStart(controller.getAnimation());
4161 }
4162 }
4163
4164 int clipSaveCount = 0;
4165 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
4166 if (clipToPadding) {
4167 clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
4168 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
4169 mScrollX + mRight - mLeft - mPaddingRight,
4170 mScrollY + mBottom - mTop - mPaddingBottom);
4171 }
4172
4173 // We will draw our child's animation, let's reset the flag
4174 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
4175 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
4176
4177 boolean more = false;
4178 final long drawingTime = getDrawingTime();
4179
4180 if (usingRenderNodeProperties) canvas.insertReorderBarrier();
4181 final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
4182 int transientIndex = transientCount != 0 ? 0 : -1;
4183 // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
4184 // draw reordering internally
4185 final ArrayList<View> preorderedList = usingRenderNodeProperties
4186 ? null : buildOrderedChildList();
4187 final boolean customOrder = preorderedList == null
4188 && isChildrenDrawingOrderEnabled();
4189 for (int i = 0; i < childrenCount; i++) {
4190 while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
4191 final View transientChild = mTransientViews.get(transientIndex);
4192 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
4193 transientChild.getAnimation() != null) {
4194 more |= drawChild(canvas, transientChild, drawingTime);
4195 }
4196 transientIndex++;
4197 if (transientIndex >= transientCount) {
4198 transientIndex = -1;
4199 }
4200 }
4201
4202 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
4203 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
4204 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
4205 more |= drawChild(canvas, child, drawingTime);
4206 }
4207 }
4208 while (transientIndex >= 0) {
4209 // there may be additional transient views after the normal views
4210 final View transientChild = mTransientViews.get(transientIndex);
4211 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
4212 transientChild.getAnimation() != null) {
4213 more |= drawChild(canvas, transientChild, drawingTime);
4214 }
4215 transientIndex++;
4216 if (transientIndex >= transientCount) {
4217 break;
4218 }
4219 }
4220 if (preorderedList != null) preorderedList.clear();
4221
4222 // Draw any disappearing views that have animations
4223 if (mDisappearingChildren != null) {
4224 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4225 final int disappearingCount = disappearingChildren.size() - 1;
4226 // Go backwards -- we may delete as animations finish
4227 for (int i = disappearingCount; i >= 0; i--) {
4228 final View child = disappearingChildren.get(i);
4229 more |= drawChild(canvas, child, drawingTime);
4230 }
4231 }
4232 if (usingRenderNodeProperties) canvas.insertInorderBarrier();
4233
4234 if (isShowingLayoutBounds()) {
4235 onDebugDraw(canvas);
4236 }
4237
4238 if (clipToPadding) {
4239 canvas.restoreToCount(clipSaveCount);
4240 }
4241
4242 // mGroupFlags might have been updated by drawChild()
4243 flags = mGroupFlags;
4244
4245 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
4246 invalidate(true);
4247 }
4248
4249 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
4250 mLayoutAnimationController.isDone() && !more) {
4251 // We want to erase the drawing cache and notify the listener after the
4252 // next frame is drawn because one extra invalidate() is caused by
4253 // drawChild() after the animation is over
4254 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
4255 final Runnable end = new Runnable() {
4256 @Override
4257 public void run() {
4258 notifyAnimationListener();
4259 }
4260 };
4261 post(end);
4262 }
4263 }
4264
4265 /**
4266 * Returns the ViewGroupOverlay for this view group, creating it if it does
4267 * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
4268 * {@link ViewGroupOverlay} allows views to be added to the overlay. These
4269 * views, like overlay drawables, are visual-only; they do not receive input
4270 * events and should not be used as anything other than a temporary
4271 * representation of a view in a parent container, such as might be used
4272 * by an animation effect.
4273 *
4274 * <p>Note: Overlays do not currently work correctly with {@link
4275 * SurfaceView} or {@link TextureView}; contents in overlays for these
4276 * types of views may not display correctly.</p>
4277 *
4278 * @return The ViewGroupOverlay object for this view.
4279 * @see ViewGroupOverlay
4280 */
4281 @Override
4282 public ViewGroupOverlay getOverlay() {
4283 if (mOverlay == null) {
4284 mOverlay = new ViewGroupOverlay(mContext, this);
4285 }
4286 return (ViewGroupOverlay) mOverlay;
4287 }
4288
4289 /**
4290 * Converts drawing order position to container position. Override this
4291 * if you want to change the drawing order of children. By default, it
4292 * returns drawingPosition.
4293 * <p>
4294 * NOTE: In order for this method to be called, you must enable child ordering
4295 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
4296 *
4297 * @param drawingPosition the drawing order position.
4298 * @return the container position of a child for this drawing order position.
4299 *
4300 * @see #setChildrenDrawingOrderEnabled(boolean)
4301 * @see #isChildrenDrawingOrderEnabled()
4302 */
4303 protected int getChildDrawingOrder(int childCount, int drawingPosition) {
4304 return drawingPosition;
4305 }
4306
4307 /**
4308 * Converts drawing order position to container position.
4309 * <p>
4310 * Children are not necessarily drawn in the order in which they appear in the container.
4311 * ViewGroups can enable a custom ordering via {@link #setChildrenDrawingOrderEnabled(boolean)}.
4312 * This method returns the container position of a child that appears in the given position
4313 * in the current drawing order.
4314 *
4315 * @param drawingPosition the drawing order position.
4316 * @return the container position of a child for this drawing order position.
4317 *
4318 * @see #getChildDrawingOrder(int, int)}
4319 */
4320 public final int getChildDrawingOrder(int drawingPosition) {
4321 return getChildDrawingOrder(getChildCount(), drawingPosition);
4322 }
4323
4324 private boolean hasChildWithZ() {
4325 for (int i = 0; i < mChildrenCount; i++) {
4326 if (mChildren[i].getZ() != 0) return true;
4327 }
4328 return false;
4329 }
4330
4331 /**
4332 * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
4333 * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
4334 * after use to avoid leaking child Views.
4335 *
4336 * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
4337 * children.
4338 */
4339 ArrayList<View> buildOrderedChildList() {
4340 final int childrenCount = mChildrenCount;
4341 if (childrenCount <= 1 || !hasChildWithZ()) return null;
4342
4343 if (mPreSortedChildren == null) {
4344 mPreSortedChildren = new ArrayList<>(childrenCount);
4345 } else {
4346 // callers should clear, so clear shouldn't be necessary, but for safety...
4347 mPreSortedChildren.clear();
4348 mPreSortedChildren.ensureCapacity(childrenCount);
4349 }
4350
4351 final boolean customOrder = isChildrenDrawingOrderEnabled();
4352 for (int i = 0; i < childrenCount; i++) {
4353 // add next child (in child order) to end of list
4354 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
4355 final View nextChild = mChildren[childIndex];
4356 final float currentZ = nextChild.getZ();
4357
4358 // insert ahead of any Views with greater Z
4359 int insertIndex = i;
4360 while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
4361 insertIndex--;
4362 }
4363 mPreSortedChildren.add(insertIndex, nextChild);
4364 }
4365 return mPreSortedChildren;
4366 }
4367
4368 private void notifyAnimationListener() {
4369 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
4370 mGroupFlags |= FLAG_ANIMATION_DONE;
4371
4372 if (mAnimationListener != null) {
4373 final Runnable end = new Runnable() {
4374 @Override
4375 public void run() {
4376 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
4377 }
4378 };
4379 post(end);
4380 }
4381
4382 invalidate(true);
4383 }
4384
4385 /**
4386 * This method is used to cause children of this ViewGroup to restore or recreate their
4387 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
4388 * to recreate its own display list, which would happen if it went through the normal
4389 * draw/dispatchDraw mechanisms.
4390 *
4391 * @hide
4392 */
4393 @Override
4394 @UnsupportedAppUsage
4395 protected void dispatchGetDisplayList() {
4396 final int count = mChildrenCount;
4397 final View[] children = mChildren;
4398 for (int i = 0; i < count; i++) {
4399 final View child = children[i];
4400 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
4401 recreateChildDisplayList(child);
4402 }
4403 }
4404 final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
4405 for (int i = 0; i < transientCount; ++i) {
4406 View child = mTransientViews.get(i);
4407 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
4408 recreateChildDisplayList(child);
4409 }
4410 }
4411 if (mOverlay != null) {
4412 View overlayView = mOverlay.getOverlayView();
4413 recreateChildDisplayList(overlayView);
4414 }
4415 if (mDisappearingChildren != null) {
4416 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4417 final int disappearingCount = disappearingChildren.size();
4418 for (int i = 0; i < disappearingCount; ++i) {
4419 final View child = disappearingChildren.get(i);
4420 recreateChildDisplayList(child);
4421 }
4422 }
4423 }
4424
4425 private void recreateChildDisplayList(View child) {
4426 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
4427 child.mPrivateFlags &= ~PFLAG_INVALIDATED;
4428 child.updateDisplayListIfDirty();
4429 child.mRecreateDisplayList = false;
4430 }
4431
4432 /**
4433 * Draw one child of this View Group. This method is responsible for getting
4434 * the canvas in the right state. This includes clipping, translating so
4435 * that the child's scrolled origin is at 0, 0, and applying any animation
4436 * transformations.
4437 *
4438 * @param canvas The canvas on which to draw the child
4439 * @param child Who to draw
4440 * @param drawingTime The time at which draw is occurring
4441 * @return True if an invalidate() was issued
4442 */
4443 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
4444 return child.draw(canvas, this, drawingTime);
4445 }
4446
4447 @Override
4448 void getScrollIndicatorBounds(@NonNull Rect out) {
4449 super.getScrollIndicatorBounds(out);
4450
4451 // If we have padding and we're supposed to clip children to that
4452 // padding, offset the scroll indicators to match our clip bounds.
4453 final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
4454 if (clipToPadding) {
4455 out.left += mPaddingLeft;
4456 out.right -= mPaddingRight;
4457 out.top += mPaddingTop;
4458 out.bottom -= mPaddingBottom;
4459 }
4460 }
4461
4462 /**
4463 * Returns whether this group's children are clipped to their bounds before drawing.
4464 * The default value is true.
4465 * @see #setClipChildren(boolean)
4466 *
4467 * @return True if the group's children will be clipped to their bounds,
4468 * false otherwise.
4469 */
4470 @ViewDebug.ExportedProperty(category = "drawing")
4471 @InspectableProperty
4472 public boolean getClipChildren() {
4473 return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
4474 }
4475
4476 /**
4477 * By default, children are clipped to their bounds before drawing. This
4478 * allows view groups to override this behavior for animations, etc.
4479 *
4480 * @param clipChildren true to clip children to their bounds,
4481 * false otherwise
4482 * @attr ref android.R.styleable#ViewGroup_clipChildren
4483 */
4484 public void setClipChildren(boolean clipChildren) {
4485 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
4486 if (clipChildren != previousValue) {
4487 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
4488 for (int i = 0; i < mChildrenCount; ++i) {
4489 View child = getChildAt(i);
4490 if (child.mRenderNode != null) {
4491 child.mRenderNode.setClipToBounds(clipChildren);
4492 }
4493 }
4494 invalidate(true);
4495 }
4496 }
4497
4498 /**
4499 * Sets whether this ViewGroup will clip its children to its padding and resize (but not
4500 * clip) any EdgeEffect to the padded region, if padding is present.
4501 * <p>
4502 * By default, children are clipped to the padding of their parent
4503 * ViewGroup. This clipping behavior is only enabled if padding is non-zero.
4504 *
4505 * @param clipToPadding true to clip children to the padding of the group, and resize (but
4506 * not clip) any EdgeEffect to the padded region. False otherwise.
4507 * @attr ref android.R.styleable#ViewGroup_clipToPadding
4508 */
4509 public void setClipToPadding(boolean clipToPadding) {
4510 if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) {
4511 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
4512 invalidate(true);
4513 }
4514 }
4515
4516 /**
4517 * Returns whether this ViewGroup will clip its children to its padding, and resize (but
4518 * not clip) any EdgeEffect to the padded region, if padding is present.
4519 * <p>
4520 * By default, children are clipped to the padding of their parent
4521 * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
4522 *
4523 * @return true if this ViewGroup clips children to its padding and resizes (but doesn't
4524 * clip) any EdgeEffect to the padded region, false otherwise.
4525 *
4526 * @attr ref android.R.styleable#ViewGroup_clipToPadding
4527 */
4528 @ViewDebug.ExportedProperty(category = "drawing")
4529 @InspectableProperty
4530 public boolean getClipToPadding() {
4531 return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
4532 }
4533
4534 @Override
4535 public void dispatchSetSelected(boolean selected) {
4536 final View[] children = mChildren;
4537 final int count = mChildrenCount;
4538 for (int i = 0; i < count; i++) {
4539 children[i].setSelected(selected);
4540 }
4541 }
4542
4543 @Override
4544 public void dispatchSetActivated(boolean activated) {
4545 final View[] children = mChildren;
4546 final int count = mChildrenCount;
4547 for (int i = 0; i < count; i++) {
4548 children[i].setActivated(activated);
4549 }
4550 }
4551
4552 @Override
4553 protected void dispatchSetPressed(boolean pressed) {
4554 final View[] children = mChildren;
4555 final int count = mChildrenCount;
4556 for (int i = 0; i < count; i++) {
4557 final View child = children[i];
4558 // Children that are clickable on their own should not
4559 // show a pressed state when their parent view does.
4560 // Clearing a pressed state always propagates.
4561 if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
4562 child.setPressed(pressed);
4563 }
4564 }
4565 }
4566
4567 /**
4568 * Dispatches drawable hotspot changes to child views that meet at least
4569 * one of the following criteria:
4570 * <ul>
4571 * <li>Returns {@code false} from both {@link View#isClickable()} and
4572 * {@link View#isLongClickable()}</li>
4573 * <li>Requests duplication of parent state via
4574 * {@link View#setDuplicateParentStateEnabled(boolean)}</li>
4575 * </ul>
4576 *
4577 * @param x hotspot x coordinate
4578 * @param y hotspot y coordinate
4579 * @see #drawableHotspotChanged(float, float)
4580 */
4581 @Override
4582 public void dispatchDrawableHotspotChanged(float x, float y) {
4583 final int count = mChildrenCount;
4584 if (count == 0) {
4585 return;
4586 }
4587
4588 final View[] children = mChildren;
4589 for (int i = 0; i < count; i++) {
4590 final View child = children[i];
4591 // Children that are clickable on their own should not
4592 // receive hotspots when their parent view does.
4593 final boolean nonActionable = !child.isClickable() && !child.isLongClickable();
4594 final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0;
4595 if (nonActionable || duplicatesState) {
4596 final float[] point = getTempLocationF();
4597 point[0] = x;
4598 point[1] = y;
4599 transformPointToViewLocal(point, child);
4600 child.drawableHotspotChanged(point[0], point[1]);
4601 }
4602 }
4603 }
4604
4605 @Override
4606 void dispatchCancelPendingInputEvents() {
4607 super.dispatchCancelPendingInputEvents();
4608
4609 final View[] children = mChildren;
4610 final int count = mChildrenCount;
4611 for (int i = 0; i < count; i++) {
4612 children[i].dispatchCancelPendingInputEvents();
4613 }
4614 }
4615
4616 /**
4617 * When this property is set to true, this ViewGroup supports static transformations on
4618 * children; this causes
4619 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
4620 * invoked when a child is drawn.
4621 *
4622 * Any subclass overriding
4623 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
4624 * set this property to true.
4625 *
4626 * @param enabled True to enable static transformations on children, false otherwise.
4627 *
4628 * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
4629 */
4630 protected void setStaticTransformationsEnabled(boolean enabled) {
4631 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
4632 }
4633
4634 /**
4635 * Sets <code>t</code> to be the static transformation of the child, if set, returning a
4636 * boolean to indicate whether a static transform was set. The default implementation
4637 * simply returns <code>false</code>; subclasses may override this method for different
4638 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
4639 * for this method to be called.
4640 *
4641 * @param child The child view whose static transform is being requested
4642 * @param t The Transformation which will hold the result
4643 * @return true if the transformation was set, false otherwise
4644 * @see #setStaticTransformationsEnabled(boolean)
4645 */
4646 protected boolean getChildStaticTransformation(View child, Transformation t) {
4647 return false;
4648 }
4649
4650 Transformation getChildTransformation() {
4651 if (mChildTransformation == null) {
4652 mChildTransformation = new Transformation();
4653 }
4654 return mChildTransformation;
4655 }
4656
4657 /**
4658 * {@hide}
4659 */
4660 @Override
4661 protected <T extends View> T findViewTraversal(@IdRes int id) {
4662 if (id == mID) {
4663 return (T) this;
4664 }
4665
4666 final View[] where = mChildren;
4667 final int len = mChildrenCount;
4668
4669 for (int i = 0; i < len; i++) {
4670 View v = where[i];
4671
4672 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
4673 v = v.findViewById(id);
4674
4675 if (v != null) {
4676 return (T) v;
4677 }
4678 }
4679 }
4680
4681 return null;
4682 }
4683
4684 /**
4685 * {@hide}
4686 */
4687 @Override
4688 protected <T extends View> T findViewWithTagTraversal(Object tag) {
4689 if (tag != null && tag.equals(mTag)) {
4690 return (T) this;
4691 }
4692
4693 final View[] where = mChildren;
4694 final int len = mChildrenCount;
4695
4696 for (int i = 0; i < len; i++) {
4697 View v = where[i];
4698
4699 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
4700 v = v.findViewWithTag(tag);
4701
4702 if (v != null) {
4703 return (T) v;
4704 }
4705 }
4706 }
4707
4708 return null;
4709 }
4710
4711 /**
4712 * {@hide}
4713 */
4714 @Override
4715 protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
4716 View childToSkip) {
4717 if (predicate.test(this)) {
4718 return (T) this;
4719 }
4720
4721 final View[] where = mChildren;
4722 final int len = mChildrenCount;
4723
4724 for (int i = 0; i < len; i++) {
4725 View v = where[i];
4726
4727 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
4728 v = v.findViewByPredicate(predicate);
4729
4730 if (v != null) {
4731 return (T) v;
4732 }
4733 }
4734 }
4735
4736 return null;
4737 }
4738
4739 /**
4740 * This method adds a view to this container at the specified index purely for the
4741 * purposes of allowing that view to draw even though it is not a normal child of
4742 * the container. That is, the view does not participate in layout, focus, accessibility,
4743 * input, or other normal view operations; it is purely an item to be drawn during the normal
4744 * rendering operation of this container. The index that it is added at is the order
4745 * in which it will be drawn, with respect to the other views in the container.
4746 * For example, a transient view added at index 0 will be drawn before all other views
4747 * in the container because it will be drawn first (including before any real view
4748 * at index 0). There can be more than one transient view at any particular index;
4749 * these views will be drawn in the order in which they were added to the list of
4750 * transient views. The index of transient views can also be greater than the number
4751 * of normal views in the container; that just means that they will be drawn after all
4752 * other views are drawn.
4753 *
4754 * <p>Note that since transient views do not participate in layout, they must be sized
4755 * manually or, more typically, they should just use the size that they had before they
4756 * were removed from their container.</p>
4757 *
4758 * <p>Transient views are useful for handling animations of views that have been removed
4759 * from the container, but which should be animated out after the removal. Adding these
4760 * views as transient views allows them to participate in drawing without side-effecting
4761 * the layout of the container.</p>
4762 *
4763 * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed}
4764 * from the container when they are no longer needed. For example, a transient view
4765 * which is added in order to fade it out in its old location should be removed
4766 * once the animation is complete.</p>
4767 *
4768 * @param view The view to be added. The view must not have a parent.
4769 * @param index The index at which this view should be drawn, must be >= 0.
4770 * This value is relative to the {@link #getChildAt(int) index} values in the normal
4771 * child list of this container, where any transient view at a particular index will
4772 * be drawn before any normal child at that same index.
4773 *
4774 * @hide
4775 */
4776 @UnsupportedAppUsage
4777 public void addTransientView(View view, int index) {
4778 if (index < 0 || view == null) {
4779 return;
4780 }
4781 if (view.mParent != null) {
4782 throw new IllegalStateException("The specified view already has a parent "
4783 + view.mParent);
4784 }
4785
4786 if (mTransientIndices == null) {
4787 mTransientIndices = new ArrayList<Integer>();
4788 mTransientViews = new ArrayList<View>();
4789 }
4790 final int oldSize = mTransientIndices.size();
4791 if (oldSize > 0) {
4792 int insertionIndex;
4793 for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) {
4794 if (index < mTransientIndices.get(insertionIndex)) {
4795 break;
4796 }
4797 }
4798 mTransientIndices.add(insertionIndex, index);
4799 mTransientViews.add(insertionIndex, view);
4800 } else {
4801 mTransientIndices.add(index);
4802 mTransientViews.add(view);
4803 }
4804 view.mParent = this;
4805 if (mAttachInfo != null) {
4806 view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags & VISIBILITY_MASK));
4807 }
4808 invalidate(true);
4809 }
4810
4811 /**
4812 * Removes a view from the list of transient views in this container. If there is no
4813 * such transient view, this method does nothing.
4814 *
4815 * @param view The transient view to be removed
4816 *
4817 * @hide
4818 */
4819 @UnsupportedAppUsage
4820 public void removeTransientView(View view) {
4821 if (mTransientViews == null) {
4822 return;
4823 }
4824 final int size = mTransientViews.size();
4825 for (int i = 0; i < size; ++i) {
4826 if (view == mTransientViews.get(i)) {
4827 mTransientViews.remove(i);
4828 mTransientIndices.remove(i);
4829 view.mParent = null;
4830 if (view.mAttachInfo != null) {
4831 view.dispatchDetachedFromWindow();
4832 }
4833 invalidate(true);
4834 return;
4835 }
4836 }
4837 }
4838
4839 /**
4840 * Returns the number of transient views in this container. Specific transient
4841 * views and the index at which they were added can be retrieved via
4842 * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}.
4843 *
4844 * @see #addTransientView(View, int)
4845 * @return The number of transient views in this container
4846 *
4847 * @hide
4848 */
4849 @UnsupportedAppUsage
4850 public int getTransientViewCount() {
4851 return mTransientIndices == null ? 0 : mTransientIndices.size();
4852 }
4853
4854 /**
4855 * Given a valid position within the list of transient views, returns the index of
4856 * the transient view at that position.
4857 *
4858 * @param position The position of the index being queried. Must be at least 0
4859 * and less than the value returned by {@link #getTransientViewCount()}.
4860 * @return The index of the transient view stored in the given position if the
4861 * position is valid, otherwise -1
4862 *
4863 * @hide
4864 */
4865 public int getTransientViewIndex(int position) {
4866 if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) {
4867 return -1;
4868 }
4869 return mTransientIndices.get(position);
4870 }
4871
4872 /**
4873 * Given a valid position within the list of transient views, returns the
4874 * transient view at that position.
4875 *
4876 * @param position The position of the view being queried. Must be at least 0
4877 * and less than the value returned by {@link #getTransientViewCount()}.
4878 * @return The transient view stored in the given position if the
4879 * position is valid, otherwise null
4880 *
4881 * @hide
4882 */
4883 @UnsupportedAppUsage
4884 public View getTransientView(int position) {
4885 if (mTransientViews == null || position >= mTransientViews.size()) {
4886 return null;
4887 }
4888 return mTransientViews.get(position);
4889 }
4890
4891 /**
4892 * <p>Adds a child view. If no layout parameters are already set on the child, the
4893 * default parameters for this ViewGroup are set on the child.</p>
4894 *
4895 * <p><strong>Note:</strong> do not invoke this method from
4896 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4897 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4898 *
4899 * @param child the child view to add
4900 *
4901 * @see #generateDefaultLayoutParams()
4902 */
4903 public void addView(View child) {
4904 addView(child, -1);
4905 }
4906
4907 /**
4908 * Adds a child view. If no layout parameters are already set on the child, the
4909 * default parameters for this ViewGroup are set on the child.
4910 *
4911 * <p><strong>Note:</strong> do not invoke this method from
4912 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4913 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4914 *
4915 * @param child the child view to add
4916 * @param index the position at which to add the child
4917 *
4918 * @see #generateDefaultLayoutParams()
4919 */
4920 public void addView(View child, int index) {
4921 if (child == null) {
4922 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4923 }
4924 LayoutParams params = child.getLayoutParams();
4925 if (params == null) {
4926 params = generateDefaultLayoutParams();
4927 if (params == null) {
4928 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
4929 }
4930 }
4931 addView(child, index, params);
4932 }
4933
4934 /**
4935 * Adds a child view with this ViewGroup's default layout parameters and the
4936 * specified width and height.
4937 *
4938 * <p><strong>Note:</strong> do not invoke this method from
4939 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4940 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4941 *
4942 * @param child the child view to add
4943 */
4944 public void addView(View child, int width, int height) {
4945 final LayoutParams params = generateDefaultLayoutParams();
4946 params.width = width;
4947 params.height = height;
4948 addView(child, -1, params);
4949 }
4950
4951 /**
4952 * Adds a child view with the specified layout parameters.
4953 *
4954 * <p><strong>Note:</strong> do not invoke this method from
4955 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4956 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4957 *
4958 * @param child the child view to add
4959 * @param params the layout parameters to set on the child
4960 */
4961 @Override
4962 public void addView(View child, LayoutParams params) {
4963 addView(child, -1, params);
4964 }
4965
4966 /**
4967 * Adds a child view with the specified layout parameters.
4968 *
4969 * <p><strong>Note:</strong> do not invoke this method from
4970 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4971 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4972 *
4973 * @param child the child view to add
4974 * @param index the position at which to add the child or -1 to add last
4975 * @param params the layout parameters to set on the child
4976 */
4977 public void addView(View child, int index, LayoutParams params) {
4978 if (DBG) {
4979 System.out.println(this + " addView");
4980 }
4981
4982 if (child == null) {
4983 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4984 }
4985
4986 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
4987 // therefore, we call requestLayout() on ourselves before, so that the child's request
4988 // will be blocked at our level
4989 requestLayout();
4990 invalidate(true);
4991 addViewInner(child, index, params, false);
4992 }
4993
4994 @Override
4995 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
4996 if (!checkLayoutParams(params)) {
4997 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
4998 }
4999 if (view.mParent != this) {
5000 throw new IllegalArgumentException("Given view not a child of " + this);
5001 }
5002 view.setLayoutParams(params);
5003 }
5004
5005 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
5006 return p != null;
5007 }
5008
5009 /**
5010 * Interface definition for a callback to be invoked when the hierarchy
5011 * within this view changed. The hierarchy changes whenever a child is added
5012 * to or removed from this view.
5013 */
5014 public interface OnHierarchyChangeListener {
5015 /**
5016 * Called when a new child is added to a parent view.
5017 *
5018 * @param parent the view in which a child was added
5019 * @param child the new child view added in the hierarchy
5020 */
5021 void onChildViewAdded(View parent, View child);
5022
5023 /**
5024 * Called when a child is removed from a parent view.
5025 *
5026 * @param parent the view from which the child was removed
5027 * @param child the child removed from the hierarchy
5028 */
5029 void onChildViewRemoved(View parent, View child);
5030 }
5031
5032 /**
5033 * Register a callback to be invoked when a child is added to or removed
5034 * from this view.
5035 *
5036 * @param listener the callback to invoke on hierarchy change
5037 */
5038 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
5039 mOnHierarchyChangeListener = listener;
5040 }
5041
5042 @UnsupportedAppUsage
5043 void dispatchViewAdded(View child) {
5044 onViewAdded(child);
5045 if (mOnHierarchyChangeListener != null) {
5046 mOnHierarchyChangeListener.onChildViewAdded(this, child);
5047 }
5048 }
5049
5050 /**
5051 * Called when a new child is added to this ViewGroup. Overrides should always
5052 * call super.onViewAdded.
5053 *
5054 * @param child the added child view
5055 */
5056 public void onViewAdded(View child) {
5057 }
5058
5059 @UnsupportedAppUsage
5060 void dispatchViewRemoved(View child) {
5061 onViewRemoved(child);
5062 if (mOnHierarchyChangeListener != null) {
5063 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
5064 }
5065 }
5066
5067 /**
5068 * Called when a child view is removed from this ViewGroup. Overrides should always
5069 * call super.onViewRemoved.
5070 *
5071 * @param child the removed child view
5072 */
5073 public void onViewRemoved(View child) {
5074 }
5075
5076 private void clearCachedLayoutMode() {
5077 if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
5078 mLayoutMode = LAYOUT_MODE_UNDEFINED;
5079 }
5080 }
5081
5082 @Override
5083 protected void onAttachedToWindow() {
5084 super.onAttachedToWindow();
5085 clearCachedLayoutMode();
5086 }
5087
5088 @Override
5089 protected void onDetachedFromWindow() {
5090 super.onDetachedFromWindow();
5091 clearCachedLayoutMode();
5092 }
5093
5094 /** @hide */
5095 @Override
5096 protected void destroyHardwareResources() {
5097 super.destroyHardwareResources();
5098 int count = getChildCount();
5099 for (int i = 0; i < count; i++) {
5100 getChildAt(i).destroyHardwareResources();
5101 }
5102 }
5103
5104 /**
5105 * Adds a view during layout. This is useful if in your onLayout() method,
5106 * you need to add more views (as does the list view for example).
5107 *
5108 * If index is negative, it means put it at the end of the list.
5109 *
5110 * @param child the view to add to the group
5111 * @param index the index at which the child must be added or -1 to add last
5112 * @param params the layout parameters to associate with the child
5113 * @return true if the child was added, false otherwise
5114 */
5115 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
5116 return addViewInLayout(child, index, params, false);
5117 }
5118
5119 /**
5120 * Adds a view during layout. This is useful if in your onLayout() method,
5121 * you need to add more views (as does the list view for example).
5122 *
5123 * If index is negative, it means put it at the end of the list.
5124 *
5125 * @param child the view to add to the group
5126 * @param index the index at which the child must be added or -1 to add last
5127 * @param params the layout parameters to associate with the child
5128 * @param preventRequestLayout if true, calling this method will not trigger a
5129 * layout request on child
5130 * @return true if the child was added, false otherwise
5131 */
5132 protected boolean addViewInLayout(View child, int index, LayoutParams params,
5133 boolean preventRequestLayout) {
5134 if (child == null) {
5135 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
5136 }
5137 child.mParent = null;
5138 addViewInner(child, index, params, preventRequestLayout);
5139 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
5140 return true;
5141 }
5142
5143 /**
5144 * Prevents the specified child to be laid out during the next layout pass.
5145 *
5146 * @param child the child on which to perform the cleanup
5147 */
5148 protected void cleanupLayoutState(View child) {
5149 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
5150 }
5151
5152 private void addViewInner(View child, int index, LayoutParams params,
5153 boolean preventRequestLayout) {
5154
5155 if (mTransition != null) {
5156 // Don't prevent other add transitions from completing, but cancel remove
5157 // transitions to let them complete the process before we add to the container
5158 mTransition.cancel(LayoutTransition.DISAPPEARING);
5159 }
5160
5161 if (child.getParent() != null) {
5162 throw new IllegalStateException("The specified child already has a parent. " +
5163 "You must call removeView() on the child's parent first.");
5164 }
5165
5166 if (mTransition != null) {
5167 mTransition.addChild(this, child);
5168 }
5169
5170 if (!checkLayoutParams(params)) {
5171 params = generateLayoutParams(params);
5172 }
5173
5174 if (preventRequestLayout) {
5175 child.mLayoutParams = params;
5176 } else {
5177 child.setLayoutParams(params);
5178 }
5179
5180 if (index < 0) {
5181 index = mChildrenCount;
5182 }
5183
5184 addInArray(child, index);
5185
5186 // tell our children
5187 if (preventRequestLayout) {
5188 child.assignParent(this);
5189 } else {
5190 child.mParent = this;
5191 }
5192 if (child.hasUnhandledKeyListener()) {
5193 incrementChildUnhandledKeyListeners();
5194 }
5195
5196 final boolean childHasFocus = child.hasFocus();
5197 if (childHasFocus) {
5198 requestChildFocus(child, child.findFocus());
5199 }
5200
5201 AttachInfo ai = mAttachInfo;
5202 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
5203 boolean lastKeepOn = ai.mKeepScreenOn;
5204 ai.mKeepScreenOn = false;
5205 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
5206 if (ai.mKeepScreenOn) {
5207 needGlobalAttributesUpdate(true);
5208 }
5209 ai.mKeepScreenOn = lastKeepOn;
5210 }
5211
5212 if (child.isLayoutDirectionInherited()) {
5213 child.resetRtlProperties();
5214 }
5215
5216 dispatchViewAdded(child);
5217
5218 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
5219 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
5220 }
5221
5222 if (child.hasTransientState()) {
5223 childHasTransientStateChanged(child, true);
5224 }
5225
5226 if (child.getVisibility() != View.GONE) {
5227 notifySubtreeAccessibilityStateChangedIfNeeded();
5228 }
5229
5230 if (mTransientIndices != null) {
5231 final int transientCount = mTransientIndices.size();
5232 for (int i = 0; i < transientCount; ++i) {
5233 final int oldIndex = mTransientIndices.get(i);
5234 if (index <= oldIndex) {
5235 mTransientIndices.set(i, oldIndex + 1);
5236 }
5237 }
5238 }
5239
5240 if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
5241 notifyChildOfDragStart(child);
5242 }
5243
5244 if (child.hasDefaultFocus()) {
5245 // When adding a child that contains default focus, either during inflation or while
5246 // manually assembling the hierarchy, update the ancestor default-focus chain.
5247 setDefaultFocus(child);
5248 }
5249
5250 touchAccessibilityNodeProviderIfNeeded(child);
5251 }
5252
5253 /**
5254 * We may need to touch the provider to bring up the a11y layer. In a11y mode
5255 * clients inspect the screen or the user touches it which triggers bringing up
5256 * of the a11y infrastructure while in autofill mode we want the infra up and
5257 * running from the beginning since we watch for a11y events to drive autofill.
5258 */
5259 private void touchAccessibilityNodeProviderIfNeeded(View child) {
5260 if (mContext.isAutofillCompatibilityEnabled()) {
5261 child.getAccessibilityNodeProvider();
5262 }
5263 }
5264
5265 private void addInArray(View child, int index) {
5266 View[] children = mChildren;
5267 final int count = mChildrenCount;
5268 final int size = children.length;
5269 if (index == count) {
5270 if (size == count) {
5271 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
5272 System.arraycopy(children, 0, mChildren, 0, size);
5273 children = mChildren;
5274 }
5275 children[mChildrenCount++] = child;
5276 } else if (index < count) {
5277 if (size == count) {
5278 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
5279 System.arraycopy(children, 0, mChildren, 0, index);
5280 System.arraycopy(children, index, mChildren, index + 1, count - index);
5281 children = mChildren;
5282 } else {
5283 System.arraycopy(children, index, children, index + 1, count - index);
5284 }
5285 children[index] = child;
5286 mChildrenCount++;
5287 if (mLastTouchDownIndex >= index) {
5288 mLastTouchDownIndex++;
5289 }
5290 } else {
5291 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
5292 }
5293 }
5294
5295 // This method also sets the child's mParent to null
5296 private void removeFromArray(int index) {
5297 final View[] children = mChildren;
5298 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
5299 children[index].mParent = null;
5300 }
5301 final int count = mChildrenCount;
5302 if (index == count - 1) {
5303 children[--mChildrenCount] = null;
5304 } else if (index >= 0 && index < count) {
5305 System.arraycopy(children, index + 1, children, index, count - index - 1);
5306 children[--mChildrenCount] = null;
5307 } else {
5308 throw new IndexOutOfBoundsException();
5309 }
5310 if (mLastTouchDownIndex == index) {
5311 mLastTouchDownTime = 0;
5312 mLastTouchDownIndex = -1;
5313 } else if (mLastTouchDownIndex > index) {
5314 mLastTouchDownIndex--;
5315 }
5316 }
5317
5318 // This method also sets the children's mParent to null
5319 private void removeFromArray(int start, int count) {
5320 final View[] children = mChildren;
5321 final int childrenCount = mChildrenCount;
5322
5323 start = Math.max(0, start);
5324 final int end = Math.min(childrenCount, start + count);
5325
5326 if (start == end) {
5327 return;
5328 }
5329
5330 if (end == childrenCount) {
5331 for (int i = start; i < end; i++) {
5332 children[i].mParent = null;
5333 children[i] = null;
5334 }
5335 } else {
5336 for (int i = start; i < end; i++) {
5337 children[i].mParent = null;
5338 }
5339
5340 // Since we're looping above, we might as well do the copy, but is arraycopy()
5341 // faster than the extra 2 bounds checks we would do in the loop?
5342 System.arraycopy(children, end, children, start, childrenCount - end);
5343
5344 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
5345 children[i] = null;
5346 }
5347 }
5348
5349 mChildrenCount -= (end - start);
5350 }
5351
5352 private void bindLayoutAnimation(View child) {
5353 Animation a = mLayoutAnimationController.getAnimationForView(child);
5354 child.setAnimation(a);
5355 }
5356
5357 /**
5358 * Subclasses should override this method to set layout animation
5359 * parameters on the supplied child.
5360 *
5361 * @param child the child to associate with animation parameters
5362 * @param params the child's layout parameters which hold the animation
5363 * parameters
5364 * @param index the index of the child in the view group
5365 * @param count the number of children in the view group
5366 */
5367 protected void attachLayoutAnimationParameters(View child,
5368 LayoutParams params, int index, int count) {
5369 LayoutAnimationController.AnimationParameters animationParams =
5370 params.layoutAnimationParameters;
5371 if (animationParams == null) {
5372 animationParams = new LayoutAnimationController.AnimationParameters();
5373 params.layoutAnimationParameters = animationParams;
5374 }
5375
5376 animationParams.count = count;
5377 animationParams.index = index;
5378 }
5379
5380 /**
5381 * {@inheritDoc}
5382 *
5383 * <p><strong>Note:</strong> do not invoke this method from
5384 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5385 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5386 */
5387 @Override
5388 public void removeView(View view) {
5389 if (removeViewInternal(view)) {
5390 requestLayout();
5391 invalidate(true);
5392 }
5393 }
5394
5395 /**
5396 * Removes a view during layout. This is useful if in your onLayout() method,
5397 * you need to remove more views.
5398 *
5399 * <p><strong>Note:</strong> do not invoke this method from
5400 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5401 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5402 *
5403 * @param view the view to remove from the group
5404 */
5405 public void removeViewInLayout(View view) {
5406 removeViewInternal(view);
5407 }
5408
5409 /**
5410 * Removes a range of views during layout. This is useful if in your onLayout() method,
5411 * you need to remove more views.
5412 *
5413 * <p><strong>Note:</strong> do not invoke this method from
5414 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5415 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5416 *
5417 * @param start the index of the first view to remove from the group
5418 * @param count the number of views to remove from the group
5419 */
5420 public void removeViewsInLayout(int start, int count) {
5421 removeViewsInternal(start, count);
5422 }
5423
5424 /**
5425 * Removes the view at the specified position in the group.
5426 *
5427 * <p><strong>Note:</strong> do not invoke this method from
5428 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5429 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5430 *
5431 * @param index the position in the group of the view to remove
5432 */
5433 public void removeViewAt(int index) {
5434 removeViewInternal(index, getChildAt(index));
5435 requestLayout();
5436 invalidate(true);
5437 }
5438
5439 /**
5440 * Removes the specified range of views from the group.
5441 *
5442 * <p><strong>Note:</strong> do not invoke this method from
5443 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5444 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5445 *
5446 * @param start the first position in the group of the range of views to remove
5447 * @param count the number of views to remove
5448 */
5449 public void removeViews(int start, int count) {
5450 removeViewsInternal(start, count);
5451 requestLayout();
5452 invalidate(true);
5453 }
5454
5455 private boolean removeViewInternal(View view) {
5456 final int index = indexOfChild(view);
5457 if (index >= 0) {
5458 removeViewInternal(index, view);
5459 return true;
5460 }
5461 return false;
5462 }
5463
5464 private void removeViewInternal(int index, View view) {
5465 if (mTransition != null) {
5466 mTransition.removeChild(this, view);
5467 }
5468
5469 boolean clearChildFocus = false;
5470 if (view == mFocused) {
5471 view.unFocus(null);
5472 clearChildFocus = true;
5473 }
5474 if (view == mFocusedInCluster) {
5475 clearFocusedInCluster(view);
5476 }
5477
5478 view.clearAccessibilityFocus();
5479
5480 cancelTouchTarget(view);
5481 cancelHoverTarget(view);
5482
5483 if (view.getAnimation() != null ||
5484 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
5485 addDisappearingView(view);
5486 } else if (view.mAttachInfo != null) {
5487 view.dispatchDetachedFromWindow();
5488 }
5489
5490 if (view.hasTransientState()) {
5491 childHasTransientStateChanged(view, false);
5492 }
5493
5494 needGlobalAttributesUpdate(false);
5495
5496 removeFromArray(index);
5497
5498 if (view.hasUnhandledKeyListener()) {
5499 decrementChildUnhandledKeyListeners();
5500 }
5501
5502 if (view == mDefaultFocus) {
5503 clearDefaultFocus(view);
5504 }
5505 if (clearChildFocus) {
5506 clearChildFocus(view);
5507 if (!rootViewRequestFocus()) {
5508 notifyGlobalFocusCleared(this);
5509 }
5510 }
5511
5512 dispatchViewRemoved(view);
5513
5514 if (view.getVisibility() != View.GONE) {
5515 notifySubtreeAccessibilityStateChangedIfNeeded();
5516 }
5517
5518 int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
5519 for (int i = 0; i < transientCount; ++i) {
5520 final int oldIndex = mTransientIndices.get(i);
5521 if (index < oldIndex) {
5522 mTransientIndices.set(i, oldIndex - 1);
5523 }
5524 }
5525
5526 if (mCurrentDragStartEvent != null) {
5527 mChildrenInterestedInDrag.remove(view);
5528 }
5529 }
5530
5531 /**
5532 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
5533 * not null, changes in layout which occur because of children being added to or removed from
5534 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
5535 * object. By default, the transition object is null (so layout changes are not animated).
5536 *
5537 * <p>Replacing a non-null transition will cause that previous transition to be
5538 * canceled, if it is currently running, to restore this container to
5539 * its correct post-transition state.</p>
5540 *
5541 * @param transition The LayoutTransition object that will animated changes in layout. A value
5542 * of <code>null</code> means no transition will run on layout changes.
5543 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
5544 */
5545 public void setLayoutTransition(LayoutTransition transition) {
5546 if (mTransition != null) {
5547 LayoutTransition previousTransition = mTransition;
5548 previousTransition.cancel();
5549 previousTransition.removeTransitionListener(mLayoutTransitionListener);
5550 }
5551 mTransition = transition;
5552 if (mTransition != null) {
5553 mTransition.addTransitionListener(mLayoutTransitionListener);
5554 }
5555 }
5556
5557 /**
5558 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
5559 * not null, changes in layout which occur because of children being added to or removed from
5560 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
5561 * object. By default, the transition object is null (so layout changes are not animated).
5562 *
5563 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
5564 * A value of <code>null</code> means no transition will run on layout changes.
5565 */
5566 public LayoutTransition getLayoutTransition() {
5567 return mTransition;
5568 }
5569
5570 private void removeViewsInternal(int start, int count) {
5571 final int end = start + count;
5572
5573 if (start < 0 || count < 0 || end > mChildrenCount) {
5574 throw new IndexOutOfBoundsException();
5575 }
5576
5577 final View focused = mFocused;
5578 final boolean detach = mAttachInfo != null;
5579 boolean clearChildFocus = false;
5580 View clearDefaultFocus = null;
5581
5582 final View[] children = mChildren;
5583
5584 for (int i = start; i < end; i++) {
5585 final View view = children[i];
5586
5587 if (mTransition != null) {
5588 mTransition.removeChild(this, view);
5589 }
5590
5591 if (view == focused) {
5592 view.unFocus(null);
5593 clearChildFocus = true;
5594 }
5595 if (view == mDefaultFocus) {
5596 clearDefaultFocus = view;
5597 }
5598 if (view == mFocusedInCluster) {
5599 clearFocusedInCluster(view);
5600 }
5601
5602 view.clearAccessibilityFocus();
5603
5604 cancelTouchTarget(view);
5605 cancelHoverTarget(view);
5606
5607 if (view.getAnimation() != null ||
5608 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
5609 addDisappearingView(view);
5610 } else if (detach) {
5611 view.dispatchDetachedFromWindow();
5612 }
5613
5614 if (view.hasTransientState()) {
5615 childHasTransientStateChanged(view, false);
5616 }
5617
5618 needGlobalAttributesUpdate(false);
5619
5620 dispatchViewRemoved(view);
5621 }
5622
5623 removeFromArray(start, count);
5624
5625 if (clearDefaultFocus != null) {
5626 clearDefaultFocus(clearDefaultFocus);
5627 }
5628 if (clearChildFocus) {
5629 clearChildFocus(focused);
5630 if (!rootViewRequestFocus()) {
5631 notifyGlobalFocusCleared(focused);
5632 }
5633 }
5634 }
5635
5636 /**
5637 * Call this method to remove all child views from the
5638 * ViewGroup.
5639 *
5640 * <p><strong>Note:</strong> do not invoke this method from
5641 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5642 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5643 */
5644 public void removeAllViews() {
5645 removeAllViewsInLayout();
5646 requestLayout();
5647 invalidate(true);
5648 }
5649
5650 /**
5651 * Called by a ViewGroup subclass to remove child views from itself,
5652 * when it must first know its size on screen before it can calculate how many
5653 * child views it will render. An example is a Gallery or a ListView, which
5654 * may "have" 50 children, but actually only render the number of children
5655 * that can currently fit inside the object on screen. Do not call
5656 * this method unless you are extending ViewGroup and understand the
5657 * view measuring and layout pipeline.
5658 *
5659 * <p><strong>Note:</strong> do not invoke this method from
5660 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5661 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5662 */
5663 public void removeAllViewsInLayout() {
5664 final int count = mChildrenCount;
5665 if (count <= 0) {
5666 return;
5667 }
5668
5669 final View[] children = mChildren;
5670 mChildrenCount = 0;
5671
5672 final View focused = mFocused;
5673 final boolean detach = mAttachInfo != null;
5674 boolean clearChildFocus = false;
5675
5676 needGlobalAttributesUpdate(false);
5677
5678 for (int i = count - 1; i >= 0; i--) {
5679 final View view = children[i];
5680
5681 if (mTransition != null) {
5682 mTransition.removeChild(this, view);
5683 }
5684
5685 if (view == focused) {
5686 view.unFocus(null);
5687 clearChildFocus = true;
5688 }
5689
5690 view.clearAccessibilityFocus();
5691
5692 cancelTouchTarget(view);
5693 cancelHoverTarget(view);
5694
5695 if (view.getAnimation() != null ||
5696 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
5697 addDisappearingView(view);
5698 } else if (detach) {
5699 view.dispatchDetachedFromWindow();
5700 }
5701
5702 if (view.hasTransientState()) {
5703 childHasTransientStateChanged(view, false);
5704 }
5705
5706 dispatchViewRemoved(view);
5707
5708 view.mParent = null;
5709 children[i] = null;
5710 }
5711
5712 if (mDefaultFocus != null) {
5713 clearDefaultFocus(mDefaultFocus);
5714 }
5715 if (mFocusedInCluster != null) {
5716 clearFocusedInCluster(mFocusedInCluster);
5717 }
5718 if (clearChildFocus) {
5719 clearChildFocus(focused);
5720 if (!rootViewRequestFocus()) {
5721 notifyGlobalFocusCleared(focused);
5722 }
5723 }
5724 }
5725
5726 /**
5727 * Finishes the removal of a detached view. This method will dispatch the detached from
5728 * window event and notify the hierarchy change listener.
5729 * <p>
5730 * This method is intended to be lightweight and makes no assumptions about whether the
5731 * parent or child should be redrawn. Proper use of this method will include also making
5732 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
5733 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
5734 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
5735 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
5736 *
5737 * @param child the child to be definitely removed from the view hierarchy
5738 * @param animate if true and the view has an animation, the view is placed in the
5739 * disappearing views list, otherwise, it is detached from the window
5740 *
5741 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5742 * @see #detachAllViewsFromParent()
5743 * @see #detachViewFromParent(View)
5744 * @see #detachViewFromParent(int)
5745 */
5746 protected void removeDetachedView(View child, boolean animate) {
5747 if (mTransition != null) {
5748 mTransition.removeChild(this, child);
5749 }
5750
5751 if (child == mFocused) {
5752 child.clearFocus();
5753 }
5754 if (child == mDefaultFocus) {
5755 clearDefaultFocus(child);
5756 }
5757 if (child == mFocusedInCluster) {
5758 clearFocusedInCluster(child);
5759 }
5760
5761 child.clearAccessibilityFocus();
5762
5763 cancelTouchTarget(child);
5764 cancelHoverTarget(child);
5765
5766 if ((animate && child.getAnimation() != null) ||
5767 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
5768 addDisappearingView(child);
5769 } else if (child.mAttachInfo != null) {
5770 child.dispatchDetachedFromWindow();
5771 }
5772
5773 if (child.hasTransientState()) {
5774 childHasTransientStateChanged(child, false);
5775 }
5776
5777 dispatchViewRemoved(child);
5778 }
5779
5780 /**
5781 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
5782 * sets the layout parameters and puts the view in the list of children so that
5783 * it can be retrieved by calling {@link #getChildAt(int)}.
5784 * <p>
5785 * This method is intended to be lightweight and makes no assumptions about whether the
5786 * parent or child should be redrawn. Proper use of this method will include also making
5787 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
5788 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
5789 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
5790 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
5791 * <p>
5792 * This method should be called only for views which were detached from their parent.
5793 *
5794 * @param child the child to attach
5795 * @param index the index at which the child should be attached
5796 * @param params the layout parameters of the child
5797 *
5798 * @see #removeDetachedView(View, boolean)
5799 * @see #detachAllViewsFromParent()
5800 * @see #detachViewFromParent(View)
5801 * @see #detachViewFromParent(int)
5802 */
5803 protected void attachViewToParent(View child, int index, LayoutParams params) {
5804 child.mLayoutParams = params;
5805
5806 if (index < 0) {
5807 index = mChildrenCount;
5808 }
5809
5810 addInArray(child, index);
5811
5812 child.mParent = this;
5813 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
5814 & ~PFLAG_DRAWING_CACHE_VALID)
5815 | PFLAG_DRAWN | PFLAG_INVALIDATED;
5816 this.mPrivateFlags |= PFLAG_INVALIDATED;
5817
5818 if (child.hasFocus()) {
5819 requestChildFocus(child, child.findFocus());
5820 }
5821 dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE
5822 && isShown());
5823 notifySubtreeAccessibilityStateChangedIfNeeded();
5824 }
5825
5826 /**
5827 * Detaches a view from its parent. Detaching a view should be followed
5828 * either by a call to
5829 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5830 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5831 * temporary; reattachment or removal should happen within the same drawing cycle as
5832 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5833 * call to {@link #getChildAt(int)}.
5834 *
5835 * @param child the child to detach
5836 *
5837 * @see #detachViewFromParent(int)
5838 * @see #detachViewsFromParent(int, int)
5839 * @see #detachAllViewsFromParent()
5840 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5841 * @see #removeDetachedView(View, boolean)
5842 */
5843 protected void detachViewFromParent(View child) {
5844 removeFromArray(indexOfChild(child));
5845 }
5846
5847 /**
5848 * Detaches a view from its parent. Detaching a view should be followed
5849 * either by a call to
5850 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5851 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5852 * temporary; reattachment or removal should happen within the same drawing cycle as
5853 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5854 * call to {@link #getChildAt(int)}.
5855 *
5856 * @param index the index of the child to detach
5857 *
5858 * @see #detachViewFromParent(View)
5859 * @see #detachAllViewsFromParent()
5860 * @see #detachViewsFromParent(int, int)
5861 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5862 * @see #removeDetachedView(View, boolean)
5863 */
5864 protected void detachViewFromParent(int index) {
5865 removeFromArray(index);
5866 }
5867
5868 /**
5869 * Detaches a range of views from their parents. Detaching a view should be followed
5870 * either by a call to
5871 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5872 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5873 * temporary; reattachment or removal should happen within the same drawing cycle as
5874 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5875 * call to {@link #getChildAt(int)}.
5876 *
5877 * @param start the first index of the childrend range to detach
5878 * @param count the number of children to detach
5879 *
5880 * @see #detachViewFromParent(View)
5881 * @see #detachViewFromParent(int)
5882 * @see #detachAllViewsFromParent()
5883 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5884 * @see #removeDetachedView(View, boolean)
5885 */
5886 protected void detachViewsFromParent(int start, int count) {
5887 removeFromArray(start, count);
5888 }
5889
5890 /**
5891 * Detaches all views from the parent. Detaching a view should be followed
5892 * either by a call to
5893 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5894 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5895 * temporary; reattachment or removal should happen within the same drawing cycle as
5896 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5897 * call to {@link #getChildAt(int)}.
5898 *
5899 * @see #detachViewFromParent(View)
5900 * @see #detachViewFromParent(int)
5901 * @see #detachViewsFromParent(int, int)
5902 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5903 * @see #removeDetachedView(View, boolean)
5904 */
5905 protected void detachAllViewsFromParent() {
5906 final int count = mChildrenCount;
5907 if (count <= 0) {
5908 return;
5909 }
5910
5911 final View[] children = mChildren;
5912 mChildrenCount = 0;
5913
5914 for (int i = count - 1; i >= 0; i--) {
5915 children[i].mParent = null;
5916 children[i] = null;
5917 }
5918 }
5919
5920 @Override
5921 @CallSuper
5922 public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
5923 /*
5924 * HW-only, Rect-ignoring damage codepath
5925 *
5926 * We don't deal with rectangles here, since RenderThread native code computes damage for
5927 * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area)
5928 */
5929
5930 // if set, combine the animation flag into the parent
5931 mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION);
5932
5933 if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) {
5934 // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential
5935 // optimization in provides in a DisplayList world.
5936 mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
5937
5938 // simplified invalidateChildInParent behavior: clear cache validity to be safe...
5939 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5940 }
5941
5942 // ... and mark inval if in software layer that needs to repaint (hw handled in native)
5943 if (mLayerType == LAYER_TYPE_SOFTWARE) {
5944 // Layered parents should be invalidated. Escalate to a full invalidate (and note that
5945 // we do this after consuming any relevant flags from the originating descendant)
5946 mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
5947 target = this;
5948 }
5949
5950 if (mParent != null) {
5951 mParent.onDescendantInvalidated(this, target);
5952 }
5953 }
5954
5955
5956 /**
5957 * Don't call or override this method. It is used for the implementation of
5958 * the view hierarchy.
5959 *
5960 * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to
5961 * draw state in descendants.
5962 */
5963 @Deprecated
5964 @Override
5965 public final void invalidateChild(View child, final Rect dirty) {
5966 final AttachInfo attachInfo = mAttachInfo;
5967 if (attachInfo != null && attachInfo.mHardwareAccelerated) {
5968 // HW accelerated fast path
5969 onDescendantInvalidated(child, child);
5970 return;
5971 }
5972
5973 ViewParent parent = this;
5974 if (attachInfo != null) {
5975 // If the child is drawing an animation, we want to copy this flag onto
5976 // ourselves and the parent to make sure the invalidate request goes
5977 // through
5978 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
5979
5980 // Check whether the child that requests the invalidate is fully opaque
5981 // Views being animated or transformed are not considered opaque because we may
5982 // be invalidating their old position and need the parent to paint behind them.
5983 Matrix childMatrix = child.getMatrix();
5984 // Mark the child as dirty, using the appropriate flag
5985 // Make sure we do not set both flags at the same time
5986
5987 if (child.mLayerType != LAYER_TYPE_NONE) {
5988 mPrivateFlags |= PFLAG_INVALIDATED;
5989 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5990 }
5991
5992 final int[] location = attachInfo.mInvalidateChildLocation;
5993 location[CHILD_LEFT_INDEX] = child.mLeft;
5994 location[CHILD_TOP_INDEX] = child.mTop;
5995 if (!childMatrix.isIdentity() ||
5996 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
5997 RectF boundingRect = attachInfo.mTmpTransformRect;
5998 boundingRect.set(dirty);
5999 Matrix transformMatrix;
6000 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
6001 Transformation t = attachInfo.mTmpTransformation;
6002 boolean transformed = getChildStaticTransformation(child, t);
6003 if (transformed) {
6004 transformMatrix = attachInfo.mTmpMatrix;
6005 transformMatrix.set(t.getMatrix());
6006 if (!childMatrix.isIdentity()) {
6007 transformMatrix.preConcat(childMatrix);
6008 }
6009 } else {
6010 transformMatrix = childMatrix;
6011 }
6012 } else {
6013 transformMatrix = childMatrix;
6014 }
6015 transformMatrix.mapRect(boundingRect);
6016 dirty.set((int) Math.floor(boundingRect.left),
6017 (int) Math.floor(boundingRect.top),
6018 (int) Math.ceil(boundingRect.right),
6019 (int) Math.ceil(boundingRect.bottom));
6020 }
6021
6022 do {
6023 View view = null;
6024 if (parent instanceof View) {
6025 view = (View) parent;
6026 }
6027
6028 if (drawAnimation) {
6029 if (view != null) {
6030 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
6031 } else if (parent instanceof ViewRootImpl) {
6032 ((ViewRootImpl) parent).mIsAnimating = true;
6033 }
6034 }
6035
6036 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
6037 // flag coming from the child that initiated the invalidate
6038 if (view != null) {
6039 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
6040 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
6041 }
6042 }
6043
6044 parent = parent.invalidateChildInParent(location, dirty);
6045 if (view != null) {
6046 // Account for transform on current parent
6047 Matrix m = view.getMatrix();
6048 if (!m.isIdentity()) {
6049 RectF boundingRect = attachInfo.mTmpTransformRect;
6050 boundingRect.set(dirty);
6051 m.mapRect(boundingRect);
6052 dirty.set((int) Math.floor(boundingRect.left),
6053 (int) Math.floor(boundingRect.top),
6054 (int) Math.ceil(boundingRect.right),
6055 (int) Math.ceil(boundingRect.bottom));
6056 }
6057 }
6058 } while (parent != null);
6059 }
6060 }
6061
6062 /**
6063 * Don't call or override this method. It is used for the implementation of
6064 * the view hierarchy.
6065 *
6066 * This implementation returns null if this ViewGroup does not have a parent,
6067 * if this ViewGroup is already fully invalidated or if the dirty rectangle
6068 * does not intersect with this ViewGroup's bounds.
6069 *
6070 * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to
6071 * draw state in descendants.
6072 */
6073 @Deprecated
6074 @Override
6075 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
6076 if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
6077 // either DRAWN, or DRAWING_CACHE_VALID
6078 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
6079 != FLAG_OPTIMIZE_INVALIDATE) {
6080 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
6081 location[CHILD_TOP_INDEX] - mScrollY);
6082 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
6083 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
6084 }
6085
6086 final int left = mLeft;
6087 final int top = mTop;
6088
6089 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
6090 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
6091 dirty.setEmpty();
6092 }
6093 }
6094
6095 location[CHILD_LEFT_INDEX] = left;
6096 location[CHILD_TOP_INDEX] = top;
6097 } else {
6098
6099 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
6100 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
6101 } else {
6102 // in case the dirty rect extends outside the bounds of this container
6103 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
6104 }
6105 location[CHILD_LEFT_INDEX] = mLeft;
6106 location[CHILD_TOP_INDEX] = mTop;
6107
6108 mPrivateFlags &= ~PFLAG_DRAWN;
6109 }
6110 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
6111 if (mLayerType != LAYER_TYPE_NONE) {
6112 mPrivateFlags |= PFLAG_INVALIDATED;
6113 }
6114
6115 return mParent;
6116 }
6117
6118 return null;
6119 }
6120
6121 /**
6122 * Offset a rectangle that is in a descendant's coordinate
6123 * space into our coordinate space.
6124 * @param descendant A descendant of this view
6125 * @param rect A rectangle defined in descendant's coordinate space.
6126 */
6127 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
6128 offsetRectBetweenParentAndChild(descendant, rect, true, false);
6129 }
6130
6131 /**
6132 * Offset a rectangle that is in our coordinate space into an ancestor's
6133 * coordinate space.
6134 * @param descendant A descendant of this view
6135 * @param rect A rectangle defined in descendant's coordinate space.
6136 */
6137 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
6138 offsetRectBetweenParentAndChild(descendant, rect, false, false);
6139 }
6140
6141 /**
6142 * Helper method that offsets a rect either from parent to descendant or
6143 * descendant to parent.
6144 */
6145 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
6146 boolean offsetFromChildToParent, boolean clipToBounds) {
6147
6148 // already in the same coord system :)
6149 if (descendant == this) {
6150 return;
6151 }
6152
6153 ViewParent theParent = descendant.mParent;
6154
6155 // search and offset up to the parent
6156 while ((theParent != null)
6157 && (theParent instanceof View)
6158 && (theParent != this)) {
6159
6160 if (offsetFromChildToParent) {
6161 rect.offset(descendant.mLeft - descendant.mScrollX,
6162 descendant.mTop - descendant.mScrollY);
6163 if (clipToBounds) {
6164 View p = (View) theParent;
6165 boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
6166 p.mBottom - p.mTop);
6167 if (!intersected) {
6168 rect.setEmpty();
6169 }
6170 }
6171 } else {
6172 if (clipToBounds) {
6173 View p = (View) theParent;
6174 boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
6175 p.mBottom - p.mTop);
6176 if (!intersected) {
6177 rect.setEmpty();
6178 }
6179 }
6180 rect.offset(descendant.mScrollX - descendant.mLeft,
6181 descendant.mScrollY - descendant.mTop);
6182 }
6183
6184 descendant = (View) theParent;
6185 theParent = descendant.mParent;
6186 }
6187
6188 // now that we are up to this view, need to offset one more time
6189 // to get into our coordinate space
6190 if (theParent == this) {
6191 if (offsetFromChildToParent) {
6192 rect.offset(descendant.mLeft - descendant.mScrollX,
6193 descendant.mTop - descendant.mScrollY);
6194 } else {
6195 rect.offset(descendant.mScrollX - descendant.mLeft,
6196 descendant.mScrollY - descendant.mTop);
6197 }
6198 } else {
6199 throw new IllegalArgumentException("parameter must be a descendant of this view");
6200 }
6201 }
6202
6203 /**
6204 * Offset the vertical location of all children of this view by the specified number of pixels.
6205 *
6206 * @param offset the number of pixels to offset
6207 *
6208 * @hide
6209 */
6210 @UnsupportedAppUsage
6211 public void offsetChildrenTopAndBottom(int offset) {
6212 final int count = mChildrenCount;
6213 final View[] children = mChildren;
6214 boolean invalidate = false;
6215
6216 for (int i = 0; i < count; i++) {
6217 final View v = children[i];
6218 v.mTop += offset;
6219 v.mBottom += offset;
6220 if (v.mRenderNode != null) {
6221 invalidate = true;
6222 v.mRenderNode.offsetTopAndBottom(offset);
6223 }
6224 }
6225
6226 if (invalidate) {
6227 invalidateViewProperty(false, false);
6228 }
6229 notifySubtreeAccessibilityStateChangedIfNeeded();
6230 }
6231
6232 @Override
6233 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
6234 return getChildVisibleRect(child, r, offset, false);
6235 }
6236
6237 /**
6238 * @param forceParentCheck true to guarantee that this call will propagate to all ancestors,
6239 * false otherwise
6240 *
6241 * @hide
6242 */
6243 public boolean getChildVisibleRect(
6244 View child, Rect r, android.graphics.Point offset, boolean forceParentCheck) {
6245 // It doesn't make a whole lot of sense to call this on a view that isn't attached,
6246 // but for some simple tests it can be useful. If we don't have attach info this
6247 // will allocate memory.
6248 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
6249 rect.set(r);
6250
6251 if (!child.hasIdentityMatrix()) {
6252 child.getMatrix().mapRect(rect);
6253 }
6254
6255 final int dx = child.mLeft - mScrollX;
6256 final int dy = child.mTop - mScrollY;
6257
6258 rect.offset(dx, dy);
6259
6260 if (offset != null) {
6261 if (!child.hasIdentityMatrix()) {
6262 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
6263 : new float[2];
6264 position[0] = offset.x;
6265 position[1] = offset.y;
6266 child.getMatrix().mapPoints(position);
6267 offset.x = Math.round(position[0]);
6268 offset.y = Math.round(position[1]);
6269 }
6270 offset.x += dx;
6271 offset.y += dy;
6272 }
6273
6274 final int width = mRight - mLeft;
6275 final int height = mBottom - mTop;
6276
6277 boolean rectIsVisible = true;
6278 if (mParent == null ||
6279 (mParent instanceof ViewGroup && ((ViewGroup) mParent).getClipChildren())) {
6280 // Clip to bounds.
6281 rectIsVisible = rect.intersect(0, 0, width, height);
6282 }
6283
6284 if ((forceParentCheck || rectIsVisible)
6285 && (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
6286 // Clip to padding.
6287 rectIsVisible = rect.intersect(mPaddingLeft, mPaddingTop,
6288 width - mPaddingRight, height - mPaddingBottom);
6289 }
6290
6291 if ((forceParentCheck || rectIsVisible) && mClipBounds != null) {
6292 // Clip to clipBounds.
6293 rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
6294 mClipBounds.bottom);
6295 }
6296 r.set((int) Math.floor(rect.left), (int) Math.floor(rect.top),
6297 (int) Math.ceil(rect.right), (int) Math.ceil(rect.bottom));
6298
6299 if ((forceParentCheck || rectIsVisible) && mParent != null) {
6300 if (mParent instanceof ViewGroup) {
6301 rectIsVisible = ((ViewGroup) mParent)
6302 .getChildVisibleRect(this, r, offset, forceParentCheck);
6303 } else {
6304 rectIsVisible = mParent.getChildVisibleRect(this, r, offset);
6305 }
6306 }
6307 return rectIsVisible;
6308 }
6309
6310 @Override
6311 public final void layout(int l, int t, int r, int b) {
6312 if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
6313 if (mTransition != null) {
6314 mTransition.layoutChange(this);
6315 }
6316 super.layout(l, t, r, b);
6317 } else {
6318 // record the fact that we noop'd it; request layout when transition finishes
6319 mLayoutCalledWhileSuppressed = true;
6320 }
6321 }
6322
6323 @Override
6324 protected abstract void onLayout(boolean changed,
6325 int l, int t, int r, int b);
6326
6327 /**
6328 * Indicates whether the view group has the ability to animate its children
6329 * after the first layout.
6330 *
6331 * @return true if the children can be animated, false otherwise
6332 */
6333 protected boolean canAnimate() {
6334 return mLayoutAnimationController != null;
6335 }
6336
6337 /**
6338 * Runs the layout animation. Calling this method triggers a relayout of
6339 * this view group.
6340 */
6341 public void startLayoutAnimation() {
6342 if (mLayoutAnimationController != null) {
6343 mGroupFlags |= FLAG_RUN_ANIMATION;
6344 requestLayout();
6345 }
6346 }
6347
6348 /**
6349 * Schedules the layout animation to be played after the next layout pass
6350 * of this view group. This can be used to restart the layout animation
6351 * when the content of the view group changes or when the activity is
6352 * paused and resumed.
6353 */
6354 public void scheduleLayoutAnimation() {
6355 mGroupFlags |= FLAG_RUN_ANIMATION;
6356 }
6357
6358 /**
6359 * Sets the layout animation controller used to animate the group's
6360 * children after the first layout.
6361 *
6362 * @param controller the animation controller
6363 */
6364 public void setLayoutAnimation(LayoutAnimationController controller) {
6365 mLayoutAnimationController = controller;
6366 if (mLayoutAnimationController != null) {
6367 mGroupFlags |= FLAG_RUN_ANIMATION;
6368 }
6369 }
6370
6371 /**
6372 * Returns the layout animation controller used to animate the group's
6373 * children.
6374 *
6375 * @return the current animation controller
6376 */
6377 @InspectableProperty
6378 public LayoutAnimationController getLayoutAnimation() {
6379 return mLayoutAnimationController;
6380 }
6381
6382 /**
6383 * Indicates whether the children's drawing cache is used during a layout
6384 * animation. By default, the drawing cache is enabled but this will prevent
6385 * nested layout animations from working. To nest animations, you must disable
6386 * the cache.
6387 *
6388 * @return true if the animation cache is enabled, false otherwise
6389 *
6390 * @see #setAnimationCacheEnabled(boolean)
6391 * @see View#setDrawingCacheEnabled(boolean)
6392 *
6393 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6394 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
6395 */
6396 @Deprecated
6397 @InspectableProperty(name = "animationCache")
6398 public boolean isAnimationCacheEnabled() {
6399 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
6400 }
6401
6402 /**
6403 * Enables or disables the children's drawing cache during a layout animation.
6404 * By default, the drawing cache is enabled but this will prevent nested
6405 * layout animations from working. To nest animations, you must disable the
6406 * cache.
6407 *
6408 * @param enabled true to enable the animation cache, false otherwise
6409 *
6410 * @see #isAnimationCacheEnabled()
6411 * @see View#setDrawingCacheEnabled(boolean)
6412 *
6413 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6414 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
6415 */
6416 @Deprecated
6417 public void setAnimationCacheEnabled(boolean enabled) {
6418 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
6419 }
6420
6421 /**
6422 * Indicates whether this ViewGroup will always try to draw its children using their
6423 * drawing cache. By default this property is enabled.
6424 *
6425 * @return true if the animation cache is enabled, false otherwise
6426 *
6427 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
6428 * @see #setChildrenDrawnWithCacheEnabled(boolean)
6429 * @see View#setDrawingCacheEnabled(boolean)
6430 *
6431 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6432 * Child views may no longer have their caching behavior disabled by parents.
6433 */
6434 @Deprecated
6435 @InspectableProperty(name = "alwaysDrawnWithCache")
6436 public boolean isAlwaysDrawnWithCacheEnabled() {
6437 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
6438 }
6439
6440 /**
6441 * Indicates whether this ViewGroup will always try to draw its children using their
6442 * drawing cache. This property can be set to true when the cache rendering is
6443 * slightly different from the children's normal rendering. Renderings can be different,
6444 * for instance, when the cache's quality is set to low.
6445 *
6446 * When this property is disabled, the ViewGroup will use the drawing cache of its
6447 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
6448 * when to start using the drawing cache and when to stop using it.
6449 *
6450 * @param always true to always draw with the drawing cache, false otherwise
6451 *
6452 * @see #isAlwaysDrawnWithCacheEnabled()
6453 * @see #setChildrenDrawnWithCacheEnabled(boolean)
6454 * @see View#setDrawingCacheEnabled(boolean)
6455 * @see View#setDrawingCacheQuality(int)
6456 *
6457 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6458 * Child views may no longer have their caching behavior disabled by parents.
6459 */
6460 @Deprecated
6461 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
6462 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
6463 }
6464
6465 /**
6466 * Indicates whether the ViewGroup is currently drawing its children using
6467 * their drawing cache.
6468 *
6469 * @return true if children should be drawn with their cache, false otherwise
6470 *
6471 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
6472 * @see #setChildrenDrawnWithCacheEnabled(boolean)
6473 *
6474 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6475 * Child views may no longer be forced to cache their rendering state by their parents.
6476 * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
6477 */
6478 @Deprecated
6479 protected boolean isChildrenDrawnWithCacheEnabled() {
6480 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
6481 }
6482
6483 /**
6484 * Tells the ViewGroup to draw its children using their drawing cache. This property
6485 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
6486 * will be used only if it has been enabled.
6487 *
6488 * Subclasses should call this method to start and stop using the drawing cache when
6489 * they perform performance sensitive operations, like scrolling or animating.
6490 *
6491 * @param enabled true if children should be drawn with their cache, false otherwise
6492 *
6493 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
6494 * @see #isChildrenDrawnWithCacheEnabled()
6495 *
6496 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6497 * Child views may no longer be forced to cache their rendering state by their parents.
6498 * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
6499 */
6500 @Deprecated
6501 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
6502 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
6503 }
6504
6505 /**
6506 * Indicates whether the ViewGroup is drawing its children in the order defined by
6507 * {@link #getChildDrawingOrder(int, int)}.
6508 *
6509 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
6510 * false otherwise
6511 *
6512 * @see #setChildrenDrawingOrderEnabled(boolean)
6513 * @see #getChildDrawingOrder(int, int)
6514 */
6515 @ViewDebug.ExportedProperty(category = "drawing")
6516 protected boolean isChildrenDrawingOrderEnabled() {
6517 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
6518 }
6519
6520 /**
6521 * Tells the ViewGroup whether to draw its children in the order defined by the method
6522 * {@link #getChildDrawingOrder(int, int)}.
6523 * <p>
6524 * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
6525 * will override custom child ordering done via this method.
6526 *
6527 * @param enabled true if the order of the children when drawing is determined by
6528 * {@link #getChildDrawingOrder(int, int)}, false otherwise
6529 *
6530 * @see #isChildrenDrawingOrderEnabled()
6531 * @see #getChildDrawingOrder(int, int)
6532 */
6533 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
6534 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
6535 }
6536
6537 private boolean hasBooleanFlag(int flag) {
6538 return (mGroupFlags & flag) == flag;
6539 }
6540
6541 private void setBooleanFlag(int flag, boolean value) {
6542 if (value) {
6543 mGroupFlags |= flag;
6544 } else {
6545 mGroupFlags &= ~flag;
6546 }
6547 }
6548
6549 /**
6550 * Returns an integer indicating what types of drawing caches are kept in memory.
6551 *
6552 * @see #setPersistentDrawingCache(int)
6553 * @see #setAnimationCacheEnabled(boolean)
6554 *
6555 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
6556 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
6557 * and {@link #PERSISTENT_ALL_CACHES}
6558 *
6559 * @deprecated The view drawing cache was largely made obsolete with the introduction of
6560 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
6561 * layers are largely unnecessary and can easily result in a net loss in performance due to the
6562 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
6563 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
6564 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
6565 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
6566 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
6567 * software-rendered usages are discouraged and have compatibility issues with hardware-only
6568 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
6569 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
6570 * reports or unit testing the {@link PixelCopy} API is recommended.
6571 */
6572 @Deprecated
6573 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
6574 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
6575 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
6576 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
6577 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
6578 })
6579 @InspectableProperty(enumMapping = {
6580 @EnumEntry(value = PERSISTENT_NO_CACHE, name = "none"),
6581 @EnumEntry(value = PERSISTENT_ANIMATION_CACHE, name = "animation"),
6582 @EnumEntry(value = PERSISTENT_SCROLLING_CACHE, name = "scrolling"),
6583 @EnumEntry(value = PERSISTENT_ALL_CACHES, name = "all"),
6584 })
6585 public int getPersistentDrawingCache() {
6586 return mPersistentDrawingCache;
6587 }
6588
6589 /**
6590 * Indicates what types of drawing caches should be kept in memory after
6591 * they have been created.
6592 *
6593 * @see #getPersistentDrawingCache()
6594 * @see #setAnimationCacheEnabled(boolean)
6595 *
6596 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
6597 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
6598 * and {@link #PERSISTENT_ALL_CACHES}
6599 *
6600 * @deprecated The view drawing cache was largely made obsolete with the introduction of
6601 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
6602 * layers are largely unnecessary and can easily result in a net loss in performance due to the
6603 * cost of creating and updating the layer. In the rare cases where caching layers are useful,
6604 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
6605 * rendering. For software-rendered snapshots of a small part of the View hierarchy or
6606 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
6607 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
6608 * software-rendered usages are discouraged and have compatibility issues with hardware-only
6609 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
6610 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
6611 * reports or unit testing the {@link PixelCopy} API is recommended.
6612 */
6613 @Deprecated
6614 public void setPersistentDrawingCache(int drawingCacheToKeep) {
6615 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
6616 }
6617
6618 private void setLayoutMode(int layoutMode, boolean explicitly) {
6619 mLayoutMode = layoutMode;
6620 setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
6621 }
6622
6623 /**
6624 * Recursively traverse the view hierarchy, resetting the layoutMode of any
6625 * descendants that had inherited a different layoutMode from a previous parent.
6626 * Recursion terminates when a descendant's mode is:
6627 * <ul>
6628 * <li>Undefined</li>
6629 * <li>The same as the root node's</li>
6630 * <li>A mode that had been explicitly set</li>
6631 * <ul/>
6632 * The first two clauses are optimizations.
6633 * @param layoutModeOfRoot
6634 */
6635 @Override
6636 void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
6637 if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
6638 mLayoutMode == layoutModeOfRoot ||
6639 hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
6640 return;
6641 }
6642 setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
6643
6644 // apply recursively
6645 for (int i = 0, N = getChildCount(); i < N; i++) {
6646 getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
6647 }
6648 }
6649
6650 /**
6651 * Returns the basis of alignment during layout operations on this ViewGroup:
6652 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
6653 * <p>
6654 * If no layoutMode was explicitly set, either programmatically or in an XML resource,
6655 * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
6656 * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
6657 *
6658 * @return the layout mode to use during layout operations
6659 *
6660 * @see #setLayoutMode(int)
6661 */
6662 @InspectableProperty(enumMapping = {
6663 @EnumEntry(value = LAYOUT_MODE_CLIP_BOUNDS, name = "clipBounds"),
6664 @EnumEntry(value = LAYOUT_MODE_OPTICAL_BOUNDS, name = "opticalBounds")
6665 })
6666 public int getLayoutMode() {
6667 if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
6668 int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
6669 ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
6670 setLayoutMode(inheritedLayoutMode, false);
6671 }
6672 return mLayoutMode;
6673 }
6674
6675 /**
6676 * Sets the basis of alignment during the layout of this ViewGroup.
6677 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
6678 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
6679 *
6680 * @param layoutMode the layout mode to use during layout operations
6681 *
6682 * @see #getLayoutMode()
6683 * @attr ref android.R.styleable#ViewGroup_layoutMode
6684 */
6685 public void setLayoutMode(int layoutMode) {
6686 if (mLayoutMode != layoutMode) {
6687 invalidateInheritedLayoutMode(layoutMode);
6688 setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
6689 requestLayout();
6690 }
6691 }
6692
6693 /**
6694 * Returns a new set of layout parameters based on the supplied attributes set.
6695 *
6696 * @param attrs the attributes to build the layout parameters from
6697 *
6698 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
6699 * of its descendants
6700 */
6701 public LayoutParams generateLayoutParams(AttributeSet attrs) {
6702 return new LayoutParams(getContext(), attrs);
6703 }
6704
6705 /**
6706 * Returns a safe set of layout parameters based on the supplied layout params.
6707 * When a ViewGroup is passed a View whose layout params do not pass the test of
6708 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
6709 * is invoked. This method should return a new set of layout params suitable for
6710 * this ViewGroup, possibly by copying the appropriate attributes from the
6711 * specified set of layout params.
6712 *
6713 * @param p The layout parameters to convert into a suitable set of layout parameters
6714 * for this ViewGroup.
6715 *
6716 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
6717 * of its descendants
6718 */
6719 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
6720 return p;
6721 }
6722
6723 /**
6724 * Returns a set of default layout parameters. These parameters are requested
6725 * when the View passed to {@link #addView(View)} has no layout parameters
6726 * already set. If null is returned, an exception is thrown from addView.
6727 *
6728 * @return a set of default layout parameters or null
6729 */
6730 protected LayoutParams generateDefaultLayoutParams() {
6731 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
6732 }
6733
6734 @Override
6735 protected void debug(int depth) {
6736 super.debug(depth);
6737 String output;
6738
6739 if (mFocused != null) {
6740 output = debugIndent(depth);
6741 output += "mFocused";
6742 Log.d(VIEW_LOG_TAG, output);
6743 mFocused.debug(depth + 1);
6744 }
6745 if (mDefaultFocus != null) {
6746 output = debugIndent(depth);
6747 output += "mDefaultFocus";
6748 Log.d(VIEW_LOG_TAG, output);
6749 mDefaultFocus.debug(depth + 1);
6750 }
6751 if (mFocusedInCluster != null) {
6752 output = debugIndent(depth);
6753 output += "mFocusedInCluster";
6754 Log.d(VIEW_LOG_TAG, output);
6755 mFocusedInCluster.debug(depth + 1);
6756 }
6757 if (mChildrenCount != 0) {
6758 output = debugIndent(depth);
6759 output += "{";
6760 Log.d(VIEW_LOG_TAG, output);
6761 }
6762 int count = mChildrenCount;
6763 for (int i = 0; i < count; i++) {
6764 View child = mChildren[i];
6765 child.debug(depth + 1);
6766 }
6767
6768 if (mChildrenCount != 0) {
6769 output = debugIndent(depth);
6770 output += "}";
6771 Log.d(VIEW_LOG_TAG, output);
6772 }
6773 }
6774
6775 /**
6776 * Returns the position in the group of the specified child view.
6777 *
6778 * @param child the view for which to get the position
6779 * @return a positive integer representing the position of the view in the
6780 * group, or -1 if the view does not exist in the group
6781 */
6782 public int indexOfChild(View child) {
6783 final int count = mChildrenCount;
6784 final View[] children = mChildren;
6785 for (int i = 0; i < count; i++) {
6786 if (children[i] == child) {
6787 return i;
6788 }
6789 }
6790 return -1;
6791 }
6792
6793 /**
6794 * Returns the number of children in the group.
6795 *
6796 * @return a positive integer representing the number of children in
6797 * the group
6798 */
6799 public int getChildCount() {
6800 return mChildrenCount;
6801 }
6802
6803 /**
6804 * Returns the view at the specified position in the group.
6805 *
6806 * @param index the position at which to get the view from
6807 * @return the view at the specified position or null if the position
6808 * does not exist within the group
6809 */
6810 public View getChildAt(int index) {
6811 if (index < 0 || index >= mChildrenCount) {
6812 return null;
6813 }
6814 return mChildren[index];
6815 }
6816
6817 /**
6818 * Ask all of the children of this view to measure themselves, taking into
6819 * account both the MeasureSpec requirements for this view and its padding.
6820 * We skip children that are in the GONE state The heavy lifting is done in
6821 * getChildMeasureSpec.
6822 *
6823 * @param widthMeasureSpec The width requirements for this view
6824 * @param heightMeasureSpec The height requirements for this view
6825 */
6826 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
6827 final int size = mChildrenCount;
6828 final View[] children = mChildren;
6829 for (int i = 0; i < size; ++i) {
6830 final View child = children[i];
6831 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
6832 measureChild(child, widthMeasureSpec, heightMeasureSpec);
6833 }
6834 }
6835 }
6836
6837 /**
6838 * Ask one of the children of this view to measure itself, taking into
6839 * account both the MeasureSpec requirements for this view and its padding.
6840 * The heavy lifting is done in getChildMeasureSpec.
6841 *
6842 * @param child The child to measure
6843 * @param parentWidthMeasureSpec The width requirements for this view
6844 * @param parentHeightMeasureSpec The height requirements for this view
6845 */
6846 protected void measureChild(View child, int parentWidthMeasureSpec,
6847 int parentHeightMeasureSpec) {
6848 final LayoutParams lp = child.getLayoutParams();
6849
6850 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
6851 mPaddingLeft + mPaddingRight, lp.width);
6852 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
6853 mPaddingTop + mPaddingBottom, lp.height);
6854
6855 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
6856 }
6857
6858 /**
6859 * Ask one of the children of this view to measure itself, taking into
6860 * account both the MeasureSpec requirements for this view and its padding
6861 * and margins. The child must have MarginLayoutParams The heavy lifting is
6862 * done in getChildMeasureSpec.
6863 *
6864 * @param child The child to measure
6865 * @param parentWidthMeasureSpec The width requirements for this view
6866 * @param widthUsed Extra space that has been used up by the parent
6867 * horizontally (possibly by other children of the parent)
6868 * @param parentHeightMeasureSpec The height requirements for this view
6869 * @param heightUsed Extra space that has been used up by the parent
6870 * vertically (possibly by other children of the parent)
6871 */
6872 protected void measureChildWithMargins(View child,
6873 int parentWidthMeasureSpec, int widthUsed,
6874 int parentHeightMeasureSpec, int heightUsed) {
6875 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
6876
6877 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
6878 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
6879 + widthUsed, lp.width);
6880 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
6881 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
6882 + heightUsed, lp.height);
6883
6884 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
6885 }
6886
6887 /**
6888 * Does the hard part of measureChildren: figuring out the MeasureSpec to
6889 * pass to a particular child. This method figures out the right MeasureSpec
6890 * for one dimension (height or width) of one child view.
6891 *
6892 * The goal is to combine information from our MeasureSpec with the
6893 * LayoutParams of the child to get the best possible results. For example,
6894 * if the this view knows its size (because its MeasureSpec has a mode of
6895 * EXACTLY), and the child has indicated in its LayoutParams that it wants
6896 * to be the same size as the parent, the parent should ask the child to
6897 * layout given an exact size.
6898 *
6899 * @param spec The requirements for this view
6900 * @param padding The padding of this view for the current dimension and
6901 * margins, if applicable
6902 * @param childDimension How big the child wants to be in the current
6903 * dimension
6904 * @return a MeasureSpec integer for the child
6905 */
6906 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
6907 int specMode = MeasureSpec.getMode(spec);
6908 int specSize = MeasureSpec.getSize(spec);
6909
6910 int size = Math.max(0, specSize - padding);
6911
6912 int resultSize = 0;
6913 int resultMode = 0;
6914
6915 switch (specMode) {
6916 // Parent has imposed an exact size on us
6917 case MeasureSpec.EXACTLY:
6918 if (childDimension >= 0) {
6919 resultSize = childDimension;
6920 resultMode = MeasureSpec.EXACTLY;
6921 } else if (childDimension == LayoutParams.MATCH_PARENT) {
6922 // Child wants to be our size. So be it.
6923 resultSize = size;
6924 resultMode = MeasureSpec.EXACTLY;
6925 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6926 // Child wants to determine its own size. It can't be
6927 // bigger than us.
6928 resultSize = size;
6929 resultMode = MeasureSpec.AT_MOST;
6930 }
6931 break;
6932
6933 // Parent has imposed a maximum size on us
6934 case MeasureSpec.AT_MOST:
6935 if (childDimension >= 0) {
6936 // Child wants a specific size... so be it
6937 resultSize = childDimension;
6938 resultMode = MeasureSpec.EXACTLY;
6939 } else if (childDimension == LayoutParams.MATCH_PARENT) {
6940 // Child wants to be our size, but our size is not fixed.
6941 // Constrain child to not be bigger than us.
6942 resultSize = size;
6943 resultMode = MeasureSpec.AT_MOST;
6944 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6945 // Child wants to determine its own size. It can't be
6946 // bigger than us.
6947 resultSize = size;
6948 resultMode = MeasureSpec.AT_MOST;
6949 }
6950 break;
6951
6952 // Parent asked to see how big we want to be
6953 case MeasureSpec.UNSPECIFIED:
6954 if (childDimension >= 0) {
6955 // Child wants a specific size... let him have it
6956 resultSize = childDimension;
6957 resultMode = MeasureSpec.EXACTLY;
6958 } else if (childDimension == LayoutParams.MATCH_PARENT) {
6959 // Child wants to be our size... find out how big it should
6960 // be
6961 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
6962 resultMode = MeasureSpec.UNSPECIFIED;
6963 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6964 // Child wants to determine its own size.... find out how
6965 // big it should be
6966 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
6967 resultMode = MeasureSpec.UNSPECIFIED;
6968 }
6969 break;
6970 }
6971 //noinspection ResourceType
6972 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
6973 }
6974
6975
6976 /**
6977 * Removes any pending animations for views that have been removed. Call
6978 * this if you don't want animations for exiting views to stack up.
6979 */
6980 public void clearDisappearingChildren() {
6981 final ArrayList<View> disappearingChildren = mDisappearingChildren;
6982 if (disappearingChildren != null) {
6983 final int count = disappearingChildren.size();
6984 for (int i = 0; i < count; i++) {
6985 final View view = disappearingChildren.get(i);
6986 if (view.mAttachInfo != null) {
6987 view.dispatchDetachedFromWindow();
6988 }
6989 view.clearAnimation();
6990 }
6991 disappearingChildren.clear();
6992 invalidate();
6993 }
6994 }
6995
6996 /**
6997 * Add a view which is removed from mChildren but still needs animation
6998 *
6999 * @param v View to add
7000 */
7001 private void addDisappearingView(View v) {
7002 ArrayList<View> disappearingChildren = mDisappearingChildren;
7003
7004 if (disappearingChildren == null) {
7005 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
7006 }
7007
7008 disappearingChildren.add(v);
7009 }
7010
7011 /**
7012 * Cleanup a view when its animation is done. This may mean removing it from
7013 * the list of disappearing views.
7014 *
7015 * @param view The view whose animation has finished
7016 * @param animation The animation, cannot be null
7017 */
7018 void finishAnimatingView(final View view, Animation animation) {
7019 final ArrayList<View> disappearingChildren = mDisappearingChildren;
7020 if (disappearingChildren != null) {
7021 if (disappearingChildren.contains(view)) {
7022 disappearingChildren.remove(view);
7023
7024 if (view.mAttachInfo != null) {
7025 view.dispatchDetachedFromWindow();
7026 }
7027
7028 view.clearAnimation();
7029 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
7030 }
7031 }
7032
7033 if (animation != null && !animation.getFillAfter()) {
7034 view.clearAnimation();
7035 }
7036
7037 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
7038 view.onAnimationEnd();
7039 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
7040 // so we'd rather be safe than sorry
7041 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
7042 // Draw one more frame after the animation is done
7043 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
7044 }
7045 }
7046
7047 /**
7048 * Utility function called by View during invalidation to determine whether a view that
7049 * is invisible or gone should still be invalidated because it is being transitioned (and
7050 * therefore still needs to be drawn).
7051 */
7052 boolean isViewTransitioning(View view) {
7053 return (mTransitioningViews != null && mTransitioningViews.contains(view));
7054 }
7055
7056 /**
7057 * This method tells the ViewGroup that the given View object, which should have this
7058 * ViewGroup as its parent,
7059 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
7060 * is removed from its parent. This allows animations, such as those used by
7061 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
7062 * the removal of views. A call to this method should always be accompanied by a later call
7063 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
7064 * so that the View finally gets removed.
7065 *
7066 * @param view The View object to be kept visible even if it gets removed from its parent.
7067 */
7068 public void startViewTransition(View view) {
7069 if (view.mParent == this) {
7070 if (mTransitioningViews == null) {
7071 mTransitioningViews = new ArrayList<View>();
7072 }
7073 mTransitioningViews.add(view);
7074 }
7075 }
7076
7077 /**
7078 * This method should always be called following an earlier call to
7079 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
7080 * and will no longer be displayed. Note that this method does not perform the functionality
7081 * of removing a view from its parent; it just discontinues the display of a View that
7082 * has previously been removed.
7083 *
7084 * @return view The View object that has been removed but is being kept around in the visible
7085 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
7086 */
7087 public void endViewTransition(View view) {
7088 if (mTransitioningViews != null) {
7089 mTransitioningViews.remove(view);
7090 final ArrayList<View> disappearingChildren = mDisappearingChildren;
7091 if (disappearingChildren != null && disappearingChildren.contains(view)) {
7092 disappearingChildren.remove(view);
7093 if (mVisibilityChangingChildren != null &&
7094 mVisibilityChangingChildren.contains(view)) {
7095 mVisibilityChangingChildren.remove(view);
7096 } else {
7097 if (view.mAttachInfo != null) {
7098 view.dispatchDetachedFromWindow();
7099 }
7100 if (view.mParent != null) {
7101 view.mParent = null;
7102 }
7103 }
7104 invalidate();
7105 }
7106 }
7107 }
7108
7109 private LayoutTransition.TransitionListener mLayoutTransitionListener =
7110 new LayoutTransition.TransitionListener() {
7111 @Override
7112 public void startTransition(LayoutTransition transition, ViewGroup container,
7113 View view, int transitionType) {
7114 // We only care about disappearing items, since we need special logic to keep
7115 // those items visible after they've been 'removed'
7116 if (transitionType == LayoutTransition.DISAPPEARING) {
7117 startViewTransition(view);
7118 }
7119 }
7120
7121 @Override
7122 public void endTransition(LayoutTransition transition, ViewGroup container,
7123 View view, int transitionType) {
7124 if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
7125 requestLayout();
7126 mLayoutCalledWhileSuppressed = false;
7127 }
7128 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
7129 endViewTransition(view);
7130 }
7131 }
7132 };
7133
7134 /**
7135 * Tells this ViewGroup to suppress all layout() calls until layout
7136 * suppression is disabled with a later call to suppressLayout(false).
7137 * When layout suppression is disabled, a requestLayout() call is sent
7138 * if layout() was attempted while layout was being suppressed.
7139 */
7140 public void suppressLayout(boolean suppress) {
7141 mSuppressLayout = suppress;
7142 if (!suppress) {
7143 if (mLayoutCalledWhileSuppressed) {
7144 requestLayout();
7145 mLayoutCalledWhileSuppressed = false;
7146 }
7147 }
7148 }
7149
7150 /**
7151 * Returns whether layout calls on this container are currently being
7152 * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
7153 *
7154 * @return true if layout calls are currently suppressed, false otherwise.
7155 */
7156 public boolean isLayoutSuppressed() {
7157 return mSuppressLayout;
7158 }
7159
7160 @Override
7161 public boolean gatherTransparentRegion(Region region) {
7162 // If no transparent regions requested, we are always opaque.
7163 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
7164 if (meOpaque && region == null) {
7165 // The caller doesn't care about the region, so stop now.
7166 return true;
7167 }
7168 super.gatherTransparentRegion(region);
7169 // Instead of naively traversing the view tree, we have to traverse according to the Z
7170 // order here. We need to go with the same order as dispatchDraw().
7171 // One example is that after surfaceView punch a hole, we will still allow other views drawn
7172 // on top of that hole. In this case, those other views should be able to cut the
7173 // transparent region into smaller area.
7174 final int childrenCount = mChildrenCount;
7175 boolean noneOfTheChildrenAreTransparent = true;
7176 if (childrenCount > 0) {
7177 final ArrayList<View> preorderedList = buildOrderedChildList();
7178 final boolean customOrder = preorderedList == null
7179 && isChildrenDrawingOrderEnabled();
7180 final View[] children = mChildren;
7181 for (int i = 0; i < childrenCount; i++) {
7182 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
7183 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
7184 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
7185 if (!child.gatherTransparentRegion(region)) {
7186 noneOfTheChildrenAreTransparent = false;
7187 }
7188 }
7189 }
7190 if (preorderedList != null) preorderedList.clear();
7191 }
7192 return meOpaque || noneOfTheChildrenAreTransparent;
7193 }
7194
7195 @Override
7196 public void requestTransparentRegion(View child) {
7197 if (child != null) {
7198 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
7199 if (mParent != null) {
7200 mParent.requestTransparentRegion(this);
7201 }
7202 }
7203 }
7204
7205 /**
7206 * @hide
7207 */
7208 @Override
7209 public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
7210 final int childrenCount = mChildrenCount;
7211 final ArrayList<View> preorderedList = buildTouchDispatchChildList();
7212 final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
7213 final View[] children = mChildren;
7214 for (int i = childrenCount - 1; i >= 0; i--) {
7215 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
7216 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
7217 if (child == view) {
7218 // We've reached the target view.
7219 break;
7220 }
7221 if (!child.canReceivePointerEvents()) {
7222 // This child cannot be touched. Skip it.
7223 continue;
7224 }
7225 applyOpToRegionByBounds(touchableRegion, child, Region.Op.DIFFERENCE);
7226 }
7227
7228 // The touchable region should not exceed the bounds of its container.
7229 applyOpToRegionByBounds(touchableRegion, this, Region.Op.INTERSECT);
7230
7231 final ViewParent parent = getParent();
7232 if (parent != null) {
7233 parent.subtractObscuredTouchableRegion(touchableRegion, this);
7234 }
7235 }
7236
7237 private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) {
7238 final int[] locationInWindow = new int[2];
7239 view.getLocationInWindow(locationInWindow);
7240 final int x = locationInWindow[0];
7241 final int y = locationInWindow[1];
7242 region.op(x, y, x + view.getWidth(), y + view.getHeight(), op);
7243 }
7244
7245 @Override
7246 public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
7247 insets = super.dispatchApplyWindowInsets(insets);
7248 if (insets.isConsumed()) {
7249 return insets;
7250 }
7251 if (View.sBrokenInsetsDispatch) {
7252 return brokenDispatchApplyWindowInsets(insets);
7253 } else {
7254 return newDispatchApplyWindowInsets(insets);
7255 }
7256 }
7257
7258 private WindowInsets brokenDispatchApplyWindowInsets(WindowInsets insets) {
7259 final int count = getChildCount();
7260 for (int i = 0; i < count; i++) {
7261 insets = getChildAt(i).dispatchApplyWindowInsets(insets);
7262 if (insets.isConsumed()) {
7263 break;
7264 }
7265 }
7266 return insets;
7267 }
7268
7269 private WindowInsets newDispatchApplyWindowInsets(WindowInsets insets) {
7270 final int count = getChildCount();
7271 for (int i = 0; i < count; i++) {
7272 getChildAt(i).dispatchApplyWindowInsets(insets);
7273 }
7274 return insets;
7275 }
7276
7277 @Override
7278 public void setWindowInsetsAnimationCallback(
7279 @Nullable WindowInsetsAnimation.Callback callback) {
7280 super.setWindowInsetsAnimationCallback(callback);
7281 mInsetsAnimationDispatchMode = callback != null
7282 ? callback.getDispatchMode()
7283 : DISPATCH_MODE_CONTINUE_ON_SUBTREE;
7284 }
7285
7286 /**
7287 * @hide
7288 */
7289 @Override
7290 public boolean hasWindowInsetsAnimationCallback() {
7291 if (super.hasWindowInsetsAnimationCallback()) {
7292 return true;
7293 }
7294
7295 // If we are root-level content view that fits insets, we imitate consuming behavior, so
7296 // no child will retrieve window insets animation callback.
7297 // See dispatchWindowInsetsAnimationPrepare.
7298 boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
7299 || isFrameworkOptionalFitsSystemWindows();
7300 if (isOptionalFitSystemWindows && mAttachInfo != null
7301 && mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
7302 return false;
7303 }
7304
7305 final int count = getChildCount();
7306 for (int i = 0; i < count; i++) {
7307 if (getChildAt(i).hasWindowInsetsAnimationCallback()) {
7308 return true;
7309 }
7310 }
7311 return false;
7312 }
7313
7314 @Override
7315 public void dispatchWindowInsetsAnimationPrepare(
7316 @NonNull WindowInsetsAnimation animation) {
7317 super.dispatchWindowInsetsAnimationPrepare(animation);
7318
7319 // If we are root-level content view that fits insets, set dispatch mode to stop to imitate
7320 // consume behavior.
7321 boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
7322 || isFrameworkOptionalFitsSystemWindows();
7323 if (isOptionalFitSystemWindows && mAttachInfo != null
7324 && getListenerInfo().mWindowInsetsAnimationCallback == null
7325 && mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
7326 mInsetsAnimationDispatchMode = DISPATCH_MODE_STOP;
7327 return;
7328 }
7329
7330 if (mInsetsAnimationDispatchMode == DISPATCH_MODE_STOP) {
7331 return;
7332 }
7333 final int count = getChildCount();
7334 for (int i = 0; i < count; i++) {
7335 getChildAt(i).dispatchWindowInsetsAnimationPrepare(animation);
7336 }
7337 }
7338
7339 @Override
7340 @NonNull
7341 public Bounds dispatchWindowInsetsAnimationStart(
7342 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) {
7343 bounds = super.dispatchWindowInsetsAnimationStart(animation, bounds);
7344 if (mInsetsAnimationDispatchMode == DISPATCH_MODE_STOP) {
7345 return bounds;
7346 }
7347 final int count = getChildCount();
7348 for (int i = 0; i < count; i++) {
7349 getChildAt(i).dispatchWindowInsetsAnimationStart(animation, bounds);
7350 }
7351 return bounds;
7352 }
7353
7354 @Override
7355 @NonNull
7356 public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
7357 @NonNull List<WindowInsetsAnimation> runningAnimations) {
7358 insets = super.dispatchWindowInsetsAnimationProgress(insets, runningAnimations);
7359 if (mInsetsAnimationDispatchMode == DISPATCH_MODE_STOP) {
7360 return insets;
7361 }
7362 final int count = getChildCount();
7363 for (int i = 0; i < count; i++) {
7364 getChildAt(i).dispatchWindowInsetsAnimationProgress(insets, runningAnimations);
7365 }
7366 return insets;
7367 }
7368
7369 @Override
7370 public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
7371 super.dispatchWindowInsetsAnimationEnd(animation);
7372 if (mInsetsAnimationDispatchMode == DISPATCH_MODE_STOP) {
7373 return;
7374 }
7375 final int count = getChildCount();
7376 for (int i = 0; i < count; i++) {
7377 getChildAt(i).dispatchWindowInsetsAnimationEnd(animation);
7378 }
7379 }
7380
7381 /**
7382 * Offsets the given rectangle in parent's local coordinates into child's coordinate space
7383 * and clips the result to the child View's bounds, padding and clipRect if appropriate. If the
7384 * resulting rectangle is not empty, the request is forwarded to the child.
7385 * <p>
7386 * Note: This method does not account for any static View transformations which may be
7387 * applied to the child view.
7388 *
7389 * @param child the child to dispatch to
7390 * @param localVisibleRect the visible (clipped) area of this ViewGroup, in local coordinates
7391 * @param windowOffset the offset of localVisibleRect within the window
7392 * @param targets a queue to collect located targets
7393 */
7394 private void dispatchTransformedScrollCaptureSearch(View child, Rect localVisibleRect,
7395 Point windowOffset, Queue<ScrollCaptureTarget> targets) {
7396
7397 // copy local visible rect for modification and dispatch
7398 final Rect childVisibleRect = getTempRect();
7399 childVisibleRect.set(localVisibleRect);
7400
7401 // transform to child coords
7402 final Point childWindowOffset = getTempPoint();
7403 childWindowOffset.set(windowOffset.x, windowOffset.y);
7404
7405 final int dx = child.mLeft - mScrollX;
7406 final int dy = child.mTop - mScrollY;
7407
7408 childVisibleRect.offset(-dx, -dy);
7409 childWindowOffset.offset(dx, dy);
7410
7411 boolean rectIsVisible = true;
7412 final int width = mRight - mLeft;
7413 final int height = mBottom - mTop;
7414
7415 // Clip to child bounds
7416 if (getClipChildren()) {
7417 rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), child.getHeight());
7418 }
7419
7420 // Clip to child padding.
7421 if (rectIsVisible && (child instanceof ViewGroup)
7422 && ((ViewGroup) child).getClipToPadding()) {
7423 rectIsVisible = childVisibleRect.intersect(
7424 child.mPaddingLeft, child.mPaddingTop,
7425 child.getWidth() - child.mPaddingRight,
7426 child.getHeight() - child.mPaddingBottom);
7427 }
7428 // Clip to child clipBounds.
7429 if (rectIsVisible && child.mClipBounds != null) {
7430 rectIsVisible = childVisibleRect.intersect(child.mClipBounds);
7431 }
7432 if (rectIsVisible) {
7433 child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets);
7434 }
7435 }
7436
7437 /**
7438 * Handle the scroll capture search request by checking this view if applicable, then to each
7439 * child view.
7440 *
7441 * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to
7442 * the parent
7443 * @param windowOffset the offset of this view within the window
7444 * @param targets the collected list of scroll capture targets
7445 *
7446 * @hide
7447 */
7448 @Override
7449 public void dispatchScrollCaptureSearch(
7450 @NonNull Rect localVisibleRect, @NonNull Point windowOffset,
7451 @NonNull Queue<ScrollCaptureTarget> targets) {
7452
7453 // Dispatch to self first.
7454 super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
7455
7456 // Then dispatch to children, if not excluding descendants.
7457 if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) == 0) {
7458 final int childCount = getChildCount();
7459 for (int i = 0; i < childCount; i++) {
7460 View child = getChildAt(i);
7461 // Only visible views can be captured.
7462 if (child.getVisibility() != View.VISIBLE) {
7463 continue;
7464 }
7465 // Transform to child coords and dispatch
7466 dispatchTransformedScrollCaptureSearch(child, localVisibleRect, windowOffset,
7467 targets);
7468 }
7469 }
7470 }
7471
7472 /**
7473 * Returns the animation listener to which layout animation events are
7474 * sent.
7475 *
7476 * @return an {@link android.view.animation.Animation.AnimationListener}
7477 */
7478 public Animation.AnimationListener getLayoutAnimationListener() {
7479 return mAnimationListener;
7480 }
7481
7482 @Override
7483 protected void drawableStateChanged() {
7484 super.drawableStateChanged();
7485
7486 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
7487 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
7488 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
7489 + " child has duplicateParentState set to true");
7490 }
7491
7492 final View[] children = mChildren;
7493 final int count = mChildrenCount;
7494
7495 for (int i = 0; i < count; i++) {
7496 final View child = children[i];
7497 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
7498 child.refreshDrawableState();
7499 }
7500 }
7501 }
7502 }
7503
7504 @Override
7505 public void jumpDrawablesToCurrentState() {
7506 super.jumpDrawablesToCurrentState();
7507 final View[] children = mChildren;
7508 final int count = mChildrenCount;
7509 for (int i = 0; i < count; i++) {
7510 children[i].jumpDrawablesToCurrentState();
7511 }
7512 }
7513
7514 @Override
7515 protected int[] onCreateDrawableState(int extraSpace) {
7516 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
7517 return super.onCreateDrawableState(extraSpace);
7518 }
7519
7520 int need = 0;
7521 int n = getChildCount();
7522 for (int i = 0; i < n; i++) {
7523 int[] childState = getChildAt(i).getDrawableState();
7524
7525 if (childState != null) {
7526 need += childState.length;
7527 }
7528 }
7529
7530 int[] state = super.onCreateDrawableState(extraSpace + need);
7531
7532 for (int i = 0; i < n; i++) {
7533 int[] childState = getChildAt(i).getDrawableState();
7534
7535 if (childState != null) {
7536 state = mergeDrawableStates(state, childState);
7537 }
7538 }
7539
7540 return state;
7541 }
7542
7543 /**
7544 * Sets whether this ViewGroup's drawable states also include
7545 * its children's drawable states. This is used, for example, to
7546 * make a group appear to be focused when its child EditText or button
7547 * is focused.
7548 */
7549 public void setAddStatesFromChildren(boolean addsStates) {
7550 if (addsStates) {
7551 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
7552 } else {
7553 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
7554 }
7555
7556 refreshDrawableState();
7557 }
7558
7559 /**
7560 * Returns whether this ViewGroup's drawable states also include
7561 * its children's drawable states. This is used, for example, to
7562 * make a group appear to be focused when its child EditText or button
7563 * is focused.
7564 */
7565 @InspectableProperty
7566 public boolean addStatesFromChildren() {
7567 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
7568 }
7569
7570 /**
7571 * If {@link #addStatesFromChildren} is true, refreshes this group's
7572 * drawable state (to include the states from its children).
7573 */
7574 @Override
7575 public void childDrawableStateChanged(View child) {
7576 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
7577 refreshDrawableState();
7578 }
7579 }
7580
7581 /**
7582 * Specifies the animation listener to which layout animation events must
7583 * be sent. Only
7584 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
7585 * and
7586 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
7587 * are invoked.
7588 *
7589 * @param animationListener the layout animation listener
7590 */
7591 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
7592 mAnimationListener = animationListener;
7593 }
7594
7595 /**
7596 * This method is called by LayoutTransition when there are 'changing' animations that need
7597 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
7598 * starts all pending transitions prior to the drawing phase in the current traversal.
7599 *
7600 * @param transition The LayoutTransition to be started on the next traversal.
7601 *
7602 * @hide
7603 */
7604 public void requestTransitionStart(LayoutTransition transition) {
7605 ViewRootImpl viewAncestor = getViewRootImpl();
7606 if (viewAncestor != null) {
7607 viewAncestor.requestTransitionStart(transition);
7608 }
7609 }
7610
7611 /**
7612 * @hide
7613 */
7614 @Override
7615 public boolean resolveRtlPropertiesIfNeeded() {
7616 final boolean result = super.resolveRtlPropertiesIfNeeded();
7617 // We dont need to resolve the children RTL properties if nothing has changed for the parent
7618 if (result) {
7619 int count = getChildCount();
7620 for (int i = 0; i < count; i++) {
7621 final View child = getChildAt(i);
7622 if (child.isLayoutDirectionInherited()) {
7623 child.resolveRtlPropertiesIfNeeded();
7624 }
7625 }
7626 }
7627 return result;
7628 }
7629
7630 /**
7631 * @hide
7632 */
7633 @Override
7634 public boolean resolveLayoutDirection() {
7635 final boolean result = super.resolveLayoutDirection();
7636 if (result) {
7637 int count = getChildCount();
7638 for (int i = 0; i < count; i++) {
7639 final View child = getChildAt(i);
7640 if (child.isLayoutDirectionInherited()) {
7641 child.resolveLayoutDirection();
7642 }
7643 }
7644 }
7645 return result;
7646 }
7647
7648 /**
7649 * @hide
7650 */
7651 @Override
7652 public boolean resolveTextDirection() {
7653 final boolean result = super.resolveTextDirection();
7654 if (result) {
7655 int count = getChildCount();
7656 for (int i = 0; i < count; i++) {
7657 final View child = getChildAt(i);
7658 if (child.isTextDirectionInherited()) {
7659 child.resolveTextDirection();
7660 }
7661 }
7662 }
7663 return result;
7664 }
7665
7666 /**
7667 * @hide
7668 */
7669 @Override
7670 public boolean resolveTextAlignment() {
7671 final boolean result = super.resolveTextAlignment();
7672 if (result) {
7673 int count = getChildCount();
7674 for (int i = 0; i < count; i++) {
7675 final View child = getChildAt(i);
7676 if (child.isTextAlignmentInherited()) {
7677 child.resolveTextAlignment();
7678 }
7679 }
7680 }
7681 return result;
7682 }
7683
7684 /**
7685 * @hide
7686 */
7687 @Override
7688 @UnsupportedAppUsage
7689 public void resolvePadding() {
7690 super.resolvePadding();
7691 int count = getChildCount();
7692 for (int i = 0; i < count; i++) {
7693 final View child = getChildAt(i);
7694 if (child.isLayoutDirectionInherited() && !child.isPaddingResolved()) {
7695 child.resolvePadding();
7696 }
7697 }
7698 }
7699
7700 /**
7701 * @hide
7702 */
7703 @Override
7704 protected void resolveDrawables() {
7705 super.resolveDrawables();
7706 int count = getChildCount();
7707 for (int i = 0; i < count; i++) {
7708 final View child = getChildAt(i);
7709 if (child.isLayoutDirectionInherited() && !child.areDrawablesResolved()) {
7710 child.resolveDrawables();
7711 }
7712 }
7713 }
7714
7715 /**
7716 * @hide
7717 */
7718 @Override
7719 public void resolveLayoutParams() {
7720 super.resolveLayoutParams();
7721 int count = getChildCount();
7722 for (int i = 0; i < count; i++) {
7723 final View child = getChildAt(i);
7724 child.resolveLayoutParams();
7725 }
7726 }
7727
7728 /**
7729 * @hide
7730 */
7731 @TestApi
7732 @Override
7733 public void resetResolvedLayoutDirection() {
7734 super.resetResolvedLayoutDirection();
7735
7736 int count = getChildCount();
7737 for (int i = 0; i < count; i++) {
7738 final View child = getChildAt(i);
7739 if (child.isLayoutDirectionInherited()) {
7740 child.resetResolvedLayoutDirection();
7741 }
7742 }
7743 }
7744
7745 /**
7746 * @hide
7747 */
7748 @TestApi
7749 @Override
7750 public void resetResolvedTextDirection() {
7751 super.resetResolvedTextDirection();
7752
7753 int count = getChildCount();
7754 for (int i = 0; i < count; i++) {
7755 final View child = getChildAt(i);
7756 if (child.isTextDirectionInherited()) {
7757 child.resetResolvedTextDirection();
7758 }
7759 }
7760 }
7761
7762 /**
7763 * @hide
7764 */
7765 @TestApi
7766 @Override
7767 public void resetResolvedTextAlignment() {
7768 super.resetResolvedTextAlignment();
7769
7770 int count = getChildCount();
7771 for (int i = 0; i < count; i++) {
7772 final View child = getChildAt(i);
7773 if (child.isTextAlignmentInherited()) {
7774 child.resetResolvedTextAlignment();
7775 }
7776 }
7777 }
7778
7779 /**
7780 * @hide
7781 */
7782 @TestApi
7783 @Override
7784 public void resetResolvedPadding() {
7785 super.resetResolvedPadding();
7786
7787 int count = getChildCount();
7788 for (int i = 0; i < count; i++) {
7789 final View child = getChildAt(i);
7790 if (child.isLayoutDirectionInherited()) {
7791 child.resetResolvedPadding();
7792 }
7793 }
7794 }
7795
7796 /**
7797 * @hide
7798 */
7799 @TestApi
7800 @Override
7801 protected void resetResolvedDrawables() {
7802 super.resetResolvedDrawables();
7803
7804 int count = getChildCount();
7805 for (int i = 0; i < count; i++) {
7806 final View child = getChildAt(i);
7807 if (child.isLayoutDirectionInherited()) {
7808 child.resetResolvedDrawables();
7809 }
7810 }
7811 }
7812
7813 /**
7814 * Return true if the pressed state should be delayed for children or descendants of this
7815 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
7816 * This prevents the pressed state from appearing when the user is actually trying to scroll
7817 * the content.
7818 *
7819 * The default implementation returns true for compatibility reasons. Subclasses that do
7820 * not scroll should generally override this method and return false.
7821 */
7822 public boolean shouldDelayChildPressedState() {
7823 return true;
7824 }
7825
7826 /**
7827 * @inheritDoc
7828 */
7829 @Override
7830 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
7831 return false;
7832 }
7833
7834 /**
7835 * @inheritDoc
7836 */
7837 @Override
7838 public void onNestedScrollAccepted(View child, View target, int axes) {
7839 mNestedScrollAxes = axes;
7840 }
7841
7842 /**
7843 * @inheritDoc
7844 *
7845 * <p>The default implementation of onStopNestedScroll calls
7846 * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
7847 */
7848 @Override
7849 public void onStopNestedScroll(View child) {
7850 // Stop any recursive nested scrolling.
7851 stopNestedScroll();
7852 mNestedScrollAxes = 0;
7853 }
7854
7855 /**
7856 * @inheritDoc
7857 */
7858 @Override
7859 public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
7860 int dxUnconsumed, int dyUnconsumed) {
7861 // Re-dispatch up the tree by default
7862 dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);
7863 }
7864
7865 /**
7866 * @inheritDoc
7867 */
7868 @Override
7869 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
7870 // Re-dispatch up the tree by default
7871 dispatchNestedPreScroll(dx, dy, consumed, null);
7872 }
7873
7874 /**
7875 * @inheritDoc
7876 */
7877 @Override
7878 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
7879 // Re-dispatch up the tree by default
7880 return dispatchNestedFling(velocityX, velocityY, consumed);
7881 }
7882
7883 /**
7884 * @inheritDoc
7885 */
7886 @Override
7887 public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
7888 // Re-dispatch up the tree by default
7889 return dispatchNestedPreFling(velocityX, velocityY);
7890 }
7891
7892 /**
7893 * Return the current axes of nested scrolling for this ViewGroup.
7894 *
7895 * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
7896 * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p>
7897 *
7898 * @return Flags indicating the current axes of nested scrolling
7899 * @see #SCROLL_AXIS_HORIZONTAL
7900 * @see #SCROLL_AXIS_VERTICAL
7901 * @see #SCROLL_AXIS_NONE
7902 */
7903 public int getNestedScrollAxes() {
7904 return mNestedScrollAxes;
7905 }
7906
7907 /** @hide */
7908 protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
7909 requestLayout();
7910 }
7911
7912 /** @hide */
7913 @Override
7914 public void captureTransitioningViews(List<View> transitioningViews) {
7915 if (getVisibility() != View.VISIBLE) {
7916 return;
7917 }
7918 if (isTransitionGroup()) {
7919 transitioningViews.add(this);
7920 } else {
7921 int count = getChildCount();
7922 for (int i = 0; i < count; i++) {
7923 View child = getChildAt(i);
7924 child.captureTransitioningViews(transitioningViews);
7925 }
7926 }
7927 }
7928
7929 /** @hide */
7930 @Override
7931 public void findNamedViews(Map<String, View> namedElements) {
7932 if (getVisibility() != VISIBLE && mGhostView == null) {
7933 return;
7934 }
7935 super.findNamedViews(namedElements);
7936 int count = getChildCount();
7937 for (int i = 0; i < count; i++) {
7938 View child = getChildAt(i);
7939 child.findNamedViews(namedElements);
7940 }
7941 }
7942
7943 @Override
7944 boolean hasUnhandledKeyListener() {
7945 return (mChildUnhandledKeyListeners > 0) || super.hasUnhandledKeyListener();
7946 }
7947
7948 void incrementChildUnhandledKeyListeners() {
7949 mChildUnhandledKeyListeners += 1;
7950 if (mChildUnhandledKeyListeners == 1) {
7951 if (mParent instanceof ViewGroup) {
7952 ((ViewGroup) mParent).incrementChildUnhandledKeyListeners();
7953 }
7954 }
7955 }
7956
7957 void decrementChildUnhandledKeyListeners() {
7958 mChildUnhandledKeyListeners -= 1;
7959 if (mChildUnhandledKeyListeners == 0) {
7960 if (mParent instanceof ViewGroup) {
7961 ((ViewGroup) mParent).decrementChildUnhandledKeyListeners();
7962 }
7963 }
7964 }
7965
7966 @Override
7967 View dispatchUnhandledKeyEvent(KeyEvent evt) {
7968 if (!hasUnhandledKeyListener()) {
7969 return null;
7970 }
7971 ArrayList<View> orderedViews = buildOrderedChildList();
7972 if (orderedViews != null) {
7973 try {
7974 for (int i = orderedViews.size() - 1; i >= 0; --i) {
7975 View v = orderedViews.get(i);
7976 View consumer = v.dispatchUnhandledKeyEvent(evt);
7977 if (consumer != null) {
7978 return consumer;
7979 }
7980 }
7981 } finally {
7982 orderedViews.clear();
7983 }
7984 } else {
7985 for (int i = getChildCount() - 1; i >= 0; --i) {
7986 View v = getChildAt(i);
7987 View consumer = v.dispatchUnhandledKeyEvent(evt);
7988 if (consumer != null) {
7989 return consumer;
7990 }
7991 }
7992 }
7993 if (onUnhandledKeyEvent(evt)) {
7994 return this;
7995 }
7996 return null;
7997 }
7998
7999 /**
8000 * LayoutParams are used by views to tell their parents how they want to be
8001 * laid out. See
8002 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
8003 * for a list of all child view attributes that this class supports.
8004 *
8005 * <p>
8006 * The base LayoutParams class just describes how big the view wants to be
8007 * for both width and height. For each dimension, it can specify one of:
8008 * <ul>
8009 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
8010 * means that the view wants to be as big as its parent (minus padding)
8011 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
8012 * to enclose its content (plus padding)
8013 * <li> an exact number
8014 * </ul>
8015 * There are subclasses of LayoutParams for different subclasses of
8016 * ViewGroup. For example, AbsoluteLayout has its own subclass of
8017 * LayoutParams which adds an X and Y value.</p>
8018 *
8019 * <div class="special reference">
8020 * <h3>Developer Guides</h3>
8021 * <p>For more information about creating user interface layouts, read the
8022 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
8023 * guide.</p></div>
8024 *
8025 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
8026 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
8027 */
8028 public static class LayoutParams {
8029 /**
8030 * Special value for the height or width requested by a View.
8031 * FILL_PARENT means that the view wants to be as big as its parent,
8032 * minus the parent's padding, if any. This value is deprecated
8033 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
8034 */
8035 @SuppressWarnings({"UnusedDeclaration"})
8036 @Deprecated
8037 public static final int FILL_PARENT = -1;
8038
8039 /**
8040 * Special value for the height or width requested by a View.
8041 * MATCH_PARENT means that the view wants to be as big as its parent,
8042 * minus the parent's padding, if any. Introduced in API Level 8.
8043 */
8044 public static final int MATCH_PARENT = -1;
8045
8046 /**
8047 * Special value for the height or width requested by a View.
8048 * WRAP_CONTENT means that the view wants to be just large enough to fit
8049 * its own internal content, taking its own padding into account.
8050 */
8051 public static final int WRAP_CONTENT = -2;
8052
8053 /**
8054 * Information about how wide the view wants to be. Can be one of the
8055 * constants FILL_PARENT (replaced by MATCH_PARENT
8056 * in API Level 8) or WRAP_CONTENT, or an exact size.
8057 */
8058 @ViewDebug.ExportedProperty(category = "layout", mapping = {
8059 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
8060 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
8061 })
8062 @InspectableProperty(name = "layout_width", enumMapping = {
8063 @EnumEntry(name = "match_parent", value = MATCH_PARENT),
8064 @EnumEntry(name = "wrap_content", value = WRAP_CONTENT)
8065 })
8066 public int width;
8067
8068 /**
8069 * Information about how tall the view wants to be. Can be one of the
8070 * constants FILL_PARENT (replaced by MATCH_PARENT
8071 * in API Level 8) or WRAP_CONTENT, or an exact size.
8072 */
8073 @ViewDebug.ExportedProperty(category = "layout", mapping = {
8074 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
8075 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
8076 })
8077 @InspectableProperty(name = "layout_height", enumMapping = {
8078 @EnumEntry(name = "match_parent", value = MATCH_PARENT),
8079 @EnumEntry(name = "wrap_content", value = WRAP_CONTENT)
8080 })
8081 public int height;
8082
8083 /**
8084 * Used to animate layouts.
8085 */
8086 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
8087
8088 /**
8089 * Creates a new set of layout parameters. The values are extracted from
8090 * the supplied attributes set and context. The XML attributes mapped
8091 * to this set of layout parameters are:
8092 *
8093 * <ul>
8094 * <li><code>layout_width</code>: the width, either an exact value,
8095 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
8096 * {@link #MATCH_PARENT} in API Level 8)</li>
8097 * <li><code>layout_height</code>: the height, either an exact value,
8098 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
8099 * {@link #MATCH_PARENT} in API Level 8)</li>
8100 * </ul>
8101 *
8102 * @param c the application environment
8103 * @param attrs the set of attributes from which to extract the layout
8104 * parameters' values
8105 */
8106 public LayoutParams(Context c, AttributeSet attrs) {
8107 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
8108 setBaseAttributes(a,
8109 R.styleable.ViewGroup_Layout_layout_width,
8110 R.styleable.ViewGroup_Layout_layout_height);
8111 a.recycle();
8112 }
8113
8114 /**
8115 * Creates a new set of layout parameters with the specified width
8116 * and height.
8117 *
8118 * @param width the width, either {@link #WRAP_CONTENT},
8119 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
8120 * API Level 8), or a fixed size in pixels
8121 * @param height the height, either {@link #WRAP_CONTENT},
8122 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
8123 * API Level 8), or a fixed size in pixels
8124 */
8125 public LayoutParams(int width, int height) {
8126 this.width = width;
8127 this.height = height;
8128 }
8129
8130 /**
8131 * Copy constructor. Clones the width and height values of the source.
8132 *
8133 * @param source The layout params to copy from.
8134 */
8135 public LayoutParams(LayoutParams source) {
8136 this.width = source.width;
8137 this.height = source.height;
8138 }
8139
8140 /**
8141 * Used internally by MarginLayoutParams.
8142 * @hide
8143 */
8144 @UnsupportedAppUsage
8145 LayoutParams() {
8146 }
8147
8148 /**
8149 * Extracts the layout parameters from the supplied attributes.
8150 *
8151 * @param a the style attributes to extract the parameters from
8152 * @param widthAttr the identifier of the width attribute
8153 * @param heightAttr the identifier of the height attribute
8154 */
8155 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
8156 width = a.getLayoutDimension(widthAttr, "layout_width");
8157 height = a.getLayoutDimension(heightAttr, "layout_height");
8158 }
8159
8160 /**
8161 * Resolve layout parameters depending on the layout direction. Subclasses that care about
8162 * layoutDirection changes should override this method. The default implementation does
8163 * nothing.
8164 *
8165 * @param layoutDirection the direction of the layout
8166 *
8167 * {@link View#LAYOUT_DIRECTION_LTR}
8168 * {@link View#LAYOUT_DIRECTION_RTL}
8169 */
8170 public void resolveLayoutDirection(int layoutDirection) {
8171 }
8172
8173 /**
8174 * Returns a String representation of this set of layout parameters.
8175 *
8176 * @param output the String to prepend to the internal representation
8177 * @return a String with the following format: output +
8178 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
8179 *
8180 * @hide
8181 */
8182 public String debug(String output) {
8183 return output + "ViewGroup.LayoutParams={ width="
8184 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
8185 }
8186
8187 /**
8188 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
8189 *
8190 * @param view the view that contains these layout parameters
8191 * @param canvas the canvas on which to draw
8192 *
8193 * @hide
8194 */
8195 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
8196 }
8197
8198 /**
8199 * Converts the specified size to a readable String.
8200 *
8201 * @param size the size to convert
8202 * @return a String instance representing the supplied size
8203 *
8204 * @hide
8205 */
8206 protected static String sizeToString(int size) {
8207 if (size == WRAP_CONTENT) {
8208 return "wrap-content";
8209 }
8210 if (size == MATCH_PARENT) {
8211 return "match-parent";
8212 }
8213 return String.valueOf(size);
8214 }
8215
8216 /** @hide */
8217 void encode(@NonNull ViewHierarchyEncoder encoder) {
8218 encoder.beginObject(this);
8219 encodeProperties(encoder);
8220 encoder.endObject();
8221 }
8222
8223 /** @hide */
8224 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
8225 encoder.addProperty("width", width);
8226 encoder.addProperty("height", height);
8227 }
8228 }
8229
8230 /**
8231 * Per-child layout information for layouts that support margins.
8232 * See
8233 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
8234 * for a list of all child view attributes that this class supports.
8235 *
8236 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_margin
8237 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginHorizontal
8238 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginVertical
8239 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
8240 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
8241 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
8242 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
8243 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
8244 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
8245 */
8246 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
8247 /**
8248 * The left margin in pixels of the child. Margin values should be positive.
8249 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
8250 * to this field.
8251 */
8252 @ViewDebug.ExportedProperty(category = "layout")
8253 @InspectableProperty(name = "layout_marginLeft")
8254 public int leftMargin;
8255
8256 /**
8257 * The top margin in pixels of the child. Margin values should be positive.
8258 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
8259 * to this field.
8260 */
8261 @ViewDebug.ExportedProperty(category = "layout")
8262 @InspectableProperty(name = "layout_marginTop")
8263 public int topMargin;
8264
8265 /**
8266 * The right margin in pixels of the child. Margin values should be positive.
8267 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
8268 * to this field.
8269 */
8270 @ViewDebug.ExportedProperty(category = "layout")
8271 @InspectableProperty(name = "layout_marginRight")
8272 public int rightMargin;
8273
8274 /**
8275 * The bottom margin in pixels of the child. Margin values should be positive.
8276 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
8277 * to this field.
8278 */
8279 @ViewDebug.ExportedProperty(category = "layout")
8280 @InspectableProperty(name = "layout_marginBottom")
8281 public int bottomMargin;
8282
8283 /**
8284 * The start margin in pixels of the child. Margin values should be positive.
8285 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
8286 * to this field.
8287 */
8288 @ViewDebug.ExportedProperty(category = "layout")
8289 @UnsupportedAppUsage
8290 private int startMargin = DEFAULT_MARGIN_RELATIVE;
8291
8292 /**
8293 * The end margin in pixels of the child. Margin values should be positive.
8294 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
8295 * to this field.
8296 */
8297 @ViewDebug.ExportedProperty(category = "layout")
8298 @UnsupportedAppUsage
8299 private int endMargin = DEFAULT_MARGIN_RELATIVE;
8300
8301 /**
8302 * The default start and end margin.
8303 * @hide
8304 */
8305 public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
8306
8307 /**
8308 * Bit 0: layout direction
8309 * Bit 1: layout direction
8310 * Bit 2: left margin undefined
8311 * Bit 3: right margin undefined
8312 * Bit 4: is RTL compatibility mode
8313 * Bit 5: need resolution
8314 *
8315 * Bit 6 to 7 not used
8316 *
8317 * @hide
8318 */
8319 @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
8320 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
8321 equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
8322 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
8323 equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
8324 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
8325 equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
8326 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
8327 equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
8328 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
8329 equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
8330 }, formatToHexString = true)
8331 byte mMarginFlags;
8332
8333 private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
8334 private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
8335 private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
8336 private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
8337 private static final int NEED_RESOLUTION_MASK = 0x00000020;
8338
8339 private static final int DEFAULT_MARGIN_RESOLVED = 0;
8340 private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
8341
8342 /**
8343 * Creates a new set of layout parameters. The values are extracted from
8344 * the supplied attributes set and context.
8345 *
8346 * @param c the application environment
8347 * @param attrs the set of attributes from which to extract the layout
8348 * parameters' values
8349 */
8350 public MarginLayoutParams(Context c, AttributeSet attrs) {
8351 super();
8352
8353 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
8354 setBaseAttributes(a,
8355 R.styleable.ViewGroup_MarginLayout_layout_width,
8356 R.styleable.ViewGroup_MarginLayout_layout_height);
8357
8358 int margin = a.getDimensionPixelSize(
8359 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
8360 if (margin >= 0) {
8361 leftMargin = margin;
8362 topMargin = margin;
8363 rightMargin= margin;
8364 bottomMargin = margin;
8365 } else {
8366 int horizontalMargin = a.getDimensionPixelSize(
8367 R.styleable.ViewGroup_MarginLayout_layout_marginHorizontal, -1);
8368 int verticalMargin = a.getDimensionPixelSize(
8369 R.styleable.ViewGroup_MarginLayout_layout_marginVertical, -1);
8370
8371 if (horizontalMargin >= 0) {
8372 leftMargin = horizontalMargin;
8373 rightMargin = horizontalMargin;
8374 } else {
8375 leftMargin = a.getDimensionPixelSize(
8376 R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
8377 UNDEFINED_MARGIN);
8378 if (leftMargin == UNDEFINED_MARGIN) {
8379 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
8380 leftMargin = DEFAULT_MARGIN_RESOLVED;
8381 }
8382 rightMargin = a.getDimensionPixelSize(
8383 R.styleable.ViewGroup_MarginLayout_layout_marginRight,
8384 UNDEFINED_MARGIN);
8385 if (rightMargin == UNDEFINED_MARGIN) {
8386 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
8387 rightMargin = DEFAULT_MARGIN_RESOLVED;
8388 }
8389 }
8390
8391 startMargin = a.getDimensionPixelSize(
8392 R.styleable.ViewGroup_MarginLayout_layout_marginStart,
8393 DEFAULT_MARGIN_RELATIVE);
8394 endMargin = a.getDimensionPixelSize(
8395 R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
8396 DEFAULT_MARGIN_RELATIVE);
8397
8398 if (verticalMargin >= 0) {
8399 topMargin = verticalMargin;
8400 bottomMargin = verticalMargin;
8401 } else {
8402 topMargin = a.getDimensionPixelSize(
8403 R.styleable.ViewGroup_MarginLayout_layout_marginTop,
8404 DEFAULT_MARGIN_RESOLVED);
8405 bottomMargin = a.getDimensionPixelSize(
8406 R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
8407 DEFAULT_MARGIN_RESOLVED);
8408 }
8409
8410 if (isMarginRelative()) {
8411 mMarginFlags |= NEED_RESOLUTION_MASK;
8412 }
8413 }
8414
8415 final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
8416 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
8417 if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
8418 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
8419 }
8420
8421 // Layout direction is LTR by default
8422 mMarginFlags |= LAYOUT_DIRECTION_LTR;
8423
8424 a.recycle();
8425 }
8426
8427 public MarginLayoutParams(int width, int height) {
8428 super(width, height);
8429
8430 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
8431 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
8432
8433 mMarginFlags &= ~NEED_RESOLUTION_MASK;
8434 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
8435 }
8436
8437 /**
8438 * Copy constructor. Clones the width, height and margin values of the source.
8439 *
8440 * @param source The layout params to copy from.
8441 */
8442 public MarginLayoutParams(MarginLayoutParams source) {
8443 this.width = source.width;
8444 this.height = source.height;
8445
8446 this.leftMargin = source.leftMargin;
8447 this.topMargin = source.topMargin;
8448 this.rightMargin = source.rightMargin;
8449 this.bottomMargin = source.bottomMargin;
8450 this.startMargin = source.startMargin;
8451 this.endMargin = source.endMargin;
8452
8453 this.mMarginFlags = source.mMarginFlags;
8454 }
8455
8456 public MarginLayoutParams(LayoutParams source) {
8457 super(source);
8458
8459 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
8460 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
8461
8462 mMarginFlags &= ~NEED_RESOLUTION_MASK;
8463 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
8464 }
8465
8466 /**
8467 * @hide Used internally.
8468 */
8469 public final void copyMarginsFrom(MarginLayoutParams source) {
8470 this.leftMargin = source.leftMargin;
8471 this.topMargin = source.topMargin;
8472 this.rightMargin = source.rightMargin;
8473 this.bottomMargin = source.bottomMargin;
8474 this.startMargin = source.startMargin;
8475 this.endMargin = source.endMargin;
8476
8477 this.mMarginFlags = source.mMarginFlags;
8478 }
8479
8480 /**
8481 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
8482 * to be done so that the new margins are taken into account. Left and right margins may be
8483 * overridden by {@link android.view.View#requestLayout()} depending on layout direction.
8484 * Margin values should be positive.
8485 *
8486 * @param left the left margin size
8487 * @param top the top margin size
8488 * @param right the right margin size
8489 * @param bottom the bottom margin size
8490 *
8491 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
8492 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
8493 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
8494 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
8495 */
8496 public void setMargins(int left, int top, int right, int bottom) {
8497 leftMargin = left;
8498 topMargin = top;
8499 rightMargin = right;
8500 bottomMargin = bottom;
8501 mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
8502 mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
8503 if (isMarginRelative()) {
8504 mMarginFlags |= NEED_RESOLUTION_MASK;
8505 } else {
8506 mMarginFlags &= ~NEED_RESOLUTION_MASK;
8507 }
8508 }
8509
8510 /**
8511 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
8512 * needs to be done so that the new relative margins are taken into account. Left and right
8513 * margins may be overridden by {@link android.view.View#requestLayout()} depending on
8514 * layout direction. Margin values should be positive.
8515 *
8516 * @param start the start margin size
8517 * @param top the top margin size
8518 * @param end the right margin size
8519 * @param bottom the bottom margin size
8520 *
8521 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
8522 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
8523 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
8524 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
8525 *
8526 * @hide
8527 */
8528 @UnsupportedAppUsage
8529 public void setMarginsRelative(int start, int top, int end, int bottom) {
8530 startMargin = start;
8531 topMargin = top;
8532 endMargin = end;
8533 bottomMargin = bottom;
8534 mMarginFlags |= NEED_RESOLUTION_MASK;
8535 }
8536
8537 /**
8538 * Sets the relative start margin. Margin values should be positive.
8539 *
8540 * @param start the start margin size
8541 *
8542 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
8543 */
8544 public void setMarginStart(int start) {
8545 startMargin = start;
8546 mMarginFlags |= NEED_RESOLUTION_MASK;
8547 }
8548
8549 /**
8550 * Returns the start margin in pixels.
8551 *
8552 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
8553 *
8554 * @return the start margin in pixels.
8555 */
8556 public int getMarginStart() {
8557 if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
8558 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
8559 doResolveMargins();
8560 }
8561 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
8562 case View.LAYOUT_DIRECTION_RTL:
8563 return rightMargin;
8564 case View.LAYOUT_DIRECTION_LTR:
8565 default:
8566 return leftMargin;
8567 }
8568 }
8569
8570 /**
8571 * Sets the relative end margin. Margin values should be positive.
8572 *
8573 * @param end the end margin size
8574 *
8575 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
8576 */
8577 public void setMarginEnd(int end) {
8578 endMargin = end;
8579 mMarginFlags |= NEED_RESOLUTION_MASK;
8580 }
8581
8582 /**
8583 * Returns the end margin in pixels.
8584 *
8585 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
8586 *
8587 * @return the end margin in pixels.
8588 */
8589 public int getMarginEnd() {
8590 if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
8591 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
8592 doResolveMargins();
8593 }
8594 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
8595 case View.LAYOUT_DIRECTION_RTL:
8596 return leftMargin;
8597 case View.LAYOUT_DIRECTION_LTR:
8598 default:
8599 return rightMargin;
8600 }
8601 }
8602
8603 /**
8604 * Check if margins are relative.
8605 *
8606 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
8607 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
8608 *
8609 * @return true if either marginStart or marginEnd has been set.
8610 */
8611 public boolean isMarginRelative() {
8612 return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
8613 }
8614
8615 /**
8616 * Set the layout direction
8617 * @param layoutDirection the layout direction.
8618 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
8619 * or {@link View#LAYOUT_DIRECTION_RTL}.
8620 */
8621 public void setLayoutDirection(int layoutDirection) {
8622 if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
8623 layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
8624 if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
8625 mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
8626 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
8627 if (isMarginRelative()) {
8628 mMarginFlags |= NEED_RESOLUTION_MASK;
8629 } else {
8630 mMarginFlags &= ~NEED_RESOLUTION_MASK;
8631 }
8632 }
8633 }
8634
8635 /**
8636 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
8637 * {@link View#LAYOUT_DIRECTION_RTL}.
8638 *
8639 * @return the layout direction.
8640 */
8641 public int getLayoutDirection() {
8642 return (mMarginFlags & LAYOUT_DIRECTION_MASK);
8643 }
8644
8645 /**
8646 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
8647 * may be overridden depending on layout direction.
8648 */
8649 @Override
8650 public void resolveLayoutDirection(int layoutDirection) {
8651 setLayoutDirection(layoutDirection);
8652
8653 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
8654 // Will use the left and right margins if no relative margin is defined.
8655 if (!isMarginRelative() ||
8656 (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
8657
8658 // Proceed with resolution
8659 doResolveMargins();
8660 }
8661
8662 private void doResolveMargins() {
8663 if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
8664 // if left or right margins are not defined and if we have some start or end margin
8665 // defined then use those start and end margins.
8666 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
8667 && startMargin > DEFAULT_MARGIN_RELATIVE) {
8668 leftMargin = startMargin;
8669 }
8670 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
8671 && endMargin > DEFAULT_MARGIN_RELATIVE) {
8672 rightMargin = endMargin;
8673 }
8674 } else {
8675 // We have some relative margins (either the start one or the end one or both). So use
8676 // them and override what has been defined for left and right margins. If either start
8677 // or end margin is not defined, just set it to default "0".
8678 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
8679 case View.LAYOUT_DIRECTION_RTL:
8680 leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
8681 endMargin : DEFAULT_MARGIN_RESOLVED;
8682 rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
8683 startMargin : DEFAULT_MARGIN_RESOLVED;
8684 break;
8685 case View.LAYOUT_DIRECTION_LTR:
8686 default:
8687 leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
8688 startMargin : DEFAULT_MARGIN_RESOLVED;
8689 rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
8690 endMargin : DEFAULT_MARGIN_RESOLVED;
8691 break;
8692 }
8693 }
8694 mMarginFlags &= ~NEED_RESOLUTION_MASK;
8695 }
8696
8697 /**
8698 * @hide
8699 */
8700 public boolean isLayoutRtl() {
8701 return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
8702 }
8703
8704 /**
8705 * @hide
8706 */
8707 @Override
8708 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
8709 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
8710
8711 fillDifference(canvas,
8712 view.getLeft() + oi.left,
8713 view.getTop() + oi.top,
8714 view.getRight() - oi.right,
8715 view.getBottom() - oi.bottom,
8716 leftMargin,
8717 topMargin,
8718 rightMargin,
8719 bottomMargin,
8720 paint);
8721 }
8722
8723 /** @hide */
8724 @Override
8725 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
8726 super.encodeProperties(encoder);
8727 encoder.addProperty("leftMargin", leftMargin);
8728 encoder.addProperty("topMargin", topMargin);
8729 encoder.addProperty("rightMargin", rightMargin);
8730 encoder.addProperty("bottomMargin", bottomMargin);
8731 encoder.addProperty("startMargin", startMargin);
8732 encoder.addProperty("endMargin", endMargin);
8733 }
8734 }
8735
8736 /* Describes a touched view and the ids of the pointers that it has captured.
8737 *
8738 * This code assumes that pointer ids are always in the range 0..31 such that
8739 * it can use a bitfield to track which pointer ids are present.
8740 * As it happens, the lower layers of the input dispatch pipeline also use the
8741 * same trick so the assumption should be safe here...
8742 */
8743 private static final class TouchTarget {
8744 private static final int MAX_RECYCLED = 32;
8745 private static final Object sRecycleLock = new Object[0];
8746 private static TouchTarget sRecycleBin;
8747 private static int sRecycledCount;
8748
8749 public static final int ALL_POINTER_IDS = -1; // all ones
8750
8751 // The touched child view.
8752 @UnsupportedAppUsage
8753 public View child;
8754
8755 // The combined bit mask of pointer ids for all pointers captured by the target.
8756 public int pointerIdBits;
8757
8758 // The next target in the target list.
8759 public TouchTarget next;
8760
8761 @UnsupportedAppUsage
8762 private TouchTarget() {
8763 }
8764
8765 public static TouchTarget obtain(@NonNull View child, int pointerIdBits) {
8766 if (child == null) {
8767 throw new IllegalArgumentException("child must be non-null");
8768 }
8769
8770 final TouchTarget target;
8771 synchronized (sRecycleLock) {
8772 if (sRecycleBin == null) {
8773 target = new TouchTarget();
8774 } else {
8775 target = sRecycleBin;
8776 sRecycleBin = target.next;
8777 sRecycledCount--;
8778 target.next = null;
8779 }
8780 }
8781 target.child = child;
8782 target.pointerIdBits = pointerIdBits;
8783 return target;
8784 }
8785
8786 public void recycle() {
8787 if (child == null) {
8788 throw new IllegalStateException("already recycled once");
8789 }
8790
8791 synchronized (sRecycleLock) {
8792 if (sRecycledCount < MAX_RECYCLED) {
8793 next = sRecycleBin;
8794 sRecycleBin = this;
8795 sRecycledCount += 1;
8796 } else {
8797 next = null;
8798 }
8799 child = null;
8800 }
8801 }
8802 }
8803
8804 /* Describes a hovered view. */
8805 private static final class HoverTarget {
8806 private static final int MAX_RECYCLED = 32;
8807 private static final Object sRecycleLock = new Object[0];
8808 private static HoverTarget sRecycleBin;
8809 private static int sRecycledCount;
8810
8811 // The hovered child view.
8812 public View child;
8813
8814 // The next target in the target list.
8815 public HoverTarget next;
8816
8817 private HoverTarget() {
8818 }
8819
8820 public static HoverTarget obtain(@NonNull View child) {
8821 if (child == null) {
8822 throw new IllegalArgumentException("child must be non-null");
8823 }
8824
8825 final HoverTarget target;
8826 synchronized (sRecycleLock) {
8827 if (sRecycleBin == null) {
8828 target = new HoverTarget();
8829 } else {
8830 target = sRecycleBin;
8831 sRecycleBin = target.next;
8832 sRecycledCount--;
8833 target.next = null;
8834 }
8835 }
8836 target.child = child;
8837 return target;
8838 }
8839
8840 public void recycle() {
8841 if (child == null) {
8842 throw new IllegalStateException("already recycled once");
8843 }
8844
8845 synchronized (sRecycleLock) {
8846 if (sRecycledCount < MAX_RECYCLED) {
8847 next = sRecycleBin;
8848 sRecycleBin = this;
8849 sRecycledCount += 1;
8850 } else {
8851 next = null;
8852 }
8853 child = null;
8854 }
8855 }
8856 }
8857
8858 /**
8859 * Pooled class that to hold the children for autifill.
8860 */
8861 private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> {
8862 private static final int MAX_POOL_SIZE = 32;
8863
8864 private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool =
8865 new Pools.SimplePool<>(MAX_POOL_SIZE);
8866
8867 public static ChildListForAutoFillOrContentCapture obtain() {
8868 ChildListForAutoFillOrContentCapture list = sPool.acquire();
8869 if (list == null) {
8870 list = new ChildListForAutoFillOrContentCapture();
8871 }
8872 return list;
8873 }
8874
8875 public void recycle() {
8876 clear();
8877 sPool.release(this);
8878 }
8879 }
8880
8881 /**
8882 * Pooled class that orderes the children of a ViewGroup from start
8883 * to end based on how they are laid out and the layout direction.
8884 */
8885 static class ChildListForAccessibility {
8886
8887 private static final int MAX_POOL_SIZE = 32;
8888
8889 private static final SynchronizedPool<ChildListForAccessibility> sPool =
8890 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
8891
8892 private final ArrayList<View> mChildren = new ArrayList<View>();
8893
8894 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
8895
8896 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
8897 ChildListForAccessibility list = sPool.acquire();
8898 if (list == null) {
8899 list = new ChildListForAccessibility();
8900 }
8901 list.init(parent, sort);
8902 return list;
8903 }
8904
8905 public void recycle() {
8906 clear();
8907 sPool.release(this);
8908 }
8909
8910 public int getChildCount() {
8911 return mChildren.size();
8912 }
8913
8914 public View getChildAt(int index) {
8915 return mChildren.get(index);
8916 }
8917
8918 private void init(ViewGroup parent, boolean sort) {
8919 ArrayList<View> children = mChildren;
8920 final int childCount = parent.getChildCount();
8921 for (int i = 0; i < childCount; i++) {
8922 View child = parent.getChildAt(i);
8923 children.add(child);
8924 }
8925 if (sort) {
8926 ArrayList<ViewLocationHolder> holders = mHolders;
8927 for (int i = 0; i < childCount; i++) {
8928 View child = children.get(i);
8929 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
8930 holders.add(holder);
8931 }
8932 sort(holders);
8933 for (int i = 0; i < childCount; i++) {
8934 ViewLocationHolder holder = holders.get(i);
8935 children.set(i, holder.mView);
8936 holder.recycle();
8937 }
8938 holders.clear();
8939 }
8940 }
8941
8942 private void sort(ArrayList<ViewLocationHolder> holders) {
8943 // This is gross but the least risky solution. The current comparison
8944 // strategy breaks transitivity but produces very good results. Coming
8945 // up with a new strategy requires time which we do not have, so ...
8946 try {
8947 ViewLocationHolder.setComparisonStrategy(
8948 ViewLocationHolder.COMPARISON_STRATEGY_STRIPE);
8949 Collections.sort(holders);
8950 } catch (IllegalArgumentException iae) {
8951 // Note that in practice this occurs extremely rarely in a couple
8952 // of pathological cases.
8953 ViewLocationHolder.setComparisonStrategy(
8954 ViewLocationHolder.COMPARISON_STRATEGY_LOCATION);
8955 Collections.sort(holders);
8956 }
8957 }
8958
8959 private void clear() {
8960 mChildren.clear();
8961 }
8962 }
8963
8964 /**
8965 * Pooled class that holds a View and its location with respect to
8966 * a specified root. This enables sorting of views based on their
8967 * coordinates without recomputing the position relative to the root
8968 * on every comparison.
8969 */
8970 static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
8971
8972 private static final int MAX_POOL_SIZE = 32;
8973
8974 private static final SynchronizedPool<ViewLocationHolder> sPool =
8975 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
8976
8977 public static final int COMPARISON_STRATEGY_STRIPE = 1;
8978
8979 public static final int COMPARISON_STRATEGY_LOCATION = 2;
8980
8981 private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
8982
8983 private final Rect mLocation = new Rect();
8984
8985 private ViewGroup mRoot;
8986
8987 public View mView;
8988
8989 private int mLayoutDirection;
8990
8991 public static ViewLocationHolder obtain(ViewGroup root, View view) {
8992 ViewLocationHolder holder = sPool.acquire();
8993 if (holder == null) {
8994 holder = new ViewLocationHolder();
8995 }
8996 holder.init(root, view);
8997 return holder;
8998 }
8999
9000 public static void setComparisonStrategy(int strategy) {
9001 sComparisonStrategy = strategy;
9002 }
9003
9004 public void recycle() {
9005 clear();
9006 sPool.release(this);
9007 }
9008
9009 @Override
9010 public int compareTo(ViewLocationHolder another) {
9011 // This instance is greater than an invalid argument.
9012 if (another == null) {
9013 return 1;
9014 }
9015
9016 int boundsResult = compareBoundsOfTree(this, another);
9017 if (boundsResult != 0) {
9018 return boundsResult;
9019 }
9020
9021 // Just break the tie somehow. The accessibility ids are unique
9022 // and stable, hence this is deterministic tie breaking.
9023 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
9024 }
9025
9026 /**
9027 * Compare two views based on their bounds. Use the bounds of their children to break ties.
9028 *
9029 * @param holder1 Holder of first view to compare
9030 * @param holder2 Holder of second view to compare. Must have the same root as holder1.
9031 * @return The compare result, with equality if no good comparison was found.
9032 */
9033 private static int compareBoundsOfTree(
9034 ViewLocationHolder holder1, ViewLocationHolder holder2) {
9035 if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
9036 // First is above second.
9037 if (holder1.mLocation.bottom - holder2.mLocation.top <= 0) {
9038 return -1;
9039 }
9040 // First is below second.
9041 if (holder1.mLocation.top - holder2.mLocation.bottom >= 0) {
9042 return 1;
9043 }
9044 }
9045
9046 // We are ordering left-to-right, top-to-bottom.
9047 if (holder1.mLayoutDirection == LAYOUT_DIRECTION_LTR) {
9048 final int leftDifference = holder1.mLocation.left - holder2.mLocation.left;
9049 if (leftDifference != 0) {
9050 return leftDifference;
9051 }
9052 } else { // RTL
9053 final int rightDifference = holder1.mLocation.right - holder2.mLocation.right;
9054 if (rightDifference != 0) {
9055 return -rightDifference;
9056 }
9057 }
9058 // We are ordering left-to-right, top-to-bottom.
9059 final int topDifference = holder1.mLocation.top - holder2.mLocation.top;
9060 if (topDifference != 0) {
9061 return topDifference;
9062 }
9063 // Break tie by height.
9064 final int heightDiference = holder1.mLocation.height() - holder2.mLocation.height();
9065 if (heightDiference != 0) {
9066 return -heightDiference;
9067 }
9068 // Break tie by width.
9069 final int widthDifference = holder1.mLocation.width() - holder2.mLocation.width();
9070 if (widthDifference != 0) {
9071 return -widthDifference;
9072 }
9073
9074 // Find a child of each view with different screen bounds.
9075 final Rect view1Bounds = new Rect();
9076 final Rect view2Bounds = new Rect();
9077 final Rect tempRect = new Rect();
9078 holder1.mView.getBoundsOnScreen(view1Bounds, true);
9079 holder2.mView.getBoundsOnScreen(view2Bounds, true);
9080 final View child1 = holder1.mView.findViewByPredicateTraversal((view) -> {
9081 view.getBoundsOnScreen(tempRect, true);
9082 return !tempRect.equals(view1Bounds);
9083 }, null);
9084 final View child2 = holder2.mView.findViewByPredicateTraversal((view) -> {
9085 view.getBoundsOnScreen(tempRect, true);
9086 return !tempRect.equals(view2Bounds);
9087 }, null);
9088
9089
9090 // Compare the children recursively
9091 if ((child1 != null) && (child2 != null)) {
9092 final ViewLocationHolder childHolder1 =
9093 ViewLocationHolder.obtain(holder1.mRoot, child1);
9094 final ViewLocationHolder childHolder2 =
9095 ViewLocationHolder.obtain(holder1.mRoot, child2);
9096 return compareBoundsOfTree(childHolder1, childHolder2);
9097 }
9098
9099 // If only one has a child, use that one
9100 if (child1 != null) {
9101 return 1;
9102 }
9103
9104 if (child2 != null) {
9105 return -1;
9106 }
9107
9108 // Give up
9109 return 0;
9110 }
9111
9112 private void init(ViewGroup root, View view) {
9113 Rect viewLocation = mLocation;
9114 view.getDrawingRect(viewLocation);
9115 root.offsetDescendantRectToMyCoords(view, viewLocation);
9116 mView = view;
9117 mRoot = root;
9118 mLayoutDirection = root.getLayoutDirection();
9119 }
9120
9121 private void clear() {
9122 mView = null;
9123 mRoot = null;
9124 mLocation.set(0, 0, 0, 0);
9125 }
9126 }
9127
9128 private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
9129 if (sDebugLines== null) {
9130 // TODO: This won't work with multiple UI threads in a single process
9131 sDebugLines = new float[16];
9132 }
9133
9134 sDebugLines[0] = x1;
9135 sDebugLines[1] = y1;
9136 sDebugLines[2] = x2;
9137 sDebugLines[3] = y1;
9138
9139 sDebugLines[4] = x2;
9140 sDebugLines[5] = y1;
9141 sDebugLines[6] = x2;
9142 sDebugLines[7] = y2;
9143
9144 sDebugLines[8] = x2;
9145 sDebugLines[9] = y2;
9146 sDebugLines[10] = x1;
9147 sDebugLines[11] = y2;
9148
9149 sDebugLines[12] = x1;
9150 sDebugLines[13] = y2;
9151 sDebugLines[14] = x1;
9152 sDebugLines[15] = y1;
9153
9154 canvas.drawLines(sDebugLines, paint);
9155 }
9156
9157 /** @hide */
9158 @Override
9159 @UnsupportedAppUsage
9160 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
9161 super.encodeProperties(encoder);
9162
9163 encoder.addProperty("focus:descendantFocusability", getDescendantFocusability());
9164 encoder.addProperty("drawing:clipChildren", getClipChildren());
9165 encoder.addProperty("drawing:clipToPadding", getClipToPadding());
9166 encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled());
9167 encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache());
9168
9169 int n = getChildCount();
9170 encoder.addProperty("meta:__childCount__", (short)n);
9171 for (int i = 0; i < n; i++) {
9172 encoder.addPropertyKey("meta:__child__" + i);
9173 getChildAt(i).encode(encoder);
9174 }
9175 }
9176
9177 /** @hide */
9178 @Override
9179 public final void onDescendantUnbufferedRequested() {
9180 // First look at the focused child for focused events
9181 int focusedChildNonPointerSource = InputDevice.SOURCE_CLASS_NONE;
9182 if (mFocused != null) {
9183 focusedChildNonPointerSource = mFocused.mUnbufferedInputSource
9184 & (~InputDevice.SOURCE_CLASS_POINTER);
9185 }
9186 mUnbufferedInputSource = focusedChildNonPointerSource;
9187
9188 // Request unbuffered dispatch for pointer events for this view if any child requested
9189 // unbuffered dispatch for pointer events. This is because we can't expect that the pointer
9190 // source would dispatch to the focused view.
9191 for (int i = 0; i < mChildrenCount; i++) {
9192 final View child = mChildren[i];
9193 if ((child.mUnbufferedInputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
9194 mUnbufferedInputSource |= InputDevice.SOURCE_CLASS_POINTER;
9195 break;
9196 }
9197 }
9198 if (mParent != null) {
9199 mParent.onDescendantUnbufferedRequested();
9200 }
9201 }
9202}