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