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