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