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