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