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