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.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 ChildListForAutofill 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 /** 3621 * Gets the children for autofill. Children for autofill are the first 3622 * level descendants that are important for autofill. The returned 3623 * child list object is pooled and the caller must recycle it once done. 3624 * @hide */ getChildrenForAutofill( @utofillFlags int flags)3625 private @NonNull ChildListForAutofill getChildrenForAutofill( 3626 @AutofillFlags int flags) { 3627 final ChildListForAutofill children = ChildListForAutofill 3628 .obtain(); 3629 populateChildrenForAutofill(children, flags); 3630 return children; 3631 } 3632 3633 /** @hide */ populateChildrenForAutofill(ArrayList<View> list, @AutofillFlags int flags)3634 private void populateChildrenForAutofill(ArrayList<View> list, @AutofillFlags int flags) { 3635 final int childrenCount = mChildrenCount; 3636 if (childrenCount <= 0) { 3637 return; 3638 } 3639 final ArrayList<View> preorderedList = buildOrderedChildList(); 3640 final boolean customOrder = preorderedList == null 3641 && isChildrenDrawingOrderEnabled(); 3642 for (int i = 0; i < childrenCount; i++) { 3643 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 3644 final View child = (preorderedList == null) 3645 ? mChildren[childIndex] : preorderedList.get(childIndex); 3646 if ((flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0 3647 || child.isImportantForAutofill()) { 3648 list.add(child); 3649 } else if (child instanceof ViewGroup) { 3650 ((ViewGroup) child).populateChildrenForAutofill(list, flags); 3651 } 3652 } 3653 } 3654 getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children, int childIndex)3655 private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children, 3656 int childIndex) { 3657 final View child; 3658 if (preorderedList != null) { 3659 child = preorderedList.get(childIndex); 3660 if (child == null) { 3661 throw new RuntimeException("Invalid preorderedList contained null child at index " 3662 + childIndex); 3663 } 3664 } else { 3665 child = children[childIndex]; 3666 } 3667 return child; 3668 } 3669 3670 /** @hide */ 3671 @Override 3672 @UnsupportedAppUsage onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)3673 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { 3674 super.onInitializeAccessibilityNodeInfoInternal(info); 3675 if (getAccessibilityNodeProvider() != null) { 3676 return; 3677 } 3678 if (mAttachInfo != null) { 3679 final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList; 3680 childrenForAccessibility.clear(); 3681 addChildrenForAccessibility(childrenForAccessibility); 3682 final int childrenForAccessibilityCount = childrenForAccessibility.size(); 3683 for (int i = 0; i < childrenForAccessibilityCount; i++) { 3684 final View child = childrenForAccessibility.get(i); 3685 info.addChildUnchecked(child); 3686 } 3687 childrenForAccessibility.clear(); 3688 } 3689 } 3690 3691 @Override getAccessibilityClassName()3692 public CharSequence getAccessibilityClassName() { 3693 return ViewGroup.class.getName(); 3694 } 3695 3696 @Override notifySubtreeAccessibilityStateChanged(View child, View source, int changeType)3697 public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) { 3698 // If this is a live region, we should send a subtree change event 3699 // from this view. Otherwise, we can let it propagate up. 3700 if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) { 3701 notifyViewAccessibilityStateChangedIfNeeded( 3702 AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); 3703 } else if (mParent != null) { 3704 try { 3705 mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType); 3706 } catch (AbstractMethodError e) { 3707 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + 3708 " does not fully implement ViewParent", e); 3709 } 3710 } 3711 } 3712 3713 /** @hide */ 3714 @Override notifySubtreeAccessibilityStateChangedIfNeeded()3715 public void notifySubtreeAccessibilityStateChangedIfNeeded() { 3716 if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) { 3717 return; 3718 } 3719 // If something important for a11y is happening in this subtree, make sure it's dispatched 3720 // from a view that is important for a11y so it doesn't get lost. 3721 if ((getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) 3722 && !isImportantForAccessibility() && (getChildCount() > 0)) { 3723 ViewParent a11yParent = getParentForAccessibility(); 3724 if (a11yParent instanceof View) { 3725 ((View) a11yParent).notifySubtreeAccessibilityStateChangedIfNeeded(); 3726 return; 3727 } 3728 } 3729 super.notifySubtreeAccessibilityStateChangedIfNeeded(); 3730 } 3731 3732 @Override resetSubtreeAccessibilityStateChanged()3733 void resetSubtreeAccessibilityStateChanged() { 3734 super.resetSubtreeAccessibilityStateChanged(); 3735 View[] children = mChildren; 3736 final int childCount = mChildrenCount; 3737 for (int i = 0; i < childCount; i++) { 3738 children[i].resetSubtreeAccessibilityStateChanged(); 3739 } 3740 } 3741 3742 /** 3743 * Counts the number of children of this View that will be sent to an accessibility service. 3744 * 3745 * @return The number of children an {@code AccessibilityNodeInfo} rooted at this View 3746 * would have. 3747 */ getNumChildrenForAccessibility()3748 int getNumChildrenForAccessibility() { 3749 int numChildrenForAccessibility = 0; 3750 for (int i = 0; i < getChildCount(); i++) { 3751 View child = getChildAt(i); 3752 if (child.includeForAccessibility()) { 3753 numChildrenForAccessibility++; 3754 } else if (child instanceof ViewGroup) { 3755 numChildrenForAccessibility += ((ViewGroup) child) 3756 .getNumChildrenForAccessibility(); 3757 } 3758 } 3759 return numChildrenForAccessibility; 3760 } 3761 3762 /** 3763 * {@inheritDoc} 3764 * 3765 * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p> 3766 * 3767 * @param target The target view dispatching this action 3768 * @param action Action being performed; see 3769 * {@link android.view.accessibility.AccessibilityNodeInfo} 3770 * @param args Optional action arguments 3771 * @return false by default. Subclasses should return true if they handle the event. 3772 */ 3773 @Override onNestedPrePerformAccessibilityAction(View target, int action, Bundle args)3774 public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { 3775 return false; 3776 } 3777 3778 @Override 3779 @UnsupportedAppUsage dispatchDetachedFromWindow()3780 void dispatchDetachedFromWindow() { 3781 // If we still have a touch target, we are still in the process of 3782 // dispatching motion events to a child; we need to get rid of that 3783 // child to avoid dispatching events to it after the window is torn 3784 // down. To make sure we keep the child in a consistent state, we 3785 // first send it an ACTION_CANCEL motion event. 3786 cancelAndClearTouchTargets(null); 3787 3788 // Similarly, set ACTION_EXIT to all hover targets and clear them. 3789 exitHoverTargets(); 3790 exitTooltipHoverTargets(); 3791 3792 // In case view is detached while transition is running 3793 mLayoutCalledWhileSuppressed = false; 3794 3795 // Tear down our drag tracking 3796 mChildrenInterestedInDrag = null; 3797 mIsInterestedInDrag = false; 3798 if (mCurrentDragStartEvent != null) { 3799 mCurrentDragStartEvent.recycle(); 3800 mCurrentDragStartEvent = null; 3801 } 3802 3803 final int count = mChildrenCount; 3804 final View[] children = mChildren; 3805 for (int i = 0; i < count; i++) { 3806 children[i].dispatchDetachedFromWindow(); 3807 } 3808 clearDisappearingChildren(); 3809 final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size(); 3810 for (int i = 0; i < transientCount; ++i) { 3811 View view = mTransientViews.get(i); 3812 view.dispatchDetachedFromWindow(); 3813 } 3814 super.dispatchDetachedFromWindow(); 3815 } 3816 3817 /** 3818 * @hide 3819 */ 3820 @Override internalSetPadding(int left, int top, int right, int bottom)3821 protected void internalSetPadding(int left, int top, int right, int bottom) { 3822 super.internalSetPadding(left, top, right, bottom); 3823 3824 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) { 3825 mGroupFlags |= FLAG_PADDING_NOT_NULL; 3826 } else { 3827 mGroupFlags &= ~FLAG_PADDING_NOT_NULL; 3828 } 3829 } 3830 3831 @Override dispatchSaveInstanceState(SparseArray<Parcelable> container)3832 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 3833 super.dispatchSaveInstanceState(container); 3834 final int count = mChildrenCount; 3835 final View[] children = mChildren; 3836 for (int i = 0; i < count; i++) { 3837 View c = children[i]; 3838 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 3839 c.dispatchSaveInstanceState(container); 3840 } 3841 } 3842 } 3843 3844 /** 3845 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()} 3846 * to only this view, not to its children. For use when overriding 3847 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow 3848 * subclasses to freeze their own state but not the state of their children. 3849 * 3850 * @param container the container 3851 */ dispatchFreezeSelfOnly(SparseArray<Parcelable> container)3852 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { 3853 super.dispatchSaveInstanceState(container); 3854 } 3855 3856 @Override dispatchRestoreInstanceState(SparseArray<Parcelable> container)3857 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 3858 super.dispatchRestoreInstanceState(container); 3859 final int count = mChildrenCount; 3860 final View[] children = mChildren; 3861 for (int i = 0; i < count; i++) { 3862 View c = children[i]; 3863 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 3864 c.dispatchRestoreInstanceState(container); 3865 } 3866 } 3867 } 3868 3869 /** 3870 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)} 3871 * to only this view, not to its children. For use when overriding 3872 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow 3873 * subclasses to thaw their own state but not the state of their children. 3874 * 3875 * @param container the container 3876 */ dispatchThawSelfOnly(SparseArray<Parcelable> container)3877 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { 3878 super.dispatchRestoreInstanceState(container); 3879 } 3880 3881 /** 3882 * Enables or disables the drawing cache for each child of this view group. 3883 * 3884 * @param enabled true to enable the cache, false to dispose of it 3885 * 3886 * @deprecated The view drawing cache was largely made obsolete with the introduction of 3887 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache 3888 * layers are largely unnecessary and can easily result in a net loss in performance due to the 3889 * cost of creating and updating the layer. In the rare cases where caching layers are useful, 3890 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware 3891 * rendering. For software-rendered snapshots of a small part of the View hierarchy or 3892 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or 3893 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these 3894 * software-rendered usages are discouraged and have compatibility issues with hardware-only 3895 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} 3896 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback 3897 * reports or unit testing the {@link PixelCopy} API is recommended. 3898 */ 3899 @Deprecated setChildrenDrawingCacheEnabled(boolean enabled)3900 protected void setChildrenDrawingCacheEnabled(boolean enabled) { 3901 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) { 3902 final View[] children = mChildren; 3903 final int count = mChildrenCount; 3904 for (int i = 0; i < count; i++) { 3905 children[i].setDrawingCacheEnabled(enabled); 3906 } 3907 } 3908 } 3909 3910 /** 3911 * @hide 3912 */ 3913 @Override createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren)3914 public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) { 3915 int count = mChildrenCount; 3916 int[] visibilities = null; 3917 3918 if (skipChildren) { 3919 visibilities = new int[count]; 3920 for (int i = 0; i < count; i++) { 3921 View child = getChildAt(i); 3922 visibilities[i] = child.getVisibility(); 3923 if (visibilities[i] == View.VISIBLE) { 3924 child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK) 3925 | (View.INVISIBLE & View.VISIBILITY_MASK); 3926 } 3927 } 3928 } 3929 3930 try { 3931 return super.createSnapshot(canvasProvider, skipChildren); 3932 } finally { 3933 if (skipChildren) { 3934 for (int i = 0; i < count; i++) { 3935 View child = getChildAt(i); 3936 child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK) 3937 | (visibilities[i] & View.VISIBILITY_MASK); 3938 } 3939 } 3940 } 3941 } 3942 3943 /** Return true if this ViewGroup is laying out using optical bounds. */ isLayoutModeOptical()3944 boolean isLayoutModeOptical() { 3945 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS; 3946 } 3947 3948 @Override computeOpticalInsets()3949 Insets computeOpticalInsets() { 3950 if (isLayoutModeOptical()) { 3951 int left = 0; 3952 int top = 0; 3953 int right = 0; 3954 int bottom = 0; 3955 for (int i = 0; i < mChildrenCount; i++) { 3956 View child = getChildAt(i); 3957 if (child.getVisibility() == VISIBLE) { 3958 Insets insets = child.getOpticalInsets(); 3959 left = Math.max(left, insets.left); 3960 top = Math.max(top, insets.top); 3961 right = Math.max(right, insets.right); 3962 bottom = Math.max(bottom, insets.bottom); 3963 } 3964 } 3965 return Insets.of(left, top, right, bottom); 3966 } else { 3967 return Insets.NONE; 3968 } 3969 } 3970 fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)3971 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { 3972 if (x1 != x2 && y1 != y2) { 3973 if (x1 > x2) { 3974 int tmp = x1; x1 = x2; x2 = tmp; 3975 } 3976 if (y1 > y2) { 3977 int tmp = y1; y1 = y2; y2 = tmp; 3978 } 3979 canvas.drawRect(x1, y1, x2, y2, paint); 3980 } 3981 } 3982 sign(int x)3983 private static int sign(int x) { 3984 return (x >= 0) ? 1 : -1; 3985 } 3986 drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw)3987 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) { 3988 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); 3989 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); 3990 } 3991 drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, int lineLength, int lineWidth)3992 private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, 3993 int lineLength, int lineWidth) { 3994 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); 3995 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); 3996 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); 3997 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); 3998 } 3999 fillDifference(Canvas canvas, int x2, int y2, int x3, int y3, int dx1, int dy1, int dx2, int dy2, Paint paint)4000 private static void fillDifference(Canvas canvas, 4001 int x2, int y2, int x3, int y3, 4002 int dx1, int dy1, int dx2, int dy2, Paint paint) { 4003 int x1 = x2 - dx1; 4004 int y1 = y2 - dy1; 4005 4006 int x4 = x3 + dx2; 4007 int y4 = y3 + dy2; 4008 4009 fillRect(canvas, paint, x1, y1, x4, y2); 4010 fillRect(canvas, paint, x1, y2, x2, y3); 4011 fillRect(canvas, paint, x3, y2, x4, y3); 4012 fillRect(canvas, paint, x1, y3, x4, y4); 4013 } 4014 4015 /** 4016 * @hide 4017 */ onDebugDrawMargins(Canvas canvas, Paint paint)4018 protected void onDebugDrawMargins(Canvas canvas, Paint paint) { 4019 for (int i = 0; i < getChildCount(); i++) { 4020 View c = getChildAt(i); 4021 c.getLayoutParams().onDebugDraw(c, canvas, paint); 4022 } 4023 } 4024 4025 /** 4026 * @hide 4027 */ onDebugDraw(Canvas canvas)4028 protected void onDebugDraw(Canvas canvas) { 4029 Paint paint = getDebugPaint(); 4030 4031 // Draw optical bounds 4032 { 4033 paint.setColor(Color.RED); 4034 paint.setStyle(Paint.Style.STROKE); 4035 4036 for (int i = 0; i < getChildCount(); i++) { 4037 View c = getChildAt(i); 4038 if (c.getVisibility() != View.GONE) { 4039 Insets insets = c.getOpticalInsets(); 4040 4041 drawRect(canvas, paint, 4042 c.getLeft() + insets.left, 4043 c.getTop() + insets.top, 4044 c.getRight() - insets.right - 1, 4045 c.getBottom() - insets.bottom - 1); 4046 } 4047 } 4048 } 4049 4050 // Draw margins 4051 { 4052 paint.setColor(Color.argb(63, 255, 0, 255)); 4053 paint.setStyle(Paint.Style.FILL); 4054 4055 onDebugDrawMargins(canvas, paint); 4056 } 4057 4058 // Draw clip bounds 4059 { 4060 paint.setColor(DEBUG_CORNERS_COLOR); 4061 paint.setStyle(Paint.Style.FILL); 4062 4063 int lineLength = dipsToPixels(DEBUG_CORNERS_SIZE_DIP); 4064 int lineWidth = dipsToPixels(1); 4065 for (int i = 0; i < getChildCount(); i++) { 4066 View c = getChildAt(i); 4067 if (c.getVisibility() != View.GONE) { 4068 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), 4069 paint, lineLength, lineWidth); 4070 } 4071 } 4072 } 4073 } 4074 4075 @Override dispatchDraw(Canvas canvas)4076 protected void dispatchDraw(Canvas canvas) { 4077 boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode); 4078 final int childrenCount = mChildrenCount; 4079 final View[] children = mChildren; 4080 int flags = mGroupFlags; 4081 4082 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { 4083 final boolean buildCache = !isHardwareAccelerated(); 4084 for (int i = 0; i < childrenCount; i++) { 4085 final View child = children[i]; 4086 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 4087 final LayoutParams params = child.getLayoutParams(); 4088 attachLayoutAnimationParameters(child, params, i, childrenCount); 4089 bindLayoutAnimation(child); 4090 } 4091 } 4092 4093 final LayoutAnimationController controller = mLayoutAnimationController; 4094 if (controller.willOverlap()) { 4095 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE; 4096 } 4097 4098 controller.start(); 4099 4100 mGroupFlags &= ~FLAG_RUN_ANIMATION; 4101 mGroupFlags &= ~FLAG_ANIMATION_DONE; 4102 4103 if (mAnimationListener != null) { 4104 mAnimationListener.onAnimationStart(controller.getAnimation()); 4105 } 4106 } 4107 4108 int clipSaveCount = 0; 4109 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 4110 if (clipToPadding) { 4111 clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); 4112 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, 4113 mScrollX + mRight - mLeft - mPaddingRight, 4114 mScrollY + mBottom - mTop - mPaddingBottom); 4115 } 4116 4117 // We will draw our child's animation, let's reset the flag 4118 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION; 4119 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; 4120 4121 boolean more = false; 4122 final long drawingTime = getDrawingTime(); 4123 4124 if (usingRenderNodeProperties) canvas.insertReorderBarrier(); 4125 final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); 4126 int transientIndex = transientCount != 0 ? 0 : -1; 4127 // Only use the preordered list if not HW accelerated, since the HW pipeline will do the 4128 // draw reordering internally 4129 final ArrayList<View> preorderedList = usingRenderNodeProperties 4130 ? null : buildOrderedChildList(); 4131 final boolean customOrder = preorderedList == null 4132 && isChildrenDrawingOrderEnabled(); 4133 for (int i = 0; i < childrenCount; i++) { 4134 while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { 4135 final View transientChild = mTransientViews.get(transientIndex); 4136 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || 4137 transientChild.getAnimation() != null) { 4138 more |= drawChild(canvas, transientChild, drawingTime); 4139 } 4140 transientIndex++; 4141 if (transientIndex >= transientCount) { 4142 transientIndex = -1; 4143 } 4144 } 4145 4146 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 4147 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); 4148 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 4149 more |= drawChild(canvas, child, drawingTime); 4150 } 4151 } 4152 while (transientIndex >= 0) { 4153 // there may be additional transient views after the normal views 4154 final View transientChild = mTransientViews.get(transientIndex); 4155 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || 4156 transientChild.getAnimation() != null) { 4157 more |= drawChild(canvas, transientChild, drawingTime); 4158 } 4159 transientIndex++; 4160 if (transientIndex >= transientCount) { 4161 break; 4162 } 4163 } 4164 if (preorderedList != null) preorderedList.clear(); 4165 4166 // Draw any disappearing views that have animations 4167 if (mDisappearingChildren != null) { 4168 final ArrayList<View> disappearingChildren = mDisappearingChildren; 4169 final int disappearingCount = disappearingChildren.size() - 1; 4170 // Go backwards -- we may delete as animations finish 4171 for (int i = disappearingCount; i >= 0; i--) { 4172 final View child = disappearingChildren.get(i); 4173 more |= drawChild(canvas, child, drawingTime); 4174 } 4175 } 4176 if (usingRenderNodeProperties) canvas.insertInorderBarrier(); 4177 4178 if (debugDraw()) { 4179 onDebugDraw(canvas); 4180 } 4181 4182 if (clipToPadding) { 4183 canvas.restoreToCount(clipSaveCount); 4184 } 4185 4186 // mGroupFlags might have been updated by drawChild() 4187 flags = mGroupFlags; 4188 4189 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { 4190 invalidate(true); 4191 } 4192 4193 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && 4194 mLayoutAnimationController.isDone() && !more) { 4195 // We want to erase the drawing cache and notify the listener after the 4196 // next frame is drawn because one extra invalidate() is caused by 4197 // drawChild() after the animation is over 4198 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; 4199 final Runnable end = new Runnable() { 4200 @Override 4201 public void run() { 4202 notifyAnimationListener(); 4203 } 4204 }; 4205 post(end); 4206 } 4207 } 4208 4209 /** 4210 * Returns the ViewGroupOverlay for this view group, creating it if it does 4211 * not yet exist. In addition to {@link ViewOverlay}'s support for drawables, 4212 * {@link ViewGroupOverlay} allows views to be added to the overlay. These 4213 * views, like overlay drawables, are visual-only; they do not receive input 4214 * events and should not be used as anything other than a temporary 4215 * representation of a view in a parent container, such as might be used 4216 * by an animation effect. 4217 * 4218 * <p>Note: Overlays do not currently work correctly with {@link 4219 * SurfaceView} or {@link TextureView}; contents in overlays for these 4220 * types of views may not display correctly.</p> 4221 * 4222 * @return The ViewGroupOverlay object for this view. 4223 * @see ViewGroupOverlay 4224 */ 4225 @Override getOverlay()4226 public ViewGroupOverlay getOverlay() { 4227 if (mOverlay == null) { 4228 mOverlay = new ViewGroupOverlay(mContext, this); 4229 } 4230 return (ViewGroupOverlay) mOverlay; 4231 } 4232 4233 /** 4234 * Converts drawing order position to container position. Override this 4235 * if you want to change the drawing order of children. By default, it 4236 * returns drawingPosition. 4237 * <p> 4238 * NOTE: In order for this method to be called, you must enable child ordering 4239 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}. 4240 * 4241 * @param drawingPosition the drawing order position. 4242 * @return the container position of a child for this drawing order position. 4243 * 4244 * @see #setChildrenDrawingOrderEnabled(boolean) 4245 * @see #isChildrenDrawingOrderEnabled() 4246 */ getChildDrawingOrder(int childCount, int drawingPosition)4247 protected int getChildDrawingOrder(int childCount, int drawingPosition) { 4248 return drawingPosition; 4249 } 4250 4251 /** 4252 * Converts drawing order position to container position. 4253 * <p> 4254 * Children are not necessarily drawn in the order in which they appear in the container. 4255 * ViewGroups can enable a custom ordering via {@link #setChildrenDrawingOrderEnabled(boolean)}. 4256 * This method returns the container position of a child that appears in the given position 4257 * in the current drawing order. 4258 * 4259 * @param drawingPosition the drawing order position. 4260 * @return the container position of a child for this drawing order position. 4261 * 4262 * @see #getChildDrawingOrder(int, int)} 4263 */ getChildDrawingOrder(int drawingPosition)4264 public final int getChildDrawingOrder(int drawingPosition) { 4265 return getChildDrawingOrder(getChildCount(), drawingPosition); 4266 } 4267 hasChildWithZ()4268 private boolean hasChildWithZ() { 4269 for (int i = 0; i < mChildrenCount; i++) { 4270 if (mChildren[i].getZ() != 0) return true; 4271 } 4272 return false; 4273 } 4274 4275 /** 4276 * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children, 4277 * sorted first by Z, then by child drawing order (if applicable). This list must be cleared 4278 * after use to avoid leaking child Views. 4279 * 4280 * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated 4281 * children. 4282 */ buildOrderedChildList()4283 ArrayList<View> buildOrderedChildList() { 4284 final int childrenCount = mChildrenCount; 4285 if (childrenCount <= 1 || !hasChildWithZ()) return null; 4286 4287 if (mPreSortedChildren == null) { 4288 mPreSortedChildren = new ArrayList<>(childrenCount); 4289 } else { 4290 // callers should clear, so clear shouldn't be necessary, but for safety... 4291 mPreSortedChildren.clear(); 4292 mPreSortedChildren.ensureCapacity(childrenCount); 4293 } 4294 4295 final boolean customOrder = isChildrenDrawingOrderEnabled(); 4296 for (int i = 0; i < childrenCount; i++) { 4297 // add next child (in child order) to end of list 4298 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 4299 final View nextChild = mChildren[childIndex]; 4300 final float currentZ = nextChild.getZ(); 4301 4302 // insert ahead of any Views with greater Z 4303 int insertIndex = i; 4304 while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) { 4305 insertIndex--; 4306 } 4307 mPreSortedChildren.add(insertIndex, nextChild); 4308 } 4309 return mPreSortedChildren; 4310 } 4311 notifyAnimationListener()4312 private void notifyAnimationListener() { 4313 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; 4314 mGroupFlags |= FLAG_ANIMATION_DONE; 4315 4316 if (mAnimationListener != null) { 4317 final Runnable end = new Runnable() { 4318 @Override 4319 public void run() { 4320 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation()); 4321 } 4322 }; 4323 post(end); 4324 } 4325 4326 invalidate(true); 4327 } 4328 4329 /** 4330 * This method is used to cause children of this ViewGroup to restore or recreate their 4331 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need 4332 * to recreate its own display list, which would happen if it went through the normal 4333 * draw/dispatchDraw mechanisms. 4334 * 4335 * @hide 4336 */ 4337 @Override 4338 @UnsupportedAppUsage dispatchGetDisplayList()4339 protected void dispatchGetDisplayList() { 4340 final int count = mChildrenCount; 4341 final View[] children = mChildren; 4342 for (int i = 0; i < count; i++) { 4343 final View child = children[i]; 4344 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) { 4345 recreateChildDisplayList(child); 4346 } 4347 } 4348 final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size(); 4349 for (int i = 0; i < transientCount; ++i) { 4350 View child = mTransientViews.get(i); 4351 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) { 4352 recreateChildDisplayList(child); 4353 } 4354 } 4355 if (mOverlay != null) { 4356 View overlayView = mOverlay.getOverlayView(); 4357 recreateChildDisplayList(overlayView); 4358 } 4359 if (mDisappearingChildren != null) { 4360 final ArrayList<View> disappearingChildren = mDisappearingChildren; 4361 final int disappearingCount = disappearingChildren.size(); 4362 for (int i = 0; i < disappearingCount; ++i) { 4363 final View child = disappearingChildren.get(i); 4364 recreateChildDisplayList(child); 4365 } 4366 } 4367 } 4368 recreateChildDisplayList(View child)4369 private void recreateChildDisplayList(View child) { 4370 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; 4371 child.mPrivateFlags &= ~PFLAG_INVALIDATED; 4372 child.updateDisplayListIfDirty(); 4373 child.mRecreateDisplayList = false; 4374 } 4375 4376 /** 4377 * Draw one child of this View Group. This method is responsible for getting 4378 * the canvas in the right state. This includes clipping, translating so 4379 * that the child's scrolled origin is at 0, 0, and applying any animation 4380 * transformations. 4381 * 4382 * @param canvas The canvas on which to draw the child 4383 * @param child Who to draw 4384 * @param drawingTime The time at which draw is occurring 4385 * @return True if an invalidate() was issued 4386 */ drawChild(Canvas canvas, View child, long drawingTime)4387 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 4388 return child.draw(canvas, this, drawingTime); 4389 } 4390 4391 @Override getScrollIndicatorBounds(@onNull Rect out)4392 void getScrollIndicatorBounds(@NonNull Rect out) { 4393 super.getScrollIndicatorBounds(out); 4394 4395 // If we have padding and we're supposed to clip children to that 4396 // padding, offset the scroll indicators to match our clip bounds. 4397 final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 4398 if (clipToPadding) { 4399 out.left += mPaddingLeft; 4400 out.right -= mPaddingRight; 4401 out.top += mPaddingTop; 4402 out.bottom -= mPaddingBottom; 4403 } 4404 } 4405 4406 /** 4407 * Returns whether this group's children are clipped to their bounds before drawing. 4408 * The default value is true. 4409 * @see #setClipChildren(boolean) 4410 * 4411 * @return True if the group's children will be clipped to their bounds, 4412 * false otherwise. 4413 */ 4414 @ViewDebug.ExportedProperty(category = "drawing") 4415 @InspectableProperty getClipChildren()4416 public boolean getClipChildren() { 4417 return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0); 4418 } 4419 4420 /** 4421 * By default, children are clipped to their bounds before drawing. This 4422 * allows view groups to override this behavior for animations, etc. 4423 * 4424 * @param clipChildren true to clip children to their bounds, 4425 * false otherwise 4426 * @attr ref android.R.styleable#ViewGroup_clipChildren 4427 */ setClipChildren(boolean clipChildren)4428 public void setClipChildren(boolean clipChildren) { 4429 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN; 4430 if (clipChildren != previousValue) { 4431 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); 4432 for (int i = 0; i < mChildrenCount; ++i) { 4433 View child = getChildAt(i); 4434 if (child.mRenderNode != null) { 4435 child.mRenderNode.setClipToBounds(clipChildren); 4436 } 4437 } 4438 invalidate(true); 4439 } 4440 } 4441 4442 /** 4443 * Sets whether this ViewGroup will clip its children to its padding and resize (but not 4444 * clip) any EdgeEffect to the padded region, if padding is present. 4445 * <p> 4446 * By default, children are clipped to the padding of their parent 4447 * ViewGroup. This clipping behavior is only enabled if padding is non-zero. 4448 * 4449 * @param clipToPadding true to clip children to the padding of the group, and resize (but 4450 * not clip) any EdgeEffect to the padded region. False otherwise. 4451 * @attr ref android.R.styleable#ViewGroup_clipToPadding 4452 */ setClipToPadding(boolean clipToPadding)4453 public void setClipToPadding(boolean clipToPadding) { 4454 if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) { 4455 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding); 4456 invalidate(true); 4457 } 4458 } 4459 4460 /** 4461 * Returns whether this ViewGroup will clip its children to its padding, and resize (but 4462 * not clip) any EdgeEffect to the padded region, if padding is present. 4463 * <p> 4464 * By default, children are clipped to the padding of their parent 4465 * Viewgroup. This clipping behavior is only enabled if padding is non-zero. 4466 * 4467 * @return true if this ViewGroup clips children to its padding and resizes (but doesn't 4468 * clip) any EdgeEffect to the padded region, false otherwise. 4469 * 4470 * @attr ref android.R.styleable#ViewGroup_clipToPadding 4471 */ 4472 @ViewDebug.ExportedProperty(category = "drawing") 4473 @InspectableProperty getClipToPadding()4474 public boolean getClipToPadding() { 4475 return hasBooleanFlag(FLAG_CLIP_TO_PADDING); 4476 } 4477 4478 @Override dispatchSetSelected(boolean selected)4479 public void dispatchSetSelected(boolean selected) { 4480 final View[] children = mChildren; 4481 final int count = mChildrenCount; 4482 for (int i = 0; i < count; i++) { 4483 children[i].setSelected(selected); 4484 } 4485 } 4486 4487 @Override dispatchSetActivated(boolean activated)4488 public void dispatchSetActivated(boolean activated) { 4489 final View[] children = mChildren; 4490 final int count = mChildrenCount; 4491 for (int i = 0; i < count; i++) { 4492 children[i].setActivated(activated); 4493 } 4494 } 4495 4496 @Override dispatchSetPressed(boolean pressed)4497 protected void dispatchSetPressed(boolean pressed) { 4498 final View[] children = mChildren; 4499 final int count = mChildrenCount; 4500 for (int i = 0; i < count; i++) { 4501 final View child = children[i]; 4502 // Children that are clickable on their own should not 4503 // show a pressed state when their parent view does. 4504 // Clearing a pressed state always propagates. 4505 if (!pressed || (!child.isClickable() && !child.isLongClickable())) { 4506 child.setPressed(pressed); 4507 } 4508 } 4509 } 4510 4511 /** 4512 * Dispatches drawable hotspot changes to child views that meet at least 4513 * one of the following criteria: 4514 * <ul> 4515 * <li>Returns {@code false} from both {@link View#isClickable()} and 4516 * {@link View#isLongClickable()}</li> 4517 * <li>Requests duplication of parent state via 4518 * {@link View#setDuplicateParentStateEnabled(boolean)}</li> 4519 * </ul> 4520 * 4521 * @param x hotspot x coordinate 4522 * @param y hotspot y coordinate 4523 * @see #drawableHotspotChanged(float, float) 4524 */ 4525 @Override dispatchDrawableHotspotChanged(float x, float y)4526 public void dispatchDrawableHotspotChanged(float x, float y) { 4527 final int count = mChildrenCount; 4528 if (count == 0) { 4529 return; 4530 } 4531 4532 final View[] children = mChildren; 4533 for (int i = 0; i < count; i++) { 4534 final View child = children[i]; 4535 // Children that are clickable on their own should not 4536 // receive hotspots when their parent view does. 4537 final boolean nonActionable = !child.isClickable() && !child.isLongClickable(); 4538 final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0; 4539 if (nonActionable || duplicatesState) { 4540 final float[] point = getTempPoint(); 4541 point[0] = x; 4542 point[1] = y; 4543 transformPointToViewLocal(point, child); 4544 child.drawableHotspotChanged(point[0], point[1]); 4545 } 4546 } 4547 } 4548 4549 @Override dispatchCancelPendingInputEvents()4550 void dispatchCancelPendingInputEvents() { 4551 super.dispatchCancelPendingInputEvents(); 4552 4553 final View[] children = mChildren; 4554 final int count = mChildrenCount; 4555 for (int i = 0; i < count; i++) { 4556 children[i].dispatchCancelPendingInputEvents(); 4557 } 4558 } 4559 4560 /** 4561 * When this property is set to true, this ViewGroup supports static transformations on 4562 * children; this causes 4563 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 4564 * invoked when a child is drawn. 4565 * 4566 * Any subclass overriding 4567 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 4568 * set this property to true. 4569 * 4570 * @param enabled True to enable static transformations on children, false otherwise. 4571 * 4572 * @see #getChildStaticTransformation(View, android.view.animation.Transformation) 4573 */ setStaticTransformationsEnabled(boolean enabled)4574 protected void setStaticTransformationsEnabled(boolean enabled) { 4575 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled); 4576 } 4577 4578 /** 4579 * Sets <code>t</code> to be the static transformation of the child, if set, returning a 4580 * boolean to indicate whether a static transform was set. The default implementation 4581 * simply returns <code>false</code>; subclasses may override this method for different 4582 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true 4583 * for this method to be called. 4584 * 4585 * @param child The child view whose static transform is being requested 4586 * @param t The Transformation which will hold the result 4587 * @return true if the transformation was set, false otherwise 4588 * @see #setStaticTransformationsEnabled(boolean) 4589 */ getChildStaticTransformation(View child, Transformation t)4590 protected boolean getChildStaticTransformation(View child, Transformation t) { 4591 return false; 4592 } 4593 getChildTransformation()4594 Transformation getChildTransformation() { 4595 if (mChildTransformation == null) { 4596 mChildTransformation = new Transformation(); 4597 } 4598 return mChildTransformation; 4599 } 4600 4601 /** 4602 * {@hide} 4603 */ 4604 @Override findViewTraversal(@dRes int id)4605 protected <T extends View> T findViewTraversal(@IdRes int id) { 4606 if (id == mID) { 4607 return (T) this; 4608 } 4609 4610 final View[] where = mChildren; 4611 final int len = mChildrenCount; 4612 4613 for (int i = 0; i < len; i++) { 4614 View v = where[i]; 4615 4616 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 4617 v = v.findViewById(id); 4618 4619 if (v != null) { 4620 return (T) v; 4621 } 4622 } 4623 } 4624 4625 return null; 4626 } 4627 4628 /** 4629 * {@hide} 4630 */ 4631 @Override findViewWithTagTraversal(Object tag)4632 protected <T extends View> T findViewWithTagTraversal(Object tag) { 4633 if (tag != null && tag.equals(mTag)) { 4634 return (T) this; 4635 } 4636 4637 final View[] where = mChildren; 4638 final int len = mChildrenCount; 4639 4640 for (int i = 0; i < len; i++) { 4641 View v = where[i]; 4642 4643 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 4644 v = v.findViewWithTag(tag); 4645 4646 if (v != null) { 4647 return (T) v; 4648 } 4649 } 4650 } 4651 4652 return null; 4653 } 4654 4655 /** 4656 * {@hide} 4657 */ 4658 @Override findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip)4659 protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, 4660 View childToSkip) { 4661 if (predicate.test(this)) { 4662 return (T) this; 4663 } 4664 4665 final View[] where = mChildren; 4666 final int len = mChildrenCount; 4667 4668 for (int i = 0; i < len; i++) { 4669 View v = where[i]; 4670 4671 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 4672 v = v.findViewByPredicate(predicate); 4673 4674 if (v != null) { 4675 return (T) v; 4676 } 4677 } 4678 } 4679 4680 return null; 4681 } 4682 4683 /** 4684 * This method adds a view to this container at the specified index purely for the 4685 * purposes of allowing that view to draw even though it is not a normal child of 4686 * the container. That is, the view does not participate in layout, focus, accessibility, 4687 * input, or other normal view operations; it is purely an item to be drawn during the normal 4688 * rendering operation of this container. The index that it is added at is the order 4689 * in which it will be drawn, with respect to the other views in the container. 4690 * For example, a transient view added at index 0 will be drawn before all other views 4691 * in the container because it will be drawn first (including before any real view 4692 * at index 0). There can be more than one transient view at any particular index; 4693 * these views will be drawn in the order in which they were added to the list of 4694 * transient views. The index of transient views can also be greater than the number 4695 * of normal views in the container; that just means that they will be drawn after all 4696 * other views are drawn. 4697 * 4698 * <p>Note that since transient views do not participate in layout, they must be sized 4699 * manually or, more typically, they should just use the size that they had before they 4700 * were removed from their container.</p> 4701 * 4702 * <p>Transient views are useful for handling animations of views that have been removed 4703 * from the container, but which should be animated out after the removal. Adding these 4704 * views as transient views allows them to participate in drawing without side-effecting 4705 * the layout of the container.</p> 4706 * 4707 * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed} 4708 * from the container when they are no longer needed. For example, a transient view 4709 * which is added in order to fade it out in its old location should be removed 4710 * once the animation is complete.</p> 4711 * 4712 * @param view The view to be added. The view must not have a parent. 4713 * @param index The index at which this view should be drawn, must be >= 0. 4714 * This value is relative to the {@link #getChildAt(int) index} values in the normal 4715 * child list of this container, where any transient view at a particular index will 4716 * be drawn before any normal child at that same index. 4717 * 4718 * @hide 4719 */ 4720 @UnsupportedAppUsage addTransientView(View view, int index)4721 public void addTransientView(View view, int index) { 4722 if (index < 0 || view == null) { 4723 return; 4724 } 4725 if (view.mParent != null) { 4726 throw new IllegalStateException("The specified view already has a parent " 4727 + view.mParent); 4728 } 4729 4730 if (mTransientIndices == null) { 4731 mTransientIndices = new ArrayList<Integer>(); 4732 mTransientViews = new ArrayList<View>(); 4733 } 4734 final int oldSize = mTransientIndices.size(); 4735 if (oldSize > 0) { 4736 int insertionIndex; 4737 for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) { 4738 if (index < mTransientIndices.get(insertionIndex)) { 4739 break; 4740 } 4741 } 4742 mTransientIndices.add(insertionIndex, index); 4743 mTransientViews.add(insertionIndex, view); 4744 } else { 4745 mTransientIndices.add(index); 4746 mTransientViews.add(view); 4747 } 4748 view.mParent = this; 4749 if (mAttachInfo != null) { 4750 view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags & VISIBILITY_MASK)); 4751 } 4752 invalidate(true); 4753 } 4754 4755 /** 4756 * Removes a view from the list of transient views in this container. If there is no 4757 * such transient view, this method does nothing. 4758 * 4759 * @param view The transient view to be removed 4760 * 4761 * @hide 4762 */ 4763 @UnsupportedAppUsage removeTransientView(View view)4764 public void removeTransientView(View view) { 4765 if (mTransientViews == null) { 4766 return; 4767 } 4768 final int size = mTransientViews.size(); 4769 for (int i = 0; i < size; ++i) { 4770 if (view == mTransientViews.get(i)) { 4771 mTransientViews.remove(i); 4772 mTransientIndices.remove(i); 4773 view.mParent = null; 4774 if (view.mAttachInfo != null) { 4775 view.dispatchDetachedFromWindow(); 4776 } 4777 invalidate(true); 4778 return; 4779 } 4780 } 4781 } 4782 4783 /** 4784 * Returns the number of transient views in this container. Specific transient 4785 * views and the index at which they were added can be retrieved via 4786 * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}. 4787 * 4788 * @see #addTransientView(View, int) 4789 * @return The number of transient views in this container 4790 * 4791 * @hide 4792 */ 4793 @UnsupportedAppUsage getTransientViewCount()4794 public int getTransientViewCount() { 4795 return mTransientIndices == null ? 0 : mTransientIndices.size(); 4796 } 4797 4798 /** 4799 * Given a valid position within the list of transient views, returns the index of 4800 * the transient view at that position. 4801 * 4802 * @param position The position of the index being queried. Must be at least 0 4803 * and less than the value returned by {@link #getTransientViewCount()}. 4804 * @return The index of the transient view stored in the given position if the 4805 * position is valid, otherwise -1 4806 * 4807 * @hide 4808 */ getTransientViewIndex(int position)4809 public int getTransientViewIndex(int position) { 4810 if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) { 4811 return -1; 4812 } 4813 return mTransientIndices.get(position); 4814 } 4815 4816 /** 4817 * Given a valid position within the list of transient views, returns the 4818 * transient view at that position. 4819 * 4820 * @param position The position of the view being queried. Must be at least 0 4821 * and less than the value returned by {@link #getTransientViewCount()}. 4822 * @return The transient view stored in the given position if the 4823 * position is valid, otherwise null 4824 * 4825 * @hide 4826 */ 4827 @UnsupportedAppUsage getTransientView(int position)4828 public View getTransientView(int position) { 4829 if (mTransientViews == null || position >= mTransientViews.size()) { 4830 return null; 4831 } 4832 return mTransientViews.get(position); 4833 } 4834 4835 /** 4836 * <p>Adds a child view. If no layout parameters are already set on the child, the 4837 * default parameters for this ViewGroup are set on the child.</p> 4838 * 4839 * <p><strong>Note:</strong> do not invoke this method from 4840 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 4841 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 4842 * 4843 * @param child the child view to add 4844 * 4845 * @see #generateDefaultLayoutParams() 4846 */ addView(View child)4847 public void addView(View child) { 4848 addView(child, -1); 4849 } 4850 4851 /** 4852 * Adds a child view. If no layout parameters are already set on the child, the 4853 * default parameters for this ViewGroup are set on the child. 4854 * 4855 * <p><strong>Note:</strong> do not invoke this method from 4856 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 4857 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 4858 * 4859 * @param child the child view to add 4860 * @param index the position at which to add the child 4861 * 4862 * @see #generateDefaultLayoutParams() 4863 */ addView(View child, int index)4864 public void addView(View child, int index) { 4865 if (child == null) { 4866 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); 4867 } 4868 LayoutParams params = child.getLayoutParams(); 4869 if (params == null) { 4870 params = generateDefaultLayoutParams(); 4871 if (params == null) { 4872 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); 4873 } 4874 } 4875 addView(child, index, params); 4876 } 4877 4878 /** 4879 * Adds a child view with this ViewGroup's default layout parameters and the 4880 * specified width and height. 4881 * 4882 * <p><strong>Note:</strong> do not invoke this method from 4883 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 4884 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 4885 * 4886 * @param child the child view to add 4887 */ addView(View child, int width, int height)4888 public void addView(View child, int width, int height) { 4889 final LayoutParams params = generateDefaultLayoutParams(); 4890 params.width = width; 4891 params.height = height; 4892 addView(child, -1, params); 4893 } 4894 4895 /** 4896 * Adds a child view with the specified layout parameters. 4897 * 4898 * <p><strong>Note:</strong> do not invoke this method from 4899 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 4900 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 4901 * 4902 * @param child the child view to add 4903 * @param params the layout parameters to set on the child 4904 */ 4905 @Override addView(View child, LayoutParams params)4906 public void addView(View child, LayoutParams params) { 4907 addView(child, -1, params); 4908 } 4909 4910 /** 4911 * Adds a child view with the specified layout parameters. 4912 * 4913 * <p><strong>Note:</strong> do not invoke this method from 4914 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 4915 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 4916 * 4917 * @param child the child view to add 4918 * @param index the position at which to add the child or -1 to add last 4919 * @param params the layout parameters to set on the child 4920 */ addView(View child, int index, LayoutParams params)4921 public void addView(View child, int index, LayoutParams params) { 4922 if (DBG) { 4923 System.out.println(this + " addView"); 4924 } 4925 4926 if (child == null) { 4927 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); 4928 } 4929 4930 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 4931 // therefore, we call requestLayout() on ourselves before, so that the child's request 4932 // will be blocked at our level 4933 requestLayout(); 4934 invalidate(true); 4935 addViewInner(child, index, params, false); 4936 } 4937 4938 @Override updateViewLayout(View view, ViewGroup.LayoutParams params)4939 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 4940 if (!checkLayoutParams(params)) { 4941 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this); 4942 } 4943 if (view.mParent != this) { 4944 throw new IllegalArgumentException("Given view not a child of " + this); 4945 } 4946 view.setLayoutParams(params); 4947 } 4948 checkLayoutParams(ViewGroup.LayoutParams p)4949 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 4950 return p != null; 4951 } 4952 4953 /** 4954 * Interface definition for a callback to be invoked when the hierarchy 4955 * within this view changed. The hierarchy changes whenever a child is added 4956 * to or removed from this view. 4957 */ 4958 public interface OnHierarchyChangeListener { 4959 /** 4960 * Called when a new child is added to a parent view. 4961 * 4962 * @param parent the view in which a child was added 4963 * @param child the new child view added in the hierarchy 4964 */ onChildViewAdded(View parent, View child)4965 void onChildViewAdded(View parent, View child); 4966 4967 /** 4968 * Called when a child is removed from a parent view. 4969 * 4970 * @param parent the view from which the child was removed 4971 * @param child the child removed from the hierarchy 4972 */ onChildViewRemoved(View parent, View child)4973 void onChildViewRemoved(View parent, View child); 4974 } 4975 4976 /** 4977 * Register a callback to be invoked when a child is added to or removed 4978 * from this view. 4979 * 4980 * @param listener the callback to invoke on hierarchy change 4981 */ setOnHierarchyChangeListener(OnHierarchyChangeListener listener)4982 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 4983 mOnHierarchyChangeListener = listener; 4984 } 4985 4986 @UnsupportedAppUsage dispatchViewAdded(View child)4987 void dispatchViewAdded(View child) { 4988 onViewAdded(child); 4989 if (mOnHierarchyChangeListener != null) { 4990 mOnHierarchyChangeListener.onChildViewAdded(this, child); 4991 } 4992 } 4993 4994 /** 4995 * Called when a new child is added to this ViewGroup. Overrides should always 4996 * call super.onViewAdded. 4997 * 4998 * @param child the added child view 4999 */ onViewAdded(View child)5000 public void onViewAdded(View child) { 5001 } 5002 5003 @UnsupportedAppUsage dispatchViewRemoved(View child)5004 void dispatchViewRemoved(View child) { 5005 onViewRemoved(child); 5006 if (mOnHierarchyChangeListener != null) { 5007 mOnHierarchyChangeListener.onChildViewRemoved(this, child); 5008 } 5009 } 5010 5011 /** 5012 * Called when a child view is removed from this ViewGroup. Overrides should always 5013 * call super.onViewRemoved. 5014 * 5015 * @param child the removed child view 5016 */ onViewRemoved(View child)5017 public void onViewRemoved(View child) { 5018 } 5019 clearCachedLayoutMode()5020 private void clearCachedLayoutMode() { 5021 if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) { 5022 mLayoutMode = LAYOUT_MODE_UNDEFINED; 5023 } 5024 } 5025 5026 @Override onAttachedToWindow()5027 protected void onAttachedToWindow() { 5028 super.onAttachedToWindow(); 5029 clearCachedLayoutMode(); 5030 } 5031 5032 @Override onDetachedFromWindow()5033 protected void onDetachedFromWindow() { 5034 super.onDetachedFromWindow(); 5035 clearCachedLayoutMode(); 5036 } 5037 5038 /** @hide */ 5039 @Override destroyHardwareResources()5040 protected void destroyHardwareResources() { 5041 super.destroyHardwareResources(); 5042 int count = getChildCount(); 5043 for (int i = 0; i < count; i++) { 5044 getChildAt(i).destroyHardwareResources(); 5045 } 5046 } 5047 5048 /** 5049 * Adds a view during layout. This is useful if in your onLayout() method, 5050 * you need to add more views (as does the list view for example). 5051 * 5052 * If index is negative, it means put it at the end of the list. 5053 * 5054 * @param child the view to add to the group 5055 * @param index the index at which the child must be added or -1 to add last 5056 * @param params the layout parameters to associate with the child 5057 * @return true if the child was added, false otherwise 5058 */ addViewInLayout(View child, int index, LayoutParams params)5059 protected boolean addViewInLayout(View child, int index, LayoutParams params) { 5060 return addViewInLayout(child, index, params, false); 5061 } 5062 5063 /** 5064 * Adds a view during layout. This is useful if in your onLayout() method, 5065 * you need to add more views (as does the list view for example). 5066 * 5067 * If index is negative, it means put it at the end of the list. 5068 * 5069 * @param child the view to add to the group 5070 * @param index the index at which the child must be added or -1 to add last 5071 * @param params the layout parameters to associate with the child 5072 * @param preventRequestLayout if true, calling this method will not trigger a 5073 * layout request on child 5074 * @return true if the child was added, false otherwise 5075 */ addViewInLayout(View child, int index, LayoutParams params, boolean preventRequestLayout)5076 protected boolean addViewInLayout(View child, int index, LayoutParams params, 5077 boolean preventRequestLayout) { 5078 if (child == null) { 5079 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); 5080 } 5081 child.mParent = null; 5082 addViewInner(child, index, params, preventRequestLayout); 5083 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; 5084 return true; 5085 } 5086 5087 /** 5088 * Prevents the specified child to be laid out during the next layout pass. 5089 * 5090 * @param child the child on which to perform the cleanup 5091 */ cleanupLayoutState(View child)5092 protected void cleanupLayoutState(View child) { 5093 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT; 5094 } 5095 addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout)5096 private void addViewInner(View child, int index, LayoutParams params, 5097 boolean preventRequestLayout) { 5098 5099 if (mTransition != null) { 5100 // Don't prevent other add transitions from completing, but cancel remove 5101 // transitions to let them complete the process before we add to the container 5102 mTransition.cancel(LayoutTransition.DISAPPEARING); 5103 } 5104 5105 if (child.getParent() != null) { 5106 throw new IllegalStateException("The specified child already has a parent. " + 5107 "You must call removeView() on the child's parent first."); 5108 } 5109 5110 if (mTransition != null) { 5111 mTransition.addChild(this, child); 5112 } 5113 5114 if (!checkLayoutParams(params)) { 5115 params = generateLayoutParams(params); 5116 } 5117 5118 if (preventRequestLayout) { 5119 child.mLayoutParams = params; 5120 } else { 5121 child.setLayoutParams(params); 5122 } 5123 5124 if (index < 0) { 5125 index = mChildrenCount; 5126 } 5127 5128 addInArray(child, index); 5129 5130 // tell our children 5131 if (preventRequestLayout) { 5132 child.assignParent(this); 5133 } else { 5134 child.mParent = this; 5135 } 5136 if (child.hasUnhandledKeyListener()) { 5137 incrementChildUnhandledKeyListeners(); 5138 } 5139 5140 final boolean childHasFocus = child.hasFocus(); 5141 if (childHasFocus) { 5142 requestChildFocus(child, child.findFocus()); 5143 } 5144 5145 AttachInfo ai = mAttachInfo; 5146 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { 5147 boolean lastKeepOn = ai.mKeepScreenOn; 5148 ai.mKeepScreenOn = false; 5149 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 5150 if (ai.mKeepScreenOn) { 5151 needGlobalAttributesUpdate(true); 5152 } 5153 ai.mKeepScreenOn = lastKeepOn; 5154 } 5155 5156 if (child.isLayoutDirectionInherited()) { 5157 child.resetRtlProperties(); 5158 } 5159 5160 dispatchViewAdded(child); 5161 5162 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 5163 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 5164 } 5165 5166 if (child.hasTransientState()) { 5167 childHasTransientStateChanged(child, true); 5168 } 5169 5170 if (child.getVisibility() != View.GONE) { 5171 notifySubtreeAccessibilityStateChangedIfNeeded(); 5172 } 5173 5174 if (mTransientIndices != null) { 5175 final int transientCount = mTransientIndices.size(); 5176 for (int i = 0; i < transientCount; ++i) { 5177 final int oldIndex = mTransientIndices.get(i); 5178 if (index <= oldIndex) { 5179 mTransientIndices.set(i, oldIndex + 1); 5180 } 5181 } 5182 } 5183 5184 if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) { 5185 notifyChildOfDragStart(child); 5186 } 5187 5188 if (child.hasDefaultFocus()) { 5189 // When adding a child that contains default focus, either during inflation or while 5190 // manually assembling the hierarchy, update the ancestor default-focus chain. 5191 setDefaultFocus(child); 5192 } 5193 5194 touchAccessibilityNodeProviderIfNeeded(child); 5195 } 5196 5197 /** 5198 * We may need to touch the provider to bring up the a11y layer. In a11y mode 5199 * clients inspect the screen or the user touches it which triggers bringing up 5200 * of the a11y infrastructure while in autofill mode we want the infra up and 5201 * running from the beginning since we watch for a11y events to drive autofill. 5202 */ touchAccessibilityNodeProviderIfNeeded(View child)5203 private void touchAccessibilityNodeProviderIfNeeded(View child) { 5204 if (mContext.isAutofillCompatibilityEnabled()) { 5205 child.getAccessibilityNodeProvider(); 5206 } 5207 } 5208 addInArray(View child, int index)5209 private void addInArray(View child, int index) { 5210 View[] children = mChildren; 5211 final int count = mChildrenCount; 5212 final int size = children.length; 5213 if (index == count) { 5214 if (size == count) { 5215 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 5216 System.arraycopy(children, 0, mChildren, 0, size); 5217 children = mChildren; 5218 } 5219 children[mChildrenCount++] = child; 5220 } else if (index < count) { 5221 if (size == count) { 5222 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 5223 System.arraycopy(children, 0, mChildren, 0, index); 5224 System.arraycopy(children, index, mChildren, index + 1, count - index); 5225 children = mChildren; 5226 } else { 5227 System.arraycopy(children, index, children, index + 1, count - index); 5228 } 5229 children[index] = child; 5230 mChildrenCount++; 5231 if (mLastTouchDownIndex >= index) { 5232 mLastTouchDownIndex++; 5233 } 5234 } else { 5235 throw new IndexOutOfBoundsException("index=" + index + " count=" + count); 5236 } 5237 } 5238 5239 // This method also sets the child's mParent to null removeFromArray(int index)5240 private void removeFromArray(int index) { 5241 final View[] children = mChildren; 5242 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) { 5243 children[index].mParent = null; 5244 } 5245 final int count = mChildrenCount; 5246 if (index == count - 1) { 5247 children[--mChildrenCount] = null; 5248 } else if (index >= 0 && index < count) { 5249 System.arraycopy(children, index + 1, children, index, count - index - 1); 5250 children[--mChildrenCount] = null; 5251 } else { 5252 throw new IndexOutOfBoundsException(); 5253 } 5254 if (mLastTouchDownIndex == index) { 5255 mLastTouchDownTime = 0; 5256 mLastTouchDownIndex = -1; 5257 } else if (mLastTouchDownIndex > index) { 5258 mLastTouchDownIndex--; 5259 } 5260 } 5261 5262 // This method also sets the children's mParent to null removeFromArray(int start, int count)5263 private void removeFromArray(int start, int count) { 5264 final View[] children = mChildren; 5265 final int childrenCount = mChildrenCount; 5266 5267 start = Math.max(0, start); 5268 final int end = Math.min(childrenCount, start + count); 5269 5270 if (start == end) { 5271 return; 5272 } 5273 5274 if (end == childrenCount) { 5275 for (int i = start; i < end; i++) { 5276 children[i].mParent = null; 5277 children[i] = null; 5278 } 5279 } else { 5280 for (int i = start; i < end; i++) { 5281 children[i].mParent = null; 5282 } 5283 5284 // Since we're looping above, we might as well do the copy, but is arraycopy() 5285 // faster than the extra 2 bounds checks we would do in the loop? 5286 System.arraycopy(children, end, children, start, childrenCount - end); 5287 5288 for (int i = childrenCount - (end - start); i < childrenCount; i++) { 5289 children[i] = null; 5290 } 5291 } 5292 5293 mChildrenCount -= (end - start); 5294 } 5295 bindLayoutAnimation(View child)5296 private void bindLayoutAnimation(View child) { 5297 Animation a = mLayoutAnimationController.getAnimationForView(child); 5298 child.setAnimation(a); 5299 } 5300 5301 /** 5302 * Subclasses should override this method to set layout animation 5303 * parameters on the supplied child. 5304 * 5305 * @param child the child to associate with animation parameters 5306 * @param params the child's layout parameters which hold the animation 5307 * parameters 5308 * @param index the index of the child in the view group 5309 * @param count the number of children in the view group 5310 */ attachLayoutAnimationParameters(View child, LayoutParams params, int index, int count)5311 protected void attachLayoutAnimationParameters(View child, 5312 LayoutParams params, int index, int count) { 5313 LayoutAnimationController.AnimationParameters animationParams = 5314 params.layoutAnimationParameters; 5315 if (animationParams == null) { 5316 animationParams = new LayoutAnimationController.AnimationParameters(); 5317 params.layoutAnimationParameters = animationParams; 5318 } 5319 5320 animationParams.count = count; 5321 animationParams.index = index; 5322 } 5323 5324 /** 5325 * {@inheritDoc} 5326 * 5327 * <p><strong>Note:</strong> do not invoke this method from 5328 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 5329 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 5330 */ 5331 @Override removeView(View view)5332 public void removeView(View view) { 5333 if (removeViewInternal(view)) { 5334 requestLayout(); 5335 invalidate(true); 5336 } 5337 } 5338 5339 /** 5340 * Removes a view during layout. This is useful if in your onLayout() method, 5341 * you need to remove more views. 5342 * 5343 * <p><strong>Note:</strong> do not invoke this method from 5344 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 5345 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 5346 * 5347 * @param view the view to remove from the group 5348 */ removeViewInLayout(View view)5349 public void removeViewInLayout(View view) { 5350 removeViewInternal(view); 5351 } 5352 5353 /** 5354 * Removes a range of views during layout. This is useful if in your onLayout() method, 5355 * you need to remove more views. 5356 * 5357 * <p><strong>Note:</strong> do not invoke this method from 5358 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 5359 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 5360 * 5361 * @param start the index of the first view to remove from the group 5362 * @param count the number of views to remove from the group 5363 */ removeViewsInLayout(int start, int count)5364 public void removeViewsInLayout(int start, int count) { 5365 removeViewsInternal(start, count); 5366 } 5367 5368 /** 5369 * Removes the view at the specified position in the group. 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 * @param index the position in the group of the view to remove 5376 */ removeViewAt(int index)5377 public void removeViewAt(int index) { 5378 removeViewInternal(index, getChildAt(index)); 5379 requestLayout(); 5380 invalidate(true); 5381 } 5382 5383 /** 5384 * Removes the specified range of views from the group. 5385 * 5386 * <p><strong>Note:</strong> do not invoke this method from 5387 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 5388 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 5389 * 5390 * @param start the first position in the group of the range of views to remove 5391 * @param count the number of views to remove 5392 */ removeViews(int start, int count)5393 public void removeViews(int start, int count) { 5394 removeViewsInternal(start, count); 5395 requestLayout(); 5396 invalidate(true); 5397 } 5398 removeViewInternal(View view)5399 private boolean removeViewInternal(View view) { 5400 final int index = indexOfChild(view); 5401 if (index >= 0) { 5402 removeViewInternal(index, view); 5403 return true; 5404 } 5405 return false; 5406 } 5407 removeViewInternal(int index, View view)5408 private void removeViewInternal(int index, View view) { 5409 if (mTransition != null) { 5410 mTransition.removeChild(this, view); 5411 } 5412 5413 boolean clearChildFocus = false; 5414 if (view == mFocused) { 5415 view.unFocus(null); 5416 clearChildFocus = true; 5417 } 5418 if (view == mFocusedInCluster) { 5419 clearFocusedInCluster(view); 5420 } 5421 5422 view.clearAccessibilityFocus(); 5423 5424 cancelTouchTarget(view); 5425 cancelHoverTarget(view); 5426 5427 if (view.getAnimation() != null || 5428 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 5429 addDisappearingView(view); 5430 } else if (view.mAttachInfo != null) { 5431 view.dispatchDetachedFromWindow(); 5432 } 5433 5434 if (view.hasTransientState()) { 5435 childHasTransientStateChanged(view, false); 5436 } 5437 5438 needGlobalAttributesUpdate(false); 5439 5440 removeFromArray(index); 5441 5442 if (view.hasUnhandledKeyListener()) { 5443 decrementChildUnhandledKeyListeners(); 5444 } 5445 5446 if (view == mDefaultFocus) { 5447 clearDefaultFocus(view); 5448 } 5449 if (clearChildFocus) { 5450 clearChildFocus(view); 5451 if (!rootViewRequestFocus()) { 5452 notifyGlobalFocusCleared(this); 5453 } 5454 } 5455 5456 dispatchViewRemoved(view); 5457 5458 if (view.getVisibility() != View.GONE) { 5459 notifySubtreeAccessibilityStateChangedIfNeeded(); 5460 } 5461 5462 int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); 5463 for (int i = 0; i < transientCount; ++i) { 5464 final int oldIndex = mTransientIndices.get(i); 5465 if (index < oldIndex) { 5466 mTransientIndices.set(i, oldIndex - 1); 5467 } 5468 } 5469 5470 if (mCurrentDragStartEvent != null) { 5471 mChildrenInterestedInDrag.remove(view); 5472 } 5473 } 5474 5475 /** 5476 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 5477 * not null, changes in layout which occur because of children being added to or removed from 5478 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 5479 * object. By default, the transition object is null (so layout changes are not animated). 5480 * 5481 * <p>Replacing a non-null transition will cause that previous transition to be 5482 * canceled, if it is currently running, to restore this container to 5483 * its correct post-transition state.</p> 5484 * 5485 * @param transition The LayoutTransition object that will animated changes in layout. A value 5486 * of <code>null</code> means no transition will run on layout changes. 5487 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 5488 */ setLayoutTransition(LayoutTransition transition)5489 public void setLayoutTransition(LayoutTransition transition) { 5490 if (mTransition != null) { 5491 LayoutTransition previousTransition = mTransition; 5492 previousTransition.cancel(); 5493 previousTransition.removeTransitionListener(mLayoutTransitionListener); 5494 } 5495 mTransition = transition; 5496 if (mTransition != null) { 5497 mTransition.addTransitionListener(mLayoutTransitionListener); 5498 } 5499 } 5500 5501 /** 5502 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 5503 * not null, changes in layout which occur because of children being added to or removed from 5504 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 5505 * object. By default, the transition object is null (so layout changes are not animated). 5506 * 5507 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout. 5508 * A value of <code>null</code> means no transition will run on layout changes. 5509 */ getLayoutTransition()5510 public LayoutTransition getLayoutTransition() { 5511 return mTransition; 5512 } 5513 removeViewsInternal(int start, int count)5514 private void removeViewsInternal(int start, int count) { 5515 final int end = start + count; 5516 5517 if (start < 0 || count < 0 || end > mChildrenCount) { 5518 throw new IndexOutOfBoundsException(); 5519 } 5520 5521 final View focused = mFocused; 5522 final boolean detach = mAttachInfo != null; 5523 boolean clearChildFocus = false; 5524 View clearDefaultFocus = null; 5525 5526 final View[] children = mChildren; 5527 5528 for (int i = start; i < end; i++) { 5529 final View view = children[i]; 5530 5531 if (mTransition != null) { 5532 mTransition.removeChild(this, view); 5533 } 5534 5535 if (view == focused) { 5536 view.unFocus(null); 5537 clearChildFocus = true; 5538 } 5539 if (view == mDefaultFocus) { 5540 clearDefaultFocus = view; 5541 } 5542 if (view == mFocusedInCluster) { 5543 clearFocusedInCluster(view); 5544 } 5545 5546 view.clearAccessibilityFocus(); 5547 5548 cancelTouchTarget(view); 5549 cancelHoverTarget(view); 5550 5551 if (view.getAnimation() != null || 5552 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 5553 addDisappearingView(view); 5554 } else if (detach) { 5555 view.dispatchDetachedFromWindow(); 5556 } 5557 5558 if (view.hasTransientState()) { 5559 childHasTransientStateChanged(view, false); 5560 } 5561 5562 needGlobalAttributesUpdate(false); 5563 5564 dispatchViewRemoved(view); 5565 } 5566 5567 removeFromArray(start, count); 5568 5569 if (clearDefaultFocus != null) { 5570 clearDefaultFocus(clearDefaultFocus); 5571 } 5572 if (clearChildFocus) { 5573 clearChildFocus(focused); 5574 if (!rootViewRequestFocus()) { 5575 notifyGlobalFocusCleared(focused); 5576 } 5577 } 5578 } 5579 5580 /** 5581 * Call this method to remove all child views from the 5582 * ViewGroup. 5583 * 5584 * <p><strong>Note:</strong> do not invoke this method from 5585 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 5586 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 5587 */ removeAllViews()5588 public void removeAllViews() { 5589 removeAllViewsInLayout(); 5590 requestLayout(); 5591 invalidate(true); 5592 } 5593 5594 /** 5595 * Called by a ViewGroup subclass to remove child views from itself, 5596 * when it must first know its size on screen before it can calculate how many 5597 * child views it will render. An example is a Gallery or a ListView, which 5598 * may "have" 50 children, but actually only render the number of children 5599 * that can currently fit inside the object on screen. Do not call 5600 * this method unless you are extending ViewGroup and understand the 5601 * view measuring and layout pipeline. 5602 * 5603 * <p><strong>Note:</strong> do not invoke this method from 5604 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 5605 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 5606 */ removeAllViewsInLayout()5607 public void removeAllViewsInLayout() { 5608 final int count = mChildrenCount; 5609 if (count <= 0) { 5610 return; 5611 } 5612 5613 final View[] children = mChildren; 5614 mChildrenCount = 0; 5615 5616 final View focused = mFocused; 5617 final boolean detach = mAttachInfo != null; 5618 boolean clearChildFocus = false; 5619 5620 needGlobalAttributesUpdate(false); 5621 5622 for (int i = count - 1; i >= 0; i--) { 5623 final View view = children[i]; 5624 5625 if (mTransition != null) { 5626 mTransition.removeChild(this, view); 5627 } 5628 5629 if (view == focused) { 5630 view.unFocus(null); 5631 clearChildFocus = true; 5632 } 5633 5634 view.clearAccessibilityFocus(); 5635 5636 cancelTouchTarget(view); 5637 cancelHoverTarget(view); 5638 5639 if (view.getAnimation() != null || 5640 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 5641 addDisappearingView(view); 5642 } else if (detach) { 5643 view.dispatchDetachedFromWindow(); 5644 } 5645 5646 if (view.hasTransientState()) { 5647 childHasTransientStateChanged(view, false); 5648 } 5649 5650 dispatchViewRemoved(view); 5651 5652 view.mParent = null; 5653 children[i] = null; 5654 } 5655 5656 if (mDefaultFocus != null) { 5657 clearDefaultFocus(mDefaultFocus); 5658 } 5659 if (mFocusedInCluster != null) { 5660 clearFocusedInCluster(mFocusedInCluster); 5661 } 5662 if (clearChildFocus) { 5663 clearChildFocus(focused); 5664 if (!rootViewRequestFocus()) { 5665 notifyGlobalFocusCleared(focused); 5666 } 5667 } 5668 } 5669 5670 /** 5671 * Finishes the removal of a detached view. This method will dispatch the detached from 5672 * window event and notify the hierarchy change listener. 5673 * <p> 5674 * This method is intended to be lightweight and makes no assumptions about whether the 5675 * parent or child should be redrawn. Proper use of this method will include also making 5676 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls. 5677 * For example, callers can {@link #post(Runnable) post} a {@link Runnable} 5678 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove 5679 * calls are finished, causing layout to be run prior to redrawing the view hierarchy. 5680 * 5681 * @param child the child to be definitely removed from the view hierarchy 5682 * @param animate if true and the view has an animation, the view is placed in the 5683 * disappearing views list, otherwise, it is detached from the window 5684 * 5685 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 5686 * @see #detachAllViewsFromParent() 5687 * @see #detachViewFromParent(View) 5688 * @see #detachViewFromParent(int) 5689 */ removeDetachedView(View child, boolean animate)5690 protected void removeDetachedView(View child, boolean animate) { 5691 if (mTransition != null) { 5692 mTransition.removeChild(this, child); 5693 } 5694 5695 if (child == mFocused) { 5696 child.clearFocus(); 5697 } 5698 if (child == mDefaultFocus) { 5699 clearDefaultFocus(child); 5700 } 5701 if (child == mFocusedInCluster) { 5702 clearFocusedInCluster(child); 5703 } 5704 5705 child.clearAccessibilityFocus(); 5706 5707 cancelTouchTarget(child); 5708 cancelHoverTarget(child); 5709 5710 if ((animate && child.getAnimation() != null) || 5711 (mTransitioningViews != null && mTransitioningViews.contains(child))) { 5712 addDisappearingView(child); 5713 } else if (child.mAttachInfo != null) { 5714 child.dispatchDetachedFromWindow(); 5715 } 5716 5717 if (child.hasTransientState()) { 5718 childHasTransientStateChanged(child, false); 5719 } 5720 5721 dispatchViewRemoved(child); 5722 } 5723 5724 /** 5725 * Attaches a view to this view group. Attaching a view assigns this group as the parent, 5726 * sets the layout parameters and puts the view in the list of children so that 5727 * it can be retrieved by calling {@link #getChildAt(int)}. 5728 * <p> 5729 * This method is intended to be lightweight and makes no assumptions about whether the 5730 * parent or child should be redrawn. Proper use of this method will include also making 5731 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls. 5732 * For example, callers can {@link #post(Runnable) post} a {@link Runnable} 5733 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach 5734 * calls are finished, causing layout to be run prior to redrawing the view hierarchy. 5735 * <p> 5736 * This method should be called only for views which were detached from their parent. 5737 * 5738 * @param child the child to attach 5739 * @param index the index at which the child should be attached 5740 * @param params the layout parameters of the child 5741 * 5742 * @see #removeDetachedView(View, boolean) 5743 * @see #detachAllViewsFromParent() 5744 * @see #detachViewFromParent(View) 5745 * @see #detachViewFromParent(int) 5746 */ attachViewToParent(View child, int index, LayoutParams params)5747 protected void attachViewToParent(View child, int index, LayoutParams params) { 5748 child.mLayoutParams = params; 5749 5750 if (index < 0) { 5751 index = mChildrenCount; 5752 } 5753 5754 addInArray(child, index); 5755 5756 child.mParent = this; 5757 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK 5758 & ~PFLAG_DRAWING_CACHE_VALID) 5759 | PFLAG_DRAWN | PFLAG_INVALIDATED; 5760 this.mPrivateFlags |= PFLAG_INVALIDATED; 5761 5762 if (child.hasFocus()) { 5763 requestChildFocus(child, child.findFocus()); 5764 } 5765 dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE 5766 && isShown()); 5767 notifySubtreeAccessibilityStateChangedIfNeeded(); 5768 } 5769 5770 /** 5771 * Detaches a view from its parent. Detaching a view should be followed 5772 * either by a call to 5773 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 5774 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 5775 * temporary; reattachment or removal should happen within the same drawing cycle as 5776 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 5777 * call to {@link #getChildAt(int)}. 5778 * 5779 * @param child the child to detach 5780 * 5781 * @see #detachViewFromParent(int) 5782 * @see #detachViewsFromParent(int, int) 5783 * @see #detachAllViewsFromParent() 5784 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 5785 * @see #removeDetachedView(View, boolean) 5786 */ detachViewFromParent(View child)5787 protected void detachViewFromParent(View child) { 5788 removeFromArray(indexOfChild(child)); 5789 } 5790 5791 /** 5792 * Detaches a view from its parent. Detaching a view should be followed 5793 * either by a call to 5794 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 5795 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 5796 * temporary; reattachment or removal should happen within the same drawing cycle as 5797 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 5798 * call to {@link #getChildAt(int)}. 5799 * 5800 * @param index the index of the child to detach 5801 * 5802 * @see #detachViewFromParent(View) 5803 * @see #detachAllViewsFromParent() 5804 * @see #detachViewsFromParent(int, int) 5805 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 5806 * @see #removeDetachedView(View, boolean) 5807 */ detachViewFromParent(int index)5808 protected void detachViewFromParent(int index) { 5809 removeFromArray(index); 5810 } 5811 5812 /** 5813 * Detaches a range of views from their parents. Detaching a view should be followed 5814 * either by a call to 5815 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 5816 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 5817 * temporary; reattachment or removal should happen within the same drawing cycle as 5818 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 5819 * call to {@link #getChildAt(int)}. 5820 * 5821 * @param start the first index of the childrend range to detach 5822 * @param count the number of children to detach 5823 * 5824 * @see #detachViewFromParent(View) 5825 * @see #detachViewFromParent(int) 5826 * @see #detachAllViewsFromParent() 5827 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 5828 * @see #removeDetachedView(View, boolean) 5829 */ detachViewsFromParent(int start, int count)5830 protected void detachViewsFromParent(int start, int count) { 5831 removeFromArray(start, count); 5832 } 5833 5834 /** 5835 * Detaches all views from the parent. Detaching a view should be followed 5836 * either by a call to 5837 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 5838 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 5839 * temporary; reattachment or removal should happen within the same drawing cycle as 5840 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 5841 * call to {@link #getChildAt(int)}. 5842 * 5843 * @see #detachViewFromParent(View) 5844 * @see #detachViewFromParent(int) 5845 * @see #detachViewsFromParent(int, int) 5846 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 5847 * @see #removeDetachedView(View, boolean) 5848 */ detachAllViewsFromParent()5849 protected void detachAllViewsFromParent() { 5850 final int count = mChildrenCount; 5851 if (count <= 0) { 5852 return; 5853 } 5854 5855 final View[] children = mChildren; 5856 mChildrenCount = 0; 5857 5858 for (int i = count - 1; i >= 0; i--) { 5859 children[i].mParent = null; 5860 children[i] = null; 5861 } 5862 } 5863 5864 @Override 5865 @CallSuper onDescendantInvalidated(@onNull View child, @NonNull View target)5866 public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { 5867 /* 5868 * HW-only, Rect-ignoring damage codepath 5869 * 5870 * We don't deal with rectangles here, since RenderThread native code computes damage for 5871 * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area) 5872 */ 5873 5874 // if set, combine the animation flag into the parent 5875 mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION); 5876 5877 if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) { 5878 // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential 5879 // optimization in provides in a DisplayList world. 5880 mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; 5881 5882 // simplified invalidateChildInParent behavior: clear cache validity to be safe... 5883 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 5884 } 5885 5886 // ... and mark inval if in software layer that needs to repaint (hw handled in native) 5887 if (mLayerType == LAYER_TYPE_SOFTWARE) { 5888 // Layered parents should be invalidated. Escalate to a full invalidate (and note that 5889 // we do this after consuming any relevant flags from the originating descendant) 5890 mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY; 5891 target = this; 5892 } 5893 5894 if (mParent != null) { 5895 mParent.onDescendantInvalidated(this, target); 5896 } 5897 } 5898 5899 5900 /** 5901 * Don't call or override this method. It is used for the implementation of 5902 * the view hierarchy. 5903 * 5904 * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to 5905 * draw state in descendants. 5906 */ 5907 @Deprecated 5908 @Override invalidateChild(View child, final Rect dirty)5909 public final void invalidateChild(View child, final Rect dirty) { 5910 final AttachInfo attachInfo = mAttachInfo; 5911 if (attachInfo != null && attachInfo.mHardwareAccelerated) { 5912 // HW accelerated fast path 5913 onDescendantInvalidated(child, child); 5914 return; 5915 } 5916 5917 ViewParent parent = this; 5918 if (attachInfo != null) { 5919 // If the child is drawing an animation, we want to copy this flag onto 5920 // ourselves and the parent to make sure the invalidate request goes 5921 // through 5922 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0; 5923 5924 // Check whether the child that requests the invalidate is fully opaque 5925 // Views being animated or transformed are not considered opaque because we may 5926 // be invalidating their old position and need the parent to paint behind them. 5927 Matrix childMatrix = child.getMatrix(); 5928 // Mark the child as dirty, using the appropriate flag 5929 // Make sure we do not set both flags at the same time 5930 5931 if (child.mLayerType != LAYER_TYPE_NONE) { 5932 mPrivateFlags |= PFLAG_INVALIDATED; 5933 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 5934 } 5935 5936 final int[] location = attachInfo.mInvalidateChildLocation; 5937 location[CHILD_LEFT_INDEX] = child.mLeft; 5938 location[CHILD_TOP_INDEX] = child.mTop; 5939 if (!childMatrix.isIdentity() || 5940 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 5941 RectF boundingRect = attachInfo.mTmpTransformRect; 5942 boundingRect.set(dirty); 5943 Matrix transformMatrix; 5944 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 5945 Transformation t = attachInfo.mTmpTransformation; 5946 boolean transformed = getChildStaticTransformation(child, t); 5947 if (transformed) { 5948 transformMatrix = attachInfo.mTmpMatrix; 5949 transformMatrix.set(t.getMatrix()); 5950 if (!childMatrix.isIdentity()) { 5951 transformMatrix.preConcat(childMatrix); 5952 } 5953 } else { 5954 transformMatrix = childMatrix; 5955 } 5956 } else { 5957 transformMatrix = childMatrix; 5958 } 5959 transformMatrix.mapRect(boundingRect); 5960 dirty.set((int) Math.floor(boundingRect.left), 5961 (int) Math.floor(boundingRect.top), 5962 (int) Math.ceil(boundingRect.right), 5963 (int) Math.ceil(boundingRect.bottom)); 5964 } 5965 5966 do { 5967 View view = null; 5968 if (parent instanceof View) { 5969 view = (View) parent; 5970 } 5971 5972 if (drawAnimation) { 5973 if (view != null) { 5974 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION; 5975 } else if (parent instanceof ViewRootImpl) { 5976 ((ViewRootImpl) parent).mIsAnimating = true; 5977 } 5978 } 5979 5980 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 5981 // flag coming from the child that initiated the invalidate 5982 if (view != null) { 5983 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { 5984 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; 5985 } 5986 } 5987 5988 parent = parent.invalidateChildInParent(location, dirty); 5989 if (view != null) { 5990 // Account for transform on current parent 5991 Matrix m = view.getMatrix(); 5992 if (!m.isIdentity()) { 5993 RectF boundingRect = attachInfo.mTmpTransformRect; 5994 boundingRect.set(dirty); 5995 m.mapRect(boundingRect); 5996 dirty.set((int) Math.floor(boundingRect.left), 5997 (int) Math.floor(boundingRect.top), 5998 (int) Math.ceil(boundingRect.right), 5999 (int) Math.ceil(boundingRect.bottom)); 6000 } 6001 } 6002 } while (parent != null); 6003 } 6004 } 6005 6006 /** 6007 * Don't call or override this method. It is used for the implementation of 6008 * the view hierarchy. 6009 * 6010 * This implementation returns null if this ViewGroup does not have a parent, 6011 * if this ViewGroup is already fully invalidated or if the dirty rectangle 6012 * does not intersect with this ViewGroup's bounds. 6013 * 6014 * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to 6015 * draw state in descendants. 6016 */ 6017 @Deprecated 6018 @Override invalidateChildInParent(final int[] location, final Rect dirty)6019 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 6020 if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) { 6021 // either DRAWN, or DRAWING_CACHE_VALID 6022 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) 6023 != FLAG_OPTIMIZE_INVALIDATE) { 6024 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, 6025 location[CHILD_TOP_INDEX] - mScrollY); 6026 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) { 6027 dirty.union(0, 0, mRight - mLeft, mBottom - mTop); 6028 } 6029 6030 final int left = mLeft; 6031 final int top = mTop; 6032 6033 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 6034 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) { 6035 dirty.setEmpty(); 6036 } 6037 } 6038 6039 location[CHILD_LEFT_INDEX] = left; 6040 location[CHILD_TOP_INDEX] = top; 6041 } else { 6042 6043 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 6044 dirty.set(0, 0, mRight - mLeft, mBottom - mTop); 6045 } else { 6046 // in case the dirty rect extends outside the bounds of this container 6047 dirty.union(0, 0, mRight - mLeft, mBottom - mTop); 6048 } 6049 location[CHILD_LEFT_INDEX] = mLeft; 6050 location[CHILD_TOP_INDEX] = mTop; 6051 6052 mPrivateFlags &= ~PFLAG_DRAWN; 6053 } 6054 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 6055 if (mLayerType != LAYER_TYPE_NONE) { 6056 mPrivateFlags |= PFLAG_INVALIDATED; 6057 } 6058 6059 return mParent; 6060 } 6061 6062 return null; 6063 } 6064 6065 /** 6066 * Offset a rectangle that is in a descendant's coordinate 6067 * space into our coordinate space. 6068 * @param descendant A descendant of this view 6069 * @param rect A rectangle defined in descendant's coordinate space. 6070 */ offsetDescendantRectToMyCoords(View descendant, Rect rect)6071 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { 6072 offsetRectBetweenParentAndChild(descendant, rect, true, false); 6073 } 6074 6075 /** 6076 * Offset a rectangle that is in our coordinate space into an ancestor's 6077 * coordinate space. 6078 * @param descendant A descendant of this view 6079 * @param rect A rectangle defined in descendant's coordinate space. 6080 */ offsetRectIntoDescendantCoords(View descendant, Rect rect)6081 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { 6082 offsetRectBetweenParentAndChild(descendant, rect, false, false); 6083 } 6084 6085 /** 6086 * Helper method that offsets a rect either from parent to descendant or 6087 * descendant to parent. 6088 */ offsetRectBetweenParentAndChild(View descendant, Rect rect, boolean offsetFromChildToParent, boolean clipToBounds)6089 void offsetRectBetweenParentAndChild(View descendant, Rect rect, 6090 boolean offsetFromChildToParent, boolean clipToBounds) { 6091 6092 // already in the same coord system :) 6093 if (descendant == this) { 6094 return; 6095 } 6096 6097 ViewParent theParent = descendant.mParent; 6098 6099 // search and offset up to the parent 6100 while ((theParent != null) 6101 && (theParent instanceof View) 6102 && (theParent != this)) { 6103 6104 if (offsetFromChildToParent) { 6105 rect.offset(descendant.mLeft - descendant.mScrollX, 6106 descendant.mTop - descendant.mScrollY); 6107 if (clipToBounds) { 6108 View p = (View) theParent; 6109 boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft, 6110 p.mBottom - p.mTop); 6111 if (!intersected) { 6112 rect.setEmpty(); 6113 } 6114 } 6115 } else { 6116 if (clipToBounds) { 6117 View p = (View) theParent; 6118 boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft, 6119 p.mBottom - p.mTop); 6120 if (!intersected) { 6121 rect.setEmpty(); 6122 } 6123 } 6124 rect.offset(descendant.mScrollX - descendant.mLeft, 6125 descendant.mScrollY - descendant.mTop); 6126 } 6127 6128 descendant = (View) theParent; 6129 theParent = descendant.mParent; 6130 } 6131 6132 // now that we are up to this view, need to offset one more time 6133 // to get into our coordinate space 6134 if (theParent == this) { 6135 if (offsetFromChildToParent) { 6136 rect.offset(descendant.mLeft - descendant.mScrollX, 6137 descendant.mTop - descendant.mScrollY); 6138 } else { 6139 rect.offset(descendant.mScrollX - descendant.mLeft, 6140 descendant.mScrollY - descendant.mTop); 6141 } 6142 } else { 6143 throw new IllegalArgumentException("parameter must be a descendant of this view"); 6144 } 6145 } 6146 6147 /** 6148 * Offset the vertical location of all children of this view by the specified number of pixels. 6149 * 6150 * @param offset the number of pixels to offset 6151 * 6152 * @hide 6153 */ 6154 @UnsupportedAppUsage offsetChildrenTopAndBottom(int offset)6155 public void offsetChildrenTopAndBottom(int offset) { 6156 final int count = mChildrenCount; 6157 final View[] children = mChildren; 6158 boolean invalidate = false; 6159 6160 for (int i = 0; i < count; i++) { 6161 final View v = children[i]; 6162 v.mTop += offset; 6163 v.mBottom += offset; 6164 if (v.mRenderNode != null) { 6165 invalidate = true; 6166 v.mRenderNode.offsetTopAndBottom(offset); 6167 } 6168 } 6169 6170 if (invalidate) { 6171 invalidateViewProperty(false, false); 6172 } 6173 notifySubtreeAccessibilityStateChangedIfNeeded(); 6174 } 6175 6176 @Override getChildVisibleRect(View child, Rect r, android.graphics.Point offset)6177 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 6178 return getChildVisibleRect(child, r, offset, false); 6179 } 6180 6181 /** 6182 * @param forceParentCheck true to guarantee that this call will propagate to all ancestors, 6183 * false otherwise 6184 * 6185 * @hide 6186 */ getChildVisibleRect( View child, Rect r, android.graphics.Point offset, boolean forceParentCheck)6187 public boolean getChildVisibleRect( 6188 View child, Rect r, android.graphics.Point offset, boolean forceParentCheck) { 6189 // It doesn't make a whole lot of sense to call this on a view that isn't attached, 6190 // but for some simple tests it can be useful. If we don't have attach info this 6191 // will allocate memory. 6192 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF(); 6193 rect.set(r); 6194 6195 if (!child.hasIdentityMatrix()) { 6196 child.getMatrix().mapRect(rect); 6197 } 6198 6199 final int dx = child.mLeft - mScrollX; 6200 final int dy = child.mTop - mScrollY; 6201 6202 rect.offset(dx, dy); 6203 6204 if (offset != null) { 6205 if (!child.hasIdentityMatrix()) { 6206 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation 6207 : new float[2]; 6208 position[0] = offset.x; 6209 position[1] = offset.y; 6210 child.getMatrix().mapPoints(position); 6211 offset.x = Math.round(position[0]); 6212 offset.y = Math.round(position[1]); 6213 } 6214 offset.x += dx; 6215 offset.y += dy; 6216 } 6217 6218 final int width = mRight - mLeft; 6219 final int height = mBottom - mTop; 6220 6221 boolean rectIsVisible = true; 6222 if (mParent == null || 6223 (mParent instanceof ViewGroup && ((ViewGroup) mParent).getClipChildren())) { 6224 // Clip to bounds. 6225 rectIsVisible = rect.intersect(0, 0, width, height); 6226 } 6227 6228 if ((forceParentCheck || rectIsVisible) 6229 && (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { 6230 // Clip to padding. 6231 rectIsVisible = rect.intersect(mPaddingLeft, mPaddingTop, 6232 width - mPaddingRight, height - mPaddingBottom); 6233 } 6234 6235 if ((forceParentCheck || rectIsVisible) && mClipBounds != null) { 6236 // Clip to clipBounds. 6237 rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right, 6238 mClipBounds.bottom); 6239 } 6240 r.set((int) Math.floor(rect.left), (int) Math.floor(rect.top), 6241 (int) Math.ceil(rect.right), (int) Math.ceil(rect.bottom)); 6242 6243 if ((forceParentCheck || rectIsVisible) && mParent != null) { 6244 if (mParent instanceof ViewGroup) { 6245 rectIsVisible = ((ViewGroup) mParent) 6246 .getChildVisibleRect(this, r, offset, forceParentCheck); 6247 } else { 6248 rectIsVisible = mParent.getChildVisibleRect(this, r, offset); 6249 } 6250 } 6251 return rectIsVisible; 6252 } 6253 6254 @Override layout(int l, int t, int r, int b)6255 public final void layout(int l, int t, int r, int b) { 6256 if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) { 6257 if (mTransition != null) { 6258 mTransition.layoutChange(this); 6259 } 6260 super.layout(l, t, r, b); 6261 } else { 6262 // record the fact that we noop'd it; request layout when transition finishes 6263 mLayoutCalledWhileSuppressed = true; 6264 } 6265 } 6266 6267 @Override onLayout(boolean changed, int l, int t, int r, int b)6268 protected abstract void onLayout(boolean changed, 6269 int l, int t, int r, int b); 6270 6271 /** 6272 * Indicates whether the view group has the ability to animate its children 6273 * after the first layout. 6274 * 6275 * @return true if the children can be animated, false otherwise 6276 */ canAnimate()6277 protected boolean canAnimate() { 6278 return mLayoutAnimationController != null; 6279 } 6280 6281 /** 6282 * Runs the layout animation. Calling this method triggers a relayout of 6283 * this view group. 6284 */ startLayoutAnimation()6285 public void startLayoutAnimation() { 6286 if (mLayoutAnimationController != null) { 6287 mGroupFlags |= FLAG_RUN_ANIMATION; 6288 requestLayout(); 6289 } 6290 } 6291 6292 /** 6293 * Schedules the layout animation to be played after the next layout pass 6294 * of this view group. This can be used to restart the layout animation 6295 * when the content of the view group changes or when the activity is 6296 * paused and resumed. 6297 */ scheduleLayoutAnimation()6298 public void scheduleLayoutAnimation() { 6299 mGroupFlags |= FLAG_RUN_ANIMATION; 6300 } 6301 6302 /** 6303 * Sets the layout animation controller used to animate the group's 6304 * children after the first layout. 6305 * 6306 * @param controller the animation controller 6307 */ setLayoutAnimation(LayoutAnimationController controller)6308 public void setLayoutAnimation(LayoutAnimationController controller) { 6309 mLayoutAnimationController = controller; 6310 if (mLayoutAnimationController != null) { 6311 mGroupFlags |= FLAG_RUN_ANIMATION; 6312 } 6313 } 6314 6315 /** 6316 * Returns the layout animation controller used to animate the group's 6317 * children. 6318 * 6319 * @return the current animation controller 6320 */ 6321 @InspectableProperty getLayoutAnimation()6322 public LayoutAnimationController getLayoutAnimation() { 6323 return mLayoutAnimationController; 6324 } 6325 6326 /** 6327 * Indicates whether the children's drawing cache is used during a layout 6328 * animation. By default, the drawing cache is enabled but this will prevent 6329 * nested layout animations from working. To nest animations, you must disable 6330 * the cache. 6331 * 6332 * @return true if the animation cache is enabled, false otherwise 6333 * 6334 * @see #setAnimationCacheEnabled(boolean) 6335 * @see View#setDrawingCacheEnabled(boolean) 6336 * 6337 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored. 6338 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}. 6339 */ 6340 @Deprecated 6341 @InspectableProperty(name = "animationCache") isAnimationCacheEnabled()6342 public boolean isAnimationCacheEnabled() { 6343 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 6344 } 6345 6346 /** 6347 * Enables or disables the children's drawing cache during a layout animation. 6348 * By default, the drawing cache is enabled but this will prevent nested 6349 * layout animations from working. To nest animations, you must disable the 6350 * cache. 6351 * 6352 * @param enabled true to enable the animation cache, false otherwise 6353 * 6354 * @see #isAnimationCacheEnabled() 6355 * @see View#setDrawingCacheEnabled(boolean) 6356 * 6357 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored. 6358 * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}. 6359 */ 6360 @Deprecated setAnimationCacheEnabled(boolean enabled)6361 public void setAnimationCacheEnabled(boolean enabled) { 6362 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); 6363 } 6364 6365 /** 6366 * Indicates whether this ViewGroup will always try to draw its children using their 6367 * drawing cache. By default this property is enabled. 6368 * 6369 * @return true if the animation cache is enabled, false otherwise 6370 * 6371 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 6372 * @see #setChildrenDrawnWithCacheEnabled(boolean) 6373 * @see View#setDrawingCacheEnabled(boolean) 6374 * 6375 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored. 6376 * Child views may no longer have their caching behavior disabled by parents. 6377 */ 6378 @Deprecated 6379 @InspectableProperty(name = "alwaysDrawnWithCache") isAlwaysDrawnWithCacheEnabled()6380 public boolean isAlwaysDrawnWithCacheEnabled() { 6381 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; 6382 } 6383 6384 /** 6385 * Indicates whether this ViewGroup will always try to draw its children using their 6386 * drawing cache. This property can be set to true when the cache rendering is 6387 * slightly different from the children's normal rendering. Renderings can be different, 6388 * for instance, when the cache's quality is set to low. 6389 * 6390 * When this property is disabled, the ViewGroup will use the drawing cache of its 6391 * children only when asked to. It's usually the task of subclasses to tell ViewGroup 6392 * when to start using the drawing cache and when to stop using it. 6393 * 6394 * @param always true to always draw with the drawing cache, false otherwise 6395 * 6396 * @see #isAlwaysDrawnWithCacheEnabled() 6397 * @see #setChildrenDrawnWithCacheEnabled(boolean) 6398 * @see View#setDrawingCacheEnabled(boolean) 6399 * @see View#setDrawingCacheQuality(int) 6400 * 6401 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored. 6402 * Child views may no longer have their caching behavior disabled by parents. 6403 */ 6404 @Deprecated setAlwaysDrawnWithCacheEnabled(boolean always)6405 public void setAlwaysDrawnWithCacheEnabled(boolean always) { 6406 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); 6407 } 6408 6409 /** 6410 * Indicates whether the ViewGroup is currently drawing its children using 6411 * their drawing cache. 6412 * 6413 * @return true if children should be drawn with their cache, false otherwise 6414 * 6415 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 6416 * @see #setChildrenDrawnWithCacheEnabled(boolean) 6417 * 6418 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored. 6419 * Child views may no longer be forced to cache their rendering state by their parents. 6420 * Use {@link View#setLayerType(int, Paint)} on individual Views instead. 6421 */ 6422 @Deprecated isChildrenDrawnWithCacheEnabled()6423 protected boolean isChildrenDrawnWithCacheEnabled() { 6424 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; 6425 } 6426 6427 /** 6428 * Tells the ViewGroup to draw its children using their drawing cache. This property 6429 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache 6430 * will be used only if it has been enabled. 6431 * 6432 * Subclasses should call this method to start and stop using the drawing cache when 6433 * they perform performance sensitive operations, like scrolling or animating. 6434 * 6435 * @param enabled true if children should be drawn with their cache, false otherwise 6436 * 6437 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 6438 * @see #isChildrenDrawnWithCacheEnabled() 6439 * 6440 * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored. 6441 * Child views may no longer be forced to cache their rendering state by their parents. 6442 * Use {@link View#setLayerType(int, Paint)} on individual Views instead. 6443 */ 6444 @Deprecated setChildrenDrawnWithCacheEnabled(boolean enabled)6445 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { 6446 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); 6447 } 6448 6449 /** 6450 * Indicates whether the ViewGroup is drawing its children in the order defined by 6451 * {@link #getChildDrawingOrder(int, int)}. 6452 * 6453 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, 6454 * false otherwise 6455 * 6456 * @see #setChildrenDrawingOrderEnabled(boolean) 6457 * @see #getChildDrawingOrder(int, int) 6458 */ 6459 @ViewDebug.ExportedProperty(category = "drawing") isChildrenDrawingOrderEnabled()6460 protected boolean isChildrenDrawingOrderEnabled() { 6461 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; 6462 } 6463 6464 /** 6465 * Tells the ViewGroup whether to draw its children in the order defined by the method 6466 * {@link #getChildDrawingOrder(int, int)}. 6467 * <p> 6468 * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)}, 6469 * will override custom child ordering done via this method. 6470 * 6471 * @param enabled true if the order of the children when drawing is determined by 6472 * {@link #getChildDrawingOrder(int, int)}, false otherwise 6473 * 6474 * @see #isChildrenDrawingOrderEnabled() 6475 * @see #getChildDrawingOrder(int, int) 6476 */ setChildrenDrawingOrderEnabled(boolean enabled)6477 protected void setChildrenDrawingOrderEnabled(boolean enabled) { 6478 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); 6479 } 6480 hasBooleanFlag(int flag)6481 private boolean hasBooleanFlag(int flag) { 6482 return (mGroupFlags & flag) == flag; 6483 } 6484 setBooleanFlag(int flag, boolean value)6485 private void setBooleanFlag(int flag, boolean value) { 6486 if (value) { 6487 mGroupFlags |= flag; 6488 } else { 6489 mGroupFlags &= ~flag; 6490 } 6491 } 6492 6493 /** 6494 * Returns an integer indicating what types of drawing caches are kept in memory. 6495 * 6496 * @see #setPersistentDrawingCache(int) 6497 * @see #setAnimationCacheEnabled(boolean) 6498 * 6499 * @return one or a combination of {@link #PERSISTENT_NO_CACHE}, 6500 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 6501 * and {@link #PERSISTENT_ALL_CACHES} 6502 * 6503 * @deprecated The view drawing cache was largely made obsolete with the introduction of 6504 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache 6505 * layers are largely unnecessary and can easily result in a net loss in performance due to the 6506 * cost of creating and updating the layer. In the rare cases where caching layers are useful, 6507 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware 6508 * rendering. For software-rendered snapshots of a small part of the View hierarchy or 6509 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or 6510 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these 6511 * software-rendered usages are discouraged and have compatibility issues with hardware-only 6512 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} 6513 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback 6514 * reports or unit testing the {@link PixelCopy} API is recommended. 6515 */ 6516 @Deprecated 6517 @ViewDebug.ExportedProperty(category = "drawing", mapping = { 6518 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), 6519 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"), 6520 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), 6521 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") 6522 }) 6523 @InspectableProperty(enumMapping = { 6524 @EnumEntry(value = PERSISTENT_NO_CACHE, name = "none"), 6525 @EnumEntry(value = PERSISTENT_ANIMATION_CACHE, name = "animation"), 6526 @EnumEntry(value = PERSISTENT_SCROLLING_CACHE, name = "scrolling"), 6527 @EnumEntry(value = PERSISTENT_ALL_CACHES, name = "all"), 6528 }) getPersistentDrawingCache()6529 public int getPersistentDrawingCache() { 6530 return mPersistentDrawingCache; 6531 } 6532 6533 /** 6534 * Indicates what types of drawing caches should be kept in memory after 6535 * they have been created. 6536 * 6537 * @see #getPersistentDrawingCache() 6538 * @see #setAnimationCacheEnabled(boolean) 6539 * 6540 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE}, 6541 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 6542 * and {@link #PERSISTENT_ALL_CACHES} 6543 * 6544 * @deprecated The view drawing cache was largely made obsolete with the introduction of 6545 * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache 6546 * layers are largely unnecessary and can easily result in a net loss in performance due to the 6547 * cost of creating and updating the layer. In the rare cases where caching layers are useful, 6548 * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware 6549 * rendering. For software-rendered snapshots of a small part of the View hierarchy or 6550 * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or 6551 * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these 6552 * software-rendered usages are discouraged and have compatibility issues with hardware-only 6553 * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} 6554 * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback 6555 * reports or unit testing the {@link PixelCopy} API is recommended. 6556 */ 6557 @Deprecated setPersistentDrawingCache(int drawingCacheToKeep)6558 public void setPersistentDrawingCache(int drawingCacheToKeep) { 6559 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; 6560 } 6561 setLayoutMode(int layoutMode, boolean explicitly)6562 private void setLayoutMode(int layoutMode, boolean explicitly) { 6563 mLayoutMode = layoutMode; 6564 setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly); 6565 } 6566 6567 /** 6568 * Recursively traverse the view hierarchy, resetting the layoutMode of any 6569 * descendants that had inherited a different layoutMode from a previous parent. 6570 * Recursion terminates when a descendant's mode is: 6571 * <ul> 6572 * <li>Undefined</li> 6573 * <li>The same as the root node's</li> 6574 * <li>A mode that had been explicitly set</li> 6575 * <ul/> 6576 * The first two clauses are optimizations. 6577 * @param layoutModeOfRoot 6578 */ 6579 @Override invalidateInheritedLayoutMode(int layoutModeOfRoot)6580 void invalidateInheritedLayoutMode(int layoutModeOfRoot) { 6581 if (mLayoutMode == LAYOUT_MODE_UNDEFINED || 6582 mLayoutMode == layoutModeOfRoot || 6583 hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) { 6584 return; 6585 } 6586 setLayoutMode(LAYOUT_MODE_UNDEFINED, false); 6587 6588 // apply recursively 6589 for (int i = 0, N = getChildCount(); i < N; i++) { 6590 getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot); 6591 } 6592 } 6593 6594 /** 6595 * Returns the basis of alignment during layout operations on this ViewGroup: 6596 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 6597 * <p> 6598 * If no layoutMode was explicitly set, either programmatically or in an XML resource, 6599 * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists, 6600 * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}. 6601 * 6602 * @return the layout mode to use during layout operations 6603 * 6604 * @see #setLayoutMode(int) 6605 */ 6606 @InspectableProperty(enumMapping = { 6607 @EnumEntry(value = LAYOUT_MODE_CLIP_BOUNDS, name = "clipBounds"), 6608 @EnumEntry(value = LAYOUT_MODE_OPTICAL_BOUNDS, name = "opticalBounds") 6609 }) getLayoutMode()6610 public int getLayoutMode() { 6611 if (mLayoutMode == LAYOUT_MODE_UNDEFINED) { 6612 int inheritedLayoutMode = (mParent instanceof ViewGroup) ? 6613 ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT; 6614 setLayoutMode(inheritedLayoutMode, false); 6615 } 6616 return mLayoutMode; 6617 } 6618 6619 /** 6620 * Sets the basis of alignment during the layout of this ViewGroup. 6621 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or 6622 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 6623 * 6624 * @param layoutMode the layout mode to use during layout operations 6625 * 6626 * @see #getLayoutMode() 6627 * @attr ref android.R.styleable#ViewGroup_layoutMode 6628 */ setLayoutMode(int layoutMode)6629 public void setLayoutMode(int layoutMode) { 6630 if (mLayoutMode != layoutMode) { 6631 invalidateInheritedLayoutMode(layoutMode); 6632 setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED); 6633 requestLayout(); 6634 } 6635 } 6636 6637 /** 6638 * Returns a new set of layout parameters based on the supplied attributes set. 6639 * 6640 * @param attrs the attributes to build the layout parameters from 6641 * 6642 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 6643 * of its descendants 6644 */ generateLayoutParams(AttributeSet attrs)6645 public LayoutParams generateLayoutParams(AttributeSet attrs) { 6646 return new LayoutParams(getContext(), attrs); 6647 } 6648 6649 /** 6650 * Returns a safe set of layout parameters based on the supplied layout params. 6651 * When a ViewGroup is passed a View whose layout params do not pass the test of 6652 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method 6653 * is invoked. This method should return a new set of layout params suitable for 6654 * this ViewGroup, possibly by copying the appropriate attributes from the 6655 * specified set of layout params. 6656 * 6657 * @param p The layout parameters to convert into a suitable set of layout parameters 6658 * for this ViewGroup. 6659 * 6660 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 6661 * of its descendants 6662 */ generateLayoutParams(ViewGroup.LayoutParams p)6663 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 6664 return p; 6665 } 6666 6667 /** 6668 * Returns a set of default layout parameters. These parameters are requested 6669 * when the View passed to {@link #addView(View)} has no layout parameters 6670 * already set. If null is returned, an exception is thrown from addView. 6671 * 6672 * @return a set of default layout parameters or null 6673 */ generateDefaultLayoutParams()6674 protected LayoutParams generateDefaultLayoutParams() { 6675 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 6676 } 6677 6678 @Override debug(int depth)6679 protected void debug(int depth) { 6680 super.debug(depth); 6681 String output; 6682 6683 if (mFocused != null) { 6684 output = debugIndent(depth); 6685 output += "mFocused"; 6686 Log.d(VIEW_LOG_TAG, output); 6687 mFocused.debug(depth + 1); 6688 } 6689 if (mDefaultFocus != null) { 6690 output = debugIndent(depth); 6691 output += "mDefaultFocus"; 6692 Log.d(VIEW_LOG_TAG, output); 6693 mDefaultFocus.debug(depth + 1); 6694 } 6695 if (mFocusedInCluster != null) { 6696 output = debugIndent(depth); 6697 output += "mFocusedInCluster"; 6698 Log.d(VIEW_LOG_TAG, output); 6699 mFocusedInCluster.debug(depth + 1); 6700 } 6701 if (mChildrenCount != 0) { 6702 output = debugIndent(depth); 6703 output += "{"; 6704 Log.d(VIEW_LOG_TAG, output); 6705 } 6706 int count = mChildrenCount; 6707 for (int i = 0; i < count; i++) { 6708 View child = mChildren[i]; 6709 child.debug(depth + 1); 6710 } 6711 6712 if (mChildrenCount != 0) { 6713 output = debugIndent(depth); 6714 output += "}"; 6715 Log.d(VIEW_LOG_TAG, output); 6716 } 6717 } 6718 6719 /** 6720 * Returns the position in the group of the specified child view. 6721 * 6722 * @param child the view for which to get the position 6723 * @return a positive integer representing the position of the view in the 6724 * group, or -1 if the view does not exist in the group 6725 */ indexOfChild(View child)6726 public int indexOfChild(View child) { 6727 final int count = mChildrenCount; 6728 final View[] children = mChildren; 6729 for (int i = 0; i < count; i++) { 6730 if (children[i] == child) { 6731 return i; 6732 } 6733 } 6734 return -1; 6735 } 6736 6737 /** 6738 * Returns the number of children in the group. 6739 * 6740 * @return a positive integer representing the number of children in 6741 * the group 6742 */ getChildCount()6743 public int getChildCount() { 6744 return mChildrenCount; 6745 } 6746 6747 /** 6748 * Returns the view at the specified position in the group. 6749 * 6750 * @param index the position at which to get the view from 6751 * @return the view at the specified position or null if the position 6752 * does not exist within the group 6753 */ getChildAt(int index)6754 public View getChildAt(int index) { 6755 if (index < 0 || index >= mChildrenCount) { 6756 return null; 6757 } 6758 return mChildren[index]; 6759 } 6760 6761 /** 6762 * Ask all of the children of this view to measure themselves, taking into 6763 * account both the MeasureSpec requirements for this view and its padding. 6764 * We skip children that are in the GONE state The heavy lifting is done in 6765 * getChildMeasureSpec. 6766 * 6767 * @param widthMeasureSpec The width requirements for this view 6768 * @param heightMeasureSpec The height requirements for this view 6769 */ measureChildren(int widthMeasureSpec, int heightMeasureSpec)6770 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 6771 final int size = mChildrenCount; 6772 final View[] children = mChildren; 6773 for (int i = 0; i < size; ++i) { 6774 final View child = children[i]; 6775 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 6776 measureChild(child, widthMeasureSpec, heightMeasureSpec); 6777 } 6778 } 6779 } 6780 6781 /** 6782 * Ask one of the children of this view to measure itself, taking into 6783 * account both the MeasureSpec requirements for this view and its padding. 6784 * The heavy lifting is done in getChildMeasureSpec. 6785 * 6786 * @param child The child to measure 6787 * @param parentWidthMeasureSpec The width requirements for this view 6788 * @param parentHeightMeasureSpec The height requirements for this view 6789 */ measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)6790 protected void measureChild(View child, int parentWidthMeasureSpec, 6791 int parentHeightMeasureSpec) { 6792 final LayoutParams lp = child.getLayoutParams(); 6793 6794 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 6795 mPaddingLeft + mPaddingRight, lp.width); 6796 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 6797 mPaddingTop + mPaddingBottom, lp.height); 6798 6799 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 6800 } 6801 6802 /** 6803 * Ask one of the children of this view to measure itself, taking into 6804 * account both the MeasureSpec requirements for this view and its padding 6805 * and margins. The child must have MarginLayoutParams The heavy lifting is 6806 * done in getChildMeasureSpec. 6807 * 6808 * @param child The child to measure 6809 * @param parentWidthMeasureSpec The width requirements for this view 6810 * @param widthUsed Extra space that has been used up by the parent 6811 * horizontally (possibly by other children of the parent) 6812 * @param parentHeightMeasureSpec The height requirements for this view 6813 * @param heightUsed Extra space that has been used up by the parent 6814 * vertically (possibly by other children of the parent) 6815 */ measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)6816 protected void measureChildWithMargins(View child, 6817 int parentWidthMeasureSpec, int widthUsed, 6818 int parentHeightMeasureSpec, int heightUsed) { 6819 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 6820 6821 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 6822 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 6823 + widthUsed, lp.width); 6824 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 6825 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 6826 + heightUsed, lp.height); 6827 6828 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 6829 } 6830 6831 /** 6832 * Does the hard part of measureChildren: figuring out the MeasureSpec to 6833 * pass to a particular child. This method figures out the right MeasureSpec 6834 * for one dimension (height or width) of one child view. 6835 * 6836 * The goal is to combine information from our MeasureSpec with the 6837 * LayoutParams of the child to get the best possible results. For example, 6838 * if the this view knows its size (because its MeasureSpec has a mode of 6839 * EXACTLY), and the child has indicated in its LayoutParams that it wants 6840 * to be the same size as the parent, the parent should ask the child to 6841 * layout given an exact size. 6842 * 6843 * @param spec The requirements for this view 6844 * @param padding The padding of this view for the current dimension and 6845 * margins, if applicable 6846 * @param childDimension How big the child wants to be in the current 6847 * dimension 6848 * @return a MeasureSpec integer for the child 6849 */ getChildMeasureSpec(int spec, int padding, int childDimension)6850 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 6851 int specMode = MeasureSpec.getMode(spec); 6852 int specSize = MeasureSpec.getSize(spec); 6853 6854 int size = Math.max(0, specSize - padding); 6855 6856 int resultSize = 0; 6857 int resultMode = 0; 6858 6859 switch (specMode) { 6860 // Parent has imposed an exact size on us 6861 case MeasureSpec.EXACTLY: 6862 if (childDimension >= 0) { 6863 resultSize = childDimension; 6864 resultMode = MeasureSpec.EXACTLY; 6865 } else if (childDimension == LayoutParams.MATCH_PARENT) { 6866 // Child wants to be our size. So be it. 6867 resultSize = size; 6868 resultMode = MeasureSpec.EXACTLY; 6869 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 6870 // Child wants to determine its own size. It can't be 6871 // bigger than us. 6872 resultSize = size; 6873 resultMode = MeasureSpec.AT_MOST; 6874 } 6875 break; 6876 6877 // Parent has imposed a maximum size on us 6878 case MeasureSpec.AT_MOST: 6879 if (childDimension >= 0) { 6880 // Child wants a specific size... so be it 6881 resultSize = childDimension; 6882 resultMode = MeasureSpec.EXACTLY; 6883 } else if (childDimension == LayoutParams.MATCH_PARENT) { 6884 // Child wants to be our size, but our size is not fixed. 6885 // Constrain child to not be bigger than us. 6886 resultSize = size; 6887 resultMode = MeasureSpec.AT_MOST; 6888 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 6889 // Child wants to determine its own size. It can't be 6890 // bigger than us. 6891 resultSize = size; 6892 resultMode = MeasureSpec.AT_MOST; 6893 } 6894 break; 6895 6896 // Parent asked to see how big we want to be 6897 case MeasureSpec.UNSPECIFIED: 6898 if (childDimension >= 0) { 6899 // Child wants a specific size... let him have it 6900 resultSize = childDimension; 6901 resultMode = MeasureSpec.EXACTLY; 6902 } else if (childDimension == LayoutParams.MATCH_PARENT) { 6903 // Child wants to be our size... find out how big it should 6904 // be 6905 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; 6906 resultMode = MeasureSpec.UNSPECIFIED; 6907 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 6908 // Child wants to determine its own size.... find out how 6909 // big it should be 6910 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; 6911 resultMode = MeasureSpec.UNSPECIFIED; 6912 } 6913 break; 6914 } 6915 //noinspection ResourceType 6916 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 6917 } 6918 6919 6920 /** 6921 * Removes any pending animations for views that have been removed. Call 6922 * this if you don't want animations for exiting views to stack up. 6923 */ clearDisappearingChildren()6924 public void clearDisappearingChildren() { 6925 final ArrayList<View> disappearingChildren = mDisappearingChildren; 6926 if (disappearingChildren != null) { 6927 final int count = disappearingChildren.size(); 6928 for (int i = 0; i < count; i++) { 6929 final View view = disappearingChildren.get(i); 6930 if (view.mAttachInfo != null) { 6931 view.dispatchDetachedFromWindow(); 6932 } 6933 view.clearAnimation(); 6934 } 6935 disappearingChildren.clear(); 6936 invalidate(); 6937 } 6938 } 6939 6940 /** 6941 * Add a view which is removed from mChildren but still needs animation 6942 * 6943 * @param v View to add 6944 */ addDisappearingView(View v)6945 private void addDisappearingView(View v) { 6946 ArrayList<View> disappearingChildren = mDisappearingChildren; 6947 6948 if (disappearingChildren == null) { 6949 disappearingChildren = mDisappearingChildren = new ArrayList<View>(); 6950 } 6951 6952 disappearingChildren.add(v); 6953 } 6954 6955 /** 6956 * Cleanup a view when its animation is done. This may mean removing it from 6957 * the list of disappearing views. 6958 * 6959 * @param view The view whose animation has finished 6960 * @param animation The animation, cannot be null 6961 */ finishAnimatingView(final View view, Animation animation)6962 void finishAnimatingView(final View view, Animation animation) { 6963 final ArrayList<View> disappearingChildren = mDisappearingChildren; 6964 if (disappearingChildren != null) { 6965 if (disappearingChildren.contains(view)) { 6966 disappearingChildren.remove(view); 6967 6968 if (view.mAttachInfo != null) { 6969 view.dispatchDetachedFromWindow(); 6970 } 6971 6972 view.clearAnimation(); 6973 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 6974 } 6975 } 6976 6977 if (animation != null && !animation.getFillAfter()) { 6978 view.clearAnimation(); 6979 } 6980 6981 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) { 6982 view.onAnimationEnd(); 6983 // Should be performed by onAnimationEnd() but this avoid an infinite loop, 6984 // so we'd rather be safe than sorry 6985 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED; 6986 // Draw one more frame after the animation is done 6987 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 6988 } 6989 } 6990 6991 /** 6992 * Utility function called by View during invalidation to determine whether a view that 6993 * is invisible or gone should still be invalidated because it is being transitioned (and 6994 * therefore still needs to be drawn). 6995 */ isViewTransitioning(View view)6996 boolean isViewTransitioning(View view) { 6997 return (mTransitioningViews != null && mTransitioningViews.contains(view)); 6998 } 6999 7000 /** 7001 * This method tells the ViewGroup that the given View object, which should have this 7002 * ViewGroup as its parent, 7003 * should be kept around (re-displayed when the ViewGroup draws its children) even if it 7004 * is removed from its parent. This allows animations, such as those used by 7005 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate 7006 * the removal of views. A call to this method should always be accompanied by a later call 7007 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished, 7008 * so that the View finally gets removed. 7009 * 7010 * @param view The View object to be kept visible even if it gets removed from its parent. 7011 */ startViewTransition(View view)7012 public void startViewTransition(View view) { 7013 if (view.mParent == this) { 7014 if (mTransitioningViews == null) { 7015 mTransitioningViews = new ArrayList<View>(); 7016 } 7017 mTransitioningViews.add(view); 7018 } 7019 } 7020 7021 /** 7022 * This method should always be called following an earlier call to 7023 * {@link #startViewTransition(View)}. The given View is finally removed from its parent 7024 * and will no longer be displayed. Note that this method does not perform the functionality 7025 * of removing a view from its parent; it just discontinues the display of a View that 7026 * has previously been removed. 7027 * 7028 * @return view The View object that has been removed but is being kept around in the visible 7029 * hierarchy by an earlier call to {@link #startViewTransition(View)}. 7030 */ endViewTransition(View view)7031 public void endViewTransition(View view) { 7032 if (mTransitioningViews != null) { 7033 mTransitioningViews.remove(view); 7034 final ArrayList<View> disappearingChildren = mDisappearingChildren; 7035 if (disappearingChildren != null && disappearingChildren.contains(view)) { 7036 disappearingChildren.remove(view); 7037 if (mVisibilityChangingChildren != null && 7038 mVisibilityChangingChildren.contains(view)) { 7039 mVisibilityChangingChildren.remove(view); 7040 } else { 7041 if (view.mAttachInfo != null) { 7042 view.dispatchDetachedFromWindow(); 7043 } 7044 if (view.mParent != null) { 7045 view.mParent = null; 7046 } 7047 } 7048 invalidate(); 7049 } 7050 } 7051 } 7052 7053 private LayoutTransition.TransitionListener mLayoutTransitionListener = 7054 new LayoutTransition.TransitionListener() { 7055 @Override 7056 public void startTransition(LayoutTransition transition, ViewGroup container, 7057 View view, int transitionType) { 7058 // We only care about disappearing items, since we need special logic to keep 7059 // those items visible after they've been 'removed' 7060 if (transitionType == LayoutTransition.DISAPPEARING) { 7061 startViewTransition(view); 7062 } 7063 } 7064 7065 @Override 7066 public void endTransition(LayoutTransition transition, ViewGroup container, 7067 View view, int transitionType) { 7068 if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) { 7069 requestLayout(); 7070 mLayoutCalledWhileSuppressed = false; 7071 } 7072 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) { 7073 endViewTransition(view); 7074 } 7075 } 7076 }; 7077 7078 /** 7079 * Tells this ViewGroup to suppress all layout() calls until layout 7080 * suppression is disabled with a later call to suppressLayout(false). 7081 * When layout suppression is disabled, a requestLayout() call is sent 7082 * if layout() was attempted while layout was being suppressed. 7083 */ suppressLayout(boolean suppress)7084 public void suppressLayout(boolean suppress) { 7085 mSuppressLayout = suppress; 7086 if (!suppress) { 7087 if (mLayoutCalledWhileSuppressed) { 7088 requestLayout(); 7089 mLayoutCalledWhileSuppressed = false; 7090 } 7091 } 7092 } 7093 7094 /** 7095 * Returns whether layout calls on this container are currently being 7096 * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}. 7097 * 7098 * @return true if layout calls are currently suppressed, false otherwise. 7099 */ isLayoutSuppressed()7100 public boolean isLayoutSuppressed() { 7101 return mSuppressLayout; 7102 } 7103 7104 @Override gatherTransparentRegion(Region region)7105 public boolean gatherTransparentRegion(Region region) { 7106 // If no transparent regions requested, we are always opaque. 7107 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0; 7108 if (meOpaque && region == null) { 7109 // The caller doesn't care about the region, so stop now. 7110 return true; 7111 } 7112 super.gatherTransparentRegion(region); 7113 // Instead of naively traversing the view tree, we have to traverse according to the Z 7114 // order here. We need to go with the same order as dispatchDraw(). 7115 // One example is that after surfaceView punch a hole, we will still allow other views drawn 7116 // on top of that hole. In this case, those other views should be able to cut the 7117 // transparent region into smaller area. 7118 final int childrenCount = mChildrenCount; 7119 boolean noneOfTheChildrenAreTransparent = true; 7120 if (childrenCount > 0) { 7121 final ArrayList<View> preorderedList = buildOrderedChildList(); 7122 final boolean customOrder = preorderedList == null 7123 && isChildrenDrawingOrderEnabled(); 7124 final View[] children = mChildren; 7125 for (int i = 0; i < childrenCount; i++) { 7126 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 7127 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); 7128 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 7129 if (!child.gatherTransparentRegion(region)) { 7130 noneOfTheChildrenAreTransparent = false; 7131 } 7132 } 7133 } 7134 if (preorderedList != null) preorderedList.clear(); 7135 } 7136 return meOpaque || noneOfTheChildrenAreTransparent; 7137 } 7138 7139 @Override requestTransparentRegion(View child)7140 public void requestTransparentRegion(View child) { 7141 if (child != null) { 7142 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; 7143 if (mParent != null) { 7144 mParent.requestTransparentRegion(this); 7145 } 7146 } 7147 } 7148 7149 /** 7150 * @hide 7151 */ 7152 @Override subtractObscuredTouchableRegion(Region touchableRegion, View view)7153 public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { 7154 final int childrenCount = mChildrenCount; 7155 final ArrayList<View> preorderedList = buildTouchDispatchChildList(); 7156 final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); 7157 final View[] children = mChildren; 7158 for (int i = childrenCount - 1; i >= 0; i--) { 7159 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 7160 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); 7161 if (child == view) { 7162 // We've reached the target view. 7163 break; 7164 } 7165 if (!child.canReceivePointerEvents()) { 7166 // This child cannot be touched. Skip it. 7167 continue; 7168 } 7169 applyOpToRegionByBounds(touchableRegion, child, Region.Op.DIFFERENCE); 7170 } 7171 7172 // The touchable region should not exceed the bounds of its container. 7173 applyOpToRegionByBounds(touchableRegion, this, Region.Op.INTERSECT); 7174 7175 final ViewParent parent = getParent(); 7176 if (parent != null) { 7177 parent.subtractObscuredTouchableRegion(touchableRegion, this); 7178 } 7179 } 7180 applyOpToRegionByBounds(Region region, View view, Region.Op op)7181 private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) { 7182 final int[] locationInWindow = new int[2]; 7183 view.getLocationInWindow(locationInWindow); 7184 final int x = locationInWindow[0]; 7185 final int y = locationInWindow[1]; 7186 region.op(x, y, x + view.getWidth(), y + view.getHeight(), op); 7187 } 7188 7189 @Override dispatchApplyWindowInsets(WindowInsets insets)7190 public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { 7191 insets = super.dispatchApplyWindowInsets(insets); 7192 if (View.sBrokenInsetsDispatch) { 7193 return brokenDispatchApplyWindowInsets(insets); 7194 } else { 7195 return newDispatchApplyWindowInsets(insets); 7196 } 7197 } 7198 brokenDispatchApplyWindowInsets(WindowInsets insets)7199 private WindowInsets brokenDispatchApplyWindowInsets(WindowInsets insets) { 7200 if (!insets.isConsumed()) { 7201 final int count = getChildCount(); 7202 for (int i = 0; i < count; i++) { 7203 insets = getChildAt(i).dispatchApplyWindowInsets(insets); 7204 if (insets.isConsumed()) { 7205 break; 7206 } 7207 } 7208 } 7209 return insets; 7210 } 7211 newDispatchApplyWindowInsets(WindowInsets insets)7212 private WindowInsets newDispatchApplyWindowInsets(WindowInsets insets) { 7213 final int count = getChildCount(); 7214 for (int i = 0; i < count; i++) { 7215 getChildAt(i).dispatchApplyWindowInsets(insets); 7216 } 7217 return insets; 7218 } 7219 7220 @Override dispatchWindowInsetsAnimationStarted(InsetsAnimation animation)7221 void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) { 7222 super.dispatchWindowInsetsAnimationStarted(animation); 7223 final int count = getChildCount(); 7224 for (int i = 0; i < count; i++) { 7225 getChildAt(i).dispatchWindowInsetsAnimationStarted(animation); 7226 } 7227 } 7228 7229 @Override dispatchWindowInsetsAnimationProgress(WindowInsets insets)7230 WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) { 7231 insets = super.dispatchWindowInsetsAnimationProgress(insets); 7232 final int count = getChildCount(); 7233 for (int i = 0; i < count; i++) { 7234 getChildAt(i).dispatchWindowInsetsAnimationProgress(insets); 7235 } 7236 return insets; 7237 } 7238 7239 @Override dispatchWindowInsetsAnimationFinished(InsetsAnimation animation)7240 void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) { 7241 super.dispatchWindowInsetsAnimationFinished(animation); 7242 final int count = getChildCount(); 7243 for (int i = 0; i < count; i++) { 7244 getChildAt(i).dispatchWindowInsetsAnimationFinished(animation); 7245 } 7246 } 7247 7248 /** 7249 * Returns the animation listener to which layout animation events are 7250 * sent. 7251 * 7252 * @return an {@link android.view.animation.Animation.AnimationListener} 7253 */ getLayoutAnimationListener()7254 public Animation.AnimationListener getLayoutAnimationListener() { 7255 return mAnimationListener; 7256 } 7257 7258 @Override drawableStateChanged()7259 protected void drawableStateChanged() { 7260 super.drawableStateChanged(); 7261 7262 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { 7263 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 7264 throw new IllegalStateException("addStateFromChildren cannot be enabled if a" 7265 + " child has duplicateParentState set to true"); 7266 } 7267 7268 final View[] children = mChildren; 7269 final int count = mChildrenCount; 7270 7271 for (int i = 0; i < count; i++) { 7272 final View child = children[i]; 7273 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { 7274 child.refreshDrawableState(); 7275 } 7276 } 7277 } 7278 } 7279 7280 @Override jumpDrawablesToCurrentState()7281 public void jumpDrawablesToCurrentState() { 7282 super.jumpDrawablesToCurrentState(); 7283 final View[] children = mChildren; 7284 final int count = mChildrenCount; 7285 for (int i = 0; i < count; i++) { 7286 children[i].jumpDrawablesToCurrentState(); 7287 } 7288 } 7289 7290 @Override onCreateDrawableState(int extraSpace)7291 protected int[] onCreateDrawableState(int extraSpace) { 7292 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { 7293 return super.onCreateDrawableState(extraSpace); 7294 } 7295 7296 int need = 0; 7297 int n = getChildCount(); 7298 for (int i = 0; i < n; i++) { 7299 int[] childState = getChildAt(i).getDrawableState(); 7300 7301 if (childState != null) { 7302 need += childState.length; 7303 } 7304 } 7305 7306 int[] state = super.onCreateDrawableState(extraSpace + need); 7307 7308 for (int i = 0; i < n; i++) { 7309 int[] childState = getChildAt(i).getDrawableState(); 7310 7311 if (childState != null) { 7312 state = mergeDrawableStates(state, childState); 7313 } 7314 } 7315 7316 return state; 7317 } 7318 7319 /** 7320 * Sets whether this ViewGroup's drawable states also include 7321 * its children's drawable states. This is used, for example, to 7322 * make a group appear to be focused when its child EditText or button 7323 * is focused. 7324 */ setAddStatesFromChildren(boolean addsStates)7325 public void setAddStatesFromChildren(boolean addsStates) { 7326 if (addsStates) { 7327 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN; 7328 } else { 7329 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN; 7330 } 7331 7332 refreshDrawableState(); 7333 } 7334 7335 /** 7336 * Returns whether this ViewGroup's drawable states also include 7337 * its children's drawable states. This is used, for example, to 7338 * make a group appear to be focused when its child EditText or button 7339 * is focused. 7340 */ 7341 @InspectableProperty addStatesFromChildren()7342 public boolean addStatesFromChildren() { 7343 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0; 7344 } 7345 7346 /** 7347 * If {@link #addStatesFromChildren} is true, refreshes this group's 7348 * drawable state (to include the states from its children). 7349 */ 7350 @Override childDrawableStateChanged(View child)7351 public void childDrawableStateChanged(View child) { 7352 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 7353 refreshDrawableState(); 7354 } 7355 } 7356 7357 /** 7358 * Specifies the animation listener to which layout animation events must 7359 * be sent. Only 7360 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)} 7361 * and 7362 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)} 7363 * are invoked. 7364 * 7365 * @param animationListener the layout animation listener 7366 */ setLayoutAnimationListener(Animation.AnimationListener animationListener)7367 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { 7368 mAnimationListener = animationListener; 7369 } 7370 7371 /** 7372 * This method is called by LayoutTransition when there are 'changing' animations that need 7373 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who 7374 * starts all pending transitions prior to the drawing phase in the current traversal. 7375 * 7376 * @param transition The LayoutTransition to be started on the next traversal. 7377 * 7378 * @hide 7379 */ requestTransitionStart(LayoutTransition transition)7380 public void requestTransitionStart(LayoutTransition transition) { 7381 ViewRootImpl viewAncestor = getViewRootImpl(); 7382 if (viewAncestor != null) { 7383 viewAncestor.requestTransitionStart(transition); 7384 } 7385 } 7386 7387 /** 7388 * @hide 7389 */ 7390 @Override resolveRtlPropertiesIfNeeded()7391 public boolean resolveRtlPropertiesIfNeeded() { 7392 final boolean result = super.resolveRtlPropertiesIfNeeded(); 7393 // We dont need to resolve the children RTL properties if nothing has changed for the parent 7394 if (result) { 7395 int count = getChildCount(); 7396 for (int i = 0; i < count; i++) { 7397 final View child = getChildAt(i); 7398 if (child.isLayoutDirectionInherited()) { 7399 child.resolveRtlPropertiesIfNeeded(); 7400 } 7401 } 7402 } 7403 return result; 7404 } 7405 7406 /** 7407 * @hide 7408 */ 7409 @Override resolveLayoutDirection()7410 public boolean resolveLayoutDirection() { 7411 final boolean result = super.resolveLayoutDirection(); 7412 if (result) { 7413 int count = getChildCount(); 7414 for (int i = 0; i < count; i++) { 7415 final View child = getChildAt(i); 7416 if (child.isLayoutDirectionInherited()) { 7417 child.resolveLayoutDirection(); 7418 } 7419 } 7420 } 7421 return result; 7422 } 7423 7424 /** 7425 * @hide 7426 */ 7427 @Override resolveTextDirection()7428 public boolean resolveTextDirection() { 7429 final boolean result = super.resolveTextDirection(); 7430 if (result) { 7431 int count = getChildCount(); 7432 for (int i = 0; i < count; i++) { 7433 final View child = getChildAt(i); 7434 if (child.isTextDirectionInherited()) { 7435 child.resolveTextDirection(); 7436 } 7437 } 7438 } 7439 return result; 7440 } 7441 7442 /** 7443 * @hide 7444 */ 7445 @Override resolveTextAlignment()7446 public boolean resolveTextAlignment() { 7447 final boolean result = super.resolveTextAlignment(); 7448 if (result) { 7449 int count = getChildCount(); 7450 for (int i = 0; i < count; i++) { 7451 final View child = getChildAt(i); 7452 if (child.isTextAlignmentInherited()) { 7453 child.resolveTextAlignment(); 7454 } 7455 } 7456 } 7457 return result; 7458 } 7459 7460 /** 7461 * @hide 7462 */ 7463 @Override 7464 @UnsupportedAppUsage resolvePadding()7465 public void resolvePadding() { 7466 super.resolvePadding(); 7467 int count = getChildCount(); 7468 for (int i = 0; i < count; i++) { 7469 final View child = getChildAt(i); 7470 if (child.isLayoutDirectionInherited() && !child.isPaddingResolved()) { 7471 child.resolvePadding(); 7472 } 7473 } 7474 } 7475 7476 /** 7477 * @hide 7478 */ 7479 @Override resolveDrawables()7480 protected void resolveDrawables() { 7481 super.resolveDrawables(); 7482 int count = getChildCount(); 7483 for (int i = 0; i < count; i++) { 7484 final View child = getChildAt(i); 7485 if (child.isLayoutDirectionInherited() && !child.areDrawablesResolved()) { 7486 child.resolveDrawables(); 7487 } 7488 } 7489 } 7490 7491 /** 7492 * @hide 7493 */ 7494 @Override resolveLayoutParams()7495 public void resolveLayoutParams() { 7496 super.resolveLayoutParams(); 7497 int count = getChildCount(); 7498 for (int i = 0; i < count; i++) { 7499 final View child = getChildAt(i); 7500 child.resolveLayoutParams(); 7501 } 7502 } 7503 7504 /** 7505 * @hide 7506 */ 7507 @TestApi 7508 @Override resetResolvedLayoutDirection()7509 public void resetResolvedLayoutDirection() { 7510 super.resetResolvedLayoutDirection(); 7511 7512 int count = getChildCount(); 7513 for (int i = 0; i < count; i++) { 7514 final View child = getChildAt(i); 7515 if (child.isLayoutDirectionInherited()) { 7516 child.resetResolvedLayoutDirection(); 7517 } 7518 } 7519 } 7520 7521 /** 7522 * @hide 7523 */ 7524 @TestApi 7525 @Override resetResolvedTextDirection()7526 public void resetResolvedTextDirection() { 7527 super.resetResolvedTextDirection(); 7528 7529 int count = getChildCount(); 7530 for (int i = 0; i < count; i++) { 7531 final View child = getChildAt(i); 7532 if (child.isTextDirectionInherited()) { 7533 child.resetResolvedTextDirection(); 7534 } 7535 } 7536 } 7537 7538 /** 7539 * @hide 7540 */ 7541 @TestApi 7542 @Override resetResolvedTextAlignment()7543 public void resetResolvedTextAlignment() { 7544 super.resetResolvedTextAlignment(); 7545 7546 int count = getChildCount(); 7547 for (int i = 0; i < count; i++) { 7548 final View child = getChildAt(i); 7549 if (child.isTextAlignmentInherited()) { 7550 child.resetResolvedTextAlignment(); 7551 } 7552 } 7553 } 7554 7555 /** 7556 * @hide 7557 */ 7558 @TestApi 7559 @Override resetResolvedPadding()7560 public void resetResolvedPadding() { 7561 super.resetResolvedPadding(); 7562 7563 int count = getChildCount(); 7564 for (int i = 0; i < count; i++) { 7565 final View child = getChildAt(i); 7566 if (child.isLayoutDirectionInherited()) { 7567 child.resetResolvedPadding(); 7568 } 7569 } 7570 } 7571 7572 /** 7573 * @hide 7574 */ 7575 @TestApi 7576 @Override resetResolvedDrawables()7577 protected void resetResolvedDrawables() { 7578 super.resetResolvedDrawables(); 7579 7580 int count = getChildCount(); 7581 for (int i = 0; i < count; i++) { 7582 final View child = getChildAt(i); 7583 if (child.isLayoutDirectionInherited()) { 7584 child.resetResolvedDrawables(); 7585 } 7586 } 7587 } 7588 7589 /** 7590 * Return true if the pressed state should be delayed for children or descendants of this 7591 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List. 7592 * This prevents the pressed state from appearing when the user is actually trying to scroll 7593 * the content. 7594 * 7595 * The default implementation returns true for compatibility reasons. Subclasses that do 7596 * not scroll should generally override this method and return false. 7597 */ shouldDelayChildPressedState()7598 public boolean shouldDelayChildPressedState() { 7599 return true; 7600 } 7601 7602 /** 7603 * @inheritDoc 7604 */ 7605 @Override onStartNestedScroll(View child, View target, int nestedScrollAxes)7606 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 7607 return false; 7608 } 7609 7610 /** 7611 * @inheritDoc 7612 */ 7613 @Override onNestedScrollAccepted(View child, View target, int axes)7614 public void onNestedScrollAccepted(View child, View target, int axes) { 7615 mNestedScrollAxes = axes; 7616 } 7617 7618 /** 7619 * @inheritDoc 7620 * 7621 * <p>The default implementation of onStopNestedScroll calls 7622 * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p> 7623 */ 7624 @Override onStopNestedScroll(View child)7625 public void onStopNestedScroll(View child) { 7626 // Stop any recursive nested scrolling. 7627 stopNestedScroll(); 7628 mNestedScrollAxes = 0; 7629 } 7630 7631 /** 7632 * @inheritDoc 7633 */ 7634 @Override onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)7635 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, 7636 int dxUnconsumed, int dyUnconsumed) { 7637 // Re-dispatch up the tree by default 7638 dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null); 7639 } 7640 7641 /** 7642 * @inheritDoc 7643 */ 7644 @Override onNestedPreScroll(View target, int dx, int dy, int[] consumed)7645 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 7646 // Re-dispatch up the tree by default 7647 dispatchNestedPreScroll(dx, dy, consumed, null); 7648 } 7649 7650 /** 7651 * @inheritDoc 7652 */ 7653 @Override onNestedFling(View target, float velocityX, float velocityY, boolean consumed)7654 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 7655 // Re-dispatch up the tree by default 7656 return dispatchNestedFling(velocityX, velocityY, consumed); 7657 } 7658 7659 /** 7660 * @inheritDoc 7661 */ 7662 @Override onNestedPreFling(View target, float velocityX, float velocityY)7663 public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 7664 // Re-dispatch up the tree by default 7665 return dispatchNestedPreFling(velocityX, velocityY); 7666 } 7667 7668 /** 7669 * Return the current axes of nested scrolling for this ViewGroup. 7670 * 7671 * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently 7672 * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p> 7673 * 7674 * @return Flags indicating the current axes of nested scrolling 7675 * @see #SCROLL_AXIS_HORIZONTAL 7676 * @see #SCROLL_AXIS_VERTICAL 7677 * @see #SCROLL_AXIS_NONE 7678 */ getNestedScrollAxes()7679 public int getNestedScrollAxes() { 7680 return mNestedScrollAxes; 7681 } 7682 7683 /** @hide */ onSetLayoutParams(View child, LayoutParams layoutParams)7684 protected void onSetLayoutParams(View child, LayoutParams layoutParams) { 7685 requestLayout(); 7686 } 7687 7688 /** @hide */ 7689 @Override captureTransitioningViews(List<View> transitioningViews)7690 public void captureTransitioningViews(List<View> transitioningViews) { 7691 if (getVisibility() != View.VISIBLE) { 7692 return; 7693 } 7694 if (isTransitionGroup()) { 7695 transitioningViews.add(this); 7696 } else { 7697 int count = getChildCount(); 7698 for (int i = 0; i < count; i++) { 7699 View child = getChildAt(i); 7700 child.captureTransitioningViews(transitioningViews); 7701 } 7702 } 7703 } 7704 7705 /** @hide */ 7706 @Override findNamedViews(Map<String, View> namedElements)7707 public void findNamedViews(Map<String, View> namedElements) { 7708 if (getVisibility() != VISIBLE && mGhostView == null) { 7709 return; 7710 } 7711 super.findNamedViews(namedElements); 7712 int count = getChildCount(); 7713 for (int i = 0; i < count; i++) { 7714 View child = getChildAt(i); 7715 child.findNamedViews(namedElements); 7716 } 7717 } 7718 7719 @Override hasUnhandledKeyListener()7720 boolean hasUnhandledKeyListener() { 7721 return (mChildUnhandledKeyListeners > 0) || super.hasUnhandledKeyListener(); 7722 } 7723 incrementChildUnhandledKeyListeners()7724 void incrementChildUnhandledKeyListeners() { 7725 mChildUnhandledKeyListeners += 1; 7726 if (mChildUnhandledKeyListeners == 1) { 7727 if (mParent instanceof ViewGroup) { 7728 ((ViewGroup) mParent).incrementChildUnhandledKeyListeners(); 7729 } 7730 } 7731 } 7732 decrementChildUnhandledKeyListeners()7733 void decrementChildUnhandledKeyListeners() { 7734 mChildUnhandledKeyListeners -= 1; 7735 if (mChildUnhandledKeyListeners == 0) { 7736 if (mParent instanceof ViewGroup) { 7737 ((ViewGroup) mParent).decrementChildUnhandledKeyListeners(); 7738 } 7739 } 7740 } 7741 7742 @Override dispatchUnhandledKeyEvent(KeyEvent evt)7743 View dispatchUnhandledKeyEvent(KeyEvent evt) { 7744 if (!hasUnhandledKeyListener()) { 7745 return null; 7746 } 7747 ArrayList<View> orderedViews = buildOrderedChildList(); 7748 if (orderedViews != null) { 7749 try { 7750 for (int i = orderedViews.size() - 1; i >= 0; --i) { 7751 View v = orderedViews.get(i); 7752 View consumer = v.dispatchUnhandledKeyEvent(evt); 7753 if (consumer != null) { 7754 return consumer; 7755 } 7756 } 7757 } finally { 7758 orderedViews.clear(); 7759 } 7760 } else { 7761 for (int i = getChildCount() - 1; i >= 0; --i) { 7762 View v = getChildAt(i); 7763 View consumer = v.dispatchUnhandledKeyEvent(evt); 7764 if (consumer != null) { 7765 return consumer; 7766 } 7767 } 7768 } 7769 if (onUnhandledKeyEvent(evt)) { 7770 return this; 7771 } 7772 return null; 7773 } 7774 7775 /** 7776 * LayoutParams are used by views to tell their parents how they want to be 7777 * laid out. See 7778 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 7779 * for a list of all child view attributes that this class supports. 7780 * 7781 * <p> 7782 * The base LayoutParams class just describes how big the view wants to be 7783 * for both width and height. For each dimension, it can specify one of: 7784 * <ul> 7785 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which 7786 * means that the view wants to be as big as its parent (minus padding) 7787 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 7788 * to enclose its content (plus padding) 7789 * <li> an exact number 7790 * </ul> 7791 * There are subclasses of LayoutParams for different subclasses of 7792 * ViewGroup. For example, AbsoluteLayout has its own subclass of 7793 * LayoutParams which adds an X and Y value.</p> 7794 * 7795 * <div class="special reference"> 7796 * <h3>Developer Guides</h3> 7797 * <p>For more information about creating user interface layouts, read the 7798 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer 7799 * guide.</p></div> 7800 * 7801 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 7802 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 7803 */ 7804 public static class LayoutParams { 7805 /** 7806 * Special value for the height or width requested by a View. 7807 * FILL_PARENT means that the view wants to be as big as its parent, 7808 * minus the parent's padding, if any. This value is deprecated 7809 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 7810 */ 7811 @SuppressWarnings({"UnusedDeclaration"}) 7812 @Deprecated 7813 public static final int FILL_PARENT = -1; 7814 7815 /** 7816 * Special value for the height or width requested by a View. 7817 * MATCH_PARENT means that the view wants to be as big as its parent, 7818 * minus the parent's padding, if any. Introduced in API Level 8. 7819 */ 7820 public static final int MATCH_PARENT = -1; 7821 7822 /** 7823 * Special value for the height or width requested by a View. 7824 * WRAP_CONTENT means that the view wants to be just large enough to fit 7825 * its own internal content, taking its own padding into account. 7826 */ 7827 public static final int WRAP_CONTENT = -2; 7828 7829 /** 7830 * Information about how wide the view wants to be. Can be one of the 7831 * constants FILL_PARENT (replaced by MATCH_PARENT 7832 * in API Level 8) or WRAP_CONTENT, or an exact size. 7833 */ 7834 @ViewDebug.ExportedProperty(category = "layout", mapping = { 7835 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 7836 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 7837 }) 7838 @InspectableProperty(name = "layout_width", enumMapping = { 7839 @EnumEntry(name = "match_parent", value = MATCH_PARENT), 7840 @EnumEntry(name = "wrap_content", value = WRAP_CONTENT) 7841 }) 7842 public int width; 7843 7844 /** 7845 * Information about how tall the view wants to be. Can be one of the 7846 * constants FILL_PARENT (replaced by MATCH_PARENT 7847 * in API Level 8) or WRAP_CONTENT, or an exact size. 7848 */ 7849 @ViewDebug.ExportedProperty(category = "layout", mapping = { 7850 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 7851 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 7852 }) 7853 @InspectableProperty(name = "layout_height", enumMapping = { 7854 @EnumEntry(name = "match_parent", value = MATCH_PARENT), 7855 @EnumEntry(name = "wrap_content", value = WRAP_CONTENT) 7856 }) 7857 public int height; 7858 7859 /** 7860 * Used to animate layouts. 7861 */ 7862 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 7863 7864 /** 7865 * Creates a new set of layout parameters. The values are extracted from 7866 * the supplied attributes set and context. The XML attributes mapped 7867 * to this set of layout parameters are: 7868 * 7869 * <ul> 7870 * <li><code>layout_width</code>: the width, either an exact value, 7871 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 7872 * {@link #MATCH_PARENT} in API Level 8)</li> 7873 * <li><code>layout_height</code>: the height, either an exact value, 7874 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 7875 * {@link #MATCH_PARENT} in API Level 8)</li> 7876 * </ul> 7877 * 7878 * @param c the application environment 7879 * @param attrs the set of attributes from which to extract the layout 7880 * parameters' values 7881 */ LayoutParams(Context c, AttributeSet attrs)7882 public LayoutParams(Context c, AttributeSet attrs) { 7883 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 7884 setBaseAttributes(a, 7885 R.styleable.ViewGroup_Layout_layout_width, 7886 R.styleable.ViewGroup_Layout_layout_height); 7887 a.recycle(); 7888 } 7889 7890 /** 7891 * Creates a new set of layout parameters with the specified width 7892 * and height. 7893 * 7894 * @param width the width, either {@link #WRAP_CONTENT}, 7895 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 7896 * API Level 8), or a fixed size in pixels 7897 * @param height the height, either {@link #WRAP_CONTENT}, 7898 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 7899 * API Level 8), or a fixed size in pixels 7900 */ LayoutParams(int width, int height)7901 public LayoutParams(int width, int height) { 7902 this.width = width; 7903 this.height = height; 7904 } 7905 7906 /** 7907 * Copy constructor. Clones the width and height values of the source. 7908 * 7909 * @param source The layout params to copy from. 7910 */ LayoutParams(LayoutParams source)7911 public LayoutParams(LayoutParams source) { 7912 this.width = source.width; 7913 this.height = source.height; 7914 } 7915 7916 /** 7917 * Used internally by MarginLayoutParams. 7918 * @hide 7919 */ 7920 @UnsupportedAppUsage LayoutParams()7921 LayoutParams() { 7922 } 7923 7924 /** 7925 * Extracts the layout parameters from the supplied attributes. 7926 * 7927 * @param a the style attributes to extract the parameters from 7928 * @param widthAttr the identifier of the width attribute 7929 * @param heightAttr the identifier of the height attribute 7930 */ setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)7931 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 7932 width = a.getLayoutDimension(widthAttr, "layout_width"); 7933 height = a.getLayoutDimension(heightAttr, "layout_height"); 7934 } 7935 7936 /** 7937 * Resolve layout parameters depending on the layout direction. Subclasses that care about 7938 * layoutDirection changes should override this method. The default implementation does 7939 * nothing. 7940 * 7941 * @param layoutDirection the direction of the layout 7942 * 7943 * {@link View#LAYOUT_DIRECTION_LTR} 7944 * {@link View#LAYOUT_DIRECTION_RTL} 7945 */ resolveLayoutDirection(int layoutDirection)7946 public void resolveLayoutDirection(int layoutDirection) { 7947 } 7948 7949 /** 7950 * Returns a String representation of this set of layout parameters. 7951 * 7952 * @param output the String to prepend to the internal representation 7953 * @return a String with the following format: output + 7954 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 7955 * 7956 * @hide 7957 */ debug(String output)7958 public String debug(String output) { 7959 return output + "ViewGroup.LayoutParams={ width=" 7960 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 7961 } 7962 7963 /** 7964 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters. 7965 * 7966 * @param view the view that contains these layout parameters 7967 * @param canvas the canvas on which to draw 7968 * 7969 * @hide 7970 */ onDebugDraw(View view, Canvas canvas, Paint paint)7971 public void onDebugDraw(View view, Canvas canvas, Paint paint) { 7972 } 7973 7974 /** 7975 * Converts the specified size to a readable String. 7976 * 7977 * @param size the size to convert 7978 * @return a String instance representing the supplied size 7979 * 7980 * @hide 7981 */ sizeToString(int size)7982 protected static String sizeToString(int size) { 7983 if (size == WRAP_CONTENT) { 7984 return "wrap-content"; 7985 } 7986 if (size == MATCH_PARENT) { 7987 return "match-parent"; 7988 } 7989 return String.valueOf(size); 7990 } 7991 7992 /** @hide */ encode(@onNull ViewHierarchyEncoder encoder)7993 void encode(@NonNull ViewHierarchyEncoder encoder) { 7994 encoder.beginObject(this); 7995 encodeProperties(encoder); 7996 encoder.endObject(); 7997 } 7998 7999 /** @hide */ encodeProperties(@onNull ViewHierarchyEncoder encoder)8000 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 8001 encoder.addProperty("width", width); 8002 encoder.addProperty("height", height); 8003 } 8004 } 8005 8006 /** 8007 * Per-child layout information for layouts that support margins. 8008 * See 8009 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes} 8010 * for a list of all child view attributes that this class supports. 8011 * 8012 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_margin 8013 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginHorizontal 8014 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginVertical 8015 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 8016 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 8017 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 8018 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 8019 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 8020 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 8021 */ 8022 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 8023 /** 8024 * The left margin in pixels of the child. Margin values should be positive. 8025 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 8026 * to this field. 8027 */ 8028 @ViewDebug.ExportedProperty(category = "layout") 8029 @InspectableProperty(name = "layout_marginLeft") 8030 public int leftMargin; 8031 8032 /** 8033 * The top margin in pixels of the child. Margin values should be positive. 8034 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 8035 * to this field. 8036 */ 8037 @ViewDebug.ExportedProperty(category = "layout") 8038 @InspectableProperty(name = "layout_marginTop") 8039 public int topMargin; 8040 8041 /** 8042 * The right margin in pixels of the child. Margin values should be positive. 8043 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 8044 * to this field. 8045 */ 8046 @ViewDebug.ExportedProperty(category = "layout") 8047 @InspectableProperty(name = "layout_marginRight") 8048 public int rightMargin; 8049 8050 /** 8051 * The bottom margin in pixels of the child. Margin values should be positive. 8052 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 8053 * to this field. 8054 */ 8055 @ViewDebug.ExportedProperty(category = "layout") 8056 @InspectableProperty(name = "layout_marginBottom") 8057 public int bottomMargin; 8058 8059 /** 8060 * The start margin in pixels of the child. Margin values should be positive. 8061 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 8062 * to this field. 8063 */ 8064 @ViewDebug.ExportedProperty(category = "layout") 8065 @UnsupportedAppUsage 8066 private int startMargin = DEFAULT_MARGIN_RELATIVE; 8067 8068 /** 8069 * The end margin in pixels of the child. Margin values should be positive. 8070 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 8071 * to this field. 8072 */ 8073 @ViewDebug.ExportedProperty(category = "layout") 8074 @UnsupportedAppUsage 8075 private int endMargin = DEFAULT_MARGIN_RELATIVE; 8076 8077 /** 8078 * The default start and end margin. 8079 * @hide 8080 */ 8081 public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE; 8082 8083 /** 8084 * Bit 0: layout direction 8085 * Bit 1: layout direction 8086 * Bit 2: left margin undefined 8087 * Bit 3: right margin undefined 8088 * Bit 4: is RTL compatibility mode 8089 * Bit 5: need resolution 8090 * 8091 * Bit 6 to 7 not used 8092 * 8093 * @hide 8094 */ 8095 @ViewDebug.ExportedProperty(category = "layout", flagMapping = { 8096 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK, 8097 equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"), 8098 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK, 8099 equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"), 8100 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK, 8101 equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"), 8102 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK, 8103 equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"), 8104 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK, 8105 equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK") 8106 }, formatToHexString = true) 8107 byte mMarginFlags; 8108 8109 private static final int LAYOUT_DIRECTION_MASK = 0x00000003; 8110 private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004; 8111 private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008; 8112 private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010; 8113 private static final int NEED_RESOLUTION_MASK = 0x00000020; 8114 8115 private static final int DEFAULT_MARGIN_RESOLVED = 0; 8116 private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE; 8117 8118 /** 8119 * Creates a new set of layout parameters. The values are extracted from 8120 * the supplied attributes set and context. 8121 * 8122 * @param c the application environment 8123 * @param attrs the set of attributes from which to extract the layout 8124 * parameters' values 8125 */ MarginLayoutParams(Context c, AttributeSet attrs)8126 public MarginLayoutParams(Context c, AttributeSet attrs) { 8127 super(); 8128 8129 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); 8130 setBaseAttributes(a, 8131 R.styleable.ViewGroup_MarginLayout_layout_width, 8132 R.styleable.ViewGroup_MarginLayout_layout_height); 8133 8134 int margin = a.getDimensionPixelSize( 8135 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); 8136 if (margin >= 0) { 8137 leftMargin = margin; 8138 topMargin = margin; 8139 rightMargin= margin; 8140 bottomMargin = margin; 8141 } else { 8142 int horizontalMargin = a.getDimensionPixelSize( 8143 R.styleable.ViewGroup_MarginLayout_layout_marginHorizontal, -1); 8144 int verticalMargin = a.getDimensionPixelSize( 8145 R.styleable.ViewGroup_MarginLayout_layout_marginVertical, -1); 8146 8147 if (horizontalMargin >= 0) { 8148 leftMargin = horizontalMargin; 8149 rightMargin = horizontalMargin; 8150 } else { 8151 leftMargin = a.getDimensionPixelSize( 8152 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 8153 UNDEFINED_MARGIN); 8154 if (leftMargin == UNDEFINED_MARGIN) { 8155 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; 8156 leftMargin = DEFAULT_MARGIN_RESOLVED; 8157 } 8158 rightMargin = a.getDimensionPixelSize( 8159 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 8160 UNDEFINED_MARGIN); 8161 if (rightMargin == UNDEFINED_MARGIN) { 8162 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; 8163 rightMargin = DEFAULT_MARGIN_RESOLVED; 8164 } 8165 } 8166 8167 startMargin = a.getDimensionPixelSize( 8168 R.styleable.ViewGroup_MarginLayout_layout_marginStart, 8169 DEFAULT_MARGIN_RELATIVE); 8170 endMargin = a.getDimensionPixelSize( 8171 R.styleable.ViewGroup_MarginLayout_layout_marginEnd, 8172 DEFAULT_MARGIN_RELATIVE); 8173 8174 if (verticalMargin >= 0) { 8175 topMargin = verticalMargin; 8176 bottomMargin = verticalMargin; 8177 } else { 8178 topMargin = a.getDimensionPixelSize( 8179 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 8180 DEFAULT_MARGIN_RESOLVED); 8181 bottomMargin = a.getDimensionPixelSize( 8182 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 8183 DEFAULT_MARGIN_RESOLVED); 8184 } 8185 8186 if (isMarginRelative()) { 8187 mMarginFlags |= NEED_RESOLUTION_MASK; 8188 } 8189 } 8190 8191 final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport(); 8192 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; 8193 if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) { 8194 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK; 8195 } 8196 8197 // Layout direction is LTR by default 8198 mMarginFlags |= LAYOUT_DIRECTION_LTR; 8199 8200 a.recycle(); 8201 } 8202 MarginLayoutParams(int width, int height)8203 public MarginLayoutParams(int width, int height) { 8204 super(width, height); 8205 8206 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; 8207 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; 8208 8209 mMarginFlags &= ~NEED_RESOLUTION_MASK; 8210 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK; 8211 } 8212 8213 /** 8214 * Copy constructor. Clones the width, height and margin values of the source. 8215 * 8216 * @param source The layout params to copy from. 8217 */ MarginLayoutParams(MarginLayoutParams source)8218 public MarginLayoutParams(MarginLayoutParams source) { 8219 this.width = source.width; 8220 this.height = source.height; 8221 8222 this.leftMargin = source.leftMargin; 8223 this.topMargin = source.topMargin; 8224 this.rightMargin = source.rightMargin; 8225 this.bottomMargin = source.bottomMargin; 8226 this.startMargin = source.startMargin; 8227 this.endMargin = source.endMargin; 8228 8229 this.mMarginFlags = source.mMarginFlags; 8230 } 8231 MarginLayoutParams(LayoutParams source)8232 public MarginLayoutParams(LayoutParams source) { 8233 super(source); 8234 8235 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; 8236 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; 8237 8238 mMarginFlags &= ~NEED_RESOLUTION_MASK; 8239 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK; 8240 } 8241 8242 /** 8243 * @hide Used internally. 8244 */ copyMarginsFrom(MarginLayoutParams source)8245 public final void copyMarginsFrom(MarginLayoutParams source) { 8246 this.leftMargin = source.leftMargin; 8247 this.topMargin = source.topMargin; 8248 this.rightMargin = source.rightMargin; 8249 this.bottomMargin = source.bottomMargin; 8250 this.startMargin = source.startMargin; 8251 this.endMargin = source.endMargin; 8252 8253 this.mMarginFlags = source.mMarginFlags; 8254 } 8255 8256 /** 8257 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs 8258 * to be done so that the new margins are taken into account. Left and right margins may be 8259 * overridden by {@link android.view.View#requestLayout()} depending on layout direction. 8260 * Margin values should be positive. 8261 * 8262 * @param left the left margin size 8263 * @param top the top margin size 8264 * @param right the right margin size 8265 * @param bottom the bottom margin size 8266 * 8267 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 8268 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 8269 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 8270 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 8271 */ setMargins(int left, int top, int right, int bottom)8272 public void setMargins(int left, int top, int right, int bottom) { 8273 leftMargin = left; 8274 topMargin = top; 8275 rightMargin = right; 8276 bottomMargin = bottom; 8277 mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK; 8278 mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK; 8279 if (isMarginRelative()) { 8280 mMarginFlags |= NEED_RESOLUTION_MASK; 8281 } else { 8282 mMarginFlags &= ~NEED_RESOLUTION_MASK; 8283 } 8284 } 8285 8286 /** 8287 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()} 8288 * needs to be done so that the new relative margins are taken into account. Left and right 8289 * margins may be overridden by {@link android.view.View#requestLayout()} depending on 8290 * layout direction. Margin values should be positive. 8291 * 8292 * @param start the start margin size 8293 * @param top the top margin size 8294 * @param end the right margin size 8295 * @param bottom the bottom margin size 8296 * 8297 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 8298 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 8299 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 8300 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 8301 * 8302 * @hide 8303 */ 8304 @UnsupportedAppUsage setMarginsRelative(int start, int top, int end, int bottom)8305 public void setMarginsRelative(int start, int top, int end, int bottom) { 8306 startMargin = start; 8307 topMargin = top; 8308 endMargin = end; 8309 bottomMargin = bottom; 8310 mMarginFlags |= NEED_RESOLUTION_MASK; 8311 } 8312 8313 /** 8314 * Sets the relative start margin. Margin values should be positive. 8315 * 8316 * @param start the start margin size 8317 * 8318 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 8319 */ setMarginStart(int start)8320 public void setMarginStart(int start) { 8321 startMargin = start; 8322 mMarginFlags |= NEED_RESOLUTION_MASK; 8323 } 8324 8325 /** 8326 * Returns the start margin in pixels. 8327 * 8328 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 8329 * 8330 * @return the start margin in pixels. 8331 */ getMarginStart()8332 public int getMarginStart() { 8333 if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin; 8334 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) { 8335 doResolveMargins(); 8336 } 8337 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { 8338 case View.LAYOUT_DIRECTION_RTL: 8339 return rightMargin; 8340 case View.LAYOUT_DIRECTION_LTR: 8341 default: 8342 return leftMargin; 8343 } 8344 } 8345 8346 /** 8347 * Sets the relative end margin. Margin values should be positive. 8348 * 8349 * @param end the end margin size 8350 * 8351 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 8352 */ setMarginEnd(int end)8353 public void setMarginEnd(int end) { 8354 endMargin = end; 8355 mMarginFlags |= NEED_RESOLUTION_MASK; 8356 } 8357 8358 /** 8359 * Returns the end margin in pixels. 8360 * 8361 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 8362 * 8363 * @return the end margin in pixels. 8364 */ getMarginEnd()8365 public int getMarginEnd() { 8366 if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin; 8367 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) { 8368 doResolveMargins(); 8369 } 8370 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { 8371 case View.LAYOUT_DIRECTION_RTL: 8372 return leftMargin; 8373 case View.LAYOUT_DIRECTION_LTR: 8374 default: 8375 return rightMargin; 8376 } 8377 } 8378 8379 /** 8380 * Check if margins are relative. 8381 * 8382 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 8383 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 8384 * 8385 * @return true if either marginStart or marginEnd has been set. 8386 */ isMarginRelative()8387 public boolean isMarginRelative() { 8388 return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE); 8389 } 8390 8391 /** 8392 * Set the layout direction 8393 * @param layoutDirection the layout direction. 8394 * Should be either {@link View#LAYOUT_DIRECTION_LTR} 8395 * or {@link View#LAYOUT_DIRECTION_RTL}. 8396 */ setLayoutDirection(int layoutDirection)8397 public void setLayoutDirection(int layoutDirection) { 8398 if (layoutDirection != View.LAYOUT_DIRECTION_LTR && 8399 layoutDirection != View.LAYOUT_DIRECTION_RTL) return; 8400 if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) { 8401 mMarginFlags &= ~LAYOUT_DIRECTION_MASK; 8402 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK); 8403 if (isMarginRelative()) { 8404 mMarginFlags |= NEED_RESOLUTION_MASK; 8405 } else { 8406 mMarginFlags &= ~NEED_RESOLUTION_MASK; 8407 } 8408 } 8409 } 8410 8411 /** 8412 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or 8413 * {@link View#LAYOUT_DIRECTION_RTL}. 8414 * 8415 * @return the layout direction. 8416 */ getLayoutDirection()8417 public int getLayoutDirection() { 8418 return (mMarginFlags & LAYOUT_DIRECTION_MASK); 8419 } 8420 8421 /** 8422 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins 8423 * may be overridden depending on layout direction. 8424 */ 8425 @Override resolveLayoutDirection(int layoutDirection)8426 public void resolveLayoutDirection(int layoutDirection) { 8427 setLayoutDirection(layoutDirection); 8428 8429 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything 8430 // Will use the left and right margins if no relative margin is defined. 8431 if (!isMarginRelative() || 8432 (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return; 8433 8434 // Proceed with resolution 8435 doResolveMargins(); 8436 } 8437 doResolveMargins()8438 private void doResolveMargins() { 8439 if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) { 8440 // if left or right margins are not defined and if we have some start or end margin 8441 // defined then use those start and end margins. 8442 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK 8443 && startMargin > DEFAULT_MARGIN_RELATIVE) { 8444 leftMargin = startMargin; 8445 } 8446 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK 8447 && endMargin > DEFAULT_MARGIN_RELATIVE) { 8448 rightMargin = endMargin; 8449 } 8450 } else { 8451 // We have some relative margins (either the start one or the end one or both). So use 8452 // them and override what has been defined for left and right margins. If either start 8453 // or end margin is not defined, just set it to default "0". 8454 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { 8455 case View.LAYOUT_DIRECTION_RTL: 8456 leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? 8457 endMargin : DEFAULT_MARGIN_RESOLVED; 8458 rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? 8459 startMargin : DEFAULT_MARGIN_RESOLVED; 8460 break; 8461 case View.LAYOUT_DIRECTION_LTR: 8462 default: 8463 leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? 8464 startMargin : DEFAULT_MARGIN_RESOLVED; 8465 rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? 8466 endMargin : DEFAULT_MARGIN_RESOLVED; 8467 break; 8468 } 8469 } 8470 mMarginFlags &= ~NEED_RESOLUTION_MASK; 8471 } 8472 8473 /** 8474 * @hide 8475 */ isLayoutRtl()8476 public boolean isLayoutRtl() { 8477 return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL); 8478 } 8479 8480 /** 8481 * @hide 8482 */ 8483 @Override onDebugDraw(View view, Canvas canvas, Paint paint)8484 public void onDebugDraw(View view, Canvas canvas, Paint paint) { 8485 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; 8486 8487 fillDifference(canvas, 8488 view.getLeft() + oi.left, 8489 view.getTop() + oi.top, 8490 view.getRight() - oi.right, 8491 view.getBottom() - oi.bottom, 8492 leftMargin, 8493 topMargin, 8494 rightMargin, 8495 bottomMargin, 8496 paint); 8497 } 8498 8499 /** @hide */ 8500 @Override encodeProperties(@onNull ViewHierarchyEncoder encoder)8501 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 8502 super.encodeProperties(encoder); 8503 encoder.addProperty("leftMargin", leftMargin); 8504 encoder.addProperty("topMargin", topMargin); 8505 encoder.addProperty("rightMargin", rightMargin); 8506 encoder.addProperty("bottomMargin", bottomMargin); 8507 encoder.addProperty("startMargin", startMargin); 8508 encoder.addProperty("endMargin", endMargin); 8509 } 8510 } 8511 8512 /* Describes a touched view and the ids of the pointers that it has captured. 8513 * 8514 * This code assumes that pointer ids are always in the range 0..31 such that 8515 * it can use a bitfield to track which pointer ids are present. 8516 * As it happens, the lower layers of the input dispatch pipeline also use the 8517 * same trick so the assumption should be safe here... 8518 */ 8519 private static final class TouchTarget { 8520 private static final int MAX_RECYCLED = 32; 8521 private static final Object sRecycleLock = new Object[0]; 8522 private static TouchTarget sRecycleBin; 8523 private static int sRecycledCount; 8524 8525 public static final int ALL_POINTER_IDS = -1; // all ones 8526 8527 // The touched child view. 8528 @UnsupportedAppUsage 8529 public View child; 8530 8531 // The combined bit mask of pointer ids for all pointers captured by the target. 8532 public int pointerIdBits; 8533 8534 // The next target in the target list. 8535 public TouchTarget next; 8536 8537 @UnsupportedAppUsage TouchTarget()8538 private TouchTarget() { 8539 } 8540 obtain(@onNull View child, int pointerIdBits)8541 public static TouchTarget obtain(@NonNull View child, int pointerIdBits) { 8542 if (child == null) { 8543 throw new IllegalArgumentException("child must be non-null"); 8544 } 8545 8546 final TouchTarget target; 8547 synchronized (sRecycleLock) { 8548 if (sRecycleBin == null) { 8549 target = new TouchTarget(); 8550 } else { 8551 target = sRecycleBin; 8552 sRecycleBin = target.next; 8553 sRecycledCount--; 8554 target.next = null; 8555 } 8556 } 8557 target.child = child; 8558 target.pointerIdBits = pointerIdBits; 8559 return target; 8560 } 8561 recycle()8562 public void recycle() { 8563 if (child == null) { 8564 throw new IllegalStateException("already recycled once"); 8565 } 8566 8567 synchronized (sRecycleLock) { 8568 if (sRecycledCount < MAX_RECYCLED) { 8569 next = sRecycleBin; 8570 sRecycleBin = this; 8571 sRecycledCount += 1; 8572 } else { 8573 next = null; 8574 } 8575 child = null; 8576 } 8577 } 8578 } 8579 8580 /* Describes a hovered view. */ 8581 private static final class HoverTarget { 8582 private static final int MAX_RECYCLED = 32; 8583 private static final Object sRecycleLock = new Object[0]; 8584 private static HoverTarget sRecycleBin; 8585 private static int sRecycledCount; 8586 8587 // The hovered child view. 8588 public View child; 8589 8590 // The next target in the target list. 8591 public HoverTarget next; 8592 HoverTarget()8593 private HoverTarget() { 8594 } 8595 obtain(@onNull View child)8596 public static HoverTarget obtain(@NonNull View child) { 8597 if (child == null) { 8598 throw new IllegalArgumentException("child must be non-null"); 8599 } 8600 8601 final HoverTarget target; 8602 synchronized (sRecycleLock) { 8603 if (sRecycleBin == null) { 8604 target = new HoverTarget(); 8605 } else { 8606 target = sRecycleBin; 8607 sRecycleBin = target.next; 8608 sRecycledCount--; 8609 target.next = null; 8610 } 8611 } 8612 target.child = child; 8613 return target; 8614 } 8615 recycle()8616 public void recycle() { 8617 if (child == null) { 8618 throw new IllegalStateException("already recycled once"); 8619 } 8620 8621 synchronized (sRecycleLock) { 8622 if (sRecycledCount < MAX_RECYCLED) { 8623 next = sRecycleBin; 8624 sRecycleBin = this; 8625 sRecycledCount += 1; 8626 } else { 8627 next = null; 8628 } 8629 child = null; 8630 } 8631 } 8632 } 8633 8634 /** 8635 * Pooled class that to hold the children for autifill. 8636 */ 8637 private static class ChildListForAutofill extends ArrayList<View> { 8638 private static final int MAX_POOL_SIZE = 32; 8639 8640 private static final Pools.SimplePool<ChildListForAutofill> sPool = 8641 new Pools.SimplePool<>(MAX_POOL_SIZE); 8642 obtain()8643 public static ChildListForAutofill obtain() { 8644 ChildListForAutofill list = sPool.acquire(); 8645 if (list == null) { 8646 list = new ChildListForAutofill(); 8647 } 8648 return list; 8649 } 8650 recycle()8651 public void recycle() { 8652 clear(); 8653 sPool.release(this); 8654 } 8655 } 8656 8657 /** 8658 * Pooled class that orderes the children of a ViewGroup from start 8659 * to end based on how they are laid out and the layout direction. 8660 */ 8661 static class ChildListForAccessibility { 8662 8663 private static final int MAX_POOL_SIZE = 32; 8664 8665 private static final SynchronizedPool<ChildListForAccessibility> sPool = 8666 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE); 8667 8668 private final ArrayList<View> mChildren = new ArrayList<View>(); 8669 8670 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>(); 8671 obtain(ViewGroup parent, boolean sort)8672 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) { 8673 ChildListForAccessibility list = sPool.acquire(); 8674 if (list == null) { 8675 list = new ChildListForAccessibility(); 8676 } 8677 list.init(parent, sort); 8678 return list; 8679 } 8680 recycle()8681 public void recycle() { 8682 clear(); 8683 sPool.release(this); 8684 } 8685 getChildCount()8686 public int getChildCount() { 8687 return mChildren.size(); 8688 } 8689 getChildAt(int index)8690 public View getChildAt(int index) { 8691 return mChildren.get(index); 8692 } 8693 init(ViewGroup parent, boolean sort)8694 private void init(ViewGroup parent, boolean sort) { 8695 ArrayList<View> children = mChildren; 8696 final int childCount = parent.getChildCount(); 8697 for (int i = 0; i < childCount; i++) { 8698 View child = parent.getChildAt(i); 8699 children.add(child); 8700 } 8701 if (sort) { 8702 ArrayList<ViewLocationHolder> holders = mHolders; 8703 for (int i = 0; i < childCount; i++) { 8704 View child = children.get(i); 8705 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child); 8706 holders.add(holder); 8707 } 8708 sort(holders); 8709 for (int i = 0; i < childCount; i++) { 8710 ViewLocationHolder holder = holders.get(i); 8711 children.set(i, holder.mView); 8712 holder.recycle(); 8713 } 8714 holders.clear(); 8715 } 8716 } 8717 sort(ArrayList<ViewLocationHolder> holders)8718 private void sort(ArrayList<ViewLocationHolder> holders) { 8719 // This is gross but the least risky solution. The current comparison 8720 // strategy breaks transitivity but produces very good results. Coming 8721 // up with a new strategy requires time which we do not have, so ... 8722 try { 8723 ViewLocationHolder.setComparisonStrategy( 8724 ViewLocationHolder.COMPARISON_STRATEGY_STRIPE); 8725 Collections.sort(holders); 8726 } catch (IllegalArgumentException iae) { 8727 // Note that in practice this occurs extremely rarely in a couple 8728 // of pathological cases. 8729 ViewLocationHolder.setComparisonStrategy( 8730 ViewLocationHolder.COMPARISON_STRATEGY_LOCATION); 8731 Collections.sort(holders); 8732 } 8733 } 8734 clear()8735 private void clear() { 8736 mChildren.clear(); 8737 } 8738 } 8739 8740 /** 8741 * Pooled class that holds a View and its location with respect to 8742 * a specified root. This enables sorting of views based on their 8743 * coordinates without recomputing the position relative to the root 8744 * on every comparison. 8745 */ 8746 static class ViewLocationHolder implements Comparable<ViewLocationHolder> { 8747 8748 private static final int MAX_POOL_SIZE = 32; 8749 8750 private static final SynchronizedPool<ViewLocationHolder> sPool = 8751 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE); 8752 8753 public static final int COMPARISON_STRATEGY_STRIPE = 1; 8754 8755 public static final int COMPARISON_STRATEGY_LOCATION = 2; 8756 8757 private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE; 8758 8759 private final Rect mLocation = new Rect(); 8760 8761 private ViewGroup mRoot; 8762 8763 public View mView; 8764 8765 private int mLayoutDirection; 8766 obtain(ViewGroup root, View view)8767 public static ViewLocationHolder obtain(ViewGroup root, View view) { 8768 ViewLocationHolder holder = sPool.acquire(); 8769 if (holder == null) { 8770 holder = new ViewLocationHolder(); 8771 } 8772 holder.init(root, view); 8773 return holder; 8774 } 8775 setComparisonStrategy(int strategy)8776 public static void setComparisonStrategy(int strategy) { 8777 sComparisonStrategy = strategy; 8778 } 8779 recycle()8780 public void recycle() { 8781 clear(); 8782 sPool.release(this); 8783 } 8784 8785 @Override compareTo(ViewLocationHolder another)8786 public int compareTo(ViewLocationHolder another) { 8787 // This instance is greater than an invalid argument. 8788 if (another == null) { 8789 return 1; 8790 } 8791 8792 int boundsResult = compareBoundsOfTree(this, another); 8793 if (boundsResult != 0) { 8794 return boundsResult; 8795 } 8796 8797 // Just break the tie somehow. The accessibility ids are unique 8798 // and stable, hence this is deterministic tie breaking. 8799 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId(); 8800 } 8801 8802 /** 8803 * Compare two views based on their bounds. Use the bounds of their children to break ties. 8804 * 8805 * @param holder1 Holder of first view to compare 8806 * @param holder2 Holder of second view to compare. Must have the same root as holder1. 8807 * @return The compare result, with equality if no good comparison was found. 8808 */ compareBoundsOfTree( ViewLocationHolder holder1, ViewLocationHolder holder2)8809 private static int compareBoundsOfTree( 8810 ViewLocationHolder holder1, ViewLocationHolder holder2) { 8811 if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) { 8812 // First is above second. 8813 if (holder1.mLocation.bottom - holder2.mLocation.top <= 0) { 8814 return -1; 8815 } 8816 // First is below second. 8817 if (holder1.mLocation.top - holder2.mLocation.bottom >= 0) { 8818 return 1; 8819 } 8820 } 8821 8822 // We are ordering left-to-right, top-to-bottom. 8823 if (holder1.mLayoutDirection == LAYOUT_DIRECTION_LTR) { 8824 final int leftDifference = holder1.mLocation.left - holder2.mLocation.left; 8825 if (leftDifference != 0) { 8826 return leftDifference; 8827 } 8828 } else { // RTL 8829 final int rightDifference = holder1.mLocation.right - holder2.mLocation.right; 8830 if (rightDifference != 0) { 8831 return -rightDifference; 8832 } 8833 } 8834 // We are ordering left-to-right, top-to-bottom. 8835 final int topDifference = holder1.mLocation.top - holder2.mLocation.top; 8836 if (topDifference != 0) { 8837 return topDifference; 8838 } 8839 // Break tie by height. 8840 final int heightDiference = holder1.mLocation.height() - holder2.mLocation.height(); 8841 if (heightDiference != 0) { 8842 return -heightDiference; 8843 } 8844 // Break tie by width. 8845 final int widthDifference = holder1.mLocation.width() - holder2.mLocation.width(); 8846 if (widthDifference != 0) { 8847 return -widthDifference; 8848 } 8849 8850 // Find a child of each view with different screen bounds. 8851 final Rect view1Bounds = new Rect(); 8852 final Rect view2Bounds = new Rect(); 8853 final Rect tempRect = new Rect(); 8854 holder1.mView.getBoundsOnScreen(view1Bounds, true); 8855 holder2.mView.getBoundsOnScreen(view2Bounds, true); 8856 final View child1 = holder1.mView.findViewByPredicateTraversal((view) -> { 8857 view.getBoundsOnScreen(tempRect, true); 8858 return !tempRect.equals(view1Bounds); 8859 }, null); 8860 final View child2 = holder2.mView.findViewByPredicateTraversal((view) -> { 8861 view.getBoundsOnScreen(tempRect, true); 8862 return !tempRect.equals(view2Bounds); 8863 }, null); 8864 8865 8866 // Compare the children recursively 8867 if ((child1 != null) && (child2 != null)) { 8868 final ViewLocationHolder childHolder1 = 8869 ViewLocationHolder.obtain(holder1.mRoot, child1); 8870 final ViewLocationHolder childHolder2 = 8871 ViewLocationHolder.obtain(holder1.mRoot, child2); 8872 return compareBoundsOfTree(childHolder1, childHolder2); 8873 } 8874 8875 // If only one has a child, use that one 8876 if (child1 != null) { 8877 return 1; 8878 } 8879 8880 if (child2 != null) { 8881 return -1; 8882 } 8883 8884 // Give up 8885 return 0; 8886 } 8887 init(ViewGroup root, View view)8888 private void init(ViewGroup root, View view) { 8889 Rect viewLocation = mLocation; 8890 view.getDrawingRect(viewLocation); 8891 root.offsetDescendantRectToMyCoords(view, viewLocation); 8892 mView = view; 8893 mRoot = root; 8894 mLayoutDirection = root.getLayoutDirection(); 8895 } 8896 clear()8897 private void clear() { 8898 mView = null; 8899 mRoot = null; 8900 mLocation.set(0, 0, 0, 0); 8901 } 8902 } 8903 drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)8904 private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { 8905 if (sDebugLines== null) { 8906 // TODO: This won't work with multiple UI threads in a single process 8907 sDebugLines = new float[16]; 8908 } 8909 8910 sDebugLines[0] = x1; 8911 sDebugLines[1] = y1; 8912 sDebugLines[2] = x2; 8913 sDebugLines[3] = y1; 8914 8915 sDebugLines[4] = x2; 8916 sDebugLines[5] = y1; 8917 sDebugLines[6] = x2; 8918 sDebugLines[7] = y2; 8919 8920 sDebugLines[8] = x2; 8921 sDebugLines[9] = y2; 8922 sDebugLines[10] = x1; 8923 sDebugLines[11] = y2; 8924 8925 sDebugLines[12] = x1; 8926 sDebugLines[13] = y2; 8927 sDebugLines[14] = x1; 8928 sDebugLines[15] = y1; 8929 8930 canvas.drawLines(sDebugLines, paint); 8931 } 8932 8933 /** @hide */ 8934 @Override 8935 @UnsupportedAppUsage encodeProperties(@onNull ViewHierarchyEncoder encoder)8936 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 8937 super.encodeProperties(encoder); 8938 8939 encoder.addProperty("focus:descendantFocusability", getDescendantFocusability()); 8940 encoder.addProperty("drawing:clipChildren", getClipChildren()); 8941 encoder.addProperty("drawing:clipToPadding", getClipToPadding()); 8942 encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled()); 8943 encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache()); 8944 8945 int n = getChildCount(); 8946 encoder.addProperty("meta:__childCount__", (short)n); 8947 for (int i = 0; i < n; i++) { 8948 encoder.addPropertyKey("meta:__child__" + i); 8949 getChildAt(i).encode(encoder); 8950 } 8951 } 8952 } 8953