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