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