ViewGroup.java revision b989502e5cf44d65c6dddc0179b6d9b6e61ef7fd
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 float x = ev.getX(actionIndex); 1880 final float y = ev.getY(actionIndex); 1881 1882 if (mOverlay != null) { 1883 ViewOverlay overlay = (ViewOverlay) mOverlay; 1884 // Check to see whether the overlay can handle the event 1885 final View child = mOverlay; 1886 if (canViewReceivePointerEvents(child) && 1887 isTransformedTouchPointInView(x, y, child, null)) { 1888 newTouchTarget = getTouchTarget(child); 1889 if (newTouchTarget != null) { 1890 newTouchTarget.pointerIdBits |= idBitsToAssign; 1891 } else { 1892 resetCancelNextUpFlag(child); 1893 if (dispatchTransformedTouchEvent(ev, false, child, 1894 idBitsToAssign)) { 1895 mLastTouchDownTime = ev.getDownTime(); 1896 mLastTouchDownX = ev.getX(); 1897 mLastTouchDownY = ev.getY(); 1898 newTouchTarget = addTouchTarget(child, idBitsToAssign); 1899 alreadyDispatchedToNewTouchTarget = true; 1900 } 1901 } 1902 } 1903 } 1904 1905 final int childrenCount = mChildrenCount; 1906 if (newTouchTarget == null && childrenCount != 0) { 1907 // Find a child that can receive the event. 1908 // Scan children from front to back. 1909 final View[] children = mChildren; 1910 1911 final boolean customOrder = isChildrenDrawingOrderEnabled(); 1912 for (int i = childrenCount - 1; i >= 0; i--) { 1913 final int childIndex = customOrder ? 1914 getChildDrawingOrder(childrenCount, i) : i; 1915 final View child = children[childIndex]; 1916 if (!canViewReceivePointerEvents(child) 1917 || !isTransformedTouchPointInView(x, y, child, null)) { 1918 continue; 1919 } 1920 1921 newTouchTarget = getTouchTarget(child); 1922 if (newTouchTarget != null) { 1923 // Child is already receiving touch within its bounds. 1924 // Give it the new pointer in addition to the ones it is handling. 1925 newTouchTarget.pointerIdBits |= idBitsToAssign; 1926 break; 1927 } 1928 1929 resetCancelNextUpFlag(child); 1930 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 1931 // Child wants to receive touch within its bounds. 1932 mLastTouchDownTime = ev.getDownTime(); 1933 mLastTouchDownIndex = childIndex; 1934 mLastTouchDownX = ev.getX(); 1935 mLastTouchDownY = ev.getY(); 1936 newTouchTarget = addTouchTarget(child, idBitsToAssign); 1937 alreadyDispatchedToNewTouchTarget = true; 1938 break; 1939 } 1940 } 1941 } 1942 1943 if (newTouchTarget == null && mFirstTouchTarget != null) { 1944 // Did not find a child to receive the event. 1945 // Assign the pointer to the least recently added target. 1946 newTouchTarget = mFirstTouchTarget; 1947 while (newTouchTarget.next != null) { 1948 newTouchTarget = newTouchTarget.next; 1949 } 1950 newTouchTarget.pointerIdBits |= idBitsToAssign; 1951 } 1952 } 1953 } 1954 1955 // Dispatch to touch targets. 1956 if (mFirstTouchTarget == null) { 1957 // No touch targets so treat this as an ordinary view. 1958 handled = dispatchTransformedTouchEvent(ev, canceled, null, 1959 TouchTarget.ALL_POINTER_IDS); 1960 } else { 1961 // Dispatch to touch targets, excluding the new touch target if we already 1962 // dispatched to it. Cancel touch targets if necessary. 1963 TouchTarget predecessor = null; 1964 TouchTarget target = mFirstTouchTarget; 1965 while (target != null) { 1966 final TouchTarget next = target.next; 1967 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { 1968 handled = true; 1969 } else { 1970 final boolean cancelChild = resetCancelNextUpFlag(target.child) 1971 || intercepted; 1972 if (dispatchTransformedTouchEvent(ev, cancelChild, 1973 target.child, target.pointerIdBits)) { 1974 handled = true; 1975 } 1976 if (cancelChild) { 1977 if (predecessor == null) { 1978 mFirstTouchTarget = next; 1979 } else { 1980 predecessor.next = next; 1981 } 1982 target.recycle(); 1983 target = next; 1984 continue; 1985 } 1986 } 1987 predecessor = target; 1988 target = next; 1989 } 1990 } 1991 1992 // Update list of touch targets for pointer up or cancel, if needed. 1993 if (canceled 1994 || actionMasked == MotionEvent.ACTION_UP 1995 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 1996 resetTouchState(); 1997 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { 1998 final int actionIndex = ev.getActionIndex(); 1999 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); 2000 removePointersFromTouchTargets(idBitsToRemove); 2001 } 2002 } 2003 2004 if (!handled && mInputEventConsistencyVerifier != null) { 2005 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); 2006 } 2007 return handled; 2008 } 2009 2010 /** 2011 * Resets all touch state in preparation for a new cycle. 2012 */ 2013 private void resetTouchState() { 2014 clearTouchTargets(); 2015 resetCancelNextUpFlag(this); 2016 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 2017 } 2018 2019 /** 2020 * Resets the cancel next up flag. 2021 * Returns true if the flag was previously set. 2022 */ 2023 private static boolean resetCancelNextUpFlag(View view) { 2024 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { 2025 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; 2026 return true; 2027 } 2028 return false; 2029 } 2030 2031 /** 2032 * Clears all touch targets. 2033 */ 2034 private void clearTouchTargets() { 2035 TouchTarget target = mFirstTouchTarget; 2036 if (target != null) { 2037 do { 2038 TouchTarget next = target.next; 2039 target.recycle(); 2040 target = next; 2041 } while (target != null); 2042 mFirstTouchTarget = null; 2043 } 2044 } 2045 2046 /** 2047 * Cancels and clears all touch targets. 2048 */ 2049 private void cancelAndClearTouchTargets(MotionEvent event) { 2050 if (mFirstTouchTarget != null) { 2051 boolean syntheticEvent = false; 2052 if (event == null) { 2053 final long now = SystemClock.uptimeMillis(); 2054 event = MotionEvent.obtain(now, now, 2055 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 2056 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 2057 syntheticEvent = true; 2058 } 2059 2060 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 2061 resetCancelNextUpFlag(target.child); 2062 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits); 2063 } 2064 clearTouchTargets(); 2065 2066 if (syntheticEvent) { 2067 event.recycle(); 2068 } 2069 } 2070 } 2071 2072 /** 2073 * Gets the touch target for specified child view. 2074 * Returns null if not found. 2075 */ 2076 private TouchTarget getTouchTarget(View child) { 2077 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 2078 if (target.child == child) { 2079 return target; 2080 } 2081 } 2082 return null; 2083 } 2084 2085 /** 2086 * Adds a touch target for specified child to the beginning of the list. 2087 * Assumes the target child is not already present. 2088 */ 2089 private TouchTarget addTouchTarget(View child, int pointerIdBits) { 2090 TouchTarget target = TouchTarget.obtain(child, pointerIdBits); 2091 target.next = mFirstTouchTarget; 2092 mFirstTouchTarget = target; 2093 return target; 2094 } 2095 2096 /** 2097 * Removes the pointer ids from consideration. 2098 */ 2099 private void removePointersFromTouchTargets(int pointerIdBits) { 2100 TouchTarget predecessor = null; 2101 TouchTarget target = mFirstTouchTarget; 2102 while (target != null) { 2103 final TouchTarget next = target.next; 2104 if ((target.pointerIdBits & pointerIdBits) != 0) { 2105 target.pointerIdBits &= ~pointerIdBits; 2106 if (target.pointerIdBits == 0) { 2107 if (predecessor == null) { 2108 mFirstTouchTarget = next; 2109 } else { 2110 predecessor.next = next; 2111 } 2112 target.recycle(); 2113 target = next; 2114 continue; 2115 } 2116 } 2117 predecessor = target; 2118 target = next; 2119 } 2120 } 2121 2122 private void cancelTouchTarget(View view) { 2123 TouchTarget predecessor = null; 2124 TouchTarget target = mFirstTouchTarget; 2125 while (target != null) { 2126 final TouchTarget next = target.next; 2127 if (target.child == view) { 2128 if (predecessor == null) { 2129 mFirstTouchTarget = next; 2130 } else { 2131 predecessor.next = next; 2132 } 2133 target.recycle(); 2134 2135 final long now = SystemClock.uptimeMillis(); 2136 MotionEvent event = MotionEvent.obtain(now, now, 2137 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 2138 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 2139 view.dispatchTouchEvent(event); 2140 event.recycle(); 2141 return; 2142 } 2143 predecessor = target; 2144 target = next; 2145 } 2146 } 2147 2148 /** 2149 * Returns true if a child view can receive pointer events. 2150 * @hide 2151 */ 2152 private static boolean canViewReceivePointerEvents(View child) { 2153 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE 2154 || child.getAnimation() != null; 2155 } 2156 2157 /** 2158 * Returns true if a child view contains the specified point when transformed 2159 * into its coordinate space. 2160 * Child must not be null. 2161 * @hide 2162 */ 2163 protected boolean isTransformedTouchPointInView(float x, float y, View child, 2164 PointF outLocalPoint) { 2165 float localX = x + mScrollX - child.mLeft; 2166 float localY = y + mScrollY - child.mTop; 2167 if (! child.hasIdentityMatrix() && mAttachInfo != null) { 2168 final float[] localXY = mAttachInfo.mTmpTransformLocation; 2169 localXY[0] = localX; 2170 localXY[1] = localY; 2171 child.getInverseMatrix().mapPoints(localXY); 2172 localX = localXY[0]; 2173 localY = localXY[1]; 2174 } 2175 final boolean isInView = child.pointInView(localX, localY); 2176 if (isInView && outLocalPoint != null) { 2177 outLocalPoint.set(localX, localY); 2178 } 2179 return isInView; 2180 } 2181 2182 /** 2183 * Transforms a motion event into the coordinate space of a particular child view, 2184 * filters out irrelevant pointer ids, and overrides its action if necessary. 2185 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 2186 */ 2187 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, 2188 View child, int desiredPointerIdBits) { 2189 final boolean handled; 2190 2191 // Canceling motions is a special case. We don't need to perform any transformations 2192 // or filtering. The important part is the action, not the contents. 2193 final int oldAction = event.getAction(); 2194 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { 2195 event.setAction(MotionEvent.ACTION_CANCEL); 2196 if (child == null) { 2197 handled = super.dispatchTouchEvent(event); 2198 } else { 2199 handled = child.dispatchTouchEvent(event); 2200 } 2201 event.setAction(oldAction); 2202 return handled; 2203 } 2204 2205 // Calculate the number of pointers to deliver. 2206 final int oldPointerIdBits = event.getPointerIdBits(); 2207 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; 2208 2209 // If for some reason we ended up in an inconsistent state where it looks like we 2210 // might produce a motion event with no pointers in it, then drop the event. 2211 if (newPointerIdBits == 0) { 2212 return false; 2213 } 2214 2215 // If the number of pointers is the same and we don't need to perform any fancy 2216 // irreversible transformations, then we can reuse the motion event for this 2217 // dispatch as long as we are careful to revert any changes we make. 2218 // Otherwise we need to make a copy. 2219 final MotionEvent transformedEvent; 2220 if (newPointerIdBits == oldPointerIdBits) { 2221 if (child == null || child.hasIdentityMatrix()) { 2222 if (child == null) { 2223 handled = super.dispatchTouchEvent(event); 2224 } else { 2225 final float offsetX = mScrollX - child.mLeft; 2226 final float offsetY = mScrollY - child.mTop; 2227 event.offsetLocation(offsetX, offsetY); 2228 2229 handled = child.dispatchTouchEvent(event); 2230 2231 event.offsetLocation(-offsetX, -offsetY); 2232 } 2233 return handled; 2234 } 2235 transformedEvent = MotionEvent.obtain(event); 2236 } else { 2237 transformedEvent = event.split(newPointerIdBits); 2238 } 2239 2240 // Perform any necessary transformations and dispatch. 2241 if (child == null) { 2242 handled = super.dispatchTouchEvent(transformedEvent); 2243 } else { 2244 final float offsetX = mScrollX - child.mLeft; 2245 final float offsetY = mScrollY - child.mTop; 2246 transformedEvent.offsetLocation(offsetX, offsetY); 2247 if (! child.hasIdentityMatrix()) { 2248 transformedEvent.transform(child.getInverseMatrix()); 2249 } 2250 2251 handled = child.dispatchTouchEvent(transformedEvent); 2252 } 2253 2254 // Done. 2255 transformedEvent.recycle(); 2256 return handled; 2257 } 2258 2259 /** 2260 * Enable or disable the splitting of MotionEvents to multiple children during touch event 2261 * dispatch. This behavior is enabled by default for applications that target an 2262 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer. 2263 * 2264 * <p>When this option is enabled MotionEvents may be split and dispatched to different child 2265 * views depending on where each pointer initially went down. This allows for user interactions 2266 * such as scrolling two panes of content independently, chording of buttons, and performing 2267 * independent gestures on different pieces of content. 2268 * 2269 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple 2270 * child views. <code>false</code> to only allow one child view to be the target of 2271 * any MotionEvent received by this ViewGroup. 2272 */ 2273 public void setMotionEventSplittingEnabled(boolean split) { 2274 // TODO Applications really shouldn't change this setting mid-touch event, 2275 // but perhaps this should handle that case and send ACTION_CANCELs to any child views 2276 // with gestures in progress when this is changed. 2277 if (split) { 2278 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 2279 } else { 2280 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS; 2281 } 2282 } 2283 2284 /** 2285 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 2286 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 2287 */ 2288 public boolean isMotionEventSplittingEnabled() { 2289 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS; 2290 } 2291 2292 /** 2293 * {@inheritDoc} 2294 */ 2295 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 2296 2297 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { 2298 // We're already in this state, assume our ancestors are too 2299 return; 2300 } 2301 2302 if (disallowIntercept) { 2303 mGroupFlags |= FLAG_DISALLOW_INTERCEPT; 2304 } else { 2305 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 2306 } 2307 2308 // Pass it up to our parent 2309 if (mParent != null) { 2310 mParent.requestDisallowInterceptTouchEvent(disallowIntercept); 2311 } 2312 } 2313 2314 /** 2315 * Implement this method to intercept all touch screen motion events. This 2316 * allows you to watch events as they are dispatched to your children, and 2317 * take ownership of the current gesture at any point. 2318 * 2319 * <p>Using this function takes some care, as it has a fairly complicated 2320 * interaction with {@link View#onTouchEvent(MotionEvent) 2321 * View.onTouchEvent(MotionEvent)}, and using it requires implementing 2322 * that method as well as this one in the correct way. Events will be 2323 * received in the following order: 2324 * 2325 * <ol> 2326 * <li> You will receive the down event here. 2327 * <li> The down event will be handled either by a child of this view 2328 * group, or given to your own onTouchEvent() method to handle; this means 2329 * you should implement onTouchEvent() to return true, so you will 2330 * continue to see the rest of the gesture (instead of looking for 2331 * a parent view to handle it). Also, by returning true from 2332 * onTouchEvent(), you will not receive any following 2333 * events in onInterceptTouchEvent() and all touch processing must 2334 * happen in onTouchEvent() like normal. 2335 * <li> For as long as you return false from this function, each following 2336 * event (up to and including the final up) will be delivered first here 2337 * and then to the target's onTouchEvent(). 2338 * <li> If you return true from here, you will not receive any 2339 * following events: the target view will receive the same event but 2340 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further 2341 * events will be delivered to your onTouchEvent() method and no longer 2342 * appear here. 2343 * </ol> 2344 * 2345 * @param ev The motion event being dispatched down the hierarchy. 2346 * @return Return true to steal motion events from the children and have 2347 * them dispatched to this ViewGroup through onTouchEvent(). 2348 * The current target will receive an ACTION_CANCEL event, and no further 2349 * messages will be delivered here. 2350 */ 2351 public boolean onInterceptTouchEvent(MotionEvent ev) { 2352 return false; 2353 } 2354 2355 /** 2356 * {@inheritDoc} 2357 * 2358 * Looks for a view to give focus to respecting the setting specified by 2359 * {@link #getDescendantFocusability()}. 2360 * 2361 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to 2362 * find focus within the children of this group when appropriate. 2363 * 2364 * @see #FOCUS_BEFORE_DESCENDANTS 2365 * @see #FOCUS_AFTER_DESCENDANTS 2366 * @see #FOCUS_BLOCK_DESCENDANTS 2367 * @see #onRequestFocusInDescendants(int, android.graphics.Rect) 2368 */ 2369 @Override 2370 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 2371 if (DBG) { 2372 System.out.println(this + " ViewGroup.requestFocus direction=" 2373 + direction); 2374 } 2375 int descendantFocusability = getDescendantFocusability(); 2376 2377 switch (descendantFocusability) { 2378 case FOCUS_BLOCK_DESCENDANTS: 2379 return super.requestFocus(direction, previouslyFocusedRect); 2380 case FOCUS_BEFORE_DESCENDANTS: { 2381 final boolean took = super.requestFocus(direction, previouslyFocusedRect); 2382 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); 2383 } 2384 case FOCUS_AFTER_DESCENDANTS: { 2385 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); 2386 return took ? took : super.requestFocus(direction, previouslyFocusedRect); 2387 } 2388 default: 2389 throw new IllegalStateException("descendant focusability must be " 2390 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " 2391 + "but is " + descendantFocusability); 2392 } 2393 } 2394 2395 /** 2396 * Look for a descendant to call {@link View#requestFocus} on. 2397 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)} 2398 * when it wants to request focus within its children. Override this to 2399 * customize how your {@link ViewGroup} requests focus within its children. 2400 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT 2401 * @param previouslyFocusedRect The rectangle (in this View's coordinate system) 2402 * to give a finer grained hint about where focus is coming from. May be null 2403 * if there is no hint. 2404 * @return Whether focus was taken. 2405 */ 2406 @SuppressWarnings({"ConstantConditions"}) 2407 protected boolean onRequestFocusInDescendants(int direction, 2408 Rect previouslyFocusedRect) { 2409 int index; 2410 int increment; 2411 int end; 2412 int count = mChildrenCount; 2413 if ((direction & FOCUS_FORWARD) != 0) { 2414 index = 0; 2415 increment = 1; 2416 end = count; 2417 } else { 2418 index = count - 1; 2419 increment = -1; 2420 end = -1; 2421 } 2422 final View[] children = mChildren; 2423 for (int i = index; i != end; i += increment) { 2424 View child = children[i]; 2425 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2426 if (child.requestFocus(direction, previouslyFocusedRect)) { 2427 return true; 2428 } 2429 } 2430 } 2431 return false; 2432 } 2433 2434 /** 2435 * {@inheritDoc} 2436 * 2437 * @hide 2438 */ 2439 @Override 2440 public void dispatchStartTemporaryDetach() { 2441 super.dispatchStartTemporaryDetach(); 2442 final int count = mChildrenCount; 2443 final View[] children = mChildren; 2444 for (int i = 0; i < count; i++) { 2445 children[i].dispatchStartTemporaryDetach(); 2446 } 2447 } 2448 2449 /** 2450 * {@inheritDoc} 2451 * 2452 * @hide 2453 */ 2454 @Override 2455 public void dispatchFinishTemporaryDetach() { 2456 super.dispatchFinishTemporaryDetach(); 2457 final int count = mChildrenCount; 2458 final View[] children = mChildren; 2459 for (int i = 0; i < count; i++) { 2460 children[i].dispatchFinishTemporaryDetach(); 2461 } 2462 } 2463 2464 /** 2465 * {@inheritDoc} 2466 */ 2467 @Override 2468 void dispatchAttachedToWindow(AttachInfo info, int visibility) { 2469 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; 2470 super.dispatchAttachedToWindow(info, visibility); 2471 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; 2472 2473 final int count = mChildrenCount; 2474 final View[] children = mChildren; 2475 for (int i = 0; i < count; i++) { 2476 final View child = children[i]; 2477 child.dispatchAttachedToWindow(info, 2478 visibility | (child.mViewFlags & VISIBILITY_MASK)); 2479 } 2480 } 2481 2482 @Override 2483 void dispatchScreenStateChanged(int screenState) { 2484 super.dispatchScreenStateChanged(screenState); 2485 2486 final int count = mChildrenCount; 2487 final View[] children = mChildren; 2488 for (int i = 0; i < count; i++) { 2489 children[i].dispatchScreenStateChanged(screenState); 2490 } 2491 } 2492 2493 @Override 2494 boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 2495 boolean handled = false; 2496 if (includeForAccessibility()) { 2497 handled = super.dispatchPopulateAccessibilityEventInternal(event); 2498 if (handled) { 2499 return handled; 2500 } 2501 } 2502 // Let our children have a shot in populating the event. 2503 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true); 2504 try { 2505 final int childCount = children.getChildCount(); 2506 for (int i = 0; i < childCount; i++) { 2507 View child = children.getChildAt(i); 2508 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2509 handled = child.dispatchPopulateAccessibilityEvent(event); 2510 if (handled) { 2511 return handled; 2512 } 2513 } 2514 } 2515 } finally { 2516 children.recycle(); 2517 } 2518 return false; 2519 } 2520 2521 @Override 2522 void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { 2523 super.onInitializeAccessibilityNodeInfoInternal(info); 2524 if (mAttachInfo != null) { 2525 ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList; 2526 childrenForAccessibility.clear(); 2527 addChildrenForAccessibility(childrenForAccessibility); 2528 final int childrenForAccessibilityCount = childrenForAccessibility.size(); 2529 for (int i = 0; i < childrenForAccessibilityCount; i++) { 2530 View child = childrenForAccessibility.get(i); 2531 info.addChild(child); 2532 } 2533 childrenForAccessibility.clear(); 2534 } 2535 } 2536 2537 @Override 2538 void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { 2539 super.onInitializeAccessibilityEventInternal(event); 2540 event.setClassName(ViewGroup.class.getName()); 2541 } 2542 2543 /** 2544 * @hide 2545 */ 2546 @Override 2547 public void resetAccessibilityStateChanged() { 2548 super.resetAccessibilityStateChanged(); 2549 View[] children = mChildren; 2550 final int childCount = mChildrenCount; 2551 for (int i = 0; i < childCount; i++) { 2552 View child = children[i]; 2553 child.resetAccessibilityStateChanged(); 2554 } 2555 } 2556 2557 /** 2558 * {@inheritDoc} 2559 */ 2560 @Override 2561 void dispatchDetachedFromWindow() { 2562 // If we still have a touch target, we are still in the process of 2563 // dispatching motion events to a child; we need to get rid of that 2564 // child to avoid dispatching events to it after the window is torn 2565 // down. To make sure we keep the child in a consistent state, we 2566 // first send it an ACTION_CANCEL motion event. 2567 cancelAndClearTouchTargets(null); 2568 2569 // Similarly, set ACTION_EXIT to all hover targets and clear them. 2570 exitHoverTargets(); 2571 2572 // In case view is detached while transition is running 2573 mLayoutCalledWhileSuppressed = false; 2574 2575 // Tear down our drag tracking 2576 mDragNotifiedChildren = null; 2577 if (mCurrentDrag != null) { 2578 mCurrentDrag.recycle(); 2579 mCurrentDrag = null; 2580 } 2581 2582 final int count = mChildrenCount; 2583 final View[] children = mChildren; 2584 for (int i = 0; i < count; i++) { 2585 children[i].dispatchDetachedFromWindow(); 2586 } 2587 super.dispatchDetachedFromWindow(); 2588 } 2589 2590 /** 2591 * @hide 2592 */ 2593 @Override 2594 protected void internalSetPadding(int left, int top, int right, int bottom) { 2595 super.internalSetPadding(left, top, right, bottom); 2596 2597 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) { 2598 mGroupFlags |= FLAG_PADDING_NOT_NULL; 2599 } else { 2600 mGroupFlags &= ~FLAG_PADDING_NOT_NULL; 2601 } 2602 } 2603 2604 /** 2605 * {@inheritDoc} 2606 */ 2607 @Override 2608 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 2609 super.dispatchSaveInstanceState(container); 2610 final int count = mChildrenCount; 2611 final View[] children = mChildren; 2612 for (int i = 0; i < count; i++) { 2613 View c = children[i]; 2614 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 2615 c.dispatchSaveInstanceState(container); 2616 } 2617 } 2618 } 2619 2620 /** 2621 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()} 2622 * to only this view, not to its children. For use when overriding 2623 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow 2624 * subclasses to freeze their own state but not the state of their children. 2625 * 2626 * @param container the container 2627 */ 2628 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { 2629 super.dispatchSaveInstanceState(container); 2630 } 2631 2632 /** 2633 * {@inheritDoc} 2634 */ 2635 @Override 2636 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 2637 super.dispatchRestoreInstanceState(container); 2638 final int count = mChildrenCount; 2639 final View[] children = mChildren; 2640 for (int i = 0; i < count; i++) { 2641 View c = children[i]; 2642 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 2643 c.dispatchRestoreInstanceState(container); 2644 } 2645 } 2646 } 2647 2648 /** 2649 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)} 2650 * to only this view, not to its children. For use when overriding 2651 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow 2652 * subclasses to thaw their own state but not the state of their children. 2653 * 2654 * @param container the container 2655 */ 2656 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { 2657 super.dispatchRestoreInstanceState(container); 2658 } 2659 2660 /** 2661 * Enables or disables the drawing cache for each child of this view group. 2662 * 2663 * @param enabled true to enable the cache, false to dispose of it 2664 */ 2665 protected void setChildrenDrawingCacheEnabled(boolean enabled) { 2666 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) { 2667 final View[] children = mChildren; 2668 final int count = mChildrenCount; 2669 for (int i = 0; i < count; i++) { 2670 children[i].setDrawingCacheEnabled(enabled); 2671 } 2672 } 2673 } 2674 2675 @Override 2676 protected void onAnimationStart() { 2677 super.onAnimationStart(); 2678 2679 // When this ViewGroup's animation starts, build the cache for the children 2680 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2681 final int count = mChildrenCount; 2682 final View[] children = mChildren; 2683 final boolean buildCache = !isHardwareAccelerated(); 2684 2685 for (int i = 0; i < count; i++) { 2686 final View child = children[i]; 2687 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2688 child.setDrawingCacheEnabled(true); 2689 if (buildCache) { 2690 child.buildDrawingCache(true); 2691 } 2692 } 2693 } 2694 2695 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 2696 } 2697 } 2698 2699 @Override 2700 protected void onAnimationEnd() { 2701 super.onAnimationEnd(); 2702 2703 // When this ViewGroup's animation ends, destroy the cache of the children 2704 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2705 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 2706 2707 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 2708 setChildrenDrawingCacheEnabled(false); 2709 } 2710 } 2711 } 2712 2713 @Override 2714 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) { 2715 int count = mChildrenCount; 2716 int[] visibilities = null; 2717 2718 if (skipChildren) { 2719 visibilities = new int[count]; 2720 for (int i = 0; i < count; i++) { 2721 View child = getChildAt(i); 2722 visibilities[i] = child.getVisibility(); 2723 if (visibilities[i] == View.VISIBLE) { 2724 child.setVisibility(INVISIBLE); 2725 } 2726 } 2727 } 2728 2729 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren); 2730 2731 if (skipChildren) { 2732 for (int i = 0; i < count; i++) { 2733 getChildAt(i).setVisibility(visibilities[i]); 2734 } 2735 } 2736 2737 return b; 2738 } 2739 2740 /** Return true if this ViewGroup is laying out using optical bounds. */ 2741 boolean isLayoutModeOptical() { 2742 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS; 2743 } 2744 2745 Insets computeOpticalInsets() { 2746 if (isLayoutModeOptical()) { 2747 int left = 0; 2748 int top = 0; 2749 int right = 0; 2750 int bottom = 0; 2751 for (int i = 0; i < mChildrenCount; i++) { 2752 View child = getChildAt(i); 2753 if (child.getVisibility() == VISIBLE) { 2754 Insets insets = child.getOpticalInsets(); 2755 left = Math.max(left, insets.left); 2756 top = Math.max(top, insets.top); 2757 right = Math.max(right, insets.right); 2758 bottom = Math.max(bottom, insets.bottom); 2759 } 2760 } 2761 return Insets.of(left, top, right, bottom); 2762 } else { 2763 return Insets.NONE; 2764 } 2765 } 2766 2767 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { 2768 if (x1 != x2 && y1 != y2) { 2769 if (x1 > x2) { 2770 int tmp = x1; x1 = x2; x2 = tmp; 2771 } 2772 if (y1 > y2) { 2773 int tmp = y1; y1 = y2; y2 = tmp; 2774 } 2775 canvas.drawRect(x1, y1, x2, y2, paint); 2776 } 2777 } 2778 2779 private static int sign(int x) { 2780 return (x >= 0) ? 1 : -1; 2781 } 2782 2783 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) { 2784 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); 2785 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); 2786 } 2787 2788 private int dipsToPixels(int dips) { 2789 float scale = getContext().getResources().getDisplayMetrics().density; 2790 return (int) (dips * scale + 0.5f); 2791 } 2792 2793 private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, 2794 int lineLength, int lineWidth) { 2795 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); 2796 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); 2797 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); 2798 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); 2799 } 2800 2801 private static void fillDifference(Canvas canvas, 2802 int x2, int y2, int x3, int y3, 2803 int dx1, int dy1, int dx2, int dy2, Paint paint) { 2804 int x1 = x2 - dx1; 2805 int y1 = y2 - dy1; 2806 2807 int x4 = x3 + dx2; 2808 int y4 = y3 + dy2; 2809 2810 fillRect(canvas, paint, x1, y1, x4, y2); 2811 fillRect(canvas, paint, x1, y2, x2, y3); 2812 fillRect(canvas, paint, x3, y2, x4, y3); 2813 fillRect(canvas, paint, x1, y3, x4, y4); 2814 } 2815 2816 /** 2817 * @hide 2818 */ 2819 protected void onDebugDrawMargins(Canvas canvas, Paint paint) { 2820 for (int i = 0; i < getChildCount(); i++) { 2821 View c = getChildAt(i); 2822 c.getLayoutParams().onDebugDraw(c, canvas, paint); 2823 } 2824 } 2825 2826 /** 2827 * @hide 2828 */ 2829 protected void onDebugDraw(Canvas canvas) { 2830 Paint paint = getDebugPaint(); 2831 2832 // Draw optical bounds 2833 { 2834 paint.setColor(Color.RED); 2835 paint.setStyle(Paint.Style.STROKE); 2836 2837 for (int i = 0; i < getChildCount(); i++) { 2838 View c = getChildAt(i); 2839 Insets insets = c.getOpticalInsets(); 2840 2841 drawRect(canvas, paint, 2842 c.getLeft() + insets.left, 2843 c.getTop() + insets.top, 2844 c.getRight() - insets.right - 1, 2845 c.getBottom() - insets.bottom - 1); 2846 } 2847 } 2848 2849 // Draw margins 2850 { 2851 paint.setColor(Color.argb(63, 255, 0, 255)); 2852 paint.setStyle(Paint.Style.FILL); 2853 2854 onDebugDrawMargins(canvas, paint); 2855 } 2856 2857 // Draw clip bounds 2858 { 2859 paint.setColor(Color.rgb(63, 127, 255)); 2860 paint.setStyle(Paint.Style.FILL); 2861 2862 int lineLength = dipsToPixels(8); 2863 int lineWidth = dipsToPixels(1); 2864 for (int i = 0; i < getChildCount(); i++) { 2865 View c = getChildAt(i); 2866 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), 2867 paint, lineLength, lineWidth); 2868 } 2869 } 2870 } 2871 2872 /** 2873 * {@inheritDoc} 2874 */ 2875 @Override 2876 protected void dispatchDraw(Canvas canvas) { 2877 final int count = mChildrenCount; 2878 final View[] children = mChildren; 2879 int flags = mGroupFlags; 2880 2881 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { 2882 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 2883 2884 final boolean buildCache = !isHardwareAccelerated(); 2885 for (int i = 0; i < count; i++) { 2886 final View child = children[i]; 2887 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2888 final LayoutParams params = child.getLayoutParams(); 2889 attachLayoutAnimationParameters(child, params, i, count); 2890 bindLayoutAnimation(child); 2891 if (cache) { 2892 child.setDrawingCacheEnabled(true); 2893 if (buildCache) { 2894 child.buildDrawingCache(true); 2895 } 2896 } 2897 } 2898 } 2899 2900 final LayoutAnimationController controller = mLayoutAnimationController; 2901 if (controller.willOverlap()) { 2902 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE; 2903 } 2904 2905 controller.start(); 2906 2907 mGroupFlags &= ~FLAG_RUN_ANIMATION; 2908 mGroupFlags &= ~FLAG_ANIMATION_DONE; 2909 2910 if (cache) { 2911 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 2912 } 2913 2914 if (mAnimationListener != null) { 2915 mAnimationListener.onAnimationStart(controller.getAnimation()); 2916 } 2917 } 2918 2919 int saveCount = 0; 2920 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 2921 if (clipToPadding) { 2922 saveCount = canvas.save(); 2923 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, 2924 mScrollX + mRight - mLeft - mPaddingRight, 2925 mScrollY + mBottom - mTop - mPaddingBottom); 2926 2927 } 2928 2929 // We will draw our child's animation, let's reset the flag 2930 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION; 2931 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; 2932 2933 boolean more = false; 2934 final long drawingTime = getDrawingTime(); 2935 2936 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { 2937 for (int i = 0; i < count; i++) { 2938 final View child = children[i]; 2939 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 2940 more |= drawChild(canvas, child, drawingTime); 2941 } 2942 } 2943 } else { 2944 for (int i = 0; i < count; i++) { 2945 final View child = children[getChildDrawingOrder(count, i)]; 2946 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 2947 more |= drawChild(canvas, child, drawingTime); 2948 } 2949 } 2950 } 2951 2952 // Draw any disappearing views that have animations 2953 if (mDisappearingChildren != null) { 2954 final ArrayList<View> disappearingChildren = mDisappearingChildren; 2955 final int disappearingCount = disappearingChildren.size() - 1; 2956 // Go backwards -- we may delete as animations finish 2957 for (int i = disappearingCount; i >= 0; i--) { 2958 final View child = disappearingChildren.get(i); 2959 more |= drawChild(canvas, child, drawingTime); 2960 } 2961 } 2962 2963 if (debugDraw()) { 2964 onDebugDraw(canvas); 2965 } 2966 2967 if (clipToPadding) { 2968 canvas.restoreToCount(saveCount); 2969 } 2970 2971 // mGroupFlags might have been updated by drawChild() 2972 flags = mGroupFlags; 2973 2974 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { 2975 invalidate(true); 2976 } 2977 2978 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && 2979 mLayoutAnimationController.isDone() && !more) { 2980 // We want to erase the drawing cache and notify the listener after the 2981 // next frame is drawn because one extra invalidate() is caused by 2982 // drawChild() after the animation is over 2983 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; 2984 final Runnable end = new Runnable() { 2985 public void run() { 2986 notifyAnimationListener(); 2987 } 2988 }; 2989 post(end); 2990 } 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 mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED) 3059 == PFLAG_INVALIDATED; 3060 mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED; 3061 mOverlay.getDisplayList(); 3062 mOverlay.mRecreateDisplayList = false; 3063 } 3064 } 3065 3066 /** 3067 * Draw one child of this View Group. This method is responsible for getting 3068 * the canvas in the right state. This includes clipping, translating so 3069 * that the child's scrolled origin is at 0, 0, and applying any animation 3070 * transformations. 3071 * 3072 * @param canvas The canvas on which to draw the child 3073 * @param child Who to draw 3074 * @param drawingTime The time at which draw is occurring 3075 * @return True if an invalidate() was issued 3076 */ 3077 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 3078 return child.draw(canvas, this, drawingTime); 3079 } 3080 3081 /** 3082 * By default, children are clipped to their bounds before drawing. This 3083 * allows view groups to override this behavior for animations, etc. 3084 * 3085 * @param clipChildren true to clip children to their bounds, 3086 * false otherwise 3087 * @attr ref android.R.styleable#ViewGroup_clipChildren 3088 */ 3089 public void setClipChildren(boolean clipChildren) { 3090 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN; 3091 if (clipChildren != previousValue) { 3092 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); 3093 for (int i = 0; i < mChildrenCount; ++i) { 3094 View child = getChildAt(i); 3095 if (child.mDisplayList != null) { 3096 child.mDisplayList.setClipChildren(clipChildren); 3097 } 3098 } 3099 } 3100 } 3101 3102 /** 3103 * By default, children are clipped to the padding of the ViewGroup. This 3104 * allows view groups to override this behavior 3105 * 3106 * @param clipToPadding true to clip children to the padding of the 3107 * group, false otherwise 3108 * @attr ref android.R.styleable#ViewGroup_clipToPadding 3109 */ 3110 public void setClipToPadding(boolean clipToPadding) { 3111 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding); 3112 } 3113 3114 /** 3115 * {@inheritDoc} 3116 */ 3117 @Override 3118 public void dispatchSetSelected(boolean selected) { 3119 final View[] children = mChildren; 3120 final int count = mChildrenCount; 3121 for (int i = 0; i < count; i++) { 3122 children[i].setSelected(selected); 3123 } 3124 } 3125 3126 /** 3127 * {@inheritDoc} 3128 */ 3129 @Override 3130 public void dispatchSetActivated(boolean activated) { 3131 final View[] children = mChildren; 3132 final int count = mChildrenCount; 3133 for (int i = 0; i < count; i++) { 3134 children[i].setActivated(activated); 3135 } 3136 } 3137 3138 @Override 3139 protected void dispatchSetPressed(boolean pressed) { 3140 final View[] children = mChildren; 3141 final int count = mChildrenCount; 3142 for (int i = 0; i < count; i++) { 3143 final View child = children[i]; 3144 // Children that are clickable on their own should not 3145 // show a pressed state when their parent view does. 3146 // Clearing a pressed state always propagates. 3147 if (!pressed || (!child.isClickable() && !child.isLongClickable())) { 3148 child.setPressed(pressed); 3149 } 3150 } 3151 } 3152 3153 /** 3154 * When this property is set to true, this ViewGroup supports static transformations on 3155 * children; this causes 3156 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 3157 * invoked when a child is drawn. 3158 * 3159 * Any subclass overriding 3160 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 3161 * set this property to true. 3162 * 3163 * @param enabled True to enable static transformations on children, false otherwise. 3164 * 3165 * @see #getChildStaticTransformation(View, android.view.animation.Transformation) 3166 */ 3167 protected void setStaticTransformationsEnabled(boolean enabled) { 3168 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled); 3169 } 3170 3171 /** 3172 * Sets <code>t</code> to be the static transformation of the child, if set, returning a 3173 * boolean to indicate whether a static transform was set. The default implementation 3174 * simply returns <code>false</code>; subclasses may override this method for different 3175 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true 3176 * for this method to be called. 3177 * 3178 * @param child The child view whose static transform is being requested 3179 * @param t The Transformation which will hold the result 3180 * @return true if the transformation was set, false otherwise 3181 * @see #setStaticTransformationsEnabled(boolean) 3182 */ 3183 protected boolean getChildStaticTransformation(View child, Transformation t) { 3184 return false; 3185 } 3186 3187 /** 3188 * {@hide} 3189 */ 3190 @Override 3191 protected View findViewTraversal(int id) { 3192 if (id == mID) { 3193 return this; 3194 } 3195 3196 final View[] where = mChildren; 3197 final int len = mChildrenCount; 3198 3199 for (int i = 0; i < len; i++) { 3200 View v = where[i]; 3201 3202 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 3203 v = v.findViewById(id); 3204 3205 if (v != null) { 3206 return v; 3207 } 3208 } 3209 } 3210 3211 return null; 3212 } 3213 3214 /** 3215 * {@hide} 3216 */ 3217 @Override 3218 protected View findViewWithTagTraversal(Object tag) { 3219 if (tag != null && tag.equals(mTag)) { 3220 return this; 3221 } 3222 3223 final View[] where = mChildren; 3224 final int len = mChildrenCount; 3225 3226 for (int i = 0; i < len; i++) { 3227 View v = where[i]; 3228 3229 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 3230 v = v.findViewWithTag(tag); 3231 3232 if (v != null) { 3233 return v; 3234 } 3235 } 3236 } 3237 3238 return null; 3239 } 3240 3241 /** 3242 * {@hide} 3243 */ 3244 @Override 3245 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { 3246 if (predicate.apply(this)) { 3247 return this; 3248 } 3249 3250 final View[] where = mChildren; 3251 final int len = mChildrenCount; 3252 3253 for (int i = 0; i < len; i++) { 3254 View v = where[i]; 3255 3256 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 3257 v = v.findViewByPredicate(predicate); 3258 3259 if (v != null) { 3260 return v; 3261 } 3262 } 3263 } 3264 3265 return null; 3266 } 3267 3268 /** 3269 * <p>Adds a child view. If no layout parameters are already set on the child, the 3270 * default parameters for this ViewGroup are set on the child.</p> 3271 * 3272 * <p><strong>Note:</strong> do not invoke this method from 3273 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3274 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3275 * 3276 * @param child the child view to add 3277 * 3278 * @see #generateDefaultLayoutParams() 3279 */ 3280 public void addView(View child) { 3281 addView(child, -1); 3282 } 3283 3284 /** 3285 * Adds a child view. If no layout parameters are already set on the child, the 3286 * default parameters for this ViewGroup are set on the child. 3287 * 3288 * <p><strong>Note:</strong> do not invoke this method from 3289 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3290 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3291 * 3292 * @param child the child view to add 3293 * @param index the position at which to add the child 3294 * 3295 * @see #generateDefaultLayoutParams() 3296 */ 3297 public void addView(View child, int index) { 3298 LayoutParams params = child.getLayoutParams(); 3299 if (params == null) { 3300 params = generateDefaultLayoutParams(); 3301 if (params == null) { 3302 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); 3303 } 3304 } 3305 addView(child, index, params); 3306 } 3307 3308 /** 3309 * Adds a child view with this ViewGroup's default layout parameters and the 3310 * specified width and height. 3311 * 3312 * <p><strong>Note:</strong> do not invoke this method from 3313 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3314 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3315 * 3316 * @param child the child view to add 3317 */ 3318 public void addView(View child, int width, int height) { 3319 final LayoutParams params = generateDefaultLayoutParams(); 3320 params.width = width; 3321 params.height = height; 3322 addView(child, -1, params); 3323 } 3324 3325 /** 3326 * Adds a child view with the specified layout parameters. 3327 * 3328 * <p><strong>Note:</strong> do not invoke this method from 3329 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3330 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3331 * 3332 * @param child the child view to add 3333 * @param params the layout parameters to set on the child 3334 */ 3335 public void addView(View child, LayoutParams params) { 3336 addView(child, -1, params); 3337 } 3338 3339 /** 3340 * Adds a child view with the specified layout parameters. 3341 * 3342 * <p><strong>Note:</strong> do not invoke this method from 3343 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3344 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3345 * 3346 * @param child the child view to add 3347 * @param index the position at which to add the child 3348 * @param params the layout parameters to set on the child 3349 */ 3350 public void addView(View child, int index, LayoutParams params) { 3351 if (DBG) { 3352 System.out.println(this + " addView"); 3353 } 3354 3355 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 3356 // therefore, we call requestLayout() on ourselves before, so that the child's request 3357 // will be blocked at our level 3358 requestLayout(); 3359 invalidate(true); 3360 addViewInner(child, index, params, false); 3361 } 3362 3363 /** 3364 * {@inheritDoc} 3365 */ 3366 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 3367 if (!checkLayoutParams(params)) { 3368 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this); 3369 } 3370 if (view.mParent != this) { 3371 throw new IllegalArgumentException("Given view not a child of " + this); 3372 } 3373 view.setLayoutParams(params); 3374 } 3375 3376 /** 3377 * {@inheritDoc} 3378 */ 3379 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 3380 return p != null; 3381 } 3382 3383 /** 3384 * Interface definition for a callback to be invoked when the hierarchy 3385 * within this view changed. The hierarchy changes whenever a child is added 3386 * to or removed from this view. 3387 */ 3388 public interface OnHierarchyChangeListener { 3389 /** 3390 * Called when a new child is added to a parent view. 3391 * 3392 * @param parent the view in which a child was added 3393 * @param child the new child view added in the hierarchy 3394 */ 3395 void onChildViewAdded(View parent, View child); 3396 3397 /** 3398 * Called when a child is removed from a parent view. 3399 * 3400 * @param parent the view from which the child was removed 3401 * @param child the child removed from the hierarchy 3402 */ 3403 void onChildViewRemoved(View parent, View child); 3404 } 3405 3406 /** 3407 * Register a callback to be invoked when a child is added to or removed 3408 * from this view. 3409 * 3410 * @param listener the callback to invoke on hierarchy change 3411 */ 3412 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 3413 mOnHierarchyChangeListener = listener; 3414 } 3415 3416 /** 3417 * @hide 3418 */ 3419 protected void onViewAdded(View child) { 3420 if (mOnHierarchyChangeListener != null) { 3421 mOnHierarchyChangeListener.onChildViewAdded(this, child); 3422 } 3423 } 3424 3425 /** 3426 * @hide 3427 */ 3428 protected void onViewRemoved(View child) { 3429 if (mOnHierarchyChangeListener != null) { 3430 mOnHierarchyChangeListener.onChildViewRemoved(this, child); 3431 } 3432 } 3433 3434 /** 3435 * Adds a view during layout. This is useful if in your onLayout() method, 3436 * you need to add more views (as does the list view for example). 3437 * 3438 * If index is negative, it means put it at the end of the list. 3439 * 3440 * @param child the view to add to the group 3441 * @param index the index at which the child must be added 3442 * @param params the layout parameters to associate with the child 3443 * @return true if the child was added, false otherwise 3444 */ 3445 protected boolean addViewInLayout(View child, int index, LayoutParams params) { 3446 return addViewInLayout(child, index, params, false); 3447 } 3448 3449 /** 3450 * Adds a view during layout. This is useful if in your onLayout() method, 3451 * you need to add more views (as does the list view for example). 3452 * 3453 * If index is negative, it means put it at the end of the list. 3454 * 3455 * @param child the view to add to the group 3456 * @param index the index at which the child must be added 3457 * @param params the layout parameters to associate with the child 3458 * @param preventRequestLayout if true, calling this method will not trigger a 3459 * layout request on child 3460 * @return true if the child was added, false otherwise 3461 */ 3462 protected boolean addViewInLayout(View child, int index, LayoutParams params, 3463 boolean preventRequestLayout) { 3464 child.mParent = null; 3465 addViewInner(child, index, params, preventRequestLayout); 3466 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; 3467 return true; 3468 } 3469 3470 /** 3471 * Prevents the specified child to be laid out during the next layout pass. 3472 * 3473 * @param child the child on which to perform the cleanup 3474 */ 3475 protected void cleanupLayoutState(View child) { 3476 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT; 3477 } 3478 3479 private void addViewInner(View child, int index, LayoutParams params, 3480 boolean preventRequestLayout) { 3481 3482 if (mTransition != null) { 3483 // Don't prevent other add transitions from completing, but cancel remove 3484 // transitions to let them complete the process before we add to the container 3485 mTransition.cancel(LayoutTransition.DISAPPEARING); 3486 } 3487 3488 if (child.getParent() != null) { 3489 throw new IllegalStateException("The specified child already has a parent. " + 3490 "You must call removeView() on the child's parent first."); 3491 } 3492 3493 if (mTransition != null) { 3494 mTransition.addChild(this, child); 3495 } 3496 3497 if (!checkLayoutParams(params)) { 3498 params = generateLayoutParams(params); 3499 } 3500 3501 if (preventRequestLayout) { 3502 child.mLayoutParams = params; 3503 } else { 3504 child.setLayoutParams(params); 3505 } 3506 3507 if (index < 0) { 3508 index = mChildrenCount; 3509 } 3510 3511 addInArray(child, index); 3512 3513 // tell our children 3514 if (preventRequestLayout) { 3515 child.assignParent(this); 3516 } else { 3517 child.mParent = this; 3518 } 3519 3520 if (child.hasFocus()) { 3521 requestChildFocus(child, child.findFocus()); 3522 } 3523 3524 AttachInfo ai = mAttachInfo; 3525 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { 3526 boolean lastKeepOn = ai.mKeepScreenOn; 3527 ai.mKeepScreenOn = false; 3528 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 3529 if (ai.mKeepScreenOn) { 3530 needGlobalAttributesUpdate(true); 3531 } 3532 ai.mKeepScreenOn = lastKeepOn; 3533 } 3534 3535 if (child.isLayoutDirectionInherited()) { 3536 child.resetRtlProperties(); 3537 } 3538 3539 onViewAdded(child); 3540 3541 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 3542 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 3543 } 3544 3545 if (child.hasTransientState()) { 3546 childHasTransientStateChanged(child, true); 3547 } 3548 } 3549 3550 private void addInArray(View child, int index) { 3551 View[] children = mChildren; 3552 final int count = mChildrenCount; 3553 final int size = children.length; 3554 if (index == count) { 3555 if (size == count) { 3556 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 3557 System.arraycopy(children, 0, mChildren, 0, size); 3558 children = mChildren; 3559 } 3560 children[mChildrenCount++] = child; 3561 } else if (index < count) { 3562 if (size == count) { 3563 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 3564 System.arraycopy(children, 0, mChildren, 0, index); 3565 System.arraycopy(children, index, mChildren, index + 1, count - index); 3566 children = mChildren; 3567 } else { 3568 System.arraycopy(children, index, children, index + 1, count - index); 3569 } 3570 children[index] = child; 3571 mChildrenCount++; 3572 if (mLastTouchDownIndex >= index) { 3573 mLastTouchDownIndex++; 3574 } 3575 } else { 3576 throw new IndexOutOfBoundsException("index=" + index + " count=" + count); 3577 } 3578 } 3579 3580 // This method also sets the child's mParent to null 3581 private void removeFromArray(int index) { 3582 final View[] children = mChildren; 3583 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) { 3584 children[index].mParent = null; 3585 } 3586 final int count = mChildrenCount; 3587 if (index == count - 1) { 3588 children[--mChildrenCount] = null; 3589 } else if (index >= 0 && index < count) { 3590 System.arraycopy(children, index + 1, children, index, count - index - 1); 3591 children[--mChildrenCount] = null; 3592 } else { 3593 throw new IndexOutOfBoundsException(); 3594 } 3595 if (mLastTouchDownIndex == index) { 3596 mLastTouchDownTime = 0; 3597 mLastTouchDownIndex = -1; 3598 } else if (mLastTouchDownIndex > index) { 3599 mLastTouchDownIndex--; 3600 } 3601 } 3602 3603 // This method also sets the children's mParent to null 3604 private void removeFromArray(int start, int count) { 3605 final View[] children = mChildren; 3606 final int childrenCount = mChildrenCount; 3607 3608 start = Math.max(0, start); 3609 final int end = Math.min(childrenCount, start + count); 3610 3611 if (start == end) { 3612 return; 3613 } 3614 3615 if (end == childrenCount) { 3616 for (int i = start; i < end; i++) { 3617 children[i].mParent = null; 3618 children[i] = null; 3619 } 3620 } else { 3621 for (int i = start; i < end; i++) { 3622 children[i].mParent = null; 3623 } 3624 3625 // Since we're looping above, we might as well do the copy, but is arraycopy() 3626 // faster than the extra 2 bounds checks we would do in the loop? 3627 System.arraycopy(children, end, children, start, childrenCount - end); 3628 3629 for (int i = childrenCount - (end - start); i < childrenCount; i++) { 3630 children[i] = null; 3631 } 3632 } 3633 3634 mChildrenCount -= (end - start); 3635 } 3636 3637 private void bindLayoutAnimation(View child) { 3638 Animation a = mLayoutAnimationController.getAnimationForView(child); 3639 child.setAnimation(a); 3640 } 3641 3642 /** 3643 * Subclasses should override this method to set layout animation 3644 * parameters on the supplied child. 3645 * 3646 * @param child the child to associate with animation parameters 3647 * @param params the child's layout parameters which hold the animation 3648 * parameters 3649 * @param index the index of the child in the view group 3650 * @param count the number of children in the view group 3651 */ 3652 protected void attachLayoutAnimationParameters(View child, 3653 LayoutParams params, int index, int count) { 3654 LayoutAnimationController.AnimationParameters animationParams = 3655 params.layoutAnimationParameters; 3656 if (animationParams == null) { 3657 animationParams = new LayoutAnimationController.AnimationParameters(); 3658 params.layoutAnimationParameters = animationParams; 3659 } 3660 3661 animationParams.count = count; 3662 animationParams.index = index; 3663 } 3664 3665 /** 3666 * {@inheritDoc} 3667 * 3668 * <p><strong>Note:</strong> do not invoke this method from 3669 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3670 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3671 */ 3672 public void removeView(View view) { 3673 removeViewInternal(view); 3674 requestLayout(); 3675 invalidate(true); 3676 } 3677 3678 /** 3679 * Removes a view during layout. This is useful if in your onLayout() method, 3680 * you need to remove more views. 3681 * 3682 * <p><strong>Note:</strong> do not invoke this method from 3683 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3684 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3685 * 3686 * @param view the view to remove from the group 3687 */ 3688 public void removeViewInLayout(View view) { 3689 removeViewInternal(view); 3690 } 3691 3692 /** 3693 * Removes a range of views during layout. This is useful if in your onLayout() method, 3694 * you need to remove more views. 3695 * 3696 * <p><strong>Note:</strong> do not invoke this method from 3697 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3698 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3699 * 3700 * @param start the index of the first view to remove from the group 3701 * @param count the number of views to remove from the group 3702 */ 3703 public void removeViewsInLayout(int start, int count) { 3704 removeViewsInternal(start, count); 3705 } 3706 3707 /** 3708 * Removes the view at the specified position in the group. 3709 * 3710 * <p><strong>Note:</strong> do not invoke this method from 3711 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3712 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3713 * 3714 * @param index the position in the group of the view to remove 3715 */ 3716 public void removeViewAt(int index) { 3717 removeViewInternal(index, getChildAt(index)); 3718 requestLayout(); 3719 invalidate(true); 3720 } 3721 3722 /** 3723 * Removes the specified range of views from the group. 3724 * 3725 * <p><strong>Note:</strong> do not invoke this method from 3726 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3727 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3728 * 3729 * @param start the first position in the group of the range of views to remove 3730 * @param count the number of views to remove 3731 */ 3732 public void removeViews(int start, int count) { 3733 removeViewsInternal(start, count); 3734 requestLayout(); 3735 invalidate(true); 3736 } 3737 3738 private void removeViewInternal(View view) { 3739 final int index = indexOfChild(view); 3740 if (index >= 0) { 3741 removeViewInternal(index, view); 3742 } 3743 } 3744 3745 private void removeViewInternal(int index, View view) { 3746 3747 if (mTransition != null) { 3748 mTransition.removeChild(this, view); 3749 } 3750 3751 boolean clearChildFocus = false; 3752 if (view == mFocused) { 3753 view.unFocus(); 3754 clearChildFocus = true; 3755 } 3756 3757 if (view.isAccessibilityFocused()) { 3758 view.clearAccessibilityFocus(); 3759 } 3760 3761 cancelTouchTarget(view); 3762 cancelHoverTarget(view); 3763 3764 if (view.getAnimation() != null || 3765 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3766 addDisappearingView(view); 3767 } else if (view.mAttachInfo != null) { 3768 view.dispatchDetachedFromWindow(); 3769 } 3770 3771 if (view.hasTransientState()) { 3772 childHasTransientStateChanged(view, false); 3773 } 3774 3775 needGlobalAttributesUpdate(false); 3776 3777 removeFromArray(index); 3778 3779 if (clearChildFocus) { 3780 clearChildFocus(view); 3781 if (!rootViewRequestFocus()) { 3782 notifyGlobalFocusCleared(this); 3783 } 3784 } 3785 3786 onViewRemoved(view); 3787 } 3788 3789 /** 3790 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 3791 * not null, changes in layout which occur because of children being added to or removed from 3792 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 3793 * object. By default, the transition object is null (so layout changes are not animated). 3794 * 3795 * @param transition The LayoutTransition object that will animated changes in layout. A value 3796 * of <code>null</code> means no transition will run on layout changes. 3797 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 3798 */ 3799 public void setLayoutTransition(LayoutTransition transition) { 3800 if (mTransition != null) { 3801 mTransition.removeTransitionListener(mLayoutTransitionListener); 3802 } 3803 mTransition = transition; 3804 if (mTransition != null) { 3805 mTransition.addTransitionListener(mLayoutTransitionListener); 3806 } 3807 } 3808 3809 /** 3810 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 3811 * not null, changes in layout which occur because of children being added to or removed from 3812 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 3813 * object. By default, the transition object is null (so layout changes are not animated). 3814 * 3815 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout. 3816 * A value of <code>null</code> means no transition will run on layout changes. 3817 */ 3818 public LayoutTransition getLayoutTransition() { 3819 return mTransition; 3820 } 3821 3822 private void removeViewsInternal(int start, int count) { 3823 final View focused = mFocused; 3824 final boolean detach = mAttachInfo != null; 3825 boolean clearChildFocus = false; 3826 3827 final View[] children = mChildren; 3828 final int end = start + count; 3829 3830 for (int i = start; i < end; i++) { 3831 final View view = children[i]; 3832 3833 if (mTransition != null) { 3834 mTransition.removeChild(this, view); 3835 } 3836 3837 if (view == focused) { 3838 view.unFocus(); 3839 clearChildFocus = true; 3840 } 3841 3842 if (view.isAccessibilityFocused()) { 3843 view.clearAccessibilityFocus(); 3844 } 3845 3846 cancelTouchTarget(view); 3847 cancelHoverTarget(view); 3848 3849 if (view.getAnimation() != null || 3850 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3851 addDisappearingView(view); 3852 } else if (detach) { 3853 view.dispatchDetachedFromWindow(); 3854 } 3855 3856 if (view.hasTransientState()) { 3857 childHasTransientStateChanged(view, false); 3858 } 3859 3860 needGlobalAttributesUpdate(false); 3861 3862 onViewRemoved(view); 3863 } 3864 3865 removeFromArray(start, count); 3866 3867 if (clearChildFocus) { 3868 clearChildFocus(focused); 3869 if (!rootViewRequestFocus()) { 3870 notifyGlobalFocusCleared(focused); 3871 } 3872 } 3873 } 3874 3875 /** 3876 * Call this method to remove all child views from the 3877 * ViewGroup. 3878 * 3879 * <p><strong>Note:</strong> do not invoke this method from 3880 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3881 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3882 */ 3883 public void removeAllViews() { 3884 removeAllViewsInLayout(); 3885 requestLayout(); 3886 invalidate(true); 3887 } 3888 3889 /** 3890 * Called by a ViewGroup subclass to remove child views from itself, 3891 * when it must first know its size on screen before it can calculate how many 3892 * child views it will render. An example is a Gallery or a ListView, which 3893 * may "have" 50 children, but actually only render the number of children 3894 * that can currently fit inside the object on screen. Do not call 3895 * this method unless you are extending ViewGroup and understand the 3896 * view measuring and layout pipeline. 3897 * 3898 * <p><strong>Note:</strong> do not invoke this method from 3899 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3900 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3901 */ 3902 public void removeAllViewsInLayout() { 3903 final int count = mChildrenCount; 3904 if (count <= 0) { 3905 return; 3906 } 3907 3908 final View[] children = mChildren; 3909 mChildrenCount = 0; 3910 3911 final View focused = mFocused; 3912 final boolean detach = mAttachInfo != null; 3913 boolean clearChildFocus = false; 3914 3915 needGlobalAttributesUpdate(false); 3916 3917 for (int i = count - 1; i >= 0; i--) { 3918 final View view = children[i]; 3919 3920 if (mTransition != null) { 3921 mTransition.removeChild(this, view); 3922 } 3923 3924 if (view == focused) { 3925 view.unFocus(); 3926 clearChildFocus = true; 3927 } 3928 3929 if (view.isAccessibilityFocused()) { 3930 view.clearAccessibilityFocus(); 3931 } 3932 3933 cancelTouchTarget(view); 3934 cancelHoverTarget(view); 3935 3936 if (view.getAnimation() != null || 3937 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3938 addDisappearingView(view); 3939 } else if (detach) { 3940 view.dispatchDetachedFromWindow(); 3941 } 3942 3943 if (view.hasTransientState()) { 3944 childHasTransientStateChanged(view, false); 3945 } 3946 3947 onViewRemoved(view); 3948 3949 view.mParent = null; 3950 children[i] = null; 3951 } 3952 3953 if (clearChildFocus) { 3954 clearChildFocus(focused); 3955 if (!rootViewRequestFocus()) { 3956 notifyGlobalFocusCleared(focused); 3957 } 3958 } 3959 } 3960 3961 /** 3962 * Finishes the removal of a detached view. This method will dispatch the detached from 3963 * window event and notify the hierarchy change listener. 3964 * <p> 3965 * This method is intended to be lightweight and makes no assumptions about whether the 3966 * parent or child should be redrawn. Proper use of this method will include also making 3967 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls. 3968 * For example, callers can {@link #post(Runnable) post} a {@link Runnable} 3969 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove 3970 * calls are finished, causing layout to be run prior to redrawing the view hierarchy. 3971 * 3972 * @param child the child to be definitely removed from the view hierarchy 3973 * @param animate if true and the view has an animation, the view is placed in the 3974 * disappearing views list, otherwise, it is detached from the window 3975 * 3976 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3977 * @see #detachAllViewsFromParent() 3978 * @see #detachViewFromParent(View) 3979 * @see #detachViewFromParent(int) 3980 */ 3981 protected void removeDetachedView(View child, boolean animate) { 3982 if (mTransition != null) { 3983 mTransition.removeChild(this, child); 3984 } 3985 3986 if (child == mFocused) { 3987 child.clearFocus(); 3988 } 3989 3990 child.clearAccessibilityFocus(); 3991 3992 cancelTouchTarget(child); 3993 cancelHoverTarget(child); 3994 3995 if ((animate && child.getAnimation() != null) || 3996 (mTransitioningViews != null && mTransitioningViews.contains(child))) { 3997 addDisappearingView(child); 3998 } else if (child.mAttachInfo != null) { 3999 child.dispatchDetachedFromWindow(); 4000 } 4001 4002 if (child.hasTransientState()) { 4003 childHasTransientStateChanged(child, false); 4004 } 4005 4006 onViewRemoved(child); 4007 } 4008 4009 /** 4010 * Attaches a view to this view group. Attaching a view assigns this group as the parent, 4011 * sets the layout parameters and puts the view in the list of children so that 4012 * it can be retrieved by calling {@link #getChildAt(int)}. 4013 * <p> 4014 * This method is intended to be lightweight and makes no assumptions about whether the 4015 * parent or child should be redrawn. Proper use of this method will include also making 4016 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls. 4017 * For example, callers can {@link #post(Runnable) post} a {@link Runnable} 4018 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach 4019 * calls are finished, causing layout to be run prior to redrawing the view hierarchy. 4020 * <p> 4021 * This method should be called only for views which were detached from their parent. 4022 * 4023 * @param child the child to attach 4024 * @param index the index at which the child should be attached 4025 * @param params the layout parameters of the child 4026 * 4027 * @see #removeDetachedView(View, boolean) 4028 * @see #detachAllViewsFromParent() 4029 * @see #detachViewFromParent(View) 4030 * @see #detachViewFromParent(int) 4031 */ 4032 protected void attachViewToParent(View child, int index, LayoutParams params) { 4033 child.mLayoutParams = params; 4034 4035 if (index < 0) { 4036 index = mChildrenCount; 4037 } 4038 4039 addInArray(child, index); 4040 4041 child.mParent = this; 4042 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK 4043 & ~PFLAG_DRAWING_CACHE_VALID) 4044 | PFLAG_DRAWN | PFLAG_INVALIDATED; 4045 this.mPrivateFlags |= PFLAG_INVALIDATED; 4046 4047 if (child.hasFocus()) { 4048 requestChildFocus(child, child.findFocus()); 4049 } 4050 } 4051 4052 /** 4053 * Detaches a view from its parent. Detaching a view should be followed 4054 * either by a call to 4055 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4056 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4057 * temporary; reattachment or removal should happen within the same drawing cycle as 4058 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4059 * call to {@link #getChildAt(int)}. 4060 * 4061 * @param child the child to detach 4062 * 4063 * @see #detachViewFromParent(int) 4064 * @see #detachViewsFromParent(int, int) 4065 * @see #detachAllViewsFromParent() 4066 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4067 * @see #removeDetachedView(View, boolean) 4068 */ 4069 protected void detachViewFromParent(View child) { 4070 removeFromArray(indexOfChild(child)); 4071 } 4072 4073 /** 4074 * Detaches a view from its parent. Detaching a view should be followed 4075 * either by a call to 4076 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4077 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4078 * temporary; reattachment or removal should happen within the same drawing cycle as 4079 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4080 * call to {@link #getChildAt(int)}. 4081 * 4082 * @param index the index of the child to detach 4083 * 4084 * @see #detachViewFromParent(View) 4085 * @see #detachAllViewsFromParent() 4086 * @see #detachViewsFromParent(int, int) 4087 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4088 * @see #removeDetachedView(View, boolean) 4089 */ 4090 protected void detachViewFromParent(int index) { 4091 removeFromArray(index); 4092 } 4093 4094 /** 4095 * Detaches a range of views from their parents. Detaching a view should be followed 4096 * either by a call to 4097 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4098 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4099 * temporary; reattachment or removal should happen within the same drawing cycle as 4100 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4101 * call to {@link #getChildAt(int)}. 4102 * 4103 * @param start the first index of the childrend range to detach 4104 * @param count the number of children to detach 4105 * 4106 * @see #detachViewFromParent(View) 4107 * @see #detachViewFromParent(int) 4108 * @see #detachAllViewsFromParent() 4109 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4110 * @see #removeDetachedView(View, boolean) 4111 */ 4112 protected void detachViewsFromParent(int start, int count) { 4113 removeFromArray(start, count); 4114 } 4115 4116 /** 4117 * Detaches all views from the parent. Detaching a view should be followed 4118 * either by a call to 4119 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4120 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4121 * temporary; reattachment or removal should happen within the same drawing cycle as 4122 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4123 * call to {@link #getChildAt(int)}. 4124 * 4125 * @see #detachViewFromParent(View) 4126 * @see #detachViewFromParent(int) 4127 * @see #detachViewsFromParent(int, int) 4128 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4129 * @see #removeDetachedView(View, boolean) 4130 */ 4131 protected void detachAllViewsFromParent() { 4132 final int count = mChildrenCount; 4133 if (count <= 0) { 4134 return; 4135 } 4136 4137 final View[] children = mChildren; 4138 mChildrenCount = 0; 4139 4140 for (int i = count - 1; i >= 0; i--) { 4141 children[i].mParent = null; 4142 children[i] = null; 4143 } 4144 } 4145 4146 /** 4147 * Don't call or override this method. It is used for the implementation of 4148 * the view hierarchy. 4149 */ 4150 public final void invalidateChild(View child, final Rect dirty) { 4151 ViewParent parent = this; 4152 4153 final AttachInfo attachInfo = mAttachInfo; 4154 if (attachInfo != null) { 4155 // If the child is drawing an animation, we want to copy this flag onto 4156 // ourselves and the parent to make sure the invalidate request goes 4157 // through 4158 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) 4159 == PFLAG_DRAW_ANIMATION; 4160 4161 // Check whether the child that requests the invalidate is fully opaque 4162 // Views being animated or transformed are not considered opaque because we may 4163 // be invalidating their old position and need the parent to paint behind them. 4164 Matrix childMatrix = child.getMatrix(); 4165 final boolean isOpaque = child.isOpaque() && !drawAnimation && 4166 child.getAnimation() == null && childMatrix.isIdentity(); 4167 // Mark the child as dirty, using the appropriate flag 4168 // Make sure we do not set both flags at the same time 4169 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY; 4170 4171 if (child.mLayerType != LAYER_TYPE_NONE) { 4172 mPrivateFlags |= PFLAG_INVALIDATED; 4173 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 4174 child.mLocalDirtyRect.union(dirty); 4175 } 4176 4177 final int[] location = attachInfo.mInvalidateChildLocation; 4178 location[CHILD_LEFT_INDEX] = child.mLeft; 4179 location[CHILD_TOP_INDEX] = child.mTop; 4180 if (!childMatrix.isIdentity() || 4181 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 4182 RectF boundingRect = attachInfo.mTmpTransformRect; 4183 boundingRect.set(dirty); 4184 Matrix transformMatrix; 4185 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 4186 Transformation t = attachInfo.mTmpTransformation; 4187 boolean transformed = getChildStaticTransformation(child, t); 4188 if (transformed) { 4189 transformMatrix = attachInfo.mTmpMatrix; 4190 transformMatrix.set(t.getMatrix()); 4191 if (!childMatrix.isIdentity()) { 4192 transformMatrix.preConcat(childMatrix); 4193 } 4194 } else { 4195 transformMatrix = childMatrix; 4196 } 4197 } else { 4198 transformMatrix = childMatrix; 4199 } 4200 transformMatrix.mapRect(boundingRect); 4201 dirty.set((int) (boundingRect.left - 0.5f), 4202 (int) (boundingRect.top - 0.5f), 4203 (int) (boundingRect.right + 0.5f), 4204 (int) (boundingRect.bottom + 0.5f)); 4205 } 4206 4207 do { 4208 View view = null; 4209 if (parent instanceof View) { 4210 view = (View) parent; 4211 } 4212 4213 if (drawAnimation) { 4214 if (view != null) { 4215 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION; 4216 } else if (parent instanceof ViewRootImpl) { 4217 ((ViewRootImpl) parent).mIsAnimating = true; 4218 } 4219 } 4220 4221 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 4222 // flag coming from the child that initiated the invalidate 4223 if (view != null) { 4224 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && 4225 view.getSolidColor() == 0) { 4226 opaqueFlag = PFLAG_DIRTY; 4227 } 4228 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { 4229 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag; 4230 } 4231 } 4232 4233 parent = parent.invalidateChildInParent(location, dirty); 4234 if (view != null) { 4235 // Account for transform on current parent 4236 Matrix m = view.getMatrix(); 4237 if (!m.isIdentity()) { 4238 RectF boundingRect = attachInfo.mTmpTransformRect; 4239 boundingRect.set(dirty); 4240 m.mapRect(boundingRect); 4241 dirty.set((int) (boundingRect.left - 0.5f), 4242 (int) (boundingRect.top - 0.5f), 4243 (int) (boundingRect.right + 0.5f), 4244 (int) (boundingRect.bottom + 0.5f)); 4245 } 4246 } 4247 } while (parent != null); 4248 } 4249 } 4250 4251 /** 4252 * Don't call or override this method. It is used for the implementation of 4253 * the view hierarchy. 4254 * 4255 * This implementation returns null if this ViewGroup does not have a parent, 4256 * if this ViewGroup is already fully invalidated or if the dirty rectangle 4257 * does not intersect with this ViewGroup's bounds. 4258 */ 4259 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 4260 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || 4261 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { 4262 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != 4263 FLAG_OPTIMIZE_INVALIDATE) { 4264 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, 4265 location[CHILD_TOP_INDEX] - mScrollY); 4266 4267 final int left = mLeft; 4268 final int top = mTop; 4269 4270 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 4271 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) { 4272 dirty.setEmpty(); 4273 } 4274 } 4275 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 4276 4277 location[CHILD_LEFT_INDEX] = left; 4278 location[CHILD_TOP_INDEX] = top; 4279 4280 if (mLayerType != LAYER_TYPE_NONE) { 4281 mPrivateFlags |= PFLAG_INVALIDATED; 4282 mLocalDirtyRect.union(dirty); 4283 } 4284 4285 return mParent; 4286 4287 } else { 4288 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID; 4289 4290 location[CHILD_LEFT_INDEX] = mLeft; 4291 location[CHILD_TOP_INDEX] = mTop; 4292 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 4293 dirty.set(0, 0, mRight - mLeft, mBottom - mTop); 4294 } else { 4295 // in case the dirty rect extends outside the bounds of this container 4296 dirty.union(0, 0, mRight - mLeft, mBottom - mTop); 4297 } 4298 4299 if (mLayerType != LAYER_TYPE_NONE) { 4300 mPrivateFlags |= PFLAG_INVALIDATED; 4301 mLocalDirtyRect.union(dirty); 4302 } 4303 4304 return mParent; 4305 } 4306 } 4307 4308 return null; 4309 } 4310 4311 /** 4312 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the 4313 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods 4314 * do; all we want to do here is schedule a traversal with the appropriate dirty rect. 4315 * 4316 * @hide 4317 */ 4318 public void invalidateChildFast(View child, final Rect dirty) { 4319 ViewParent parent = this; 4320 4321 final AttachInfo attachInfo = mAttachInfo; 4322 if (attachInfo != null) { 4323 if (child.mLayerType != LAYER_TYPE_NONE) { 4324 child.mLocalDirtyRect.union(dirty); 4325 } 4326 4327 int left = child.mLeft; 4328 int top = child.mTop; 4329 if (!child.getMatrix().isIdentity()) { 4330 child.transformRect(dirty); 4331 } 4332 4333 do { 4334 if (parent instanceof ViewGroup) { 4335 ViewGroup parentVG = (ViewGroup) parent; 4336 if (parentVG.mLayerType != LAYER_TYPE_NONE) { 4337 // Layered parents should be recreated, not just re-issued 4338 parentVG.invalidate(); 4339 parent = null; 4340 } else { 4341 parent = parentVG.invalidateChildInParentFast(left, top, dirty); 4342 left = parentVG.mLeft; 4343 top = parentVG.mTop; 4344 } 4345 } else { 4346 // Reached the top; this calls into the usual invalidate method in 4347 // ViewRootImpl, which schedules a traversal 4348 final int[] location = attachInfo.mInvalidateChildLocation; 4349 location[0] = left; 4350 location[1] = top; 4351 parent = parent.invalidateChildInParent(location, dirty); 4352 } 4353 } while (parent != null); 4354 } 4355 } 4356 4357 /** 4358 * Quick invalidation method that simply transforms the dirty rect into the parent's 4359 * coordinate system, pruning the invalidation if the parent has already been invalidated. 4360 */ 4361 private ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) { 4362 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || 4363 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { 4364 dirty.offset(left - mScrollX, top - mScrollY); 4365 4366 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 || 4367 dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) { 4368 4369 if (mLayerType != LAYER_TYPE_NONE) { 4370 mLocalDirtyRect.union(dirty); 4371 } 4372 if (!getMatrix().isIdentity()) { 4373 transformRect(dirty); 4374 } 4375 4376 return mParent; 4377 } 4378 } 4379 4380 return null; 4381 } 4382 4383 /** 4384 * Offset a rectangle that is in a descendant's coordinate 4385 * space into our coordinate space. 4386 * @param descendant A descendant of this view 4387 * @param rect A rectangle defined in descendant's coordinate space. 4388 */ 4389 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { 4390 offsetRectBetweenParentAndChild(descendant, rect, true, false); 4391 } 4392 4393 /** 4394 * Offset a rectangle that is in our coordinate space into an ancestor's 4395 * coordinate space. 4396 * @param descendant A descendant of this view 4397 * @param rect A rectangle defined in descendant's coordinate space. 4398 */ 4399 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { 4400 offsetRectBetweenParentAndChild(descendant, rect, false, false); 4401 } 4402 4403 /** 4404 * Helper method that offsets a rect either from parent to descendant or 4405 * descendant to parent. 4406 */ 4407 void offsetRectBetweenParentAndChild(View descendant, Rect rect, 4408 boolean offsetFromChildToParent, boolean clipToBounds) { 4409 4410 // already in the same coord system :) 4411 if (descendant == this) { 4412 return; 4413 } 4414 4415 ViewParent theParent = descendant.mParent; 4416 4417 // search and offset up to the parent 4418 while ((theParent != null) 4419 && (theParent instanceof View) 4420 && (theParent != this)) { 4421 4422 if (offsetFromChildToParent) { 4423 rect.offset(descendant.mLeft - descendant.mScrollX, 4424 descendant.mTop - descendant.mScrollY); 4425 if (clipToBounds) { 4426 View p = (View) theParent; 4427 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 4428 } 4429 } else { 4430 if (clipToBounds) { 4431 View p = (View) theParent; 4432 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 4433 } 4434 rect.offset(descendant.mScrollX - descendant.mLeft, 4435 descendant.mScrollY - descendant.mTop); 4436 } 4437 4438 descendant = (View) theParent; 4439 theParent = descendant.mParent; 4440 } 4441 4442 // now that we are up to this view, need to offset one more time 4443 // to get into our coordinate space 4444 if (theParent == this) { 4445 if (offsetFromChildToParent) { 4446 rect.offset(descendant.mLeft - descendant.mScrollX, 4447 descendant.mTop - descendant.mScrollY); 4448 } else { 4449 rect.offset(descendant.mScrollX - descendant.mLeft, 4450 descendant.mScrollY - descendant.mTop); 4451 } 4452 } else { 4453 throw new IllegalArgumentException("parameter must be a descendant of this view"); 4454 } 4455 } 4456 4457 /** 4458 * Offset the vertical location of all children of this view by the specified number of pixels. 4459 * 4460 * @param offset the number of pixels to offset 4461 * 4462 * @hide 4463 */ 4464 public void offsetChildrenTopAndBottom(int offset) { 4465 final int count = mChildrenCount; 4466 final View[] children = mChildren; 4467 4468 for (int i = 0; i < count; i++) { 4469 final View v = children[i]; 4470 v.mTop += offset; 4471 v.mBottom += offset; 4472 if (v.mDisplayList != null) { 4473 v.mDisplayList.offsetTopAndBottom(offset); 4474 invalidateViewProperty(false, false); 4475 } 4476 } 4477 } 4478 4479 /** 4480 * {@inheritDoc} 4481 */ 4482 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 4483 // It doesn't make a whole lot of sense to call this on a view that isn't attached, 4484 // but for some simple tests it can be useful. If we don't have attach info this 4485 // will allocate memory. 4486 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF(); 4487 rect.set(r); 4488 4489 if (!child.hasIdentityMatrix()) { 4490 child.getMatrix().mapRect(rect); 4491 } 4492 4493 int dx = child.mLeft - mScrollX; 4494 int dy = child.mTop - mScrollY; 4495 4496 rect.offset(dx, dy); 4497 4498 if (offset != null) { 4499 if (!child.hasIdentityMatrix()) { 4500 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation 4501 : new float[2]; 4502 position[0] = offset.x; 4503 position[1] = offset.y; 4504 child.getMatrix().mapPoints(position); 4505 offset.x = (int) (position[0] + 0.5f); 4506 offset.y = (int) (position[1] + 0.5f); 4507 } 4508 offset.x += dx; 4509 offset.y += dy; 4510 } 4511 4512 if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) { 4513 if (mParent == null) return true; 4514 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f), 4515 (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f)); 4516 return mParent.getChildVisibleRect(this, r, offset); 4517 } 4518 4519 return false; 4520 } 4521 4522 /** 4523 * {@inheritDoc} 4524 */ 4525 @Override 4526 public final void layout(int l, int t, int r, int b) { 4527 if (mTransition == null || !mTransition.isChangingLayout()) { 4528 if (mTransition != null) { 4529 mTransition.layoutChange(this); 4530 } 4531 super.layout(l, t, r, b); 4532 } else { 4533 // record the fact that we noop'd it; request layout when transition finishes 4534 mLayoutCalledWhileSuppressed = true; 4535 } 4536 } 4537 4538 /** 4539 * {@inheritDoc} 4540 */ 4541 @Override 4542 protected abstract void onLayout(boolean changed, 4543 int l, int t, int r, int b); 4544 4545 /** 4546 * Indicates whether the view group has the ability to animate its children 4547 * after the first layout. 4548 * 4549 * @return true if the children can be animated, false otherwise 4550 */ 4551 protected boolean canAnimate() { 4552 return mLayoutAnimationController != null; 4553 } 4554 4555 /** 4556 * Runs the layout animation. Calling this method triggers a relayout of 4557 * this view group. 4558 */ 4559 public void startLayoutAnimation() { 4560 if (mLayoutAnimationController != null) { 4561 mGroupFlags |= FLAG_RUN_ANIMATION; 4562 requestLayout(); 4563 } 4564 } 4565 4566 /** 4567 * Schedules the layout animation to be played after the next layout pass 4568 * of this view group. This can be used to restart the layout animation 4569 * when the content of the view group changes or when the activity is 4570 * paused and resumed. 4571 */ 4572 public void scheduleLayoutAnimation() { 4573 mGroupFlags |= FLAG_RUN_ANIMATION; 4574 } 4575 4576 /** 4577 * Sets the layout animation controller used to animate the group's 4578 * children after the first layout. 4579 * 4580 * @param controller the animation controller 4581 */ 4582 public void setLayoutAnimation(LayoutAnimationController controller) { 4583 mLayoutAnimationController = controller; 4584 if (mLayoutAnimationController != null) { 4585 mGroupFlags |= FLAG_RUN_ANIMATION; 4586 } 4587 } 4588 4589 /** 4590 * Returns the layout animation controller used to animate the group's 4591 * children. 4592 * 4593 * @return the current animation controller 4594 */ 4595 public LayoutAnimationController getLayoutAnimation() { 4596 return mLayoutAnimationController; 4597 } 4598 4599 /** 4600 * Indicates whether the children's drawing cache is used during a layout 4601 * animation. By default, the drawing cache is enabled but this will prevent 4602 * nested layout animations from working. To nest animations, you must disable 4603 * the cache. 4604 * 4605 * @return true if the animation cache is enabled, false otherwise 4606 * 4607 * @see #setAnimationCacheEnabled(boolean) 4608 * @see View#setDrawingCacheEnabled(boolean) 4609 */ 4610 @ViewDebug.ExportedProperty 4611 public boolean isAnimationCacheEnabled() { 4612 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 4613 } 4614 4615 /** 4616 * Enables or disables the children's drawing cache during a layout animation. 4617 * By default, the drawing cache is enabled but this will prevent nested 4618 * layout animations from working. To nest animations, you must disable the 4619 * cache. 4620 * 4621 * @param enabled true to enable the animation cache, false otherwise 4622 * 4623 * @see #isAnimationCacheEnabled() 4624 * @see View#setDrawingCacheEnabled(boolean) 4625 */ 4626 public void setAnimationCacheEnabled(boolean enabled) { 4627 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); 4628 } 4629 4630 /** 4631 * Indicates whether this ViewGroup will always try to draw its children using their 4632 * drawing cache. By default this property is enabled. 4633 * 4634 * @return true if the animation cache is enabled, false otherwise 4635 * 4636 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 4637 * @see #setChildrenDrawnWithCacheEnabled(boolean) 4638 * @see View#setDrawingCacheEnabled(boolean) 4639 */ 4640 @ViewDebug.ExportedProperty(category = "drawing") 4641 public boolean isAlwaysDrawnWithCacheEnabled() { 4642 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; 4643 } 4644 4645 /** 4646 * Indicates whether this ViewGroup will always try to draw its children using their 4647 * drawing cache. This property can be set to true when the cache rendering is 4648 * slightly different from the children's normal rendering. Renderings can be different, 4649 * for instance, when the cache's quality is set to low. 4650 * 4651 * When this property is disabled, the ViewGroup will use the drawing cache of its 4652 * children only when asked to. It's usually the task of subclasses to tell ViewGroup 4653 * when to start using the drawing cache and when to stop using it. 4654 * 4655 * @param always true to always draw with the drawing cache, false otherwise 4656 * 4657 * @see #isAlwaysDrawnWithCacheEnabled() 4658 * @see #setChildrenDrawnWithCacheEnabled(boolean) 4659 * @see View#setDrawingCacheEnabled(boolean) 4660 * @see View#setDrawingCacheQuality(int) 4661 */ 4662 public void setAlwaysDrawnWithCacheEnabled(boolean always) { 4663 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); 4664 } 4665 4666 /** 4667 * Indicates whether the ViewGroup is currently drawing its children using 4668 * their drawing cache. 4669 * 4670 * @return true if children should be drawn with their cache, false otherwise 4671 * 4672 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 4673 * @see #setChildrenDrawnWithCacheEnabled(boolean) 4674 */ 4675 @ViewDebug.ExportedProperty(category = "drawing") 4676 protected boolean isChildrenDrawnWithCacheEnabled() { 4677 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; 4678 } 4679 4680 /** 4681 * Tells the ViewGroup to draw its children using their drawing cache. This property 4682 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache 4683 * will be used only if it has been enabled. 4684 * 4685 * Subclasses should call this method to start and stop using the drawing cache when 4686 * they perform performance sensitive operations, like scrolling or animating. 4687 * 4688 * @param enabled true if children should be drawn with their cache, false otherwise 4689 * 4690 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 4691 * @see #isChildrenDrawnWithCacheEnabled() 4692 */ 4693 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { 4694 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); 4695 } 4696 4697 /** 4698 * Indicates whether the ViewGroup is drawing its children in the order defined by 4699 * {@link #getChildDrawingOrder(int, int)}. 4700 * 4701 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, 4702 * false otherwise 4703 * 4704 * @see #setChildrenDrawingOrderEnabled(boolean) 4705 * @see #getChildDrawingOrder(int, int) 4706 */ 4707 @ViewDebug.ExportedProperty(category = "drawing") 4708 protected boolean isChildrenDrawingOrderEnabled() { 4709 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; 4710 } 4711 4712 /** 4713 * Tells the ViewGroup whether to draw its children in the order defined by the method 4714 * {@link #getChildDrawingOrder(int, int)}. 4715 * 4716 * @param enabled true if the order of the children when drawing is determined by 4717 * {@link #getChildDrawingOrder(int, int)}, false otherwise 4718 * 4719 * @see #isChildrenDrawingOrderEnabled() 4720 * @see #getChildDrawingOrder(int, int) 4721 */ 4722 protected void setChildrenDrawingOrderEnabled(boolean enabled) { 4723 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); 4724 } 4725 4726 private void setBooleanFlag(int flag, boolean value) { 4727 if (value) { 4728 mGroupFlags |= flag; 4729 } else { 4730 mGroupFlags &= ~flag; 4731 } 4732 } 4733 4734 /** 4735 * Returns an integer indicating what types of drawing caches are kept in memory. 4736 * 4737 * @see #setPersistentDrawingCache(int) 4738 * @see #setAnimationCacheEnabled(boolean) 4739 * 4740 * @return one or a combination of {@link #PERSISTENT_NO_CACHE}, 4741 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 4742 * and {@link #PERSISTENT_ALL_CACHES} 4743 */ 4744 @ViewDebug.ExportedProperty(category = "drawing", mapping = { 4745 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), 4746 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"), 4747 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), 4748 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") 4749 }) 4750 public int getPersistentDrawingCache() { 4751 return mPersistentDrawingCache; 4752 } 4753 4754 /** 4755 * Indicates what types of drawing caches should be kept in memory after 4756 * they have been created. 4757 * 4758 * @see #getPersistentDrawingCache() 4759 * @see #setAnimationCacheEnabled(boolean) 4760 * 4761 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE}, 4762 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 4763 * and {@link #PERSISTENT_ALL_CACHES} 4764 */ 4765 public void setPersistentDrawingCache(int drawingCacheToKeep) { 4766 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; 4767 } 4768 4769 /** 4770 * Returns the basis of alignment during layout operations on this view group: 4771 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 4772 * 4773 * @return the layout mode to use during layout operations 4774 * 4775 * @see #setLayoutMode(int) 4776 */ 4777 public int getLayoutMode() { 4778 return mLayoutMode; 4779 } 4780 4781 /** 4782 * Sets the basis of alignment during the layout of this view group. 4783 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or 4784 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 4785 * <p> 4786 * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}. 4787 * 4788 * @param layoutMode the layout mode to use during layout operations 4789 * 4790 * @see #getLayoutMode() 4791 */ 4792 public void setLayoutMode(int layoutMode) { 4793 if (mLayoutMode != layoutMode) { 4794 mLayoutMode = layoutMode; 4795 requestLayout(); 4796 } 4797 } 4798 4799 /** 4800 * Returns a new set of layout parameters based on the supplied attributes set. 4801 * 4802 * @param attrs the attributes to build the layout parameters from 4803 * 4804 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 4805 * of its descendants 4806 */ 4807 public LayoutParams generateLayoutParams(AttributeSet attrs) { 4808 return new LayoutParams(getContext(), attrs); 4809 } 4810 4811 /** 4812 * Returns a safe set of layout parameters based on the supplied layout params. 4813 * When a ViewGroup is passed a View whose layout params do not pass the test of 4814 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method 4815 * is invoked. This method should return a new set of layout params suitable for 4816 * this ViewGroup, possibly by copying the appropriate attributes from the 4817 * specified set of layout params. 4818 * 4819 * @param p The layout parameters to convert into a suitable set of layout parameters 4820 * for this ViewGroup. 4821 * 4822 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 4823 * of its descendants 4824 */ 4825 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 4826 return p; 4827 } 4828 4829 /** 4830 * Returns a set of default layout parameters. These parameters are requested 4831 * when the View passed to {@link #addView(View)} has no layout parameters 4832 * already set. If null is returned, an exception is thrown from addView. 4833 * 4834 * @return a set of default layout parameters or null 4835 */ 4836 protected LayoutParams generateDefaultLayoutParams() { 4837 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 4838 } 4839 4840 /** 4841 * {@inheritDoc} 4842 */ 4843 @Override 4844 protected void debug(int depth) { 4845 super.debug(depth); 4846 String output; 4847 4848 if (mFocused != null) { 4849 output = debugIndent(depth); 4850 output += "mFocused"; 4851 Log.d(VIEW_LOG_TAG, output); 4852 } 4853 if (mChildrenCount != 0) { 4854 output = debugIndent(depth); 4855 output += "{"; 4856 Log.d(VIEW_LOG_TAG, output); 4857 } 4858 int count = mChildrenCount; 4859 for (int i = 0; i < count; i++) { 4860 View child = mChildren[i]; 4861 child.debug(depth + 1); 4862 } 4863 4864 if (mChildrenCount != 0) { 4865 output = debugIndent(depth); 4866 output += "}"; 4867 Log.d(VIEW_LOG_TAG, output); 4868 } 4869 } 4870 4871 /** 4872 * Returns the position in the group of the specified child view. 4873 * 4874 * @param child the view for which to get the position 4875 * @return a positive integer representing the position of the view in the 4876 * group, or -1 if the view does not exist in the group 4877 */ 4878 public int indexOfChild(View child) { 4879 final int count = mChildrenCount; 4880 final View[] children = mChildren; 4881 for (int i = 0; i < count; i++) { 4882 if (children[i] == child) { 4883 return i; 4884 } 4885 } 4886 return -1; 4887 } 4888 4889 /** 4890 * Returns the number of children in the group. 4891 * 4892 * @return a positive integer representing the number of children in 4893 * the group 4894 */ 4895 public int getChildCount() { 4896 return mChildrenCount; 4897 } 4898 4899 /** 4900 * Returns the view at the specified position in the group. 4901 * 4902 * @param index the position at which to get the view from 4903 * @return the view at the specified position or null if the position 4904 * does not exist within the group 4905 */ 4906 public View getChildAt(int index) { 4907 if (index < 0 || index >= mChildrenCount) { 4908 return null; 4909 } 4910 return mChildren[index]; 4911 } 4912 4913 /** 4914 * Ask all of the children of this view to measure themselves, taking into 4915 * account both the MeasureSpec requirements for this view and its padding. 4916 * We skip children that are in the GONE state The heavy lifting is done in 4917 * getChildMeasureSpec. 4918 * 4919 * @param widthMeasureSpec The width requirements for this view 4920 * @param heightMeasureSpec The height requirements for this view 4921 */ 4922 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 4923 final int size = mChildrenCount; 4924 final View[] children = mChildren; 4925 for (int i = 0; i < size; ++i) { 4926 final View child = children[i]; 4927 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 4928 measureChild(child, widthMeasureSpec, heightMeasureSpec); 4929 } 4930 } 4931 } 4932 4933 /** 4934 * Ask one of the children of this view to measure itself, taking into 4935 * account both the MeasureSpec requirements for this view and its padding. 4936 * The heavy lifting is done in getChildMeasureSpec. 4937 * 4938 * @param child The child to measure 4939 * @param parentWidthMeasureSpec The width requirements for this view 4940 * @param parentHeightMeasureSpec The height requirements for this view 4941 */ 4942 protected void measureChild(View child, int parentWidthMeasureSpec, 4943 int parentHeightMeasureSpec) { 4944 final LayoutParams lp = child.getLayoutParams(); 4945 4946 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 4947 mPaddingLeft + mPaddingRight, lp.width); 4948 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 4949 mPaddingTop + mPaddingBottom, lp.height); 4950 4951 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4952 } 4953 4954 /** 4955 * Ask one of the children of this view to measure itself, taking into 4956 * account both the MeasureSpec requirements for this view and its padding 4957 * and margins. The child must have MarginLayoutParams The heavy lifting is 4958 * done in getChildMeasureSpec. 4959 * 4960 * @param child The child to measure 4961 * @param parentWidthMeasureSpec The width requirements for this view 4962 * @param widthUsed Extra space that has been used up by the parent 4963 * horizontally (possibly by other children of the parent) 4964 * @param parentHeightMeasureSpec The height requirements for this view 4965 * @param heightUsed Extra space that has been used up by the parent 4966 * vertically (possibly by other children of the parent) 4967 */ 4968 protected void measureChildWithMargins(View child, 4969 int parentWidthMeasureSpec, int widthUsed, 4970 int parentHeightMeasureSpec, int heightUsed) { 4971 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 4972 4973 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 4974 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 4975 + widthUsed, lp.width); 4976 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 4977 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 4978 + heightUsed, lp.height); 4979 4980 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4981 } 4982 4983 /** 4984 * Does the hard part of measureChildren: figuring out the MeasureSpec to 4985 * pass to a particular child. This method figures out the right MeasureSpec 4986 * for one dimension (height or width) of one child view. 4987 * 4988 * The goal is to combine information from our MeasureSpec with the 4989 * LayoutParams of the child to get the best possible results. For example, 4990 * if the this view knows its size (because its MeasureSpec has a mode of 4991 * EXACTLY), and the child has indicated in its LayoutParams that it wants 4992 * to be the same size as the parent, the parent should ask the child to 4993 * layout given an exact size. 4994 * 4995 * @param spec The requirements for this view 4996 * @param padding The padding of this view for the current dimension and 4997 * margins, if applicable 4998 * @param childDimension How big the child wants to be in the current 4999 * dimension 5000 * @return a MeasureSpec integer for the child 5001 */ 5002 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 5003 int specMode = MeasureSpec.getMode(spec); 5004 int specSize = MeasureSpec.getSize(spec); 5005 5006 int size = Math.max(0, specSize - padding); 5007 5008 int resultSize = 0; 5009 int resultMode = 0; 5010 5011 switch (specMode) { 5012 // Parent has imposed an exact size on us 5013 case MeasureSpec.EXACTLY: 5014 if (childDimension >= 0) { 5015 resultSize = childDimension; 5016 resultMode = MeasureSpec.EXACTLY; 5017 } else if (childDimension == LayoutParams.MATCH_PARENT) { 5018 // Child wants to be our size. So be it. 5019 resultSize = size; 5020 resultMode = MeasureSpec.EXACTLY; 5021 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 5022 // Child wants to determine its own size. It can't be 5023 // bigger than us. 5024 resultSize = size; 5025 resultMode = MeasureSpec.AT_MOST; 5026 } 5027 break; 5028 5029 // Parent has imposed a maximum size on us 5030 case MeasureSpec.AT_MOST: 5031 if (childDimension >= 0) { 5032 // Child wants a specific size... so be it 5033 resultSize = childDimension; 5034 resultMode = MeasureSpec.EXACTLY; 5035 } else if (childDimension == LayoutParams.MATCH_PARENT) { 5036 // Child wants to be our size, but our size is not fixed. 5037 // Constrain child to not be bigger than us. 5038 resultSize = size; 5039 resultMode = MeasureSpec.AT_MOST; 5040 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 5041 // Child wants to determine its own size. It can't be 5042 // bigger than us. 5043 resultSize = size; 5044 resultMode = MeasureSpec.AT_MOST; 5045 } 5046 break; 5047 5048 // Parent asked to see how big we want to be 5049 case MeasureSpec.UNSPECIFIED: 5050 if (childDimension >= 0) { 5051 // Child wants a specific size... let him have it 5052 resultSize = childDimension; 5053 resultMode = MeasureSpec.EXACTLY; 5054 } else if (childDimension == LayoutParams.MATCH_PARENT) { 5055 // Child wants to be our size... find out how big it should 5056 // be 5057 resultSize = 0; 5058 resultMode = MeasureSpec.UNSPECIFIED; 5059 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 5060 // Child wants to determine its own size.... find out how 5061 // big it should be 5062 resultSize = 0; 5063 resultMode = MeasureSpec.UNSPECIFIED; 5064 } 5065 break; 5066 } 5067 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 5068 } 5069 5070 5071 /** 5072 * Removes any pending animations for views that have been removed. Call 5073 * this if you don't want animations for exiting views to stack up. 5074 */ 5075 public void clearDisappearingChildren() { 5076 if (mDisappearingChildren != null) { 5077 mDisappearingChildren.clear(); 5078 invalidate(); 5079 } 5080 } 5081 5082 /** 5083 * Add a view which is removed from mChildren but still needs animation 5084 * 5085 * @param v View to add 5086 */ 5087 private void addDisappearingView(View v) { 5088 ArrayList<View> disappearingChildren = mDisappearingChildren; 5089 5090 if (disappearingChildren == null) { 5091 disappearingChildren = mDisappearingChildren = new ArrayList<View>(); 5092 } 5093 5094 disappearingChildren.add(v); 5095 } 5096 5097 /** 5098 * Cleanup a view when its animation is done. This may mean removing it from 5099 * the list of disappearing views. 5100 * 5101 * @param view The view whose animation has finished 5102 * @param animation The animation, cannot be null 5103 */ 5104 void finishAnimatingView(final View view, Animation animation) { 5105 final ArrayList<View> disappearingChildren = mDisappearingChildren; 5106 if (disappearingChildren != null) { 5107 if (disappearingChildren.contains(view)) { 5108 disappearingChildren.remove(view); 5109 5110 if (view.mAttachInfo != null) { 5111 view.dispatchDetachedFromWindow(); 5112 } 5113 5114 view.clearAnimation(); 5115 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 5116 } 5117 } 5118 5119 if (animation != null && !animation.getFillAfter()) { 5120 view.clearAnimation(); 5121 } 5122 5123 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) { 5124 view.onAnimationEnd(); 5125 // Should be performed by onAnimationEnd() but this avoid an infinite loop, 5126 // so we'd rather be safe than sorry 5127 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED; 5128 // Draw one more frame after the animation is done 5129 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 5130 } 5131 } 5132 5133 /** 5134 * Utility function called by View during invalidation to determine whether a view that 5135 * is invisible or gone should still be invalidated because it is being transitioned (and 5136 * therefore still needs to be drawn). 5137 */ 5138 boolean isViewTransitioning(View view) { 5139 return (mTransitioningViews != null && mTransitioningViews.contains(view)); 5140 } 5141 5142 /** 5143 * This method tells the ViewGroup that the given View object, which should have this 5144 * ViewGroup as its parent, 5145 * should be kept around (re-displayed when the ViewGroup draws its children) even if it 5146 * is removed from its parent. This allows animations, such as those used by 5147 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate 5148 * the removal of views. A call to this method should always be accompanied by a later call 5149 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished, 5150 * so that the View finally gets removed. 5151 * 5152 * @param view The View object to be kept visible even if it gets removed from its parent. 5153 */ 5154 public void startViewTransition(View view) { 5155 if (view.mParent == this) { 5156 if (mTransitioningViews == null) { 5157 mTransitioningViews = new ArrayList<View>(); 5158 } 5159 mTransitioningViews.add(view); 5160 } 5161 } 5162 5163 /** 5164 * This method should always be called following an earlier call to 5165 * {@link #startViewTransition(View)}. The given View is finally removed from its parent 5166 * and will no longer be displayed. Note that this method does not perform the functionality 5167 * of removing a view from its parent; it just discontinues the display of a View that 5168 * has previously been removed. 5169 * 5170 * @return view The View object that has been removed but is being kept around in the visible 5171 * hierarchy by an earlier call to {@link #startViewTransition(View)}. 5172 */ 5173 public void endViewTransition(View view) { 5174 if (mTransitioningViews != null) { 5175 mTransitioningViews.remove(view); 5176 final ArrayList<View> disappearingChildren = mDisappearingChildren; 5177 if (disappearingChildren != null && disappearingChildren.contains(view)) { 5178 disappearingChildren.remove(view); 5179 if (mVisibilityChangingChildren != null && 5180 mVisibilityChangingChildren.contains(view)) { 5181 mVisibilityChangingChildren.remove(view); 5182 } else { 5183 if (view.mAttachInfo != null) { 5184 view.dispatchDetachedFromWindow(); 5185 } 5186 if (view.mParent != null) { 5187 view.mParent = null; 5188 } 5189 } 5190 invalidate(); 5191 } 5192 } 5193 } 5194 5195 private LayoutTransition.TransitionListener mLayoutTransitionListener = 5196 new LayoutTransition.TransitionListener() { 5197 @Override 5198 public void startTransition(LayoutTransition transition, ViewGroup container, 5199 View view, int transitionType) { 5200 // We only care about disappearing items, since we need special logic to keep 5201 // those items visible after they've been 'removed' 5202 if (transitionType == LayoutTransition.DISAPPEARING) { 5203 startViewTransition(view); 5204 } 5205 } 5206 5207 @Override 5208 public void endTransition(LayoutTransition transition, ViewGroup container, 5209 View view, int transitionType) { 5210 if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) { 5211 requestLayout(); 5212 mLayoutCalledWhileSuppressed = false; 5213 } 5214 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) { 5215 endViewTransition(view); 5216 } 5217 } 5218 }; 5219 5220 /** 5221 * Tells this ViewGroup to suppress all layout() calls until layout 5222 * suppression is disabled with a later call to suppressLayout(false). 5223 * When layout suppression is disabled, a requestLayout() call is sent 5224 * if layout() was attempted while layout was being suppressed. 5225 * 5226 * @hide 5227 */ 5228 public void suppressLayout(boolean suppress) { 5229 mSuppressLayout = suppress; 5230 if (!suppress) { 5231 if (mLayoutCalledWhileSuppressed) { 5232 requestLayout(); 5233 mLayoutCalledWhileSuppressed = false; 5234 } 5235 } 5236 } 5237 5238 /** 5239 * {@inheritDoc} 5240 */ 5241 @Override 5242 public boolean gatherTransparentRegion(Region region) { 5243 // If no transparent regions requested, we are always opaque. 5244 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0; 5245 if (meOpaque && region == null) { 5246 // The caller doesn't care about the region, so stop now. 5247 return true; 5248 } 5249 super.gatherTransparentRegion(region); 5250 final View[] children = mChildren; 5251 final int count = mChildrenCount; 5252 boolean noneOfTheChildrenAreTransparent = true; 5253 for (int i = 0; i < count; i++) { 5254 final View child = children[i]; 5255 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 5256 if (!child.gatherTransparentRegion(region)) { 5257 noneOfTheChildrenAreTransparent = false; 5258 } 5259 } 5260 } 5261 return meOpaque || noneOfTheChildrenAreTransparent; 5262 } 5263 5264 /** 5265 * {@inheritDoc} 5266 */ 5267 public void requestTransparentRegion(View child) { 5268 if (child != null) { 5269 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; 5270 if (mParent != null) { 5271 mParent.requestTransparentRegion(this); 5272 } 5273 } 5274 } 5275 5276 5277 @Override 5278 protected boolean fitSystemWindows(Rect insets) { 5279 boolean done = super.fitSystemWindows(insets); 5280 if (!done) { 5281 final int count = mChildrenCount; 5282 final View[] children = mChildren; 5283 for (int i = 0; i < count; i++) { 5284 done = children[i].fitSystemWindows(insets); 5285 if (done) { 5286 break; 5287 } 5288 } 5289 } 5290 return done; 5291 } 5292 5293 /** 5294 * Returns the animation listener to which layout animation events are 5295 * sent. 5296 * 5297 * @return an {@link android.view.animation.Animation.AnimationListener} 5298 */ 5299 public Animation.AnimationListener getLayoutAnimationListener() { 5300 return mAnimationListener; 5301 } 5302 5303 @Override 5304 protected void drawableStateChanged() { 5305 super.drawableStateChanged(); 5306 5307 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { 5308 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 5309 throw new IllegalStateException("addStateFromChildren cannot be enabled if a" 5310 + " child has duplicateParentState set to true"); 5311 } 5312 5313 final View[] children = mChildren; 5314 final int count = mChildrenCount; 5315 5316 for (int i = 0; i < count; i++) { 5317 final View child = children[i]; 5318 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { 5319 child.refreshDrawableState(); 5320 } 5321 } 5322 } 5323 } 5324 5325 @Override 5326 public void jumpDrawablesToCurrentState() { 5327 super.jumpDrawablesToCurrentState(); 5328 final View[] children = mChildren; 5329 final int count = mChildrenCount; 5330 for (int i = 0; i < count; i++) { 5331 children[i].jumpDrawablesToCurrentState(); 5332 } 5333 } 5334 5335 @Override 5336 protected int[] onCreateDrawableState(int extraSpace) { 5337 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { 5338 return super.onCreateDrawableState(extraSpace); 5339 } 5340 5341 int need = 0; 5342 int n = getChildCount(); 5343 for (int i = 0; i < n; i++) { 5344 int[] childState = getChildAt(i).getDrawableState(); 5345 5346 if (childState != null) { 5347 need += childState.length; 5348 } 5349 } 5350 5351 int[] state = super.onCreateDrawableState(extraSpace + need); 5352 5353 for (int i = 0; i < n; i++) { 5354 int[] childState = getChildAt(i).getDrawableState(); 5355 5356 if (childState != null) { 5357 state = mergeDrawableStates(state, childState); 5358 } 5359 } 5360 5361 return state; 5362 } 5363 5364 /** 5365 * Sets whether this ViewGroup's drawable states also include 5366 * its children's drawable states. This is used, for example, to 5367 * make a group appear to be focused when its child EditText or button 5368 * is focused. 5369 */ 5370 public void setAddStatesFromChildren(boolean addsStates) { 5371 if (addsStates) { 5372 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN; 5373 } else { 5374 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN; 5375 } 5376 5377 refreshDrawableState(); 5378 } 5379 5380 /** 5381 * Returns whether this ViewGroup's drawable states also include 5382 * its children's drawable states. This is used, for example, to 5383 * make a group appear to be focused when its child EditText or button 5384 * is focused. 5385 */ 5386 public boolean addStatesFromChildren() { 5387 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0; 5388 } 5389 5390 /** 5391 * If {@link #addStatesFromChildren} is true, refreshes this group's 5392 * drawable state (to include the states from its children). 5393 */ 5394 public void childDrawableStateChanged(View child) { 5395 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 5396 refreshDrawableState(); 5397 } 5398 } 5399 5400 /** 5401 * Specifies the animation listener to which layout animation events must 5402 * be sent. Only 5403 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)} 5404 * and 5405 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)} 5406 * are invoked. 5407 * 5408 * @param animationListener the layout animation listener 5409 */ 5410 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { 5411 mAnimationListener = animationListener; 5412 } 5413 5414 /** 5415 * This method is called by LayoutTransition when there are 'changing' animations that need 5416 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who 5417 * starts all pending transitions prior to the drawing phase in the current traversal. 5418 * 5419 * @param transition The LayoutTransition to be started on the next traversal. 5420 * 5421 * @hide 5422 */ 5423 public void requestTransitionStart(LayoutTransition transition) { 5424 ViewRootImpl viewAncestor = getViewRootImpl(); 5425 if (viewAncestor != null) { 5426 viewAncestor.requestTransitionStart(transition); 5427 } 5428 } 5429 5430 /** 5431 * @hide 5432 */ 5433 @Override 5434 public void resolveRtlPropertiesIfNeeded() { 5435 super.resolveRtlPropertiesIfNeeded(); 5436 int count = getChildCount(); 5437 for (int i = 0; i < count; i++) { 5438 final View child = getChildAt(i); 5439 if (child.isLayoutDirectionInherited()) { 5440 child.resolveRtlPropertiesIfNeeded(); 5441 } 5442 } 5443 } 5444 5445 /** 5446 * @hide 5447 */ 5448 @Override 5449 public boolean resolveLayoutDirection() { 5450 final boolean result = super.resolveLayoutDirection(); 5451 if (result) { 5452 int count = getChildCount(); 5453 for (int i = 0; i < count; i++) { 5454 final View child = getChildAt(i); 5455 if (child.isLayoutDirectionInherited()) { 5456 child.resolveLayoutDirection(); 5457 } 5458 } 5459 } 5460 return result; 5461 } 5462 5463 /** 5464 * @hide 5465 */ 5466 @Override 5467 public boolean resolveTextDirection() { 5468 final boolean result = super.resolveTextDirection(); 5469 if (result) { 5470 int count = getChildCount(); 5471 for (int i = 0; i < count; i++) { 5472 final View child = getChildAt(i); 5473 if (child.isTextDirectionInherited()) { 5474 child.resolveTextDirection(); 5475 } 5476 } 5477 } 5478 return result; 5479 } 5480 5481 /** 5482 * @hide 5483 */ 5484 @Override 5485 public boolean resolveTextAlignment() { 5486 final boolean result = super.resolveTextAlignment(); 5487 if (result) { 5488 int count = getChildCount(); 5489 for (int i = 0; i < count; i++) { 5490 final View child = getChildAt(i); 5491 if (child.isTextAlignmentInherited()) { 5492 child.resolveTextAlignment(); 5493 } 5494 } 5495 } 5496 return result; 5497 } 5498 5499 /** 5500 * @hide 5501 */ 5502 @Override 5503 public void resolvePadding() { 5504 super.resolvePadding(); 5505 int count = getChildCount(); 5506 for (int i = 0; i < count; i++) { 5507 final View child = getChildAt(i); 5508 if (child.isLayoutDirectionInherited()) { 5509 child.resolvePadding(); 5510 } 5511 } 5512 } 5513 5514 /** 5515 * @hide 5516 */ 5517 @Override 5518 protected void resolveDrawables() { 5519 super.resolveDrawables(); 5520 int count = getChildCount(); 5521 for (int i = 0; i < count; i++) { 5522 final View child = getChildAt(i); 5523 if (child.isLayoutDirectionInherited()) { 5524 child.resolveDrawables(); 5525 } 5526 } 5527 } 5528 5529 /** 5530 * @hide 5531 */ 5532 @Override 5533 public void resolveLayoutParams() { 5534 super.resolveLayoutParams(); 5535 int count = getChildCount(); 5536 for (int i = 0; i < count; i++) { 5537 final View child = getChildAt(i); 5538 child.resolveLayoutParams(); 5539 } 5540 } 5541 5542 /** 5543 * @hide 5544 */ 5545 @Override 5546 public void resetResolvedLayoutDirection() { 5547 super.resetResolvedLayoutDirection(); 5548 5549 int count = getChildCount(); 5550 for (int i = 0; i < count; i++) { 5551 final View child = getChildAt(i); 5552 if (child.isLayoutDirectionInherited()) { 5553 child.resetResolvedLayoutDirection(); 5554 } 5555 } 5556 } 5557 5558 /** 5559 * @hide 5560 */ 5561 @Override 5562 public void resetResolvedTextDirection() { 5563 super.resetResolvedTextDirection(); 5564 5565 int count = getChildCount(); 5566 for (int i = 0; i < count; i++) { 5567 final View child = getChildAt(i); 5568 if (child.isTextDirectionInherited()) { 5569 child.resetResolvedTextDirection(); 5570 } 5571 } 5572 } 5573 5574 /** 5575 * @hide 5576 */ 5577 @Override 5578 public void resetResolvedTextAlignment() { 5579 super.resetResolvedTextAlignment(); 5580 5581 int count = getChildCount(); 5582 for (int i = 0; i < count; i++) { 5583 final View child = getChildAt(i); 5584 if (child.isTextAlignmentInherited()) { 5585 child.resetResolvedTextAlignment(); 5586 } 5587 } 5588 } 5589 5590 /** 5591 * @hide 5592 */ 5593 @Override 5594 public void resetResolvedPadding() { 5595 super.resetResolvedPadding(); 5596 5597 int count = getChildCount(); 5598 for (int i = 0; i < count; i++) { 5599 final View child = getChildAt(i); 5600 if (child.isLayoutDirectionInherited()) { 5601 child.resetResolvedPadding(); 5602 } 5603 } 5604 } 5605 5606 /** 5607 * @hide 5608 */ 5609 @Override 5610 protected void resetResolvedDrawables() { 5611 super.resetResolvedDrawables(); 5612 5613 int count = getChildCount(); 5614 for (int i = 0; i < count; i++) { 5615 final View child = getChildAt(i); 5616 if (child.isLayoutDirectionInherited()) { 5617 child.resetResolvedDrawables(); 5618 } 5619 } 5620 } 5621 5622 /** 5623 * Return true if the pressed state should be delayed for children or descendants of this 5624 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List. 5625 * This prevents the pressed state from appearing when the user is actually trying to scroll 5626 * the content. 5627 * 5628 * The default implementation returns true for compatibility reasons. Subclasses that do 5629 * not scroll should generally override this method and return false. 5630 */ 5631 public boolean shouldDelayChildPressedState() { 5632 return true; 5633 } 5634 5635 /** @hide */ 5636 protected void onSetLayoutParams(View child, LayoutParams layoutParams) { 5637 } 5638 5639 /** 5640 * LayoutParams are used by views to tell their parents how they want to be 5641 * laid out. See 5642 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 5643 * for a list of all child view attributes that this class supports. 5644 * 5645 * <p> 5646 * The base LayoutParams class just describes how big the view wants to be 5647 * for both width and height. For each dimension, it can specify one of: 5648 * <ul> 5649 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which 5650 * means that the view wants to be as big as its parent (minus padding) 5651 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 5652 * to enclose its content (plus padding) 5653 * <li> an exact number 5654 * </ul> 5655 * There are subclasses of LayoutParams for different subclasses of 5656 * ViewGroup. For example, AbsoluteLayout has its own subclass of 5657 * LayoutParams which adds an X and Y value.</p> 5658 * 5659 * <div class="special reference"> 5660 * <h3>Developer Guides</h3> 5661 * <p>For more information about creating user interface layouts, read the 5662 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer 5663 * guide.</p></div> 5664 * 5665 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 5666 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 5667 */ 5668 public static class LayoutParams { 5669 /** 5670 * Special value for the height or width requested by a View. 5671 * FILL_PARENT means that the view wants to be as big as its parent, 5672 * minus the parent's padding, if any. This value is deprecated 5673 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 5674 */ 5675 @SuppressWarnings({"UnusedDeclaration"}) 5676 @Deprecated 5677 public static final int FILL_PARENT = -1; 5678 5679 /** 5680 * Special value for the height or width requested by a View. 5681 * MATCH_PARENT means that the view wants to be as big as its parent, 5682 * minus the parent's padding, if any. Introduced in API Level 8. 5683 */ 5684 public static final int MATCH_PARENT = -1; 5685 5686 /** 5687 * Special value for the height or width requested by a View. 5688 * WRAP_CONTENT means that the view wants to be just large enough to fit 5689 * its own internal content, taking its own padding into account. 5690 */ 5691 public static final int WRAP_CONTENT = -2; 5692 5693 /** 5694 * Information about how wide the view wants to be. Can be one of the 5695 * constants FILL_PARENT (replaced by MATCH_PARENT , 5696 * in API Level 8) or WRAP_CONTENT. or an exact size. 5697 */ 5698 @ViewDebug.ExportedProperty(category = "layout", mapping = { 5699 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 5700 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 5701 }) 5702 public int width; 5703 5704 /** 5705 * Information about how tall the view wants to be. Can be one of the 5706 * constants FILL_PARENT (replaced by MATCH_PARENT , 5707 * in API Level 8) or WRAP_CONTENT. or an exact size. 5708 */ 5709 @ViewDebug.ExportedProperty(category = "layout", mapping = { 5710 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 5711 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 5712 }) 5713 public int height; 5714 5715 /** 5716 * Used to animate layouts. 5717 */ 5718 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 5719 5720 /** 5721 * Creates a new set of layout parameters. The values are extracted from 5722 * the supplied attributes set and context. The XML attributes mapped 5723 * to this set of layout parameters are: 5724 * 5725 * <ul> 5726 * <li><code>layout_width</code>: the width, either an exact value, 5727 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 5728 * {@link #MATCH_PARENT} in API Level 8)</li> 5729 * <li><code>layout_height</code>: the height, either an exact value, 5730 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 5731 * {@link #MATCH_PARENT} in API Level 8)</li> 5732 * </ul> 5733 * 5734 * @param c the application environment 5735 * @param attrs the set of attributes from which to extract the layout 5736 * parameters' values 5737 */ 5738 public LayoutParams(Context c, AttributeSet attrs) { 5739 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 5740 setBaseAttributes(a, 5741 R.styleable.ViewGroup_Layout_layout_width, 5742 R.styleable.ViewGroup_Layout_layout_height); 5743 a.recycle(); 5744 } 5745 5746 /** 5747 * Creates a new set of layout parameters with the specified width 5748 * and height. 5749 * 5750 * @param width the width, either {@link #WRAP_CONTENT}, 5751 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 5752 * API Level 8), or a fixed size in pixels 5753 * @param height the height, either {@link #WRAP_CONTENT}, 5754 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 5755 * API Level 8), or a fixed size in pixels 5756 */ 5757 public LayoutParams(int width, int height) { 5758 this.width = width; 5759 this.height = height; 5760 } 5761 5762 /** 5763 * Copy constructor. Clones the width and height values of the source. 5764 * 5765 * @param source The layout params to copy from. 5766 */ 5767 public LayoutParams(LayoutParams source) { 5768 this.width = source.width; 5769 this.height = source.height; 5770 } 5771 5772 /** 5773 * Used internally by MarginLayoutParams. 5774 * @hide 5775 */ 5776 LayoutParams() { 5777 } 5778 5779 /** 5780 * Extracts the layout parameters from the supplied attributes. 5781 * 5782 * @param a the style attributes to extract the parameters from 5783 * @param widthAttr the identifier of the width attribute 5784 * @param heightAttr the identifier of the height attribute 5785 */ 5786 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 5787 width = a.getLayoutDimension(widthAttr, "layout_width"); 5788 height = a.getLayoutDimension(heightAttr, "layout_height"); 5789 } 5790 5791 /** 5792 * Resolve layout parameters depending on the layout direction. Subclasses that care about 5793 * layoutDirection changes should override this method. The default implementation does 5794 * nothing. 5795 * 5796 * @param layoutDirection the direction of the layout 5797 * 5798 * {@link View#LAYOUT_DIRECTION_LTR} 5799 * {@link View#LAYOUT_DIRECTION_RTL} 5800 */ 5801 public void resolveLayoutDirection(int layoutDirection) { 5802 } 5803 5804 /** 5805 * Returns a String representation of this set of layout parameters. 5806 * 5807 * @param output the String to prepend to the internal representation 5808 * @return a String with the following format: output + 5809 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 5810 * 5811 * @hide 5812 */ 5813 public String debug(String output) { 5814 return output + "ViewGroup.LayoutParams={ width=" 5815 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 5816 } 5817 5818 /** 5819 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters. 5820 * 5821 * @param view the view that contains these layout parameters 5822 * @param canvas the canvas on which to draw 5823 * 5824 * @hide 5825 */ 5826 public void onDebugDraw(View view, Canvas canvas, Paint paint) { 5827 } 5828 5829 /** 5830 * Converts the specified size to a readable String. 5831 * 5832 * @param size the size to convert 5833 * @return a String instance representing the supplied size 5834 * 5835 * @hide 5836 */ 5837 protected static String sizeToString(int size) { 5838 if (size == WRAP_CONTENT) { 5839 return "wrap-content"; 5840 } 5841 if (size == MATCH_PARENT) { 5842 return "match-parent"; 5843 } 5844 return String.valueOf(size); 5845 } 5846 } 5847 5848 /** 5849 * Per-child layout information for layouts that support margins. 5850 * See 5851 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes} 5852 * for a list of all child view attributes that this class supports. 5853 */ 5854 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 5855 /** 5856 * The left margin in pixels of the child. 5857 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5858 * to this field. 5859 */ 5860 @ViewDebug.ExportedProperty(category = "layout") 5861 public int leftMargin; 5862 5863 /** 5864 * The top margin in pixels of the child. 5865 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5866 * to this field. 5867 */ 5868 @ViewDebug.ExportedProperty(category = "layout") 5869 public int topMargin; 5870 5871 /** 5872 * The right margin in pixels of the child. 5873 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5874 * to this field. 5875 */ 5876 @ViewDebug.ExportedProperty(category = "layout") 5877 public int rightMargin; 5878 5879 /** 5880 * The bottom margin in pixels of the child. 5881 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5882 * to this field. 5883 */ 5884 @ViewDebug.ExportedProperty(category = "layout") 5885 public int bottomMargin; 5886 5887 /** 5888 * The start margin in pixels of the child. 5889 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5890 * to this field. 5891 */ 5892 @ViewDebug.ExportedProperty(category = "layout") 5893 private int startMargin = DEFAULT_MARGIN_RELATIVE; 5894 5895 /** 5896 * The end margin in pixels of the child. 5897 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5898 * to this field. 5899 */ 5900 @ViewDebug.ExportedProperty(category = "layout") 5901 private int endMargin = DEFAULT_MARGIN_RELATIVE; 5902 5903 /** 5904 * The default start and end margin. 5905 * @hide 5906 */ 5907 public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE; 5908 5909 /** 5910 * Bit 0: layout direction 5911 * Bit 1: layout direction 5912 * Bit 2: left margin undefined 5913 * Bit 3: right margin undefined 5914 * Bit 4: is RTL compatibility mode 5915 * Bit 5: need resolution 5916 * 5917 * Bit 6 to 7 not used 5918 * 5919 * @hide 5920 */ 5921 @ViewDebug.ExportedProperty(category = "layout", flagMapping = { 5922 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK, 5923 equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"), 5924 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK, 5925 equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"), 5926 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK, 5927 equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"), 5928 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK, 5929 equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"), 5930 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK, 5931 equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK") 5932 }) 5933 byte mMarginFlags; 5934 5935 private static final int LAYOUT_DIRECTION_MASK = 0x00000003; 5936 private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004; 5937 private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008; 5938 private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010; 5939 private static final int NEED_RESOLUTION_MASK = 0x00000020; 5940 5941 private static final int DEFAULT_MARGIN_RESOLVED = 0; 5942 private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE; 5943 5944 /** 5945 * Creates a new set of layout parameters. The values are extracted from 5946 * the supplied attributes set and context. 5947 * 5948 * @param c the application environment 5949 * @param attrs the set of attributes from which to extract the layout 5950 * parameters' values 5951 */ 5952 public MarginLayoutParams(Context c, AttributeSet attrs) { 5953 super(); 5954 5955 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); 5956 setBaseAttributes(a, 5957 R.styleable.ViewGroup_MarginLayout_layout_width, 5958 R.styleable.ViewGroup_MarginLayout_layout_height); 5959 5960 int margin = a.getDimensionPixelSize( 5961 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); 5962 if (margin >= 0) { 5963 leftMargin = margin; 5964 topMargin = margin; 5965 rightMargin= margin; 5966 bottomMargin = margin; 5967 } else { 5968 leftMargin = a.getDimensionPixelSize( 5969 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 5970 UNDEFINED_MARGIN); 5971 if (leftMargin == UNDEFINED_MARGIN) { 5972 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; 5973 leftMargin = DEFAULT_MARGIN_RESOLVED; 5974 } 5975 rightMargin = a.getDimensionPixelSize( 5976 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 5977 UNDEFINED_MARGIN); 5978 if (rightMargin == UNDEFINED_MARGIN) { 5979 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; 5980 rightMargin = DEFAULT_MARGIN_RESOLVED; 5981 } 5982 5983 topMargin = a.getDimensionPixelSize( 5984 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 5985 DEFAULT_MARGIN_RESOLVED); 5986 bottomMargin = a.getDimensionPixelSize( 5987 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 5988 DEFAULT_MARGIN_RESOLVED); 5989 5990 startMargin = a.getDimensionPixelSize( 5991 R.styleable.ViewGroup_MarginLayout_layout_marginStart, 5992 DEFAULT_MARGIN_RELATIVE); 5993 endMargin = a.getDimensionPixelSize( 5994 R.styleable.ViewGroup_MarginLayout_layout_marginEnd, 5995 DEFAULT_MARGIN_RELATIVE); 5996 5997 if (isMarginRelative()) { 5998 mMarginFlags |= NEED_RESOLUTION_MASK; 5999 } 6000 } 6001 6002 final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport(); 6003 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; 6004 if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) { 6005 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK; 6006 } 6007 6008 // Layout direction is LTR by default 6009 mMarginFlags |= LAYOUT_DIRECTION_LTR; 6010 6011 a.recycle(); 6012 } 6013 6014 /** 6015 * {@inheritDoc} 6016 */ 6017 public MarginLayoutParams(int width, int height) { 6018 super(width, height); 6019 6020 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; 6021 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; 6022 6023 mMarginFlags &= ~NEED_RESOLUTION_MASK; 6024 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK; 6025 } 6026 6027 /** 6028 * Copy constructor. Clones the width, height and margin values of the source. 6029 * 6030 * @param source The layout params to copy from. 6031 */ 6032 public MarginLayoutParams(MarginLayoutParams source) { 6033 this.width = source.width; 6034 this.height = source.height; 6035 6036 this.leftMargin = source.leftMargin; 6037 this.topMargin = source.topMargin; 6038 this.rightMargin = source.rightMargin; 6039 this.bottomMargin = source.bottomMargin; 6040 this.startMargin = source.startMargin; 6041 this.endMargin = source.endMargin; 6042 6043 this.mMarginFlags = source.mMarginFlags; 6044 } 6045 6046 /** 6047 * {@inheritDoc} 6048 */ 6049 public MarginLayoutParams(LayoutParams source) { 6050 super(source); 6051 6052 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; 6053 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; 6054 6055 mMarginFlags &= ~NEED_RESOLUTION_MASK; 6056 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK; 6057 } 6058 6059 /** 6060 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs 6061 * to be done so that the new margins are taken into account. Left and right margins may be 6062 * overriden by {@link android.view.View#requestLayout()} depending on layout direction. 6063 * 6064 * @param left the left margin size 6065 * @param top the top margin size 6066 * @param right the right margin size 6067 * @param bottom the bottom margin size 6068 * 6069 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 6070 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 6071 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 6072 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 6073 */ 6074 public void setMargins(int left, int top, int right, int bottom) { 6075 leftMargin = left; 6076 topMargin = top; 6077 rightMargin = right; 6078 bottomMargin = bottom; 6079 mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK; 6080 mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK; 6081 if (isMarginRelative()) { 6082 mMarginFlags |= NEED_RESOLUTION_MASK; 6083 } else { 6084 mMarginFlags &= ~NEED_RESOLUTION_MASK; 6085 } 6086 } 6087 6088 /** 6089 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()} 6090 * needs to be done so that the new relative margins are taken into account. Left and right 6091 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout 6092 * direction. 6093 * 6094 * @param start the start margin size 6095 * @param top the top margin size 6096 * @param end the right margin size 6097 * @param bottom the bottom margin size 6098 * 6099 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6100 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 6101 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6102 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 6103 * 6104 * @hide 6105 */ 6106 public void setMarginsRelative(int start, int top, int end, int bottom) { 6107 startMargin = start; 6108 topMargin = top; 6109 endMargin = end; 6110 bottomMargin = bottom; 6111 mMarginFlags |= NEED_RESOLUTION_MASK; 6112 } 6113 6114 /** 6115 * Sets the relative start margin. 6116 * 6117 * @param start the start margin size 6118 * 6119 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6120 */ 6121 public void setMarginStart(int start) { 6122 startMargin = start; 6123 mMarginFlags |= NEED_RESOLUTION_MASK; 6124 } 6125 6126 /** 6127 * Returns the start margin in pixels. 6128 * 6129 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6130 * 6131 * @return the start margin in pixels. 6132 */ 6133 public int getMarginStart() { 6134 if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin; 6135 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) { 6136 doResolveMargins(); 6137 } 6138 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { 6139 case View.LAYOUT_DIRECTION_RTL: 6140 return rightMargin; 6141 case View.LAYOUT_DIRECTION_LTR: 6142 default: 6143 return leftMargin; 6144 } 6145 } 6146 6147 /** 6148 * Sets the relative end margin. 6149 * 6150 * @param end the end margin size 6151 * 6152 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6153 */ 6154 public void setMarginEnd(int end) { 6155 endMargin = end; 6156 mMarginFlags |= NEED_RESOLUTION_MASK; 6157 } 6158 6159 /** 6160 * Returns the end margin in pixels. 6161 * 6162 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6163 * 6164 * @return the end margin in pixels. 6165 */ 6166 public int getMarginEnd() { 6167 if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin; 6168 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) { 6169 doResolveMargins(); 6170 } 6171 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { 6172 case View.LAYOUT_DIRECTION_RTL: 6173 return leftMargin; 6174 case View.LAYOUT_DIRECTION_LTR: 6175 default: 6176 return rightMargin; 6177 } 6178 } 6179 6180 /** 6181 * Check if margins are relative. 6182 * 6183 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6184 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6185 * 6186 * @return true if either marginStart or marginEnd has been set. 6187 */ 6188 public boolean isMarginRelative() { 6189 return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE); 6190 } 6191 6192 /** 6193 * Set the layout direction 6194 * @param layoutDirection the layout direction. 6195 * Should be either {@link View#LAYOUT_DIRECTION_LTR} 6196 * or {@link View#LAYOUT_DIRECTION_RTL}. 6197 */ 6198 public void setLayoutDirection(int layoutDirection) { 6199 if (layoutDirection != View.LAYOUT_DIRECTION_LTR && 6200 layoutDirection != View.LAYOUT_DIRECTION_RTL) return; 6201 if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) { 6202 mMarginFlags &= ~LAYOUT_DIRECTION_MASK; 6203 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK); 6204 if (isMarginRelative()) { 6205 mMarginFlags |= NEED_RESOLUTION_MASK; 6206 } else { 6207 mMarginFlags &= ~NEED_RESOLUTION_MASK; 6208 } 6209 } 6210 } 6211 6212 /** 6213 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or 6214 * {@link View#LAYOUT_DIRECTION_RTL}. 6215 * 6216 * @return the layout direction. 6217 */ 6218 public int getLayoutDirection() { 6219 return (mMarginFlags & LAYOUT_DIRECTION_MASK); 6220 } 6221 6222 /** 6223 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins 6224 * may be overridden depending on layout direction. 6225 */ 6226 @Override 6227 public void resolveLayoutDirection(int layoutDirection) { 6228 setLayoutDirection(layoutDirection); 6229 6230 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything 6231 // Will use the left and right margins if no relative margin is defined. 6232 if (!isMarginRelative() || 6233 (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return; 6234 6235 // Proceed with resolution 6236 doResolveMargins(); 6237 } 6238 6239 private void doResolveMargins() { 6240 if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) { 6241 // if left or right margins are not defined and if we have some start or end margin 6242 // defined then use those start and end margins. 6243 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK 6244 && startMargin > DEFAULT_MARGIN_RELATIVE) { 6245 leftMargin = startMargin; 6246 } 6247 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK 6248 && endMargin > DEFAULT_MARGIN_RELATIVE) { 6249 rightMargin = endMargin; 6250 } 6251 } else { 6252 // We have some relative margins (either the start one or the end one or both). So use 6253 // them and override what has been defined for left and right margins. If either start 6254 // or end margin is not defined, just set it to default "0". 6255 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { 6256 case View.LAYOUT_DIRECTION_RTL: 6257 leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? 6258 endMargin : DEFAULT_MARGIN_RESOLVED; 6259 rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? 6260 startMargin : DEFAULT_MARGIN_RESOLVED; 6261 break; 6262 case View.LAYOUT_DIRECTION_LTR: 6263 default: 6264 leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? 6265 startMargin : DEFAULT_MARGIN_RESOLVED; 6266 rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? 6267 endMargin : DEFAULT_MARGIN_RESOLVED; 6268 break; 6269 } 6270 } 6271 mMarginFlags &= ~NEED_RESOLUTION_MASK; 6272 } 6273 6274 /** 6275 * @hide 6276 */ 6277 public boolean isLayoutRtl() { 6278 return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL); 6279 } 6280 6281 /** 6282 * @hide 6283 */ 6284 @Override 6285 public void onDebugDraw(View view, Canvas canvas, Paint paint) { 6286 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; 6287 6288 fillDifference(canvas, 6289 view.getLeft() + oi.left, 6290 view.getTop() + oi.top, 6291 view.getRight() - oi.right, 6292 view.getBottom() - oi.bottom, 6293 leftMargin, 6294 topMargin, 6295 rightMargin, 6296 bottomMargin, 6297 paint); 6298 } 6299 } 6300 6301 /* Describes a touched view and the ids of the pointers that it has captured. 6302 * 6303 * This code assumes that pointer ids are always in the range 0..31 such that 6304 * it can use a bitfield to track which pointer ids are present. 6305 * As it happens, the lower layers of the input dispatch pipeline also use the 6306 * same trick so the assumption should be safe here... 6307 */ 6308 private static final class TouchTarget { 6309 private static final int MAX_RECYCLED = 32; 6310 private static final Object sRecycleLock = new Object(); 6311 private static TouchTarget sRecycleBin; 6312 private static int sRecycledCount; 6313 6314 public static final int ALL_POINTER_IDS = -1; // all ones 6315 6316 // The touched child view. 6317 public View child; 6318 6319 // The combined bit mask of pointer ids for all pointers captured by the target. 6320 public int pointerIdBits; 6321 6322 // The next target in the target list. 6323 public TouchTarget next; 6324 6325 private TouchTarget() { 6326 } 6327 6328 public static TouchTarget obtain(View child, int pointerIdBits) { 6329 final TouchTarget target; 6330 synchronized (sRecycleLock) { 6331 if (sRecycleBin == null) { 6332 target = new TouchTarget(); 6333 } else { 6334 target = sRecycleBin; 6335 sRecycleBin = target.next; 6336 sRecycledCount--; 6337 target.next = null; 6338 } 6339 } 6340 target.child = child; 6341 target.pointerIdBits = pointerIdBits; 6342 return target; 6343 } 6344 6345 public void recycle() { 6346 synchronized (sRecycleLock) { 6347 if (sRecycledCount < MAX_RECYCLED) { 6348 next = sRecycleBin; 6349 sRecycleBin = this; 6350 sRecycledCount += 1; 6351 } else { 6352 next = null; 6353 } 6354 child = null; 6355 } 6356 } 6357 } 6358 6359 /* Describes a hovered view. */ 6360 private static final class HoverTarget { 6361 private static final int MAX_RECYCLED = 32; 6362 private static final Object sRecycleLock = new Object(); 6363 private static HoverTarget sRecycleBin; 6364 private static int sRecycledCount; 6365 6366 // The hovered child view. 6367 public View child; 6368 6369 // The next target in the target list. 6370 public HoverTarget next; 6371 6372 private HoverTarget() { 6373 } 6374 6375 public static HoverTarget obtain(View child) { 6376 final HoverTarget target; 6377 synchronized (sRecycleLock) { 6378 if (sRecycleBin == null) { 6379 target = new HoverTarget(); 6380 } else { 6381 target = sRecycleBin; 6382 sRecycleBin = target.next; 6383 sRecycledCount--; 6384 target.next = null; 6385 } 6386 } 6387 target.child = child; 6388 return target; 6389 } 6390 6391 public void recycle() { 6392 synchronized (sRecycleLock) { 6393 if (sRecycledCount < MAX_RECYCLED) { 6394 next = sRecycleBin; 6395 sRecycleBin = this; 6396 sRecycledCount += 1; 6397 } else { 6398 next = null; 6399 } 6400 child = null; 6401 } 6402 } 6403 } 6404 6405 /** 6406 * Pooled class that orderes the children of a ViewGroup from start 6407 * to end based on how they are laid out and the layout direction. 6408 */ 6409 static class ChildListForAccessibility { 6410 6411 private static final int MAX_POOL_SIZE = 32; 6412 6413 private static final SynchronizedPool<ChildListForAccessibility> sPool = 6414 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE); 6415 6416 private final ArrayList<View> mChildren = new ArrayList<View>(); 6417 6418 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>(); 6419 6420 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) { 6421 ChildListForAccessibility list = sPool.acquire(); 6422 if (list == null) { 6423 list = new ChildListForAccessibility(); 6424 } 6425 list.init(parent, sort); 6426 return list; 6427 } 6428 6429 public void recycle() { 6430 clear(); 6431 sPool.release(this); 6432 } 6433 6434 public int getChildCount() { 6435 return mChildren.size(); 6436 } 6437 6438 public View getChildAt(int index) { 6439 return mChildren.get(index); 6440 } 6441 6442 public int getChildIndex(View child) { 6443 return mChildren.indexOf(child); 6444 } 6445 6446 private void init(ViewGroup parent, boolean sort) { 6447 ArrayList<View> children = mChildren; 6448 final int childCount = parent.getChildCount(); 6449 for (int i = 0; i < childCount; i++) { 6450 View child = parent.getChildAt(i); 6451 children.add(child); 6452 } 6453 if (sort) { 6454 ArrayList<ViewLocationHolder> holders = mHolders; 6455 for (int i = 0; i < childCount; i++) { 6456 View child = children.get(i); 6457 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child); 6458 holders.add(holder); 6459 } 6460 Collections.sort(holders); 6461 for (int i = 0; i < childCount; i++) { 6462 ViewLocationHolder holder = holders.get(i); 6463 children.set(i, holder.mView); 6464 holder.recycle(); 6465 } 6466 holders.clear(); 6467 } 6468 } 6469 6470 private void clear() { 6471 mChildren.clear(); 6472 } 6473 } 6474 6475 /** 6476 * Pooled class that holds a View and its location with respect to 6477 * a specified root. This enables sorting of views based on their 6478 * coordinates without recomputing the position relative to the root 6479 * on every comparison. 6480 */ 6481 static class ViewLocationHolder implements Comparable<ViewLocationHolder> { 6482 6483 private static final int MAX_POOL_SIZE = 32; 6484 6485 private static final SynchronizedPool<ViewLocationHolder> sPool = 6486 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE); 6487 6488 private final Rect mLocation = new Rect(); 6489 6490 public View mView; 6491 6492 private int mLayoutDirection; 6493 6494 public static ViewLocationHolder obtain(ViewGroup root, View view) { 6495 ViewLocationHolder holder = sPool.acquire(); 6496 if (holder == null) { 6497 holder = new ViewLocationHolder(); 6498 } 6499 holder.init(root, view); 6500 return holder; 6501 } 6502 6503 public void recycle() { 6504 clear(); 6505 sPool.release(this); 6506 } 6507 6508 @Override 6509 public int compareTo(ViewLocationHolder another) { 6510 // This instance is greater than an invalid argument. 6511 if (another == null) { 6512 return 1; 6513 } 6514 if (getClass() != another.getClass()) { 6515 return 1; 6516 } 6517 // First is above second. 6518 if (mLocation.bottom - another.mLocation.top <= 0) { 6519 return -1; 6520 } 6521 // First is below second. 6522 if (mLocation.top - another.mLocation.bottom >= 0) { 6523 return 1; 6524 } 6525 // LTR 6526 if (mLayoutDirection == LAYOUT_DIRECTION_LTR) { 6527 final int leftDifference = mLocation.left - another.mLocation.left; 6528 // First more to the left than second. 6529 if (leftDifference != 0) { 6530 return leftDifference; 6531 } 6532 } else { // RTL 6533 final int rightDifference = mLocation.right - another.mLocation.right; 6534 // First more to the right than second. 6535 if (rightDifference != 0) { 6536 return -rightDifference; 6537 } 6538 } 6539 // Break tie by top. 6540 final int topDiference = mLocation.top - another.mLocation.top; 6541 if (topDiference != 0) { 6542 return topDiference; 6543 } 6544 // Break tie by height. 6545 final int heightDiference = mLocation.height() - another.mLocation.height(); 6546 if (heightDiference != 0) { 6547 return -heightDiference; 6548 } 6549 // Break tie by width. 6550 final int widthDiference = mLocation.width() - another.mLocation.width(); 6551 if (widthDiference != 0) { 6552 return -widthDiference; 6553 } 6554 // Just break the tie somehow. The accessibliity ids are unique 6555 // and stable, hence this is deterministic tie breaking. 6556 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId(); 6557 } 6558 6559 private void init(ViewGroup root, View view) { 6560 Rect viewLocation = mLocation; 6561 view.getDrawingRect(viewLocation); 6562 root.offsetDescendantRectToMyCoords(view, viewLocation); 6563 mView = view; 6564 mLayoutDirection = root.getLayoutDirection(); 6565 } 6566 6567 private void clear() { 6568 mView = null; 6569 mLocation.set(0, 0, 0, 0); 6570 } 6571 } 6572 6573 private static Paint getDebugPaint() { 6574 if (sDebugPaint == null) { 6575 sDebugPaint = new Paint(); 6576 sDebugPaint.setAntiAlias(false); 6577 } 6578 return sDebugPaint; 6579 } 6580 6581 private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { 6582 if (sDebugLines== null) { 6583 sDebugLines = new float[16]; 6584 } 6585 6586 sDebugLines[0] = x1; 6587 sDebugLines[1] = y1; 6588 sDebugLines[2] = x2; 6589 sDebugLines[3] = y1; 6590 6591 sDebugLines[4] = x2; 6592 sDebugLines[5] = y1; 6593 sDebugLines[6] = x2; 6594 sDebugLines[7] = y2; 6595 6596 sDebugLines[8] = x2; 6597 sDebugLines[9] = y2; 6598 sDebugLines[10] = x1; 6599 sDebugLines[11] = y2; 6600 6601 sDebugLines[12] = x1; 6602 sDebugLines[13] = y2; 6603 sDebugLines[14] = x1; 6604 sDebugLines[15] = y1; 6605 6606 canvas.drawLines(sDebugLines, paint); 6607 } 6608} 6609