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