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