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