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