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