ViewGroup.java revision 5e25c2c14593caee5638603120553ae1ec530f85
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 com.android.internal.R; 21 22import android.content.ClipData; 23import android.content.ClipDescription; 24import android.content.Context; 25import android.content.res.Configuration; 26import android.content.res.TypedArray; 27import android.graphics.Bitmap; 28import android.graphics.Canvas; 29import android.graphics.Matrix; 30import android.graphics.Paint; 31import android.graphics.PointF; 32import android.graphics.Rect; 33import android.graphics.RectF; 34import android.graphics.Region; 35import android.os.Parcelable; 36import android.os.SystemClock; 37import android.util.AttributeSet; 38import android.util.Log; 39import android.util.Slog; 40import android.util.SparseArray; 41import android.view.accessibility.AccessibilityEvent; 42import android.view.animation.Animation; 43import android.view.animation.AnimationUtils; 44import android.view.animation.LayoutAnimationController; 45import android.view.animation.Transformation; 46 47import java.util.ArrayList; 48 49/** 50 * <p> 51 * A <code>ViewGroup</code> is a special view that can contain other views 52 * (called children.) The view group is the base class for layouts and views 53 * containers. This class also defines the 54 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base 55 * class for layouts parameters. 56 * </p> 57 * 58 * <p> 59 * Also see {@link LayoutParams} for layout attributes. 60 * </p> 61 * 62 * @attr ref android.R.styleable#ViewGroup_clipChildren 63 * @attr ref android.R.styleable#ViewGroup_clipToPadding 64 * @attr ref android.R.styleable#ViewGroup_layoutAnimation 65 * @attr ref android.R.styleable#ViewGroup_animationCache 66 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache 67 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache 68 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren 69 * @attr ref android.R.styleable#ViewGroup_descendantFocusability 70 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 71 */ 72public abstract class ViewGroup extends View implements ViewParent, ViewManager { 73 74 private static final boolean DBG = false; 75 private static final String TAG = "ViewGroup"; 76 77 /** 78 * Views which have been hidden or removed which need to be animated on 79 * their way out. 80 * This field should be made private, so it is hidden from the SDK. 81 * {@hide} 82 */ 83 protected ArrayList<View> mDisappearingChildren; 84 85 /** 86 * Listener used to propagate events indicating when children are added 87 * and/or removed from a view group. 88 * This field should be made private, so it is hidden from the SDK. 89 * {@hide} 90 */ 91 protected OnHierarchyChangeListener mOnHierarchyChangeListener; 92 93 // The view contained within this ViewGroup that has or contains focus. 94 private View mFocused; 95 96 /** 97 * A Transformation used when drawing children, to 98 * apply on the child being drawn. 99 */ 100 private final Transformation mChildTransformation = new Transformation(); 101 102 /** 103 * Used to track the current invalidation region. 104 */ 105 private RectF mInvalidateRegion; 106 107 /** 108 * A Transformation used to calculate a correct 109 * invalidation area when the application is autoscaled. 110 */ 111 private Transformation mInvalidationTransformation; 112 113 // View currently under an ongoing drag 114 private View mCurrentDragView; 115 116 // Does this group have a child that can accept the current drag payload? 117 private boolean mChildAcceptsDrag; 118 119 // Used during drag dispatch 120 private final PointF mLocalPoint = new PointF(); 121 122 // Layout animation 123 private LayoutAnimationController mLayoutAnimationController; 124 private Animation.AnimationListener mAnimationListener; 125 126 // First touch target in the linked list of touch targets. 127 private TouchTarget mFirstTouchTarget; 128 129 // Temporary arrays for splitting pointers. 130 private int[] mTmpPointerIndexMap; 131 private int[] mTmpPointerIds; 132 private MotionEvent.PointerCoords[] mTmpPointerCoords; 133 134 /** 135 * Internal flags. 136 * 137 * This field should be made private, so it is hidden from the SDK. 138 * {@hide} 139 */ 140 protected int mGroupFlags; 141 142 // When set, ViewGroup invalidates only the child's rectangle 143 // Set by default 144 private static final int FLAG_CLIP_CHILDREN = 0x1; 145 146 // When set, ViewGroup excludes the padding area from the invalidate rectangle 147 // Set by default 148 private static final int FLAG_CLIP_TO_PADDING = 0x2; 149 150 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when 151 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set 152 private static final int FLAG_INVALIDATE_REQUIRED = 0x4; 153 154 // When set, dispatchDraw() will run the layout animation and unset the flag 155 private static final int FLAG_RUN_ANIMATION = 0x8; 156 157 // When set, there is either no layout animation on the ViewGroup or the layout 158 // animation is over 159 // Set by default 160 private static final int FLAG_ANIMATION_DONE = 0x10; 161 162 // If set, this ViewGroup has padding; if unset there is no padding and we don't need 163 // to clip it, even if FLAG_CLIP_TO_PADDING is set 164 private static final int FLAG_PADDING_NOT_NULL = 0x20; 165 166 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation 167 // Set by default 168 private static final int FLAG_ANIMATION_CACHE = 0x40; 169 170 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a 171 // layout animation; this avoid clobbering the hierarchy 172 // Automatically set when the layout animation starts, depending on the animation's 173 // characteristics 174 private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80; 175 176 // When set, the next call to drawChild() will clear mChildTransformation's matrix 177 private static final int FLAG_CLEAR_TRANSFORMATION = 0x100; 178 179 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes 180 // the children's Bitmap caches if necessary 181 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set) 182 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200; 183 184 /** 185 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)} 186 * to get the index of the child to draw for that iteration. 187 * 188 * @hide 189 */ 190 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; 191 192 /** 193 * When set, this ViewGroup supports static transformations on children; this causes 194 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 195 * invoked when a child is drawn. 196 * 197 * Any subclass overriding 198 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 199 * set this flags in {@link #mGroupFlags}. 200 * 201 * {@hide} 202 */ 203 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; 204 205 // When the previous drawChild() invocation used an alpha value that was lower than 206 // 1.0 and set it in mCachePaint 207 private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000; 208 209 /** 210 * When set, this ViewGroup's drawable states also include those 211 * of its children. 212 */ 213 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000; 214 215 /** 216 * When set, this ViewGroup tries to always draw its children using their drawing cache. 217 */ 218 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; 219 220 /** 221 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to 222 * draw its children with their drawing cache. 223 */ 224 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; 225 226 /** 227 * When set, this group will go through its list of children to notify them of 228 * any drawable state change. 229 */ 230 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000; 231 232 private static final int FLAG_MASK_FOCUSABILITY = 0x60000; 233 234 /** 235 * This view will get focus before any of its descendants. 236 */ 237 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000; 238 239 /** 240 * This view will get focus only if none of its descendants want it. 241 */ 242 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000; 243 244 /** 245 * This view will block any of its descendants from getting focus, even 246 * if they are focusable. 247 */ 248 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000; 249 250 /** 251 * Used to map between enum in attrubutes and flag values. 252 */ 253 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS = 254 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, 255 FOCUS_BLOCK_DESCENDANTS}; 256 257 /** 258 * When set, this ViewGroup should not intercept touch events. 259 * {@hide} 260 */ 261 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000; 262 263 /** 264 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate. 265 */ 266 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000; 267 268 /** 269 * Indicates which types of drawing caches are to be kept in memory. 270 * This field should be made private, so it is hidden from the SDK. 271 * {@hide} 272 */ 273 protected int mPersistentDrawingCache; 274 275 /** 276 * Used to indicate that no drawing cache should be kept in memory. 277 */ 278 public static final int PERSISTENT_NO_CACHE = 0x0; 279 280 /** 281 * Used to indicate that the animation drawing cache should be kept in memory. 282 */ 283 public static final int PERSISTENT_ANIMATION_CACHE = 0x1; 284 285 /** 286 * Used to indicate that the scrolling drawing cache should be kept in memory. 287 */ 288 public static final int PERSISTENT_SCROLLING_CACHE = 0x2; 289 290 /** 291 * Used to indicate that all drawing caches should be kept in memory. 292 */ 293 public static final int PERSISTENT_ALL_CACHES = 0x3; 294 295 /** 296 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL 297 * are set at the same time. 298 */ 299 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL; 300 301 // Index of the child's left position in the mLocation array 302 private static final int CHILD_LEFT_INDEX = 0; 303 // Index of the child's top position in the mLocation array 304 private static final int CHILD_TOP_INDEX = 1; 305 306 // Child views of this ViewGroup 307 private View[] mChildren; 308 // Number of valid children in the mChildren array, the rest should be null or not 309 // considered as children 310 private int mChildrenCount; 311 312 private static final int ARRAY_INITIAL_CAPACITY = 12; 313 private static final int ARRAY_CAPACITY_INCREMENT = 12; 314 315 // Used to draw cached views 316 private final Paint mCachePaint = new Paint(); 317 318 // Used to animate add/remove changes in layout 319 private LayoutTransition mTransition; 320 321 // The set of views that are currently being transitioned. This list is used to track views 322 // being removed that should not actually be removed from the parent yet because they are 323 // being animated. 324 private ArrayList<View> mTransitioningViews; 325 326 // List of children changing visibility. This is used to potentially keep rendering 327 // views during a transition when they otherwise would have become gone/invisible 328 private ArrayList<View> mVisibilityChangingChildren; 329 330 public ViewGroup(Context context) { 331 super(context); 332 initViewGroup(); 333 } 334 335 public ViewGroup(Context context, AttributeSet attrs) { 336 super(context, attrs); 337 initViewGroup(); 338 initFromAttributes(context, attrs); 339 } 340 341 public ViewGroup(Context context, AttributeSet attrs, int defStyle) { 342 super(context, attrs, defStyle); 343 initViewGroup(); 344 initFromAttributes(context, attrs); 345 } 346 347 private void initViewGroup() { 348 // ViewGroup doesn't draw by default 349 setFlags(WILL_NOT_DRAW, DRAW_MASK); 350 mGroupFlags |= FLAG_CLIP_CHILDREN; 351 mGroupFlags |= FLAG_CLIP_TO_PADDING; 352 mGroupFlags |= FLAG_ANIMATION_DONE; 353 mGroupFlags |= FLAG_ANIMATION_CACHE; 354 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; 355 356 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); 357 358 mChildren = new View[ARRAY_INITIAL_CAPACITY]; 359 mChildrenCount = 0; 360 361 mCachePaint.setDither(false); 362 363 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; 364 } 365 366 private void initFromAttributes(Context context, AttributeSet attrs) { 367 TypedArray a = context.obtainStyledAttributes(attrs, 368 R.styleable.ViewGroup); 369 370 final int N = a.getIndexCount(); 371 for (int i = 0; i < N; i++) { 372 int attr = a.getIndex(i); 373 switch (attr) { 374 case R.styleable.ViewGroup_clipChildren: 375 setClipChildren(a.getBoolean(attr, true)); 376 break; 377 case R.styleable.ViewGroup_clipToPadding: 378 setClipToPadding(a.getBoolean(attr, true)); 379 break; 380 case R.styleable.ViewGroup_animationCache: 381 setAnimationCacheEnabled(a.getBoolean(attr, true)); 382 break; 383 case R.styleable.ViewGroup_persistentDrawingCache: 384 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE)); 385 break; 386 case R.styleable.ViewGroup_addStatesFromChildren: 387 setAddStatesFromChildren(a.getBoolean(attr, false)); 388 break; 389 case R.styleable.ViewGroup_alwaysDrawnWithCache: 390 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true)); 391 break; 392 case R.styleable.ViewGroup_layoutAnimation: 393 int id = a.getResourceId(attr, -1); 394 if (id > 0) { 395 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id)); 396 } 397 break; 398 case R.styleable.ViewGroup_descendantFocusability: 399 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]); 400 break; 401 case R.styleable.ViewGroup_splitMotionEvents: 402 setMotionEventSplittingEnabled(a.getBoolean(attr, false)); 403 break; 404 case R.styleable.ViewGroup_animateLayoutChanges: 405 boolean animateLayoutChanges = a.getBoolean(attr, false); 406 if (animateLayoutChanges) { 407 setLayoutTransition(new LayoutTransition()); 408 } 409 break; 410 } 411 } 412 413 a.recycle(); 414 } 415 416 /** 417 * Gets the descendant focusability of this view group. The descendant 418 * focusability defines the relationship between this view group and its 419 * descendants when looking for a view to take focus in 420 * {@link #requestFocus(int, android.graphics.Rect)}. 421 * 422 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 423 * {@link #FOCUS_BLOCK_DESCENDANTS}. 424 */ 425 @ViewDebug.ExportedProperty(category = "focus", mapping = { 426 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"), 427 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"), 428 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS") 429 }) 430 public int getDescendantFocusability() { 431 return mGroupFlags & FLAG_MASK_FOCUSABILITY; 432 } 433 434 /** 435 * Set the descendant focusability of this view group. This defines the relationship 436 * between this view group and its descendants when looking for a view to 437 * take focus in {@link #requestFocus(int, android.graphics.Rect)}. 438 * 439 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 440 * {@link #FOCUS_BLOCK_DESCENDANTS}. 441 */ 442 public void setDescendantFocusability(int focusability) { 443 switch (focusability) { 444 case FOCUS_BEFORE_DESCENDANTS: 445 case FOCUS_AFTER_DESCENDANTS: 446 case FOCUS_BLOCK_DESCENDANTS: 447 break; 448 default: 449 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, " 450 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS"); 451 } 452 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY; 453 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY); 454 } 455 456 /** 457 * {@inheritDoc} 458 */ 459 @Override 460 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) { 461 if (mFocused != null) { 462 mFocused.unFocus(); 463 mFocused = null; 464 } 465 super.handleFocusGainInternal(direction, previouslyFocusedRect); 466 } 467 468 /** 469 * {@inheritDoc} 470 */ 471 public void requestChildFocus(View child, View focused) { 472 if (DBG) { 473 System.out.println(this + " requestChildFocus()"); 474 } 475 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) { 476 return; 477 } 478 479 // Unfocus us, if necessary 480 super.unFocus(); 481 482 // We had a previous notion of who had focus. Clear it. 483 if (mFocused != child) { 484 if (mFocused != null) { 485 mFocused.unFocus(); 486 } 487 488 mFocused = child; 489 } 490 if (mParent != null) { 491 mParent.requestChildFocus(this, focused); 492 } 493 } 494 495 /** 496 * {@inheritDoc} 497 */ 498 public void focusableViewAvailable(View v) { 499 if (mParent != null 500 // shortcut: don't report a new focusable view if we block our descendants from 501 // getting focus 502 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) 503 // shortcut: don't report a new focusable view if we already are focused 504 // (and we don't prefer our descendants) 505 // 506 // note: knowing that mFocused is non-null is not a good enough reason 507 // to break the traversal since in that case we'd actually have to find 508 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and 509 // an ancestor of v; this will get checked for at ViewRoot 510 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) { 511 mParent.focusableViewAvailable(v); 512 } 513 } 514 515 /** 516 * {@inheritDoc} 517 */ 518 public boolean showContextMenuForChild(View originalView) { 519 return mParent != null && mParent.showContextMenuForChild(originalView); 520 } 521 522 /** 523 * {@inheritDoc} 524 */ 525 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 526 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null; 527 } 528 529 /** 530 * Find the nearest view in the specified direction that wants to take 531 * focus. 532 * 533 * @param focused The view that currently has focus 534 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and 535 * FOCUS_RIGHT, or 0 for not applicable. 536 */ 537 public View focusSearch(View focused, int direction) { 538 if (isRootNamespace()) { 539 // root namespace means we should consider ourselves the top of the 540 // tree for focus searching; otherwise we could be focus searching 541 // into other tabs. see LocalActivityManager and TabHost for more info 542 return FocusFinder.getInstance().findNextFocus(this, focused, direction); 543 } else if (mParent != null) { 544 return mParent.focusSearch(focused, direction); 545 } 546 return null; 547 } 548 549 /** 550 * {@inheritDoc} 551 */ 552 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 553 return false; 554 } 555 556 /** 557 * {@inheritDoc} 558 */ 559 @Override 560 public boolean dispatchUnhandledMove(View focused, int direction) { 561 return mFocused != null && 562 mFocused.dispatchUnhandledMove(focused, direction); 563 } 564 565 /** 566 * {@inheritDoc} 567 */ 568 public void clearChildFocus(View child) { 569 if (DBG) { 570 System.out.println(this + " clearChildFocus()"); 571 } 572 573 mFocused = null; 574 if (mParent != null) { 575 mParent.clearChildFocus(this); 576 } 577 } 578 579 /** 580 * {@inheritDoc} 581 */ 582 @Override 583 public void clearFocus() { 584 super.clearFocus(); 585 586 // clear any child focus if it exists 587 if (mFocused != null) { 588 mFocused.clearFocus(); 589 } 590 } 591 592 /** 593 * {@inheritDoc} 594 */ 595 @Override 596 void unFocus() { 597 if (DBG) { 598 System.out.println(this + " unFocus()"); 599 } 600 601 super.unFocus(); 602 if (mFocused != null) { 603 mFocused.unFocus(); 604 } 605 mFocused = null; 606 } 607 608 /** 609 * Returns the focused child of this view, if any. The child may have focus 610 * or contain focus. 611 * 612 * @return the focused child or null. 613 */ 614 public View getFocusedChild() { 615 return mFocused; 616 } 617 618 /** 619 * Returns true if this view has or contains focus 620 * 621 * @return true if this view has or contains focus 622 */ 623 @Override 624 public boolean hasFocus() { 625 return (mPrivateFlags & FOCUSED) != 0 || mFocused != null; 626 } 627 628 /* 629 * (non-Javadoc) 630 * 631 * @see android.view.View#findFocus() 632 */ 633 @Override 634 public View findFocus() { 635 if (DBG) { 636 System.out.println("Find focus in " + this + ": flags=" 637 + isFocused() + ", child=" + mFocused); 638 } 639 640 if (isFocused()) { 641 return this; 642 } 643 644 if (mFocused != null) { 645 return mFocused.findFocus(); 646 } 647 return null; 648 } 649 650 /** 651 * {@inheritDoc} 652 */ 653 @Override 654 public boolean hasFocusable() { 655 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) { 656 return false; 657 } 658 659 if (isFocusable()) { 660 return true; 661 } 662 663 final int descendantFocusability = getDescendantFocusability(); 664 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 665 final int count = mChildrenCount; 666 final View[] children = mChildren; 667 668 for (int i = 0; i < count; i++) { 669 final View child = children[i]; 670 if (child.hasFocusable()) { 671 return true; 672 } 673 } 674 } 675 676 return false; 677 } 678 679 /** 680 * {@inheritDoc} 681 */ 682 @Override 683 public void addFocusables(ArrayList<View> views, int direction) { 684 addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); 685 } 686 687 /** 688 * {@inheritDoc} 689 */ 690 @Override 691 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 692 final int focusableCount = views.size(); 693 694 final int descendantFocusability = getDescendantFocusability(); 695 696 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 697 final int count = mChildrenCount; 698 final View[] children = mChildren; 699 700 for (int i = 0; i < count; i++) { 701 final View child = children[i]; 702 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 703 child.addFocusables(views, direction, focusableMode); 704 } 705 } 706 } 707 708 // we add ourselves (if focusable) in all cases except for when we are 709 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is 710 // to avoid the focus search finding layouts when a more precise search 711 // among the focusable children would be more interesting. 712 if ( 713 descendantFocusability != FOCUS_AFTER_DESCENDANTS || 714 // No focusable descendants 715 (focusableCount == views.size())) { 716 super.addFocusables(views, direction, focusableMode); 717 } 718 } 719 720 /** 721 * {@inheritDoc} 722 */ 723 @Override 724 public void dispatchWindowFocusChanged(boolean hasFocus) { 725 super.dispatchWindowFocusChanged(hasFocus); 726 final int count = mChildrenCount; 727 final View[] children = mChildren; 728 for (int i = 0; i < count; i++) { 729 children[i].dispatchWindowFocusChanged(hasFocus); 730 } 731 } 732 733 /** 734 * {@inheritDoc} 735 */ 736 @Override 737 public void addTouchables(ArrayList<View> views) { 738 super.addTouchables(views); 739 740 final int count = mChildrenCount; 741 final View[] children = mChildren; 742 743 for (int i = 0; i < count; i++) { 744 final View child = children[i]; 745 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 746 child.addTouchables(views); 747 } 748 } 749 } 750 751 /** 752 * {@inheritDoc} 753 */ 754 @Override 755 public void dispatchDisplayHint(int hint) { 756 super.dispatchDisplayHint(hint); 757 final int count = mChildrenCount; 758 final View[] children = mChildren; 759 for (int i = 0; i < count; i++) { 760 children[i].dispatchDisplayHint(hint); 761 } 762 } 763 764 /** 765 * @hide 766 * @param child 767 * @param visibility 768 */ 769 void onChildVisibilityChanged(View child, int visibility) { 770 if (mTransition != null) { 771 if (visibility == VISIBLE) { 772 mTransition.showChild(this, child); 773 } else { 774 mTransition.hideChild(this, child); 775 } 776 if (visibility != VISIBLE) { 777 // Only track this on disappearing views - appearing views are already visible 778 // and don't need special handling during drawChild() 779 if (mVisibilityChangingChildren == null) { 780 mVisibilityChangingChildren = new ArrayList<View>(); 781 } 782 mVisibilityChangingChildren.add(child); 783 if (mTransitioningViews != null && mTransitioningViews.contains(child)) { 784 addDisappearingView(child); 785 } 786 } 787 } 788 } 789 790 /** 791 * {@inheritDoc} 792 */ 793 @Override 794 protected void dispatchVisibilityChanged(View changedView, int visibility) { 795 super.dispatchVisibilityChanged(changedView, visibility); 796 final int count = mChildrenCount; 797 final View[] children = mChildren; 798 for (int i = 0; i < count; i++) { 799 children[i].dispatchVisibilityChanged(changedView, visibility); 800 } 801 } 802 803 /** 804 * {@inheritDoc} 805 */ 806 @Override 807 public void dispatchWindowVisibilityChanged(int visibility) { 808 super.dispatchWindowVisibilityChanged(visibility); 809 final int count = mChildrenCount; 810 final View[] children = mChildren; 811 for (int i = 0; i < count; i++) { 812 children[i].dispatchWindowVisibilityChanged(visibility); 813 } 814 } 815 816 /** 817 * {@inheritDoc} 818 */ 819 @Override 820 public void dispatchConfigurationChanged(Configuration newConfig) { 821 super.dispatchConfigurationChanged(newConfig); 822 final int count = mChildrenCount; 823 final View[] children = mChildren; 824 for (int i = 0; i < count; i++) { 825 children[i].dispatchConfigurationChanged(newConfig); 826 } 827 } 828 829 /** 830 * {@inheritDoc} 831 */ 832 public void recomputeViewAttributes(View child) { 833 ViewParent parent = mParent; 834 if (parent != null) parent.recomputeViewAttributes(this); 835 } 836 837 @Override 838 void dispatchCollectViewAttributes(int visibility) { 839 visibility |= mViewFlags&VISIBILITY_MASK; 840 super.dispatchCollectViewAttributes(visibility); 841 final int count = mChildrenCount; 842 final View[] children = mChildren; 843 for (int i = 0; i < count; i++) { 844 children[i].dispatchCollectViewAttributes(visibility); 845 } 846 } 847 848 /** 849 * {@inheritDoc} 850 */ 851 public void bringChildToFront(View child) { 852 int index = indexOfChild(child); 853 if (index >= 0) { 854 removeFromArray(index); 855 addInArray(child, mChildrenCount); 856 child.mParent = this; 857 } 858 } 859 860 /** 861 * {@inheritDoc} 862 * 863 * !!! TODO: write real docs 864 */ 865 @Override 866 public boolean dispatchDragEvent(DragEvent event) { 867 boolean retval = false; 868 final float tx = event.mX; 869 final float ty = event.mY; 870 871 // !!! BUGCHECK: If we have a ViewGroup, we must necessarily have a ViewRoot, 872 // so we don't need to check getRootView() for null here? 873 ViewRoot root = (ViewRoot)(getRootView().getParent()); 874 875 // Dispatch down the view hierarchy 876 switch (event.mAction) { 877 case DragEvent.ACTION_DRAG_STARTED: { 878 // clear state to recalculate which views we drag over 879 root.setDragFocus(event, null); 880 881 // Now dispatch down to our children, caching the responses 882 mChildAcceptsDrag = false; 883 final int count = mChildrenCount; 884 final View[] children = mChildren; 885 for (int i = 0; i < count; i++) { 886 final boolean handled = children[i].dispatchDragEvent(event); 887 children[i].mCanAcceptDrop = handled; 888 if (handled) { 889 mChildAcceptsDrag = true; 890 } 891 } 892 893 // Return HANDLED if one of our children can accept the drag 894 if (mChildAcceptsDrag) { 895 retval = true; 896 } 897 } break; 898 899 case DragEvent.ACTION_DRAG_ENDED: { 900 // Notify all of our children that the drag is over 901 final int count = mChildrenCount; 902 final View[] children = mChildren; 903 for (int i = 0; i < count; i++) { 904 children[i].dispatchDragEvent(event); 905 } 906 // We consider drag-ended to have been handled if one of our children 907 // had offered to handle the drag. 908 if (mChildAcceptsDrag) { 909 retval = true; 910 } 911 } break; 912 913 case DragEvent.ACTION_DRAG_LOCATION: { 914 // Find the [possibly new] drag target 915 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); 916 917 // If we've changed apparent drag target, tell the view root which view 918 // we're over now. This will in turn send out DRAG_ENTERED / DRAG_EXITED 919 // notifications as appropriate. 920 if (mCurrentDragView != target) { 921 root.setDragFocus(event, target); 922 mCurrentDragView = target; 923 } 924 925 // Dispatch the actual drag location notice, localized into its coordinates 926 if (target != null) { 927 event.mX = mLocalPoint.x; 928 event.mY = mLocalPoint.y; 929 930 retval = target.dispatchDragEvent(event); 931 932 event.mX = tx; 933 event.mY = ty; 934 } 935 } break; 936 937 case DragEvent.ACTION_DROP: { 938 if (View.DEBUG_DRAG) Slog.d(TAG, "Drop event: " + event); 939 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); 940 if (target != null) { 941 event.mX = mLocalPoint.x; 942 event.mY = mLocalPoint.y; 943 retval = target.dispatchDragEvent(event); 944 event.mX = tx; 945 event.mY = ty; 946 } 947 } break; 948 } 949 950 // If none of our children could handle the event, try here 951 if (!retval) { 952 retval = onDragEvent(event); 953 } 954 return retval; 955 } 956 957 // Find the frontmost child view that lies under the given point, and calculate 958 // the position within its own local coordinate system. 959 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) { 960 final float scrolledX = x + mScrollX; 961 final float scrolledY = y + mScrollY; 962 final int count = mChildrenCount; 963 final View[] children = mChildren; 964 for (int i = count - 1; i >= 0; i--) { 965 final View child = children[i]; 966 if (child.mCanAcceptDrop == false) { 967 continue; 968 } 969 970 float localX = scrolledX - child.mLeft; 971 float localY = scrolledY - child.mTop; 972 if (!child.hasIdentityMatrix() && mAttachInfo != null) { 973 // non-identity matrix: transform the point into the view's coordinates 974 final float[] localXY = mAttachInfo.mTmpTransformLocation; 975 localXY[0] = localX; 976 localXY[1] = localY; 977 child.getInverseMatrix().mapPoints(localXY); 978 localX = localXY[0]; 979 localY = localXY[1]; 980 } 981 if (localX >= 0 && localY >= 0 && localX < (child.mRight - child.mLeft) && 982 localY < (child.mBottom - child.mTop)) { 983 outLocalPoint.set(localX, localY); 984 return child; 985 } 986 } 987 return null; 988 } 989 990 /** 991 * {@inheritDoc} 992 */ 993 @Override 994 public boolean dispatchKeyEventPreIme(KeyEvent event) { 995 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 996 return super.dispatchKeyEventPreIme(event); 997 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 998 return mFocused.dispatchKeyEventPreIme(event); 999 } 1000 return false; 1001 } 1002 1003 /** 1004 * {@inheritDoc} 1005 */ 1006 @Override 1007 public boolean dispatchKeyEvent(KeyEvent event) { 1008 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1009 return super.dispatchKeyEvent(event); 1010 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1011 return mFocused.dispatchKeyEvent(event); 1012 } 1013 return false; 1014 } 1015 1016 /** 1017 * {@inheritDoc} 1018 */ 1019 @Override 1020 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 1021 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1022 return super.dispatchKeyShortcutEvent(event); 1023 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1024 return mFocused.dispatchKeyShortcutEvent(event); 1025 } 1026 return false; 1027 } 1028 1029 /** 1030 * {@inheritDoc} 1031 */ 1032 @Override 1033 public boolean dispatchTrackballEvent(MotionEvent event) { 1034 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1035 return super.dispatchTrackballEvent(event); 1036 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1037 return mFocused.dispatchTrackballEvent(event); 1038 } 1039 return false; 1040 } 1041 1042 /** 1043 * {@inheritDoc} 1044 */ 1045 @Override 1046 public boolean dispatchTouchEvent(MotionEvent ev) { 1047 if (!onFilterTouchEventForSecurity(ev)) { 1048 return false; 1049 } 1050 1051 final int action = ev.getAction(); 1052 final int actionMasked = action & MotionEvent.ACTION_MASK; 1053 1054 // Handle an initial down. 1055 if (actionMasked == MotionEvent.ACTION_DOWN) { 1056 // Throw away all previous state when starting a new touch gesture. 1057 // The framework may have dropped the up or cancel event for the previous gesture 1058 // due to an app switch, ANR, or some other state change. 1059 cancelAndClearTouchTargets(ev); 1060 resetTouchState(); 1061 } 1062 1063 // Check for interception. 1064 final boolean intercepted; 1065 if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { 1066 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 1067 if (!disallowIntercept) { 1068 intercepted = onInterceptTouchEvent(ev); 1069 ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it 1070 } else { 1071 intercepted = false; 1072 } 1073 } else { 1074 intercepted = true; 1075 } 1076 1077 // Check for cancelation. 1078 final boolean canceled = resetCancelNextUpFlag(this) 1079 || actionMasked == MotionEvent.ACTION_CANCEL; 1080 1081 // Update list of touch targets for pointer down, if needed. 1082 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; 1083 TouchTarget newTouchTarget = null; 1084 boolean alreadyDispatchedToNewTouchTarget = false; 1085 if (!canceled && !intercepted) { 1086 if (actionMasked == MotionEvent.ACTION_DOWN 1087 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)) { 1088 final int actionIndex = ev.getActionIndex(); // always 0 for down 1089 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) 1090 : TouchTarget.ALL_POINTER_IDS; 1091 1092 // Clean up earlier touch targets for this pointer id in case they 1093 // have become out of sync. 1094 removePointersFromTouchTargets(idBitsToAssign); 1095 1096 final int childrenCount = mChildrenCount; 1097 if (childrenCount != 0) { 1098 // Find a child that can receive the event. Scan children from front to back. 1099 final View[] children = mChildren; 1100 final float x = ev.getX(actionIndex); 1101 final float y = ev.getY(actionIndex); 1102 1103 for (int i = childrenCount - 1; i >= 0; i--) { 1104 final View child = children[i]; 1105 if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE 1106 && child.getAnimation() == null) { 1107 // Skip invisible child unless it is animating. 1108 continue; 1109 } 1110 1111 if (!isTransformedTouchPointInView(x, y, child)) { 1112 // New pointer is out of child's bounds. 1113 continue; 1114 } 1115 1116 newTouchTarget = getTouchTarget(child); 1117 if (newTouchTarget != null) { 1118 // Child is already receiving touch within its bounds. 1119 // Give it the new pointer in addition to the ones it is handling. 1120 newTouchTarget.pointerIdBits |= idBitsToAssign; 1121 break; 1122 } 1123 1124 resetCancelNextUpFlag(child); 1125 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 1126 // Child wants to receive touch within its bounds. 1127 newTouchTarget = addTouchTarget(child, idBitsToAssign); 1128 alreadyDispatchedToNewTouchTarget = true; 1129 break; 1130 } 1131 } 1132 } 1133 1134 if (newTouchTarget == null && mFirstTouchTarget != null) { 1135 // Did not find a child to receive the event. 1136 // Assign the pointer to the least recently added target. 1137 newTouchTarget = mFirstTouchTarget; 1138 while (newTouchTarget.next != null) { 1139 newTouchTarget = newTouchTarget.next; 1140 } 1141 newTouchTarget.pointerIdBits |= idBitsToAssign; 1142 } 1143 } 1144 } 1145 1146 // Dispatch to touch targets. 1147 boolean handled = false; 1148 if (mFirstTouchTarget == null) { 1149 // No touch targets so treat this as an ordinary view. 1150 handled = dispatchTransformedTouchEvent(ev, canceled, null, 1151 TouchTarget.ALL_POINTER_IDS); 1152 } else { 1153 // Dispatch to touch targets, excluding the new touch target if we already 1154 // dispatched to it. Cancel touch targets if necessary. 1155 TouchTarget predecessor = null; 1156 TouchTarget target = mFirstTouchTarget; 1157 while (target != null) { 1158 final TouchTarget next = target.next; 1159 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { 1160 handled = true; 1161 } else { 1162 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; 1163 if (dispatchTransformedTouchEvent(ev, cancelChild, 1164 target.child, target.pointerIdBits)) { 1165 handled = true; 1166 } 1167 if (cancelChild) { 1168 if (predecessor == null) { 1169 mFirstTouchTarget = next; 1170 } else { 1171 predecessor.next = next; 1172 } 1173 target.recycle(); 1174 target = next; 1175 continue; 1176 } 1177 } 1178 predecessor = target; 1179 target = next; 1180 } 1181 } 1182 1183 // Update list of touch targets for pointer up or cancel, if needed. 1184 if (canceled || actionMasked == MotionEvent.ACTION_UP) { 1185 resetTouchState(); 1186 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { 1187 final int actionIndex = ev.getActionIndex(); 1188 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); 1189 removePointersFromTouchTargets(idBitsToRemove); 1190 } 1191 1192 return handled; 1193 } 1194 1195 /* Resets all touch state in preparation for a new cycle. */ 1196 private final void resetTouchState() { 1197 clearTouchTargets(); 1198 resetCancelNextUpFlag(this); 1199 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 1200 } 1201 1202 /* Resets the cancel next up flag. 1203 * Returns true if the flag was previously set. */ 1204 private final boolean resetCancelNextUpFlag(View view) { 1205 if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { 1206 view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 1207 return true; 1208 } 1209 return false; 1210 } 1211 1212 /* Clears all touch targets. */ 1213 private final void clearTouchTargets() { 1214 TouchTarget target = mFirstTouchTarget; 1215 if (target != null) { 1216 do { 1217 TouchTarget next = target.next; 1218 target.recycle(); 1219 target = next; 1220 } while (target != null); 1221 mFirstTouchTarget = null; 1222 } 1223 } 1224 1225 /* Cancels and clears all touch targets. */ 1226 private final void cancelAndClearTouchTargets(MotionEvent event) { 1227 if (mFirstTouchTarget != null) { 1228 boolean syntheticEvent = false; 1229 if (event == null) { 1230 final long now = SystemClock.uptimeMillis(); 1231 event = MotionEvent.obtain(now, now, 1232 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 1233 syntheticEvent = true; 1234 } 1235 1236 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 1237 resetCancelNextUpFlag(target.child); 1238 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits); 1239 } 1240 clearTouchTargets(); 1241 1242 if (syntheticEvent) { 1243 event.recycle(); 1244 } 1245 } 1246 } 1247 1248 /* Gets the touch target for specified child view. 1249 * Returns null if not found. */ 1250 private final TouchTarget getTouchTarget(View child) { 1251 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 1252 if (target.child == child) { 1253 return target; 1254 } 1255 } 1256 return null; 1257 } 1258 1259 /* Adds a touch target for specified child to the beginning of the list. 1260 * Assumes the target child is not already present. */ 1261 private final TouchTarget addTouchTarget(View child, int pointerIdBits) { 1262 TouchTarget target = TouchTarget.obtain(child, pointerIdBits); 1263 target.next = mFirstTouchTarget; 1264 mFirstTouchTarget = target; 1265 return target; 1266 } 1267 1268 /* Removes the pointer ids from consideration. */ 1269 private final void removePointersFromTouchTargets(int pointerIdBits) { 1270 TouchTarget predecessor = null; 1271 TouchTarget target = mFirstTouchTarget; 1272 while (target != null) { 1273 final TouchTarget next = target.next; 1274 if ((target.pointerIdBits & pointerIdBits) != 0) { 1275 target.pointerIdBits &= ~pointerIdBits; 1276 if (target.pointerIdBits == 0) { 1277 if (predecessor == null) { 1278 mFirstTouchTarget = next; 1279 } else { 1280 predecessor.next = next; 1281 } 1282 target.recycle(); 1283 target = next; 1284 continue; 1285 } 1286 } 1287 predecessor = target; 1288 target = next; 1289 } 1290 } 1291 1292 /* Returns true if a child view contains the specified point when transformed 1293 * into its coordinate space. 1294 * Child must not be null. */ 1295 private final boolean isTransformedTouchPointInView(float x, float y, View child) { 1296 float localX = x + mScrollX - child.mLeft; 1297 float localY = y + mScrollY - child.mTop; 1298 if (! child.hasIdentityMatrix() && mAttachInfo != null) { 1299 final float[] localXY = mAttachInfo.mTmpTransformLocation; 1300 localXY[0] = localX; 1301 localXY[1] = localY; 1302 child.getInverseMatrix().mapPoints(localXY); 1303 localX = localXY[0]; 1304 localY = localXY[1]; 1305 } 1306 return child.pointInView(localX, localY); 1307 } 1308 1309 /* Transforms a motion event into the coordinate space of a particular child view, 1310 * filters out irrelevant pointer ids, and overrides its action if necessary. 1311 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. */ 1312 private final boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, 1313 View child, int desiredPointerIdBits) { 1314 final boolean handled; 1315 1316 // Canceling motions is a special case. We don't need to perform any transformations 1317 // or filtering. The important part is the action, not the contents. 1318 final int oldAction = event.getAction(); 1319 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { 1320 event.setAction(MotionEvent.ACTION_CANCEL); 1321 if (child == null) { 1322 handled = super.dispatchTouchEvent(event); 1323 } else { 1324 handled = child.dispatchTouchEvent(event); 1325 } 1326 event.setAction(oldAction); 1327 return handled; 1328 } 1329 1330 // Calculate the number of pointers to deliver. 1331 final int oldPointerCount = event.getPointerCount(); 1332 int newPointerCount = 0; 1333 if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) { 1334 newPointerCount = oldPointerCount; 1335 } else { 1336 for (int i = 0; i < oldPointerCount; i++) { 1337 final int pointerId = event.getPointerId(i); 1338 final int pointerIdBit = 1 << pointerId; 1339 if ((pointerIdBit & desiredPointerIdBits) != 0) { 1340 newPointerCount += 1; 1341 } 1342 } 1343 } 1344 1345 // If for some reason we ended up in an inconsistent state where it looks like we 1346 // might produce a motion event with no pointers in it, then drop the event. 1347 if (newPointerCount == 0) { 1348 return false; 1349 } 1350 1351 // If the number of pointers is the same and we don't need to perform any fancy 1352 // irreversible transformations, then we can reuse the motion event for this 1353 // dispatch as long as we are careful to revert any changes we make. 1354 final boolean reuse = newPointerCount == oldPointerCount 1355 && (child == null || child.hasIdentityMatrix()); 1356 if (reuse) { 1357 if (child == null) { 1358 handled = super.dispatchTouchEvent(event); 1359 } else { 1360 final float offsetX = mScrollX - child.mLeft; 1361 final float offsetY = mScrollY - child.mTop; 1362 event.offsetLocation(offsetX, offsetY); 1363 1364 handled = child.dispatchTouchEvent(event); 1365 1366 event.offsetLocation(-offsetX, -offsetY); 1367 } 1368 return handled; 1369 } 1370 1371 // Make a copy of the event. 1372 // If the number of pointers is different, then we need to filter out irrelevant pointers 1373 // as we make a copy of the motion event. 1374 MotionEvent transformedEvent; 1375 if (newPointerCount == oldPointerCount) { 1376 transformedEvent = MotionEvent.obtain(event); 1377 } else { 1378 growTmpPointerArrays(newPointerCount); 1379 final int[] newPointerIndexMap = mTmpPointerIndexMap; 1380 final int[] newPointerIds = mTmpPointerIds; 1381 final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords; 1382 1383 int newPointerIndex = 0; 1384 int oldPointerIndex = 0; 1385 while (newPointerIndex < newPointerCount) { 1386 final int pointerId = event.getPointerId(oldPointerIndex); 1387 final int pointerIdBits = 1 << pointerId; 1388 if ((pointerIdBits & desiredPointerIdBits) != 0) { 1389 newPointerIndexMap[newPointerIndex] = oldPointerIndex; 1390 newPointerIds[newPointerIndex] = pointerId; 1391 if (newPointerCoords[newPointerIndex] == null) { 1392 newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords(); 1393 } 1394 1395 newPointerIndex += 1; 1396 } 1397 oldPointerIndex += 1; 1398 } 1399 1400 final int newAction; 1401 if (cancel) { 1402 newAction = MotionEvent.ACTION_CANCEL; 1403 } else { 1404 final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK; 1405 if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN 1406 || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) { 1407 final int changedPointerId = event.getPointerId( 1408 (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK) 1409 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT); 1410 final int changedPointerIdBits = 1 << changedPointerId; 1411 if ((changedPointerIdBits & desiredPointerIdBits) != 0) { 1412 if (newPointerCount == 1) { 1413 // The first/last pointer went down/up. 1414 newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN 1415 ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP; 1416 } else { 1417 // A secondary pointer went down/up. 1418 int newChangedPointerIndex = 0; 1419 while (newPointerIds[newChangedPointerIndex] != changedPointerId) { 1420 newChangedPointerIndex += 1; 1421 } 1422 newAction = oldMaskedAction | (newChangedPointerIndex 1423 << MotionEvent.ACTION_POINTER_INDEX_SHIFT); 1424 } 1425 } else { 1426 // An unrelated pointer changed. 1427 newAction = MotionEvent.ACTION_MOVE; 1428 } 1429 } else { 1430 // Simple up/down/cancel/move motion action. 1431 newAction = oldMaskedAction; 1432 } 1433 } 1434 1435 transformedEvent = null; 1436 final int historySize = event.getHistorySize(); 1437 for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) { 1438 for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) { 1439 final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex]; 1440 oldPointerIndex = newPointerIndexMap[newPointerIndex]; 1441 if (historyIndex != historySize) { 1442 event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c); 1443 } else { 1444 event.getPointerCoords(oldPointerIndex, c); 1445 } 1446 } 1447 1448 final long eventTime; 1449 if (historyIndex != historySize) { 1450 eventTime = event.getHistoricalEventTime(historyIndex); 1451 } else { 1452 eventTime = event.getEventTime(); 1453 } 1454 1455 if (transformedEvent == null) { 1456 transformedEvent = MotionEvent.obtain( 1457 event.getDownTime(), eventTime, newAction, 1458 newPointerCount, newPointerIds, newPointerCoords, 1459 event.getMetaState(), event.getXPrecision(), event.getYPrecision(), 1460 event.getDeviceId(), event.getEdgeFlags(), event.getSource(), 1461 event.getFlags()); 1462 } else { 1463 transformedEvent.addBatch(eventTime, newPointerCoords, 0); 1464 } 1465 } 1466 } 1467 1468 // Perform any necessary transformations and dispatch. 1469 if (child == null) { 1470 handled = super.dispatchTouchEvent(transformedEvent); 1471 } else { 1472 final float offsetX = mScrollX - child.mLeft; 1473 final float offsetY = mScrollY - child.mTop; 1474 transformedEvent.offsetLocation(offsetX, offsetY); 1475 if (! child.hasIdentityMatrix()) { 1476 transformedEvent.transform(child.getInverseMatrix()); 1477 } 1478 1479 handled = child.dispatchTouchEvent(transformedEvent); 1480 } 1481 1482 // Done. 1483 transformedEvent.recycle(); 1484 return handled; 1485 } 1486 1487 /* Enlarge the temporary pointer arrays for splitting pointers. 1488 * May discard contents (but keeps PointerCoords objects to avoid reallocating them). */ 1489 private final void growTmpPointerArrays(int desiredCapacity) { 1490 final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords; 1491 int capacity; 1492 if (oldTmpPointerCoords != null) { 1493 capacity = oldTmpPointerCoords.length; 1494 if (desiredCapacity <= capacity) { 1495 return; 1496 } 1497 } else { 1498 capacity = 4; 1499 } 1500 1501 while (capacity < desiredCapacity) { 1502 capacity *= 2; 1503 } 1504 1505 mTmpPointerIndexMap = new int[capacity]; 1506 mTmpPointerIds = new int[capacity]; 1507 mTmpPointerCoords = new MotionEvent.PointerCoords[capacity]; 1508 1509 if (oldTmpPointerCoords != null) { 1510 System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0, 1511 oldTmpPointerCoords.length); 1512 } 1513 } 1514 1515 /** 1516 * Enable or disable the splitting of MotionEvents to multiple children during touch event 1517 * dispatch. This behavior is disabled by default. 1518 * 1519 * <p>When this option is enabled MotionEvents may be split and dispatched to different child 1520 * views depending on where each pointer initially went down. This allows for user interactions 1521 * such as scrolling two panes of content independently, chording of buttons, and performing 1522 * independent gestures on different pieces of content. 1523 * 1524 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple 1525 * child views. <code>false</code> to only allow one child view to be the target of 1526 * any MotionEvent received by this ViewGroup. 1527 */ 1528 public void setMotionEventSplittingEnabled(boolean split) { 1529 // TODO Applications really shouldn't change this setting mid-touch event, 1530 // but perhaps this should handle that case and send ACTION_CANCELs to any child views 1531 // with gestures in progress when this is changed. 1532 if (split) { 1533 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 1534 } else { 1535 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS; 1536 } 1537 } 1538 1539 /** 1540 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 1541 */ 1542 public boolean isMotionEventSplittingEnabled() { 1543 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS; 1544 } 1545 1546 /** 1547 * {@inheritDoc} 1548 */ 1549 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 1550 1551 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { 1552 // We're already in this state, assume our ancestors are too 1553 return; 1554 } 1555 1556 if (disallowIntercept) { 1557 mGroupFlags |= FLAG_DISALLOW_INTERCEPT; 1558 } else { 1559 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 1560 } 1561 1562 // Pass it up to our parent 1563 if (mParent != null) { 1564 mParent.requestDisallowInterceptTouchEvent(disallowIntercept); 1565 } 1566 } 1567 1568 /** 1569 * Implement this method to intercept all touch screen motion events. This 1570 * allows you to watch events as they are dispatched to your children, and 1571 * take ownership of the current gesture at any point. 1572 * 1573 * <p>Using this function takes some care, as it has a fairly complicated 1574 * interaction with {@link View#onTouchEvent(MotionEvent) 1575 * View.onTouchEvent(MotionEvent)}, and using it requires implementing 1576 * that method as well as this one in the correct way. Events will be 1577 * received in the following order: 1578 * 1579 * <ol> 1580 * <li> You will receive the down event here. 1581 * <li> The down event will be handled either by a child of this view 1582 * group, or given to your own onTouchEvent() method to handle; this means 1583 * you should implement onTouchEvent() to return true, so you will 1584 * continue to see the rest of the gesture (instead of looking for 1585 * a parent view to handle it). Also, by returning true from 1586 * onTouchEvent(), you will not receive any following 1587 * events in onInterceptTouchEvent() and all touch processing must 1588 * happen in onTouchEvent() like normal. 1589 * <li> For as long as you return false from this function, each following 1590 * event (up to and including the final up) will be delivered first here 1591 * and then to the target's onTouchEvent(). 1592 * <li> If you return true from here, you will not receive any 1593 * following events: the target view will receive the same event but 1594 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further 1595 * events will be delivered to your onTouchEvent() method and no longer 1596 * appear here. 1597 * </ol> 1598 * 1599 * @param ev The motion event being dispatched down the hierarchy. 1600 * @return Return true to steal motion events from the children and have 1601 * them dispatched to this ViewGroup through onTouchEvent(). 1602 * The current target will receive an ACTION_CANCEL event, and no further 1603 * messages will be delivered here. 1604 */ 1605 public boolean onInterceptTouchEvent(MotionEvent ev) { 1606 return false; 1607 } 1608 1609 /** 1610 * {@inheritDoc} 1611 * 1612 * Looks for a view to give focus to respecting the setting specified by 1613 * {@link #getDescendantFocusability()}. 1614 * 1615 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to 1616 * find focus within the children of this group when appropriate. 1617 * 1618 * @see #FOCUS_BEFORE_DESCENDANTS 1619 * @see #FOCUS_AFTER_DESCENDANTS 1620 * @see #FOCUS_BLOCK_DESCENDANTS 1621 * @see #onRequestFocusInDescendants 1622 */ 1623 @Override 1624 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 1625 if (DBG) { 1626 System.out.println(this + " ViewGroup.requestFocus direction=" 1627 + direction); 1628 } 1629 int descendantFocusability = getDescendantFocusability(); 1630 1631 switch (descendantFocusability) { 1632 case FOCUS_BLOCK_DESCENDANTS: 1633 return super.requestFocus(direction, previouslyFocusedRect); 1634 case FOCUS_BEFORE_DESCENDANTS: { 1635 final boolean took = super.requestFocus(direction, previouslyFocusedRect); 1636 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); 1637 } 1638 case FOCUS_AFTER_DESCENDANTS: { 1639 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); 1640 return took ? took : super.requestFocus(direction, previouslyFocusedRect); 1641 } 1642 default: 1643 throw new IllegalStateException("descendant focusability must be " 1644 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " 1645 + "but is " + descendantFocusability); 1646 } 1647 } 1648 1649 /** 1650 * Look for a descendant to call {@link View#requestFocus} on. 1651 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)} 1652 * when it wants to request focus within its children. Override this to 1653 * customize how your {@link ViewGroup} requests focus within its children. 1654 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT 1655 * @param previouslyFocusedRect The rectangle (in this View's coordinate system) 1656 * to give a finer grained hint about where focus is coming from. May be null 1657 * if there is no hint. 1658 * @return Whether focus was taken. 1659 */ 1660 @SuppressWarnings({"ConstantConditions"}) 1661 protected boolean onRequestFocusInDescendants(int direction, 1662 Rect previouslyFocusedRect) { 1663 int index; 1664 int increment; 1665 int end; 1666 int count = mChildrenCount; 1667 if ((direction & FOCUS_FORWARD) != 0) { 1668 index = 0; 1669 increment = 1; 1670 end = count; 1671 } else { 1672 index = count - 1; 1673 increment = -1; 1674 end = -1; 1675 } 1676 final View[] children = mChildren; 1677 for (int i = index; i != end; i += increment) { 1678 View child = children[i]; 1679 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1680 if (child.requestFocus(direction, previouslyFocusedRect)) { 1681 return true; 1682 } 1683 } 1684 } 1685 return false; 1686 } 1687 1688 /** 1689 * {@inheritDoc} 1690 * 1691 * @hide 1692 */ 1693 @Override 1694 public void dispatchStartTemporaryDetach() { 1695 super.dispatchStartTemporaryDetach(); 1696 final int count = mChildrenCount; 1697 final View[] children = mChildren; 1698 for (int i = 0; i < count; i++) { 1699 children[i].dispatchStartTemporaryDetach(); 1700 } 1701 } 1702 1703 /** 1704 * {@inheritDoc} 1705 * 1706 * @hide 1707 */ 1708 @Override 1709 public void dispatchFinishTemporaryDetach() { 1710 super.dispatchFinishTemporaryDetach(); 1711 final int count = mChildrenCount; 1712 final View[] children = mChildren; 1713 for (int i = 0; i < count; i++) { 1714 children[i].dispatchFinishTemporaryDetach(); 1715 } 1716 } 1717 1718 /** 1719 * {@inheritDoc} 1720 */ 1721 @Override 1722 void dispatchAttachedToWindow(AttachInfo info, int visibility) { 1723 super.dispatchAttachedToWindow(info, visibility); 1724 visibility |= mViewFlags & VISIBILITY_MASK; 1725 final int count = mChildrenCount; 1726 final View[] children = mChildren; 1727 for (int i = 0; i < count; i++) { 1728 children[i].dispatchAttachedToWindow(info, visibility); 1729 } 1730 } 1731 1732 @Override 1733 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1734 boolean populated = false; 1735 for (int i = 0, count = getChildCount(); i < count; i++) { 1736 populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event); 1737 } 1738 return populated; 1739 } 1740 1741 /** 1742 * {@inheritDoc} 1743 */ 1744 @Override 1745 void dispatchDetachedFromWindow() { 1746 // If we still have a touch target, we are still in the process of 1747 // dispatching motion events to a child; we need to get rid of that 1748 // child to avoid dispatching events to it after the window is torn 1749 // down. To make sure we keep the child in a consistent state, we 1750 // first send it an ACTION_CANCEL motion event. 1751 cancelAndClearTouchTargets(null); 1752 1753 final int count = mChildrenCount; 1754 final View[] children = mChildren; 1755 for (int i = 0; i < count; i++) { 1756 children[i].dispatchDetachedFromWindow(); 1757 } 1758 super.dispatchDetachedFromWindow(); 1759 } 1760 1761 /** 1762 * {@inheritDoc} 1763 */ 1764 @Override 1765 public void setPadding(int left, int top, int right, int bottom) { 1766 super.setPadding(left, top, right, bottom); 1767 1768 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) { 1769 mGroupFlags |= FLAG_PADDING_NOT_NULL; 1770 } else { 1771 mGroupFlags &= ~FLAG_PADDING_NOT_NULL; 1772 } 1773 } 1774 1775 /** 1776 * {@inheritDoc} 1777 */ 1778 @Override 1779 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 1780 super.dispatchSaveInstanceState(container); 1781 final int count = mChildrenCount; 1782 final View[] children = mChildren; 1783 for (int i = 0; i < count; i++) { 1784 View c = children[i]; 1785 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 1786 c.dispatchSaveInstanceState(container); 1787 } 1788 } 1789 } 1790 1791 /** 1792 * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view, 1793 * not to its children. For use when overriding 1794 * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze 1795 * their own state but not the state of their children. 1796 * 1797 * @param container the container 1798 */ 1799 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { 1800 super.dispatchSaveInstanceState(container); 1801 } 1802 1803 /** 1804 * {@inheritDoc} 1805 */ 1806 @Override 1807 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 1808 super.dispatchRestoreInstanceState(container); 1809 final int count = mChildrenCount; 1810 final View[] children = mChildren; 1811 for (int i = 0; i < count; i++) { 1812 View c = children[i]; 1813 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 1814 c.dispatchRestoreInstanceState(container); 1815 } 1816 } 1817 } 1818 1819 /** 1820 * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view, 1821 * not to its children. For use when overriding 1822 * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw 1823 * their own state but not the state of their children. 1824 * 1825 * @param container the container 1826 */ 1827 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { 1828 super.dispatchRestoreInstanceState(container); 1829 } 1830 1831 /** 1832 * Enables or disables the drawing cache for each child of this view group. 1833 * 1834 * @param enabled true to enable the cache, false to dispose of it 1835 */ 1836 protected void setChildrenDrawingCacheEnabled(boolean enabled) { 1837 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) { 1838 final View[] children = mChildren; 1839 final int count = mChildrenCount; 1840 for (int i = 0; i < count; i++) { 1841 children[i].setDrawingCacheEnabled(enabled); 1842 } 1843 } 1844 } 1845 1846 @Override 1847 protected void onAnimationStart() { 1848 super.onAnimationStart(); 1849 1850 // When this ViewGroup's animation starts, build the cache for the children 1851 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 1852 final int count = mChildrenCount; 1853 final View[] children = mChildren; 1854 1855 for (int i = 0; i < count; i++) { 1856 final View child = children[i]; 1857 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1858 child.setDrawingCacheEnabled(true); 1859 child.buildDrawingCache(true); 1860 } 1861 } 1862 1863 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 1864 } 1865 } 1866 1867 @Override 1868 protected void onAnimationEnd() { 1869 super.onAnimationEnd(); 1870 1871 // When this ViewGroup's animation ends, destroy the cache of the children 1872 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 1873 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 1874 1875 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 1876 setChildrenDrawingCacheEnabled(false); 1877 } 1878 } 1879 } 1880 1881 @Override 1882 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) { 1883 int count = mChildrenCount; 1884 int[] visibilities = null; 1885 1886 if (skipChildren) { 1887 visibilities = new int[count]; 1888 for (int i = 0; i < count; i++) { 1889 View child = getChildAt(i); 1890 visibilities[i] = child.getVisibility(); 1891 if (visibilities[i] == View.VISIBLE) { 1892 child.setVisibility(INVISIBLE); 1893 } 1894 } 1895 } 1896 1897 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren); 1898 1899 if (skipChildren) { 1900 for (int i = 0; i < count; i++) { 1901 getChildAt(i).setVisibility(visibilities[i]); 1902 } 1903 } 1904 1905 return b; 1906 } 1907 1908 /** 1909 * {@inheritDoc} 1910 */ 1911 @Override 1912 protected void dispatchDraw(Canvas canvas) { 1913 final int count = mChildrenCount; 1914 final View[] children = mChildren; 1915 int flags = mGroupFlags; 1916 1917 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { 1918 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 1919 1920 for (int i = 0; i < count; i++) { 1921 final View child = children[i]; 1922 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1923 final LayoutParams params = child.getLayoutParams(); 1924 attachLayoutAnimationParameters(child, params, i, count); 1925 bindLayoutAnimation(child); 1926 if (cache) { 1927 child.setDrawingCacheEnabled(true); 1928 child.buildDrawingCache(true); 1929 } 1930 } 1931 } 1932 1933 final LayoutAnimationController controller = mLayoutAnimationController; 1934 if (controller.willOverlap()) { 1935 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE; 1936 } 1937 1938 controller.start(); 1939 1940 mGroupFlags &= ~FLAG_RUN_ANIMATION; 1941 mGroupFlags &= ~FLAG_ANIMATION_DONE; 1942 1943 if (cache) { 1944 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 1945 } 1946 1947 if (mAnimationListener != null) { 1948 mAnimationListener.onAnimationStart(controller.getAnimation()); 1949 } 1950 } 1951 1952 int saveCount = 0; 1953 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 1954 if (clipToPadding) { 1955 saveCount = canvas.save(); 1956 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, 1957 mScrollX + mRight - mLeft - mPaddingRight, 1958 mScrollY + mBottom - mTop - mPaddingBottom); 1959 1960 } 1961 1962 // We will draw our child's animation, let's reset the flag 1963 mPrivateFlags &= ~DRAW_ANIMATION; 1964 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; 1965 1966 boolean more = false; 1967 final long drawingTime = getDrawingTime(); 1968 1969 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { 1970 for (int i = 0; i < count; i++) { 1971 final View child = children[i]; 1972 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 1973 more |= drawChild(canvas, child, drawingTime); 1974 } 1975 } 1976 } else { 1977 for (int i = 0; i < count; i++) { 1978 final View child = children[getChildDrawingOrder(count, i)]; 1979 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 1980 more |= drawChild(canvas, child, drawingTime); 1981 } 1982 } 1983 } 1984 1985 // Draw any disappearing views that have animations 1986 if (mDisappearingChildren != null) { 1987 final ArrayList<View> disappearingChildren = mDisappearingChildren; 1988 final int disappearingCount = disappearingChildren.size() - 1; 1989 // Go backwards -- we may delete as animations finish 1990 for (int i = disappearingCount; i >= 0; i--) { 1991 final View child = disappearingChildren.get(i); 1992 more |= drawChild(canvas, child, drawingTime); 1993 } 1994 } 1995 1996 if (clipToPadding) { 1997 canvas.restoreToCount(saveCount); 1998 } 1999 2000 // mGroupFlags might have been updated by drawChild() 2001 flags = mGroupFlags; 2002 2003 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { 2004 invalidate(); 2005 } 2006 2007 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && 2008 mLayoutAnimationController.isDone() && !more) { 2009 // We want to erase the drawing cache and notify the listener after the 2010 // next frame is drawn because one extra invalidate() is caused by 2011 // drawChild() after the animation is over 2012 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; 2013 final Runnable end = new Runnable() { 2014 public void run() { 2015 notifyAnimationListener(); 2016 } 2017 }; 2018 post(end); 2019 } 2020 } 2021 2022 /** 2023 * Returns the index of the child to draw for this iteration. Override this 2024 * if you want to change the drawing order of children. By default, it 2025 * returns i. 2026 * <p> 2027 * NOTE: In order for this method to be called, you must enable child ordering 2028 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}. 2029 * 2030 * @param i The current iteration. 2031 * @return The index of the child to draw this iteration. 2032 * 2033 * @see #setChildrenDrawingOrderEnabled(boolean) 2034 * @see #isChildrenDrawingOrderEnabled() 2035 */ 2036 protected int getChildDrawingOrder(int childCount, int i) { 2037 return i; 2038 } 2039 2040 private void notifyAnimationListener() { 2041 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; 2042 mGroupFlags |= FLAG_ANIMATION_DONE; 2043 2044 if (mAnimationListener != null) { 2045 final Runnable end = new Runnable() { 2046 public void run() { 2047 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation()); 2048 } 2049 }; 2050 post(end); 2051 } 2052 2053 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2054 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 2055 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 2056 setChildrenDrawingCacheEnabled(false); 2057 } 2058 } 2059 2060 invalidate(); 2061 } 2062 2063 /** 2064 * Draw one child of this View Group. This method is responsible for getting 2065 * the canvas in the right state. This includes clipping, translating so 2066 * that the child's scrolled origin is at 0, 0, and applying any animation 2067 * transformations. 2068 * 2069 * @param canvas The canvas on which to draw the child 2070 * @param child Who to draw 2071 * @param drawingTime The time at which draw is occuring 2072 * @return True if an invalidate() was issued 2073 */ 2074 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 2075 boolean more = false; 2076 2077 final int cl = child.mLeft; 2078 final int ct = child.mTop; 2079 final int cr = child.mRight; 2080 final int cb = child.mBottom; 2081 2082 final int flags = mGroupFlags; 2083 2084 if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) { 2085 mChildTransformation.clear(); 2086 mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION; 2087 } 2088 2089 Transformation transformToApply = null; 2090 Transformation invalidationTransform; 2091 final Animation a = child.getAnimation(); 2092 boolean concatMatrix = false; 2093 2094 boolean scalingRequired = false; 2095 boolean caching = false; 2096 if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || 2097 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { 2098 caching = true; 2099 if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; 2100 } 2101 2102 if (a != null) { 2103 final boolean initialized = a.isInitialized(); 2104 if (!initialized) { 2105 a.initialize(cr - cl, cb - ct, getWidth(), getHeight()); 2106 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct); 2107 child.onAnimationStart(); 2108 } 2109 2110 more = a.getTransformation(drawingTime, mChildTransformation, 2111 scalingRequired ? mAttachInfo.mApplicationScale : 1f); 2112 if (scalingRequired && mAttachInfo.mApplicationScale != 1f) { 2113 if (mInvalidationTransformation == null) { 2114 mInvalidationTransformation = new Transformation(); 2115 } 2116 invalidationTransform = mInvalidationTransformation; 2117 a.getTransformation(drawingTime, invalidationTransform, 1f); 2118 } else { 2119 invalidationTransform = mChildTransformation; 2120 } 2121 transformToApply = mChildTransformation; 2122 2123 concatMatrix = a.willChangeTransformationMatrix(); 2124 2125 if (more) { 2126 if (!a.willChangeBounds()) { 2127 if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) == 2128 FLAG_OPTIMIZE_INVALIDATE) { 2129 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 2130 } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) { 2131 // The child need to draw an animation, potentially offscreen, so 2132 // make sure we do not cancel invalidate requests 2133 mPrivateFlags |= DRAW_ANIMATION; 2134 invalidate(cl, ct, cr, cb); 2135 } 2136 } else { 2137 if (mInvalidateRegion == null) { 2138 mInvalidateRegion = new RectF(); 2139 } 2140 final RectF region = mInvalidateRegion; 2141 a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform); 2142 2143 // The child need to draw an animation, potentially offscreen, so 2144 // make sure we do not cancel invalidate requests 2145 mPrivateFlags |= DRAW_ANIMATION; 2146 2147 final int left = cl + (int) region.left; 2148 final int top = ct + (int) region.top; 2149 invalidate(left, top, left + (int) region.width(), top + (int) region.height()); 2150 } 2151 } 2152 } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == 2153 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) { 2154 final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation); 2155 if (hasTransform) { 2156 final int transformType = mChildTransformation.getTransformationType(); 2157 transformToApply = transformType != Transformation.TYPE_IDENTITY ? 2158 mChildTransformation : null; 2159 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0; 2160 } 2161 } 2162 2163 concatMatrix |= !child.hasIdentityMatrix(); 2164 2165 // Sets the flag as early as possible to allow draw() implementations 2166 // to call invalidate() successfully when doing animations 2167 child.mPrivateFlags |= DRAWN; 2168 2169 if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && 2170 (child.mPrivateFlags & DRAW_ANIMATION) == 0) { 2171 return more; 2172 } 2173 2174 child.computeScroll(); 2175 2176 final int sx = child.mScrollX; 2177 final int sy = child.mScrollY; 2178 2179 DisplayList displayList = null; 2180 Bitmap cache = null; 2181 if (caching) { 2182 if (!canvas.isHardwareAccelerated()) { 2183 cache = child.getDrawingCache(true); 2184 } else { 2185 displayList = child.getDisplayList(); 2186 } 2187 } 2188 2189 final boolean hasDisplayList = displayList != null && displayList.isReady(); 2190 final boolean hasNoCache = cache == null || hasDisplayList; 2191 2192 final int restoreTo = canvas.save(); 2193 if (hasNoCache) { 2194 canvas.translate(cl - sx, ct - sy); 2195 } else { 2196 canvas.translate(cl, ct); 2197 if (scalingRequired) { 2198 // mAttachInfo cannot be null, otherwise scalingRequired == false 2199 final float scale = 1.0f / mAttachInfo.mApplicationScale; 2200 canvas.scale(scale, scale); 2201 } 2202 } 2203 2204 float alpha = child.getAlpha(); 2205 2206 if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) { 2207 int transX = 0; 2208 int transY = 0; 2209 2210 if (hasNoCache) { 2211 transX = -sx; 2212 transY = -sy; 2213 } 2214 2215 if (transformToApply != null) { 2216 if (concatMatrix) { 2217 // Undo the scroll translation, apply the transformation matrix, 2218 // then redo the scroll translate to get the correct result. 2219 canvas.translate(-transX, -transY); 2220 canvas.concat(transformToApply.getMatrix()); 2221 canvas.translate(transX, transY); 2222 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 2223 } 2224 2225 float transformAlpha = transformToApply.getAlpha(); 2226 if (transformAlpha < 1.0f) { 2227 alpha *= transformToApply.getAlpha(); 2228 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 2229 } 2230 } 2231 2232 if (!child.hasIdentityMatrix()) { 2233 canvas.translate(-transX, -transY); 2234 canvas.concat(child.getMatrix()); 2235 canvas.translate(transX, transY); 2236 } 2237 2238 if (alpha < 1.0f) { 2239 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 2240 2241 if (hasNoCache) { 2242 final int multipliedAlpha = (int) (255 * alpha); 2243 if (!child.onSetAlpha(multipliedAlpha)) { 2244 int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; 2245 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 2246 layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; 2247 } 2248 canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha, 2249 layerFlags); 2250 } else { 2251 child.mPrivateFlags |= ALPHA_SET; 2252 } 2253 } 2254 } 2255 } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) { 2256 child.onSetAlpha(255); 2257 child.mPrivateFlags &= ~ALPHA_SET; 2258 } 2259 2260 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 2261 if (hasNoCache) { 2262 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); 2263 } else { 2264 if (!scalingRequired) { 2265 canvas.clipRect(0, 0, cr - cl, cb - ct); 2266 } else { 2267 canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); 2268 } 2269 } 2270 } 2271 2272 if (hasNoCache) { 2273 if (!hasDisplayList) { 2274 // Fast path for layouts with no backgrounds 2275 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 2276 if (ViewDebug.TRACE_HIERARCHY) { 2277 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); 2278 } 2279 child.mPrivateFlags &= ~DIRTY_MASK; 2280 child.dispatchDraw(canvas); 2281 } else { 2282 child.draw(canvas); 2283 } 2284 } else { 2285 ((HardwareCanvas) canvas).drawDisplayList(displayList); 2286 } 2287 } else if (cache != null) { 2288 final Paint cachePaint = mCachePaint; 2289 if (alpha < 1.0f) { 2290 cachePaint.setAlpha((int) (alpha * 255)); 2291 mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE; 2292 } else if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) { 2293 cachePaint.setAlpha(255); 2294 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; 2295 } 2296 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); 2297 } 2298 2299 canvas.restoreToCount(restoreTo); 2300 2301 if (a != null && !more) { 2302 child.onSetAlpha(255); 2303 finishAnimatingView(child, a); 2304 } 2305 2306 return more; 2307 } 2308 2309 /** 2310 * By default, children are clipped to their bounds before drawing. This 2311 * allows view groups to override this behavior for animations, etc. 2312 * 2313 * @param clipChildren true to clip children to their bounds, 2314 * false otherwise 2315 * @attr ref android.R.styleable#ViewGroup_clipChildren 2316 */ 2317 public void setClipChildren(boolean clipChildren) { 2318 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); 2319 } 2320 2321 /** 2322 * By default, children are clipped to the padding of the ViewGroup. This 2323 * allows view groups to override this behavior 2324 * 2325 * @param clipToPadding true to clip children to the padding of the 2326 * group, false otherwise 2327 * @attr ref android.R.styleable#ViewGroup_clipToPadding 2328 */ 2329 public void setClipToPadding(boolean clipToPadding) { 2330 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding); 2331 } 2332 2333 /** 2334 * {@inheritDoc} 2335 */ 2336 @Override 2337 public void dispatchSetSelected(boolean selected) { 2338 final View[] children = mChildren; 2339 final int count = mChildrenCount; 2340 for (int i = 0; i < count; i++) { 2341 2342 children[i].setSelected(selected); 2343 } 2344 } 2345 2346 /** 2347 * {@inheritDoc} 2348 */ 2349 @Override 2350 public void dispatchSetActivated(boolean activated) { 2351 final View[] children = mChildren; 2352 final int count = mChildrenCount; 2353 for (int i = 0; i < count; i++) { 2354 2355 children[i].setActivated(activated); 2356 } 2357 } 2358 2359 @Override 2360 protected void dispatchSetPressed(boolean pressed) { 2361 final View[] children = mChildren; 2362 final int count = mChildrenCount; 2363 for (int i = 0; i < count; i++) { 2364 children[i].setPressed(pressed); 2365 } 2366 } 2367 2368 /** 2369 * When this property is set to true, this ViewGroup supports static transformations on 2370 * children; this causes 2371 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 2372 * invoked when a child is drawn. 2373 * 2374 * Any subclass overriding 2375 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 2376 * set this property to true. 2377 * 2378 * @param enabled True to enable static transformations on children, false otherwise. 2379 * 2380 * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS 2381 */ 2382 protected void setStaticTransformationsEnabled(boolean enabled) { 2383 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled); 2384 } 2385 2386 /** 2387 * {@inheritDoc} 2388 * 2389 * @see #setStaticTransformationsEnabled(boolean) 2390 */ 2391 protected boolean getChildStaticTransformation(View child, Transformation t) { 2392 return false; 2393 } 2394 2395 /** 2396 * {@hide} 2397 */ 2398 @Override 2399 protected View findViewTraversal(int id) { 2400 if (id == mID) { 2401 return this; 2402 } 2403 2404 final View[] where = mChildren; 2405 final int len = mChildrenCount; 2406 2407 for (int i = 0; i < len; i++) { 2408 View v = where[i]; 2409 2410 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 2411 v = v.findViewById(id); 2412 2413 if (v != null) { 2414 return v; 2415 } 2416 } 2417 } 2418 2419 return null; 2420 } 2421 2422 /** 2423 * {@hide} 2424 */ 2425 @Override 2426 protected View findViewWithTagTraversal(Object tag) { 2427 if (tag != null && tag.equals(mTag)) { 2428 return this; 2429 } 2430 2431 final View[] where = mChildren; 2432 final int len = mChildrenCount; 2433 2434 for (int i = 0; i < len; i++) { 2435 View v = where[i]; 2436 2437 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 2438 v = v.findViewWithTag(tag); 2439 2440 if (v != null) { 2441 return v; 2442 } 2443 } 2444 } 2445 2446 return null; 2447 } 2448 2449 /** 2450 * Adds a child view. If no layout parameters are already set on the child, the 2451 * default parameters for this ViewGroup are set on the child. 2452 * 2453 * @param child the child view to add 2454 * 2455 * @see #generateDefaultLayoutParams() 2456 */ 2457 public void addView(View child) { 2458 addView(child, -1); 2459 } 2460 2461 /** 2462 * Adds a child view. If no layout parameters are already set on the child, the 2463 * default parameters for this ViewGroup are set on the child. 2464 * 2465 * @param child the child view to add 2466 * @param index the position at which to add the child 2467 * 2468 * @see #generateDefaultLayoutParams() 2469 */ 2470 public void addView(View child, int index) { 2471 LayoutParams params = child.getLayoutParams(); 2472 if (params == null) { 2473 params = generateDefaultLayoutParams(); 2474 if (params == null) { 2475 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); 2476 } 2477 } 2478 addView(child, index, params); 2479 } 2480 2481 /** 2482 * Adds a child view with this ViewGroup's default layout parameters and the 2483 * specified width and height. 2484 * 2485 * @param child the child view to add 2486 */ 2487 public void addView(View child, int width, int height) { 2488 final LayoutParams params = generateDefaultLayoutParams(); 2489 params.width = width; 2490 params.height = height; 2491 addView(child, -1, params); 2492 } 2493 2494 /** 2495 * Adds a child view with the specified layout parameters. 2496 * 2497 * @param child the child view to add 2498 * @param params the layout parameters to set on the child 2499 */ 2500 public void addView(View child, LayoutParams params) { 2501 addView(child, -1, params); 2502 } 2503 2504 /** 2505 * Adds a child view with the specified layout parameters. 2506 * 2507 * @param child the child view to add 2508 * @param index the position at which to add the child 2509 * @param params the layout parameters to set on the child 2510 */ 2511 public void addView(View child, int index, LayoutParams params) { 2512 if (DBG) { 2513 System.out.println(this + " addView"); 2514 } 2515 2516 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 2517 // therefore, we call requestLayout() on ourselves before, so that the child's request 2518 // will be blocked at our level 2519 requestLayout(); 2520 invalidate(); 2521 addViewInner(child, index, params, false); 2522 } 2523 2524 /** 2525 * {@inheritDoc} 2526 */ 2527 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 2528 if (!checkLayoutParams(params)) { 2529 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this); 2530 } 2531 if (view.mParent != this) { 2532 throw new IllegalArgumentException("Given view not a child of " + this); 2533 } 2534 view.setLayoutParams(params); 2535 } 2536 2537 /** 2538 * {@inheritDoc} 2539 */ 2540 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 2541 return p != null; 2542 } 2543 2544 /** 2545 * Interface definition for a callback to be invoked when the hierarchy 2546 * within this view changed. The hierarchy changes whenever a child is added 2547 * to or removed from this view. 2548 */ 2549 public interface OnHierarchyChangeListener { 2550 /** 2551 * Called when a new child is added to a parent view. 2552 * 2553 * @param parent the view in which a child was added 2554 * @param child the new child view added in the hierarchy 2555 */ 2556 void onChildViewAdded(View parent, View child); 2557 2558 /** 2559 * Called when a child is removed from a parent view. 2560 * 2561 * @param parent the view from which the child was removed 2562 * @param child the child removed from the hierarchy 2563 */ 2564 void onChildViewRemoved(View parent, View child); 2565 } 2566 2567 /** 2568 * Register a callback to be invoked when a child is added to or removed 2569 * from this view. 2570 * 2571 * @param listener the callback to invoke on hierarchy change 2572 */ 2573 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 2574 mOnHierarchyChangeListener = listener; 2575 } 2576 2577 /** 2578 * Adds a view during layout. This is useful if in your onLayout() method, 2579 * you need to add more views (as does the list view for example). 2580 * 2581 * If index is negative, it means put it at the end of the list. 2582 * 2583 * @param child the view to add to the group 2584 * @param index the index at which the child must be added 2585 * @param params the layout parameters to associate with the child 2586 * @return true if the child was added, false otherwise 2587 */ 2588 protected boolean addViewInLayout(View child, int index, LayoutParams params) { 2589 return addViewInLayout(child, index, params, false); 2590 } 2591 2592 /** 2593 * Adds a view during layout. This is useful if in your onLayout() method, 2594 * you need to add more views (as does the list view for example). 2595 * 2596 * If index is negative, it means put it at the end of the list. 2597 * 2598 * @param child the view to add to the group 2599 * @param index the index at which the child must be added 2600 * @param params the layout parameters to associate with the child 2601 * @param preventRequestLayout if true, calling this method will not trigger a 2602 * layout request on child 2603 * @return true if the child was added, false otherwise 2604 */ 2605 protected boolean addViewInLayout(View child, int index, LayoutParams params, 2606 boolean preventRequestLayout) { 2607 child.mParent = null; 2608 addViewInner(child, index, params, preventRequestLayout); 2609 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; 2610 return true; 2611 } 2612 2613 /** 2614 * Prevents the specified child to be laid out during the next layout pass. 2615 * 2616 * @param child the child on which to perform the cleanup 2617 */ 2618 protected void cleanupLayoutState(View child) { 2619 child.mPrivateFlags &= ~View.FORCE_LAYOUT; 2620 } 2621 2622 private void addViewInner(View child, int index, LayoutParams params, 2623 boolean preventRequestLayout) { 2624 2625 if (child.getParent() != null) { 2626 throw new IllegalStateException("The specified child already has a parent. " + 2627 "You must call removeView() on the child's parent first."); 2628 } 2629 2630 if (mTransition != null) { 2631 mTransition.addChild(this, child); 2632 } 2633 2634 if (!checkLayoutParams(params)) { 2635 params = generateLayoutParams(params); 2636 } 2637 2638 if (preventRequestLayout) { 2639 child.mLayoutParams = params; 2640 } else { 2641 child.setLayoutParams(params); 2642 } 2643 2644 if (index < 0) { 2645 index = mChildrenCount; 2646 } 2647 2648 addInArray(child, index); 2649 2650 // tell our children 2651 if (preventRequestLayout) { 2652 child.assignParent(this); 2653 } else { 2654 child.mParent = this; 2655 } 2656 2657 if (child.hasFocus()) { 2658 requestChildFocus(child, child.findFocus()); 2659 } 2660 2661 AttachInfo ai = mAttachInfo; 2662 if (ai != null) { 2663 boolean lastKeepOn = ai.mKeepScreenOn; 2664 ai.mKeepScreenOn = false; 2665 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 2666 if (ai.mKeepScreenOn) { 2667 needGlobalAttributesUpdate(true); 2668 } 2669 ai.mKeepScreenOn = lastKeepOn; 2670 } 2671 2672 if (mOnHierarchyChangeListener != null) { 2673 mOnHierarchyChangeListener.onChildViewAdded(this, child); 2674 } 2675 2676 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 2677 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 2678 } 2679 } 2680 2681 private void addInArray(View child, int index) { 2682 View[] children = mChildren; 2683 final int count = mChildrenCount; 2684 final int size = children.length; 2685 if (index == count) { 2686 if (size == count) { 2687 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 2688 System.arraycopy(children, 0, mChildren, 0, size); 2689 children = mChildren; 2690 } 2691 children[mChildrenCount++] = child; 2692 } else if (index < count) { 2693 if (size == count) { 2694 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 2695 System.arraycopy(children, 0, mChildren, 0, index); 2696 System.arraycopy(children, index, mChildren, index + 1, count - index); 2697 children = mChildren; 2698 } else { 2699 System.arraycopy(children, index, children, index + 1, count - index); 2700 } 2701 children[index] = child; 2702 mChildrenCount++; 2703 } else { 2704 throw new IndexOutOfBoundsException("index=" + index + " count=" + count); 2705 } 2706 } 2707 2708 // This method also sets the child's mParent to null 2709 private void removeFromArray(int index) { 2710 final View[] children = mChildren; 2711 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) { 2712 children[index].mParent = null; 2713 } 2714 final int count = mChildrenCount; 2715 if (index == count - 1) { 2716 children[--mChildrenCount] = null; 2717 } else if (index >= 0 && index < count) { 2718 System.arraycopy(children, index + 1, children, index, count - index - 1); 2719 children[--mChildrenCount] = null; 2720 } else { 2721 throw new IndexOutOfBoundsException(); 2722 } 2723 } 2724 2725 // This method also sets the children's mParent to null 2726 private void removeFromArray(int start, int count) { 2727 final View[] children = mChildren; 2728 final int childrenCount = mChildrenCount; 2729 2730 start = Math.max(0, start); 2731 final int end = Math.min(childrenCount, start + count); 2732 2733 if (start == end) { 2734 return; 2735 } 2736 2737 if (end == childrenCount) { 2738 for (int i = start; i < end; i++) { 2739 children[i].mParent = null; 2740 children[i] = null; 2741 } 2742 } else { 2743 for (int i = start; i < end; i++) { 2744 children[i].mParent = null; 2745 } 2746 2747 // Since we're looping above, we might as well do the copy, but is arraycopy() 2748 // faster than the extra 2 bounds checks we would do in the loop? 2749 System.arraycopy(children, end, children, start, childrenCount - end); 2750 2751 for (int i = childrenCount - (end - start); i < childrenCount; i++) { 2752 children[i] = null; 2753 } 2754 } 2755 2756 mChildrenCount -= (end - start); 2757 } 2758 2759 private void bindLayoutAnimation(View child) { 2760 Animation a = mLayoutAnimationController.getAnimationForView(child); 2761 child.setAnimation(a); 2762 } 2763 2764 /** 2765 * Subclasses should override this method to set layout animation 2766 * parameters on the supplied child. 2767 * 2768 * @param child the child to associate with animation parameters 2769 * @param params the child's layout parameters which hold the animation 2770 * parameters 2771 * @param index the index of the child in the view group 2772 * @param count the number of children in the view group 2773 */ 2774 protected void attachLayoutAnimationParameters(View child, 2775 LayoutParams params, int index, int count) { 2776 LayoutAnimationController.AnimationParameters animationParams = 2777 params.layoutAnimationParameters; 2778 if (animationParams == null) { 2779 animationParams = new LayoutAnimationController.AnimationParameters(); 2780 params.layoutAnimationParameters = animationParams; 2781 } 2782 2783 animationParams.count = count; 2784 animationParams.index = index; 2785 } 2786 2787 /** 2788 * {@inheritDoc} 2789 */ 2790 public void removeView(View view) { 2791 removeViewInternal(view); 2792 requestLayout(); 2793 invalidate(); 2794 } 2795 2796 /** 2797 * Removes a view during layout. This is useful if in your onLayout() method, 2798 * you need to remove more views. 2799 * 2800 * @param view the view to remove from the group 2801 */ 2802 public void removeViewInLayout(View view) { 2803 removeViewInternal(view); 2804 } 2805 2806 /** 2807 * Removes a range of views during layout. This is useful if in your onLayout() method, 2808 * you need to remove more views. 2809 * 2810 * @param start the index of the first view to remove from the group 2811 * @param count the number of views to remove from the group 2812 */ 2813 public void removeViewsInLayout(int start, int count) { 2814 removeViewsInternal(start, count); 2815 } 2816 2817 /** 2818 * Removes the view at the specified position in the group. 2819 * 2820 * @param index the position in the group of the view to remove 2821 */ 2822 public void removeViewAt(int index) { 2823 removeViewInternal(index, getChildAt(index)); 2824 requestLayout(); 2825 invalidate(); 2826 } 2827 2828 /** 2829 * Removes the specified range of views from the group. 2830 * 2831 * @param start the first position in the group of the range of views to remove 2832 * @param count the number of views to remove 2833 */ 2834 public void removeViews(int start, int count) { 2835 removeViewsInternal(start, count); 2836 requestLayout(); 2837 invalidate(); 2838 } 2839 2840 private void removeViewInternal(View view) { 2841 final int index = indexOfChild(view); 2842 if (index >= 0) { 2843 removeViewInternal(index, view); 2844 } 2845 } 2846 2847 private void removeViewInternal(int index, View view) { 2848 2849 if (mTransition != null) { 2850 mTransition.removeChild(this, view); 2851 } 2852 2853 boolean clearChildFocus = false; 2854 if (view == mFocused) { 2855 view.clearFocusForRemoval(); 2856 clearChildFocus = true; 2857 } 2858 2859 if (view.getAnimation() != null || 2860 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 2861 addDisappearingView(view); 2862 } else if (view.mAttachInfo != null) { 2863 view.dispatchDetachedFromWindow(); 2864 } 2865 2866 if (mOnHierarchyChangeListener != null) { 2867 mOnHierarchyChangeListener.onChildViewRemoved(this, view); 2868 } 2869 2870 needGlobalAttributesUpdate(false); 2871 2872 removeFromArray(index); 2873 2874 if (clearChildFocus) { 2875 clearChildFocus(view); 2876 } 2877 } 2878 2879 /** 2880 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 2881 * not null, changes in layout which occur because of children being added to or removed from 2882 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 2883 * object. By default, the transition object is null (so layout changes are not animated). 2884 * 2885 * @param transition The LayoutTransition object that will animated changes in layout. A value 2886 * of <code>null</code> means no transition will run on layout changes. 2887 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 2888 */ 2889 public void setLayoutTransition(LayoutTransition transition) { 2890 if (mTransition != null) { 2891 mTransition.removeTransitionListener(mLayoutTransitionListener); 2892 } 2893 mTransition = transition; 2894 if (mTransition != null) { 2895 mTransition.addTransitionListener(mLayoutTransitionListener); 2896 } 2897 } 2898 2899 /** 2900 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 2901 * not null, changes in layout which occur because of children being added to or removed from 2902 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 2903 * object. By default, the transition object is null (so layout changes are not animated). 2904 * 2905 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout. 2906 * A value of <code>null</code> means no transition will run on layout changes. 2907 */ 2908 public LayoutTransition getLayoutTransition() { 2909 return mTransition; 2910 } 2911 2912 private void removeViewsInternal(int start, int count) { 2913 final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener; 2914 final boolean notifyListener = onHierarchyChangeListener != null; 2915 final View focused = mFocused; 2916 final boolean detach = mAttachInfo != null; 2917 View clearChildFocus = null; 2918 2919 final View[] children = mChildren; 2920 final int end = start + count; 2921 2922 for (int i = start; i < end; i++) { 2923 final View view = children[i]; 2924 2925 if (mTransition != null) { 2926 mTransition.removeChild(this, view); 2927 } 2928 2929 if (view == focused) { 2930 view.clearFocusForRemoval(); 2931 clearChildFocus = view; 2932 } 2933 2934 if (view.getAnimation() != null || 2935 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 2936 addDisappearingView(view); 2937 } else if (detach) { 2938 view.dispatchDetachedFromWindow(); 2939 } 2940 2941 needGlobalAttributesUpdate(false); 2942 2943 if (notifyListener) { 2944 onHierarchyChangeListener.onChildViewRemoved(this, view); 2945 } 2946 } 2947 2948 removeFromArray(start, count); 2949 2950 if (clearChildFocus != null) { 2951 clearChildFocus(clearChildFocus); 2952 } 2953 } 2954 2955 /** 2956 * Call this method to remove all child views from the 2957 * ViewGroup. 2958 */ 2959 public void removeAllViews() { 2960 removeAllViewsInLayout(); 2961 requestLayout(); 2962 invalidate(); 2963 } 2964 2965 /** 2966 * Called by a ViewGroup subclass to remove child views from itself, 2967 * when it must first know its size on screen before it can calculate how many 2968 * child views it will render. An example is a Gallery or a ListView, which 2969 * may "have" 50 children, but actually only render the number of children 2970 * that can currently fit inside the object on screen. Do not call 2971 * this method unless you are extending ViewGroup and understand the 2972 * view measuring and layout pipeline. 2973 */ 2974 public void removeAllViewsInLayout() { 2975 final int count = mChildrenCount; 2976 if (count <= 0) { 2977 return; 2978 } 2979 2980 final View[] children = mChildren; 2981 mChildrenCount = 0; 2982 2983 final OnHierarchyChangeListener listener = mOnHierarchyChangeListener; 2984 final boolean notify = listener != null; 2985 final View focused = mFocused; 2986 final boolean detach = mAttachInfo != null; 2987 View clearChildFocus = null; 2988 2989 needGlobalAttributesUpdate(false); 2990 2991 for (int i = count - 1; i >= 0; i--) { 2992 final View view = children[i]; 2993 2994 if (mTransition != null) { 2995 mTransition.removeChild(this, view); 2996 } 2997 2998 if (view == focused) { 2999 view.clearFocusForRemoval(); 3000 clearChildFocus = view; 3001 } 3002 3003 if (view.getAnimation() != null || 3004 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3005 addDisappearingView(view); 3006 } else if (detach) { 3007 view.dispatchDetachedFromWindow(); 3008 } 3009 3010 if (notify) { 3011 listener.onChildViewRemoved(this, view); 3012 } 3013 3014 view.mParent = null; 3015 children[i] = null; 3016 } 3017 3018 if (clearChildFocus != null) { 3019 clearChildFocus(clearChildFocus); 3020 } 3021 } 3022 3023 /** 3024 * Finishes the removal of a detached view. This method will dispatch the detached from 3025 * window event and notify the hierarchy change listener. 3026 * 3027 * @param child the child to be definitely removed from the view hierarchy 3028 * @param animate if true and the view has an animation, the view is placed in the 3029 * disappearing views list, otherwise, it is detached from the window 3030 * 3031 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3032 * @see #detachAllViewsFromParent() 3033 * @see #detachViewFromParent(View) 3034 * @see #detachViewFromParent(int) 3035 */ 3036 protected void removeDetachedView(View child, boolean animate) { 3037 if (mTransition != null) { 3038 mTransition.removeChild(this, child); 3039 } 3040 3041 if (child == mFocused) { 3042 child.clearFocus(); 3043 } 3044 3045 if ((animate && child.getAnimation() != null) || 3046 (mTransitioningViews != null && mTransitioningViews.contains(child))) { 3047 addDisappearingView(child); 3048 } else if (child.mAttachInfo != null) { 3049 child.dispatchDetachedFromWindow(); 3050 } 3051 3052 if (mOnHierarchyChangeListener != null) { 3053 mOnHierarchyChangeListener.onChildViewRemoved(this, child); 3054 } 3055 } 3056 3057 /** 3058 * Attaches a view to this view group. Attaching a view assigns this group as the parent, 3059 * sets the layout parameters and puts the view in the list of children so it can be retrieved 3060 * by calling {@link #getChildAt(int)}. 3061 * 3062 * This method should be called only for view which were detached from their parent. 3063 * 3064 * @param child the child to attach 3065 * @param index the index at which the child should be attached 3066 * @param params the layout parameters of the child 3067 * 3068 * @see #removeDetachedView(View, boolean) 3069 * @see #detachAllViewsFromParent() 3070 * @see #detachViewFromParent(View) 3071 * @see #detachViewFromParent(int) 3072 */ 3073 protected void attachViewToParent(View child, int index, LayoutParams params) { 3074 child.mLayoutParams = params; 3075 3076 if (index < 0) { 3077 index = mChildrenCount; 3078 } 3079 3080 addInArray(child, index); 3081 3082 child.mParent = this; 3083 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN; 3084 3085 if (child.hasFocus()) { 3086 requestChildFocus(child, child.findFocus()); 3087 } 3088 } 3089 3090 /** 3091 * Detaches a view from its parent. Detaching a view should be temporary and followed 3092 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3093 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 3094 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3095 * 3096 * @param child the child to detach 3097 * 3098 * @see #detachViewFromParent(int) 3099 * @see #detachViewsFromParent(int, int) 3100 * @see #detachAllViewsFromParent() 3101 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3102 * @see #removeDetachedView(View, boolean) 3103 */ 3104 protected void detachViewFromParent(View child) { 3105 removeFromArray(indexOfChild(child)); 3106 } 3107 3108 /** 3109 * Detaches a view from its parent. Detaching a view should be temporary and followed 3110 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3111 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 3112 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3113 * 3114 * @param index the index of the child to detach 3115 * 3116 * @see #detachViewFromParent(View) 3117 * @see #detachAllViewsFromParent() 3118 * @see #detachViewsFromParent(int, int) 3119 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3120 * @see #removeDetachedView(View, boolean) 3121 */ 3122 protected void detachViewFromParent(int index) { 3123 removeFromArray(index); 3124 } 3125 3126 /** 3127 * Detaches a range of view from their parent. Detaching a view should be temporary and followed 3128 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3129 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its 3130 * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3131 * 3132 * @param start the first index of the childrend range to detach 3133 * @param count the number of children to detach 3134 * 3135 * @see #detachViewFromParent(View) 3136 * @see #detachViewFromParent(int) 3137 * @see #detachAllViewsFromParent() 3138 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3139 * @see #removeDetachedView(View, boolean) 3140 */ 3141 protected void detachViewsFromParent(int start, int count) { 3142 removeFromArray(start, count); 3143 } 3144 3145 /** 3146 * Detaches all views from the parent. Detaching a view should be temporary and followed 3147 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3148 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 3149 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3150 * 3151 * @see #detachViewFromParent(View) 3152 * @see #detachViewFromParent(int) 3153 * @see #detachViewsFromParent(int, int) 3154 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3155 * @see #removeDetachedView(View, boolean) 3156 */ 3157 protected void detachAllViewsFromParent() { 3158 final int count = mChildrenCount; 3159 if (count <= 0) { 3160 return; 3161 } 3162 3163 final View[] children = mChildren; 3164 mChildrenCount = 0; 3165 3166 for (int i = count - 1; i >= 0; i--) { 3167 children[i].mParent = null; 3168 children[i] = null; 3169 } 3170 } 3171 3172 /** 3173 * Don't call or override this method. It is used for the implementation of 3174 * the view hierarchy. 3175 */ 3176 public final void invalidateChild(View child, final Rect dirty) { 3177 if (ViewDebug.TRACE_HIERARCHY) { 3178 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD); 3179 } 3180 3181 ViewParent parent = this; 3182 3183 final AttachInfo attachInfo = mAttachInfo; 3184 if (attachInfo != null) { 3185 final int[] location = attachInfo.mInvalidateChildLocation; 3186 location[CHILD_LEFT_INDEX] = child.mLeft; 3187 location[CHILD_TOP_INDEX] = child.mTop; 3188 Matrix childMatrix = child.getMatrix(); 3189 if (!childMatrix.isIdentity()) { 3190 RectF boundingRect = attachInfo.mTmpTransformRect; 3191 boundingRect.set(dirty); 3192 childMatrix.mapRect(boundingRect); 3193 dirty.set((int) boundingRect.left, (int) boundingRect.top, 3194 (int) (boundingRect.right + 0.5f), 3195 (int) (boundingRect.bottom + 0.5f)); 3196 } 3197 3198 // If the child is drawing an animation, we want to copy this flag onto 3199 // ourselves and the parent to make sure the invalidate request goes 3200 // through 3201 final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; 3202 3203 // Check whether the child that requests the invalidate is fully opaque 3204 final boolean isOpaque = child.isOpaque() && !drawAnimation && 3205 child.getAnimation() != null; 3206 // Mark the child as dirty, using the appropriate flag 3207 // Make sure we do not set both flags at the same time 3208 final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; 3209 3210 do { 3211 View view = null; 3212 if (parent instanceof View) { 3213 view = (View) parent; 3214 } 3215 3216 if (drawAnimation) { 3217 if (view != null) { 3218 view.mPrivateFlags |= DRAW_ANIMATION; 3219 } else if (parent instanceof ViewRoot) { 3220 ((ViewRoot) parent).mIsAnimating = true; 3221 } 3222 } 3223 3224 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 3225 // flag coming from the child that initiated the invalidate 3226 if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { 3227 view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; 3228 } 3229 3230 parent = parent.invalidateChildInParent(location, dirty); 3231 if (view != null) { 3232 // Account for transform on current parent 3233 Matrix m = view.getMatrix(); 3234 if (!m.isIdentity()) { 3235 RectF boundingRect = attachInfo.mTmpTransformRect; 3236 boundingRect.set(dirty); 3237 m.mapRect(boundingRect); 3238 dirty.set((int) boundingRect.left, (int) boundingRect.top, 3239 (int) (boundingRect.right + 0.5f), 3240 (int) (boundingRect.bottom + 0.5f)); 3241 } 3242 } 3243 } while (parent != null); 3244 } 3245 } 3246 3247 /** 3248 * Don't call or override this method. It is used for the implementation of 3249 * the view hierarchy. 3250 * 3251 * This implementation returns null if this ViewGroup does not have a parent, 3252 * if this ViewGroup is already fully invalidated or if the dirty rectangle 3253 * does not intersect with this ViewGroup's bounds. 3254 */ 3255 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 3256 if (ViewDebug.TRACE_HIERARCHY) { 3257 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT); 3258 } 3259 3260 if ((mPrivateFlags & DRAWN) == DRAWN) { 3261 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != 3262 FLAG_OPTIMIZE_INVALIDATE) { 3263 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, 3264 location[CHILD_TOP_INDEX] - mScrollY); 3265 3266 final int left = mLeft; 3267 final int top = mTop; 3268 3269 if (dirty.intersect(0, 0, mRight - left, mBottom - top) || 3270 (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { 3271 mPrivateFlags &= ~DRAWING_CACHE_VALID; 3272 3273 location[CHILD_LEFT_INDEX] = left; 3274 location[CHILD_TOP_INDEX] = top; 3275 3276 return mParent; 3277 } 3278 } else { 3279 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID; 3280 3281 location[CHILD_LEFT_INDEX] = mLeft; 3282 location[CHILD_TOP_INDEX] = mTop; 3283 3284 dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX], 3285 mBottom - location[CHILD_TOP_INDEX]); 3286 3287 return mParent; 3288 } 3289 } 3290 3291 return null; 3292 } 3293 3294 /** 3295 * Offset a rectangle that is in a descendant's coordinate 3296 * space into our coordinate space. 3297 * @param descendant A descendant of this view 3298 * @param rect A rectangle defined in descendant's coordinate space. 3299 */ 3300 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { 3301 offsetRectBetweenParentAndChild(descendant, rect, true, false); 3302 } 3303 3304 /** 3305 * Offset a rectangle that is in our coordinate space into an ancestor's 3306 * coordinate space. 3307 * @param descendant A descendant of this view 3308 * @param rect A rectangle defined in descendant's coordinate space. 3309 */ 3310 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { 3311 offsetRectBetweenParentAndChild(descendant, rect, false, false); 3312 } 3313 3314 /** 3315 * Helper method that offsets a rect either from parent to descendant or 3316 * descendant to parent. 3317 */ 3318 void offsetRectBetweenParentAndChild(View descendant, Rect rect, 3319 boolean offsetFromChildToParent, boolean clipToBounds) { 3320 3321 // already in the same coord system :) 3322 if (descendant == this) { 3323 return; 3324 } 3325 3326 ViewParent theParent = descendant.mParent; 3327 3328 // search and offset up to the parent 3329 while ((theParent != null) 3330 && (theParent instanceof View) 3331 && (theParent != this)) { 3332 3333 if (offsetFromChildToParent) { 3334 rect.offset(descendant.mLeft - descendant.mScrollX, 3335 descendant.mTop - descendant.mScrollY); 3336 if (clipToBounds) { 3337 View p = (View) theParent; 3338 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 3339 } 3340 } else { 3341 if (clipToBounds) { 3342 View p = (View) theParent; 3343 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 3344 } 3345 rect.offset(descendant.mScrollX - descendant.mLeft, 3346 descendant.mScrollY - descendant.mTop); 3347 } 3348 3349 descendant = (View) theParent; 3350 theParent = descendant.mParent; 3351 } 3352 3353 // now that we are up to this view, need to offset one more time 3354 // to get into our coordinate space 3355 if (theParent == this) { 3356 if (offsetFromChildToParent) { 3357 rect.offset(descendant.mLeft - descendant.mScrollX, 3358 descendant.mTop - descendant.mScrollY); 3359 } else { 3360 rect.offset(descendant.mScrollX - descendant.mLeft, 3361 descendant.mScrollY - descendant.mTop); 3362 } 3363 } else { 3364 throw new IllegalArgumentException("parameter must be a descendant of this view"); 3365 } 3366 } 3367 3368 /** 3369 * Offset the vertical location of all children of this view by the specified number of pixels. 3370 * 3371 * @param offset the number of pixels to offset 3372 * 3373 * @hide 3374 */ 3375 public void offsetChildrenTopAndBottom(int offset) { 3376 final int count = mChildrenCount; 3377 final View[] children = mChildren; 3378 3379 for (int i = 0; i < count; i++) { 3380 final View v = children[i]; 3381 v.mTop += offset; 3382 v.mBottom += offset; 3383 } 3384 } 3385 3386 /** 3387 * {@inheritDoc} 3388 */ 3389 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 3390 int dx = child.mLeft - mScrollX; 3391 int dy = child.mTop - mScrollY; 3392 if (offset != null) { 3393 offset.x += dx; 3394 offset.y += dy; 3395 } 3396 r.offset(dx, dy); 3397 return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) && 3398 (mParent == null || mParent.getChildVisibleRect(this, r, offset)); 3399 } 3400 3401 /** 3402 * {@inheritDoc} 3403 */ 3404 @Override 3405 protected abstract void onLayout(boolean changed, 3406 int l, int t, int r, int b); 3407 3408 /** 3409 * Indicates whether the view group has the ability to animate its children 3410 * after the first layout. 3411 * 3412 * @return true if the children can be animated, false otherwise 3413 */ 3414 protected boolean canAnimate() { 3415 return mLayoutAnimationController != null; 3416 } 3417 3418 /** 3419 * Runs the layout animation. Calling this method triggers a relayout of 3420 * this view group. 3421 */ 3422 public void startLayoutAnimation() { 3423 if (mLayoutAnimationController != null) { 3424 mGroupFlags |= FLAG_RUN_ANIMATION; 3425 requestLayout(); 3426 } 3427 } 3428 3429 /** 3430 * Schedules the layout animation to be played after the next layout pass 3431 * of this view group. This can be used to restart the layout animation 3432 * when the content of the view group changes or when the activity is 3433 * paused and resumed. 3434 */ 3435 public void scheduleLayoutAnimation() { 3436 mGroupFlags |= FLAG_RUN_ANIMATION; 3437 } 3438 3439 /** 3440 * Sets the layout animation controller used to animate the group's 3441 * children after the first layout. 3442 * 3443 * @param controller the animation controller 3444 */ 3445 public void setLayoutAnimation(LayoutAnimationController controller) { 3446 mLayoutAnimationController = controller; 3447 if (mLayoutAnimationController != null) { 3448 mGroupFlags |= FLAG_RUN_ANIMATION; 3449 } 3450 } 3451 3452 /** 3453 * Returns the layout animation controller used to animate the group's 3454 * children. 3455 * 3456 * @return the current animation controller 3457 */ 3458 public LayoutAnimationController getLayoutAnimation() { 3459 return mLayoutAnimationController; 3460 } 3461 3462 /** 3463 * Indicates whether the children's drawing cache is used during a layout 3464 * animation. By default, the drawing cache is enabled but this will prevent 3465 * nested layout animations from working. To nest animations, you must disable 3466 * the cache. 3467 * 3468 * @return true if the animation cache is enabled, false otherwise 3469 * 3470 * @see #setAnimationCacheEnabled(boolean) 3471 * @see View#setDrawingCacheEnabled(boolean) 3472 */ 3473 @ViewDebug.ExportedProperty 3474 public boolean isAnimationCacheEnabled() { 3475 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 3476 } 3477 3478 /** 3479 * Enables or disables the children's drawing cache during a layout animation. 3480 * By default, the drawing cache is enabled but this will prevent nested 3481 * layout animations from working. To nest animations, you must disable the 3482 * cache. 3483 * 3484 * @param enabled true to enable the animation cache, false otherwise 3485 * 3486 * @see #isAnimationCacheEnabled() 3487 * @see View#setDrawingCacheEnabled(boolean) 3488 */ 3489 public void setAnimationCacheEnabled(boolean enabled) { 3490 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); 3491 } 3492 3493 /** 3494 * Indicates whether this ViewGroup will always try to draw its children using their 3495 * drawing cache. By default this property is enabled. 3496 * 3497 * @return true if the animation cache is enabled, false otherwise 3498 * 3499 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 3500 * @see #setChildrenDrawnWithCacheEnabled(boolean) 3501 * @see View#setDrawingCacheEnabled(boolean) 3502 */ 3503 @ViewDebug.ExportedProperty(category = "drawing") 3504 public boolean isAlwaysDrawnWithCacheEnabled() { 3505 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; 3506 } 3507 3508 /** 3509 * Indicates whether this ViewGroup will always try to draw its children using their 3510 * drawing cache. This property can be set to true when the cache rendering is 3511 * slightly different from the children's normal rendering. Renderings can be different, 3512 * for instance, when the cache's quality is set to low. 3513 * 3514 * When this property is disabled, the ViewGroup will use the drawing cache of its 3515 * children only when asked to. It's usually the task of subclasses to tell ViewGroup 3516 * when to start using the drawing cache and when to stop using it. 3517 * 3518 * @param always true to always draw with the drawing cache, false otherwise 3519 * 3520 * @see #isAlwaysDrawnWithCacheEnabled() 3521 * @see #setChildrenDrawnWithCacheEnabled(boolean) 3522 * @see View#setDrawingCacheEnabled(boolean) 3523 * @see View#setDrawingCacheQuality(int) 3524 */ 3525 public void setAlwaysDrawnWithCacheEnabled(boolean always) { 3526 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); 3527 } 3528 3529 /** 3530 * Indicates whether the ViewGroup is currently drawing its children using 3531 * their drawing cache. 3532 * 3533 * @return true if children should be drawn with their cache, false otherwise 3534 * 3535 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 3536 * @see #setChildrenDrawnWithCacheEnabled(boolean) 3537 */ 3538 @ViewDebug.ExportedProperty(category = "drawing") 3539 protected boolean isChildrenDrawnWithCacheEnabled() { 3540 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; 3541 } 3542 3543 /** 3544 * Tells the ViewGroup to draw its children using their drawing cache. This property 3545 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache 3546 * will be used only if it has been enabled. 3547 * 3548 * Subclasses should call this method to start and stop using the drawing cache when 3549 * they perform performance sensitive operations, like scrolling or animating. 3550 * 3551 * @param enabled true if children should be drawn with their cache, false otherwise 3552 * 3553 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 3554 * @see #isChildrenDrawnWithCacheEnabled() 3555 */ 3556 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { 3557 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); 3558 } 3559 3560 /** 3561 * Indicates whether the ViewGroup is drawing its children in the order defined by 3562 * {@link #getChildDrawingOrder(int, int)}. 3563 * 3564 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, 3565 * false otherwise 3566 * 3567 * @see #setChildrenDrawingOrderEnabled(boolean) 3568 * @see #getChildDrawingOrder(int, int) 3569 */ 3570 @ViewDebug.ExportedProperty(category = "drawing") 3571 protected boolean isChildrenDrawingOrderEnabled() { 3572 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; 3573 } 3574 3575 /** 3576 * Tells the ViewGroup whether to draw its children in the order defined by the method 3577 * {@link #getChildDrawingOrder(int, int)}. 3578 * 3579 * @param enabled true if the order of the children when drawing is determined by 3580 * {@link #getChildDrawingOrder(int, int)}, false otherwise 3581 * 3582 * @see #isChildrenDrawingOrderEnabled() 3583 * @see #getChildDrawingOrder(int, int) 3584 */ 3585 protected void setChildrenDrawingOrderEnabled(boolean enabled) { 3586 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); 3587 } 3588 3589 private void setBooleanFlag(int flag, boolean value) { 3590 if (value) { 3591 mGroupFlags |= flag; 3592 } else { 3593 mGroupFlags &= ~flag; 3594 } 3595 } 3596 3597 /** 3598 * Returns an integer indicating what types of drawing caches are kept in memory. 3599 * 3600 * @see #setPersistentDrawingCache(int) 3601 * @see #setAnimationCacheEnabled(boolean) 3602 * 3603 * @return one or a combination of {@link #PERSISTENT_NO_CACHE}, 3604 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 3605 * and {@link #PERSISTENT_ALL_CACHES} 3606 */ 3607 @ViewDebug.ExportedProperty(category = "drawing", mapping = { 3608 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), 3609 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"), 3610 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), 3611 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") 3612 }) 3613 public int getPersistentDrawingCache() { 3614 return mPersistentDrawingCache; 3615 } 3616 3617 /** 3618 * Indicates what types of drawing caches should be kept in memory after 3619 * they have been created. 3620 * 3621 * @see #getPersistentDrawingCache() 3622 * @see #setAnimationCacheEnabled(boolean) 3623 * 3624 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE}, 3625 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 3626 * and {@link #PERSISTENT_ALL_CACHES} 3627 */ 3628 public void setPersistentDrawingCache(int drawingCacheToKeep) { 3629 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; 3630 } 3631 3632 /** 3633 * Returns a new set of layout parameters based on the supplied attributes set. 3634 * 3635 * @param attrs the attributes to build the layout parameters from 3636 * 3637 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 3638 * of its descendants 3639 */ 3640 public LayoutParams generateLayoutParams(AttributeSet attrs) { 3641 return new LayoutParams(getContext(), attrs); 3642 } 3643 3644 /** 3645 * Returns a safe set of layout parameters based on the supplied layout params. 3646 * When a ViewGroup is passed a View whose layout params do not pass the test of 3647 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method 3648 * is invoked. This method should return a new set of layout params suitable for 3649 * this ViewGroup, possibly by copying the appropriate attributes from the 3650 * specified set of layout params. 3651 * 3652 * @param p The layout parameters to convert into a suitable set of layout parameters 3653 * for this ViewGroup. 3654 * 3655 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 3656 * of its descendants 3657 */ 3658 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 3659 return p; 3660 } 3661 3662 /** 3663 * Returns a set of default layout parameters. These parameters are requested 3664 * when the View passed to {@link #addView(View)} has no layout parameters 3665 * already set. If null is returned, an exception is thrown from addView. 3666 * 3667 * @return a set of default layout parameters or null 3668 */ 3669 protected LayoutParams generateDefaultLayoutParams() { 3670 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 3671 } 3672 3673 /** 3674 * @hide 3675 */ 3676 @Override 3677 protected boolean dispatchConsistencyCheck(int consistency) { 3678 boolean result = super.dispatchConsistencyCheck(consistency); 3679 3680 final int count = mChildrenCount; 3681 final View[] children = mChildren; 3682 for (int i = 0; i < count; i++) { 3683 if (!children[i].dispatchConsistencyCheck(consistency)) result = false; 3684 } 3685 3686 return result; 3687 } 3688 3689 /** 3690 * @hide 3691 */ 3692 @Override 3693 protected boolean onConsistencyCheck(int consistency) { 3694 boolean result = super.onConsistencyCheck(consistency); 3695 3696 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; 3697 final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; 3698 3699 if (checkLayout) { 3700 final int count = mChildrenCount; 3701 final View[] children = mChildren; 3702 for (int i = 0; i < count; i++) { 3703 if (children[i].getParent() != this) { 3704 result = false; 3705 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 3706 "View " + children[i] + " has no parent/a parent that is not " + this); 3707 } 3708 } 3709 } 3710 3711 if (checkDrawing) { 3712 // If this group is dirty, check that the parent is dirty as well 3713 if ((mPrivateFlags & DIRTY_MASK) != 0) { 3714 final ViewParent parent = getParent(); 3715 if (parent != null && !(parent instanceof ViewRoot)) { 3716 if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { 3717 result = false; 3718 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 3719 "ViewGroup " + this + " is dirty but its parent is not: " + this); 3720 } 3721 } 3722 } 3723 } 3724 3725 return result; 3726 } 3727 3728 /** 3729 * {@inheritDoc} 3730 */ 3731 @Override 3732 protected void debug(int depth) { 3733 super.debug(depth); 3734 String output; 3735 3736 if (mFocused != null) { 3737 output = debugIndent(depth); 3738 output += "mFocused"; 3739 Log.d(VIEW_LOG_TAG, output); 3740 } 3741 if (mChildrenCount != 0) { 3742 output = debugIndent(depth); 3743 output += "{"; 3744 Log.d(VIEW_LOG_TAG, output); 3745 } 3746 int count = mChildrenCount; 3747 for (int i = 0; i < count; i++) { 3748 View child = mChildren[i]; 3749 child.debug(depth + 1); 3750 } 3751 3752 if (mChildrenCount != 0) { 3753 output = debugIndent(depth); 3754 output += "}"; 3755 Log.d(VIEW_LOG_TAG, output); 3756 } 3757 } 3758 3759 /** 3760 * Returns the position in the group of the specified child view. 3761 * 3762 * @param child the view for which to get the position 3763 * @return a positive integer representing the position of the view in the 3764 * group, or -1 if the view does not exist in the group 3765 */ 3766 public int indexOfChild(View child) { 3767 final int count = mChildrenCount; 3768 final View[] children = mChildren; 3769 for (int i = 0; i < count; i++) { 3770 if (children[i] == child) { 3771 return i; 3772 } 3773 } 3774 return -1; 3775 } 3776 3777 /** 3778 * Returns the number of children in the group. 3779 * 3780 * @return a positive integer representing the number of children in 3781 * the group 3782 */ 3783 public int getChildCount() { 3784 return mChildrenCount; 3785 } 3786 3787 /** 3788 * Returns the view at the specified position in the group. 3789 * 3790 * @param index the position at which to get the view from 3791 * @return the view at the specified position or null if the position 3792 * does not exist within the group 3793 */ 3794 public View getChildAt(int index) { 3795 try { 3796 return mChildren[index]; 3797 } catch (IndexOutOfBoundsException ex) { 3798 return null; 3799 } 3800 } 3801 3802 /** 3803 * Ask all of the children of this view to measure themselves, taking into 3804 * account both the MeasureSpec requirements for this view and its padding. 3805 * We skip children that are in the GONE state The heavy lifting is done in 3806 * getChildMeasureSpec. 3807 * 3808 * @param widthMeasureSpec The width requirements for this view 3809 * @param heightMeasureSpec The height requirements for this view 3810 */ 3811 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 3812 final int size = mChildrenCount; 3813 final View[] children = mChildren; 3814 for (int i = 0; i < size; ++i) { 3815 final View child = children[i]; 3816 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 3817 measureChild(child, widthMeasureSpec, heightMeasureSpec); 3818 } 3819 } 3820 } 3821 3822 /** 3823 * Ask one of the children of this view to measure itself, taking into 3824 * account both the MeasureSpec requirements for this view and its padding. 3825 * The heavy lifting is done in getChildMeasureSpec. 3826 * 3827 * @param child The child to measure 3828 * @param parentWidthMeasureSpec The width requirements for this view 3829 * @param parentHeightMeasureSpec The height requirements for this view 3830 */ 3831 protected void measureChild(View child, int parentWidthMeasureSpec, 3832 int parentHeightMeasureSpec) { 3833 final LayoutParams lp = child.getLayoutParams(); 3834 3835 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 3836 mPaddingLeft + mPaddingRight, lp.width); 3837 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 3838 mPaddingTop + mPaddingBottom, lp.height); 3839 3840 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 3841 } 3842 3843 /** 3844 * Ask one of the children of this view to measure itself, taking into 3845 * account both the MeasureSpec requirements for this view and its padding 3846 * and margins. The child must have MarginLayoutParams The heavy lifting is 3847 * done in getChildMeasureSpec. 3848 * 3849 * @param child The child to measure 3850 * @param parentWidthMeasureSpec The width requirements for this view 3851 * @param widthUsed Extra space that has been used up by the parent 3852 * horizontally (possibly by other children of the parent) 3853 * @param parentHeightMeasureSpec The height requirements for this view 3854 * @param heightUsed Extra space that has been used up by the parent 3855 * vertically (possibly by other children of the parent) 3856 */ 3857 protected void measureChildWithMargins(View child, 3858 int parentWidthMeasureSpec, int widthUsed, 3859 int parentHeightMeasureSpec, int heightUsed) { 3860 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 3861 3862 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 3863 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 3864 + widthUsed, lp.width); 3865 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 3866 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 3867 + heightUsed, lp.height); 3868 3869 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 3870 } 3871 3872 /** 3873 * Does the hard part of measureChildren: figuring out the MeasureSpec to 3874 * pass to a particular child. This method figures out the right MeasureSpec 3875 * for one dimension (height or width) of one child view. 3876 * 3877 * The goal is to combine information from our MeasureSpec with the 3878 * LayoutParams of the child to get the best possible results. For example, 3879 * if the this view knows its size (because its MeasureSpec has a mode of 3880 * EXACTLY), and the child has indicated in its LayoutParams that it wants 3881 * to be the same size as the parent, the parent should ask the child to 3882 * layout given an exact size. 3883 * 3884 * @param spec The requirements for this view 3885 * @param padding The padding of this view for the current dimension and 3886 * margins, if applicable 3887 * @param childDimension How big the child wants to be in the current 3888 * dimension 3889 * @return a MeasureSpec integer for the child 3890 */ 3891 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 3892 int specMode = MeasureSpec.getMode(spec); 3893 int specSize = MeasureSpec.getSize(spec); 3894 3895 int size = Math.max(0, specSize - padding); 3896 3897 int resultSize = 0; 3898 int resultMode = 0; 3899 3900 switch (specMode) { 3901 // Parent has imposed an exact size on us 3902 case MeasureSpec.EXACTLY: 3903 if (childDimension >= 0) { 3904 resultSize = childDimension; 3905 resultMode = MeasureSpec.EXACTLY; 3906 } else if (childDimension == LayoutParams.MATCH_PARENT) { 3907 // Child wants to be our size. So be it. 3908 resultSize = size; 3909 resultMode = MeasureSpec.EXACTLY; 3910 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 3911 // Child wants to determine its own size. It can't be 3912 // bigger than us. 3913 resultSize = size; 3914 resultMode = MeasureSpec.AT_MOST; 3915 } 3916 break; 3917 3918 // Parent has imposed a maximum size on us 3919 case MeasureSpec.AT_MOST: 3920 if (childDimension >= 0) { 3921 // Child wants a specific size... so be it 3922 resultSize = childDimension; 3923 resultMode = MeasureSpec.EXACTLY; 3924 } else if (childDimension == LayoutParams.MATCH_PARENT) { 3925 // Child wants to be our size, but our size is not fixed. 3926 // Constrain child to not be bigger than us. 3927 resultSize = size; 3928 resultMode = MeasureSpec.AT_MOST; 3929 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 3930 // Child wants to determine its own size. It can't be 3931 // bigger than us. 3932 resultSize = size; 3933 resultMode = MeasureSpec.AT_MOST; 3934 } 3935 break; 3936 3937 // Parent asked to see how big we want to be 3938 case MeasureSpec.UNSPECIFIED: 3939 if (childDimension >= 0) { 3940 // Child wants a specific size... let him have it 3941 resultSize = childDimension; 3942 resultMode = MeasureSpec.EXACTLY; 3943 } else if (childDimension == LayoutParams.MATCH_PARENT) { 3944 // Child wants to be our size... find out how big it should 3945 // be 3946 resultSize = 0; 3947 resultMode = MeasureSpec.UNSPECIFIED; 3948 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 3949 // Child wants to determine its own size.... find out how 3950 // big it should be 3951 resultSize = 0; 3952 resultMode = MeasureSpec.UNSPECIFIED; 3953 } 3954 break; 3955 } 3956 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 3957 } 3958 3959 3960 /** 3961 * Removes any pending animations for views that have been removed. Call 3962 * this if you don't want animations for exiting views to stack up. 3963 */ 3964 public void clearDisappearingChildren() { 3965 if (mDisappearingChildren != null) { 3966 mDisappearingChildren.clear(); 3967 } 3968 } 3969 3970 /** 3971 * Add a view which is removed from mChildren but still needs animation 3972 * 3973 * @param v View to add 3974 */ 3975 private void addDisappearingView(View v) { 3976 ArrayList<View> disappearingChildren = mDisappearingChildren; 3977 3978 if (disappearingChildren == null) { 3979 disappearingChildren = mDisappearingChildren = new ArrayList<View>(); 3980 } 3981 3982 disappearingChildren.add(v); 3983 } 3984 3985 /** 3986 * Cleanup a view when its animation is done. This may mean removing it from 3987 * the list of disappearing views. 3988 * 3989 * @param view The view whose animation has finished 3990 * @param animation The animation, cannot be null 3991 */ 3992 private void finishAnimatingView(final View view, Animation animation) { 3993 final ArrayList<View> disappearingChildren = mDisappearingChildren; 3994 if (disappearingChildren != null) { 3995 if (disappearingChildren.contains(view)) { 3996 disappearingChildren.remove(view); 3997 3998 if (view.mAttachInfo != null) { 3999 view.dispatchDetachedFromWindow(); 4000 } 4001 4002 view.clearAnimation(); 4003 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 4004 } 4005 } 4006 4007 if (animation != null && !animation.getFillAfter()) { 4008 view.clearAnimation(); 4009 } 4010 4011 if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) { 4012 view.onAnimationEnd(); 4013 // Should be performed by onAnimationEnd() but this avoid an infinite loop, 4014 // so we'd rather be safe than sorry 4015 view.mPrivateFlags &= ~ANIMATION_STARTED; 4016 // Draw one more frame after the animation is done 4017 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 4018 } 4019 } 4020 4021 /** 4022 * This method tells the ViewGroup that the given View object, which should have this 4023 * ViewGroup as its parent, 4024 * should be kept around (re-displayed when the ViewGroup draws its children) even if it 4025 * is removed from its parent. This allows animations, such as those used by 4026 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate 4027 * the removal of views. A call to this method should always be accompanied by a later call 4028 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished, 4029 * so that the View finally gets removed. 4030 * 4031 * @param view The View object to be kept visible even if it gets removed from its parent. 4032 */ 4033 public void startViewTransition(View view) { 4034 if (view.mParent == this) { 4035 if (mTransitioningViews == null) { 4036 mTransitioningViews = new ArrayList<View>(); 4037 } 4038 mTransitioningViews.add(view); 4039 } 4040 } 4041 4042 /** 4043 * This method should always be called following an earlier call to 4044 * {@link #startViewTransition(View)}. The given View is finally removed from its parent 4045 * and will no longer be displayed. Note that this method does not perform the functionality 4046 * of removing a view from its parent; it just discontinues the display of a View that 4047 * has previously been removed. 4048 * 4049 * @return view The View object that has been removed but is being kept around in the visible 4050 * hierarchy by an earlier call to {@link #startViewTransition(View)}. 4051 */ 4052 public void endViewTransition(View view) { 4053 if (mTransitioningViews != null) { 4054 mTransitioningViews.remove(view); 4055 final ArrayList<View> disappearingChildren = mDisappearingChildren; 4056 if (disappearingChildren != null && disappearingChildren.contains(view)) { 4057 disappearingChildren.remove(view); 4058 if (mVisibilityChangingChildren != null && 4059 mVisibilityChangingChildren.contains(view)) { 4060 mVisibilityChangingChildren.remove(view); 4061 } else { 4062 if (view.mAttachInfo != null) { 4063 view.dispatchDetachedFromWindow(); 4064 } 4065 if (view.mParent != null) { 4066 view.mParent = null; 4067 } 4068 } 4069 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 4070 } 4071 } 4072 } 4073 4074 private LayoutTransition.TransitionListener mLayoutTransitionListener = 4075 new LayoutTransition.TransitionListener() { 4076 @Override 4077 public void startTransition(LayoutTransition transition, ViewGroup container, 4078 View view, int transitionType) { 4079 // We only care about disappearing items, since we need special logic to keep 4080 // those items visible after they've been 'removed' 4081 if (transitionType == LayoutTransition.DISAPPEARING) { 4082 startViewTransition(view); 4083 } 4084 } 4085 4086 @Override 4087 public void endTransition(LayoutTransition transition, ViewGroup container, 4088 View view, int transitionType) { 4089 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) { 4090 endViewTransition(view); 4091 } 4092 } 4093 }; 4094 4095 /** 4096 * {@inheritDoc} 4097 */ 4098 @Override 4099 public boolean gatherTransparentRegion(Region region) { 4100 // If no transparent regions requested, we are always opaque. 4101 final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0; 4102 if (meOpaque && region == null) { 4103 // The caller doesn't care about the region, so stop now. 4104 return true; 4105 } 4106 super.gatherTransparentRegion(region); 4107 final View[] children = mChildren; 4108 final int count = mChildrenCount; 4109 boolean noneOfTheChildrenAreTransparent = true; 4110 for (int i = 0; i < count; i++) { 4111 final View child = children[i]; 4112 if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) { 4113 if (!child.gatherTransparentRegion(region)) { 4114 noneOfTheChildrenAreTransparent = false; 4115 } 4116 } 4117 } 4118 return meOpaque || noneOfTheChildrenAreTransparent; 4119 } 4120 4121 /** 4122 * {@inheritDoc} 4123 */ 4124 public void requestTransparentRegion(View child) { 4125 if (child != null) { 4126 child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS; 4127 if (mParent != null) { 4128 mParent.requestTransparentRegion(this); 4129 } 4130 } 4131 } 4132 4133 4134 @Override 4135 protected boolean fitSystemWindows(Rect insets) { 4136 boolean done = super.fitSystemWindows(insets); 4137 if (!done) { 4138 final int count = mChildrenCount; 4139 final View[] children = mChildren; 4140 for (int i = 0; i < count; i++) { 4141 done = children[i].fitSystemWindows(insets); 4142 if (done) { 4143 break; 4144 } 4145 } 4146 } 4147 return done; 4148 } 4149 4150 /** 4151 * Returns the animation listener to which layout animation events are 4152 * sent. 4153 * 4154 * @return an {@link android.view.animation.Animation.AnimationListener} 4155 */ 4156 public Animation.AnimationListener getLayoutAnimationListener() { 4157 return mAnimationListener; 4158 } 4159 4160 @Override 4161 protected void drawableStateChanged() { 4162 super.drawableStateChanged(); 4163 4164 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { 4165 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 4166 throw new IllegalStateException("addStateFromChildren cannot be enabled if a" 4167 + " child has duplicateParentState set to true"); 4168 } 4169 4170 final View[] children = mChildren; 4171 final int count = mChildrenCount; 4172 4173 for (int i = 0; i < count; i++) { 4174 final View child = children[i]; 4175 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { 4176 child.refreshDrawableState(); 4177 } 4178 } 4179 } 4180 } 4181 4182 @Override 4183 protected int[] onCreateDrawableState(int extraSpace) { 4184 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { 4185 return super.onCreateDrawableState(extraSpace); 4186 } 4187 4188 int need = 0; 4189 int n = getChildCount(); 4190 for (int i = 0; i < n; i++) { 4191 int[] childState = getChildAt(i).getDrawableState(); 4192 4193 if (childState != null) { 4194 need += childState.length; 4195 } 4196 } 4197 4198 int[] state = super.onCreateDrawableState(extraSpace + need); 4199 4200 for (int i = 0; i < n; i++) { 4201 int[] childState = getChildAt(i).getDrawableState(); 4202 4203 if (childState != null) { 4204 state = mergeDrawableStates(state, childState); 4205 } 4206 } 4207 4208 return state; 4209 } 4210 4211 /** 4212 * Sets whether this ViewGroup's drawable states also include 4213 * its children's drawable states. This is used, for example, to 4214 * make a group appear to be focused when its child EditText or button 4215 * is focused. 4216 */ 4217 public void setAddStatesFromChildren(boolean addsStates) { 4218 if (addsStates) { 4219 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN; 4220 } else { 4221 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN; 4222 } 4223 4224 refreshDrawableState(); 4225 } 4226 4227 /** 4228 * Returns whether this ViewGroup's drawable states also include 4229 * its children's drawable states. This is used, for example, to 4230 * make a group appear to be focused when its child EditText or button 4231 * is focused. 4232 */ 4233 public boolean addStatesFromChildren() { 4234 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0; 4235 } 4236 4237 /** 4238 * If {link #addStatesFromChildren} is true, refreshes this group's 4239 * drawable state (to include the states from its children). 4240 */ 4241 public void childDrawableStateChanged(View child) { 4242 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 4243 refreshDrawableState(); 4244 } 4245 } 4246 4247 /** 4248 * Specifies the animation listener to which layout animation events must 4249 * be sent. Only 4250 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)} 4251 * and 4252 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)} 4253 * are invoked. 4254 * 4255 * @param animationListener the layout animation listener 4256 */ 4257 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { 4258 mAnimationListener = animationListener; 4259 } 4260 4261 /** 4262 * LayoutParams are used by views to tell their parents how they want to be 4263 * laid out. See 4264 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 4265 * for a list of all child view attributes that this class supports. 4266 * 4267 * <p> 4268 * The base LayoutParams class just describes how big the view wants to be 4269 * for both width and height. For each dimension, it can specify one of: 4270 * <ul> 4271 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which 4272 * means that the view wants to be as big as its parent (minus padding) 4273 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 4274 * to enclose its content (plus padding) 4275 * <li> an exact number 4276 * </ul> 4277 * There are subclasses of LayoutParams for different subclasses of 4278 * ViewGroup. For example, AbsoluteLayout has its own subclass of 4279 * LayoutParams which adds an X and Y value. 4280 * 4281 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 4282 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 4283 */ 4284 public static class LayoutParams { 4285 /** 4286 * Special value for the height or width requested by a View. 4287 * FILL_PARENT means that the view wants to be as big as its parent, 4288 * minus the parent's padding, if any. This value is deprecated 4289 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 4290 */ 4291 @SuppressWarnings({"UnusedDeclaration"}) 4292 @Deprecated 4293 public static final int FILL_PARENT = -1; 4294 4295 /** 4296 * Special value for the height or width requested by a View. 4297 * MATCH_PARENT means that the view wants to be as big as its parent, 4298 * minus the parent's padding, if any. Introduced in API Level 8. 4299 */ 4300 public static final int MATCH_PARENT = -1; 4301 4302 /** 4303 * Special value for the height or width requested by a View. 4304 * WRAP_CONTENT means that the view wants to be just large enough to fit 4305 * its own internal content, taking its own padding into account. 4306 */ 4307 public static final int WRAP_CONTENT = -2; 4308 4309 /** 4310 * Information about how wide the view wants to be. Can be one of the 4311 * constants FILL_PARENT (replaced by MATCH_PARENT , 4312 * in API Level 8) or WRAP_CONTENT. or an exact size. 4313 */ 4314 @ViewDebug.ExportedProperty(category = "layout", mapping = { 4315 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 4316 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 4317 }) 4318 public int width; 4319 4320 /** 4321 * Information about how tall the view wants to be. Can be one of the 4322 * constants FILL_PARENT (replaced by MATCH_PARENT , 4323 * in API Level 8) or WRAP_CONTENT. or an exact size. 4324 */ 4325 @ViewDebug.ExportedProperty(category = "layout", mapping = { 4326 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 4327 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 4328 }) 4329 public int height; 4330 4331 /** 4332 * Used to animate layouts. 4333 */ 4334 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 4335 4336 /** 4337 * Creates a new set of layout parameters. The values are extracted from 4338 * the supplied attributes set and context. The XML attributes mapped 4339 * to this set of layout parameters are: 4340 * 4341 * <ul> 4342 * <li><code>layout_width</code>: the width, either an exact value, 4343 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 4344 * {@link #MATCH_PARENT} in API Level 8)</li> 4345 * <li><code>layout_height</code>: the height, either an exact value, 4346 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 4347 * {@link #MATCH_PARENT} in API Level 8)</li> 4348 * </ul> 4349 * 4350 * @param c the application environment 4351 * @param attrs the set of attributes from which to extract the layout 4352 * parameters' values 4353 */ 4354 public LayoutParams(Context c, AttributeSet attrs) { 4355 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 4356 setBaseAttributes(a, 4357 R.styleable.ViewGroup_Layout_layout_width, 4358 R.styleable.ViewGroup_Layout_layout_height); 4359 a.recycle(); 4360 } 4361 4362 /** 4363 * Creates a new set of layout parameters with the specified width 4364 * and height. 4365 * 4366 * @param width the width, either {@link #WRAP_CONTENT}, 4367 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 4368 * API Level 8), or a fixed size in pixels 4369 * @param height the height, either {@link #WRAP_CONTENT}, 4370 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 4371 * API Level 8), or a fixed size in pixels 4372 */ 4373 public LayoutParams(int width, int height) { 4374 this.width = width; 4375 this.height = height; 4376 } 4377 4378 /** 4379 * Copy constructor. Clones the width and height values of the source. 4380 * 4381 * @param source The layout params to copy from. 4382 */ 4383 public LayoutParams(LayoutParams source) { 4384 this.width = source.width; 4385 this.height = source.height; 4386 } 4387 4388 /** 4389 * Used internally by MarginLayoutParams. 4390 * @hide 4391 */ 4392 LayoutParams() { 4393 } 4394 4395 /** 4396 * Extracts the layout parameters from the supplied attributes. 4397 * 4398 * @param a the style attributes to extract the parameters from 4399 * @param widthAttr the identifier of the width attribute 4400 * @param heightAttr the identifier of the height attribute 4401 */ 4402 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 4403 width = a.getLayoutDimension(widthAttr, "layout_width"); 4404 height = a.getLayoutDimension(heightAttr, "layout_height"); 4405 } 4406 4407 /** 4408 * Returns a String representation of this set of layout parameters. 4409 * 4410 * @param output the String to prepend to the internal representation 4411 * @return a String with the following format: output + 4412 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 4413 * 4414 * @hide 4415 */ 4416 public String debug(String output) { 4417 return output + "ViewGroup.LayoutParams={ width=" 4418 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 4419 } 4420 4421 /** 4422 * Converts the specified size to a readable String. 4423 * 4424 * @param size the size to convert 4425 * @return a String instance representing the supplied size 4426 * 4427 * @hide 4428 */ 4429 protected static String sizeToString(int size) { 4430 if (size == WRAP_CONTENT) { 4431 return "wrap-content"; 4432 } 4433 if (size == MATCH_PARENT) { 4434 return "match-parent"; 4435 } 4436 return String.valueOf(size); 4437 } 4438 } 4439 4440 /** 4441 * Per-child layout information for layouts that support margins. 4442 * See 4443 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes} 4444 * for a list of all child view attributes that this class supports. 4445 */ 4446 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 4447 /** 4448 * The left margin in pixels of the child. 4449 */ 4450 @ViewDebug.ExportedProperty(category = "layout") 4451 public int leftMargin; 4452 4453 /** 4454 * The top margin in pixels of the child. 4455 */ 4456 @ViewDebug.ExportedProperty(category = "layout") 4457 public int topMargin; 4458 4459 /** 4460 * The right margin in pixels of the child. 4461 */ 4462 @ViewDebug.ExportedProperty(category = "layout") 4463 public int rightMargin; 4464 4465 /** 4466 * The bottom margin in pixels of the child. 4467 */ 4468 @ViewDebug.ExportedProperty(category = "layout") 4469 public int bottomMargin; 4470 4471 /** 4472 * Creates a new set of layout parameters. The values are extracted from 4473 * the supplied attributes set and context. 4474 * 4475 * @param c the application environment 4476 * @param attrs the set of attributes from which to extract the layout 4477 * parameters' values 4478 */ 4479 public MarginLayoutParams(Context c, AttributeSet attrs) { 4480 super(); 4481 4482 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); 4483 setBaseAttributes(a, 4484 R.styleable.ViewGroup_MarginLayout_layout_width, 4485 R.styleable.ViewGroup_MarginLayout_layout_height); 4486 4487 int margin = a.getDimensionPixelSize( 4488 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); 4489 if (margin >= 0) { 4490 leftMargin = margin; 4491 topMargin = margin; 4492 rightMargin= margin; 4493 bottomMargin = margin; 4494 } else { 4495 leftMargin = a.getDimensionPixelSize( 4496 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0); 4497 topMargin = a.getDimensionPixelSize( 4498 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0); 4499 rightMargin = a.getDimensionPixelSize( 4500 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0); 4501 bottomMargin = a.getDimensionPixelSize( 4502 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0); 4503 } 4504 4505 a.recycle(); 4506 } 4507 4508 /** 4509 * {@inheritDoc} 4510 */ 4511 public MarginLayoutParams(int width, int height) { 4512 super(width, height); 4513 } 4514 4515 /** 4516 * Copy constructor. Clones the width, height and margin values of the source. 4517 * 4518 * @param source The layout params to copy from. 4519 */ 4520 public MarginLayoutParams(MarginLayoutParams source) { 4521 this.width = source.width; 4522 this.height = source.height; 4523 4524 this.leftMargin = source.leftMargin; 4525 this.topMargin = source.topMargin; 4526 this.rightMargin = source.rightMargin; 4527 this.bottomMargin = source.bottomMargin; 4528 } 4529 4530 /** 4531 * {@inheritDoc} 4532 */ 4533 public MarginLayoutParams(LayoutParams source) { 4534 super(source); 4535 } 4536 4537 /** 4538 * Sets the margins, in pixels. 4539 * 4540 * @param left the left margin size 4541 * @param top the top margin size 4542 * @param right the right margin size 4543 * @param bottom the bottom margin size 4544 * 4545 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 4546 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 4547 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 4548 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 4549 */ 4550 public void setMargins(int left, int top, int right, int bottom) { 4551 leftMargin = left; 4552 topMargin = top; 4553 rightMargin = right; 4554 bottomMargin = bottom; 4555 } 4556 } 4557 4558 /* Describes a touched view and the ids of the pointers that it has captured. 4559 * 4560 * This code assumes that pointer ids are always in the range 0..31 such that 4561 * it can use a bitfield to track which pointer ids are present. 4562 * As it happens, the lower layers of the input dispatch pipeline also use the 4563 * same trick so the assumption should be safe here... 4564 */ 4565 private static final class TouchTarget { 4566 private static final int MAX_RECYCLED = 32; 4567 private static final Object sRecycleLock = new Object(); 4568 private static TouchTarget sRecycleBin; 4569 private static int sRecycledCount; 4570 4571 public static final int ALL_POINTER_IDS = -1; // all ones 4572 4573 // The touched child view. 4574 public View child; 4575 4576 // The combined bit mask of pointer ids for all pointers captured by the target. 4577 public int pointerIdBits; 4578 4579 // The next target in the target list. 4580 public TouchTarget next; 4581 4582 private TouchTarget() { 4583 } 4584 4585 public static TouchTarget obtain(View child, int pointerIdBits) { 4586 final TouchTarget target; 4587 synchronized (sRecycleLock) { 4588 if (sRecycleBin == null) { 4589 target = new TouchTarget(); 4590 } else { 4591 target = sRecycleBin; 4592 sRecycleBin = target.next; 4593 sRecycledCount--; 4594 target.next = null; 4595 } 4596 } 4597 target.child = child; 4598 target.pointerIdBits = pointerIdBits; 4599 return target; 4600 } 4601 4602 public void recycle() { 4603 synchronized (sRecycleLock) { 4604 if (sRecycledCount < MAX_RECYCLED) { 4605 next = sRecycleBin; 4606 sRecycleBin = this; 4607 sRecycledCount += 1; 4608 } 4609 } 4610 } 4611 } 4612} 4613