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