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