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