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