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