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