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