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