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