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