ViewGroup.java revision 326d808b858359464b2ffeb84f2e0a8e0c79b600
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 com.android.internal.R; 20 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Paint; 26import android.graphics.Rect; 27import android.graphics.RectF; 28import android.graphics.Region; 29import android.os.Parcelable; 30import android.os.SystemClock; 31import android.util.AttributeSet; 32import android.util.Config; 33import android.util.EventLog; 34import android.util.Log; 35import android.util.SparseArray; 36import android.view.accessibility.AccessibilityEvent; 37import android.view.animation.Animation; 38import android.view.animation.AnimationUtils; 39import android.view.animation.LayoutAnimationController; 40import android.view.animation.Transformation; 41 42import java.util.ArrayList; 43 44/** 45 * <p> 46 * A <code>ViewGroup</code> is a special view that can contain other views 47 * (called children.) The view group is the base class for layouts and views 48 * containers. This class also defines the 49 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base 50 * class for layouts parameters. 51 * </p> 52 * 53 * <p> 54 * Also see {@link LayoutParams} for layout attributes. 55 * </p> 56 * 57 * @attr ref android.R.styleable#ViewGroup_clipChildren 58 * @attr ref android.R.styleable#ViewGroup_clipToPadding 59 * @attr ref android.R.styleable#ViewGroup_layoutAnimation 60 * @attr ref android.R.styleable#ViewGroup_animationCache 61 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache 62 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache 63 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren 64 * @attr ref android.R.styleable#ViewGroup_descendantFocusability 65 */ 66public abstract class ViewGroup extends View implements ViewParent, ViewManager { 67 private static final boolean DBG = false; 68 69 /** 70 * Views which have been hidden or removed which need to be animated on 71 * their way out. 72 * This field should be made private, so it is hidden from the SDK. 73 * {@hide} 74 */ 75 protected ArrayList<View> mDisappearingChildren; 76 77 /** 78 * Listener used to propagate events indicating when children are added 79 * and/or removed from a view group. 80 * This field should be made private, so it is hidden from the SDK. 81 * {@hide} 82 */ 83 protected OnHierarchyChangeListener mOnHierarchyChangeListener; 84 85 // The view contained within this ViewGroup that has or contains focus. 86 private View mFocused; 87 88 // The current transformation to apply on the child being drawn 89 private Transformation mChildTransformation; 90 private RectF mInvalidateRegion; 91 92 // Target of Motion events 93 private View mMotionTarget; 94 private final Rect mTempRect = new Rect(); 95 96 // Layout animation 97 private LayoutAnimationController mLayoutAnimationController; 98 private Animation.AnimationListener mAnimationListener; 99 100 /** 101 * Internal flags. 102 * 103 * This field should be made private, so it is hidden from the SDK. 104 * {@hide} 105 */ 106 protected int mGroupFlags; 107 108 // When set, ViewGroup invalidates only the child's rectangle 109 // Set by default 110 private static final int FLAG_CLIP_CHILDREN = 0x1; 111 112 // When set, ViewGroup excludes the padding area from the invalidate rectangle 113 // Set by default 114 private static final int FLAG_CLIP_TO_PADDING = 0x2; 115 116 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when 117 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set 118 private static final int FLAG_INVALIDATE_REQUIRED = 0x4; 119 120 // When set, dispatchDraw() will run the layout animation and unset the flag 121 private static final int FLAG_RUN_ANIMATION = 0x8; 122 123 // When set, there is either no layout animation on the ViewGroup or the layout 124 // animation is over 125 // Set by default 126 private static final int FLAG_ANIMATION_DONE = 0x10; 127 128 // If set, this ViewGroup has padding; if unset there is no padding and we don't need 129 // to clip it, even if FLAG_CLIP_TO_PADDING is set 130 private static final int FLAG_PADDING_NOT_NULL = 0x20; 131 132 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation 133 // Set by default 134 private static final int FLAG_ANIMATION_CACHE = 0x40; 135 136 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a 137 // layout animation; this avoid clobbering the hierarchy 138 // Automatically set when the layout animation starts, depending on the animation's 139 // characteristics 140 private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80; 141 142 // When set, the next call to drawChild() will clear mChildTransformation's matrix 143 private static final int FLAG_CLEAR_TRANSFORMATION = 0x100; 144 145 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes 146 // the children's Bitmap caches if necessary 147 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set) 148 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200; 149 150 /** 151 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)} 152 * to get the index of the child to draw for that iteration. 153 * 154 * @hide 155 */ 156 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; 157 158 /** 159 * When set, this ViewGroup supports static transformations on children; this causes 160 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 161 * invoked when a child is drawn. 162 * 163 * Any subclass overriding 164 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 165 * set this flags in {@link #mGroupFlags}. 166 * 167 * {@hide} 168 */ 169 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; 170 171 // When the previous drawChild() invocation used an alpha value that was lower than 172 // 1.0 and set it in mCachePaint 173 private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000; 174 175 /** 176 * When set, this ViewGroup's drawable states also include those 177 * of its children. 178 */ 179 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000; 180 181 /** 182 * When set, this ViewGroup tries to always draw its children using their drawing cache. 183 */ 184 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; 185 186 /** 187 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to 188 * draw its children with their drawing cache. 189 */ 190 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; 191 192 /** 193 * When set, this group will go through its list of children to notify them of 194 * any drawable state change. 195 */ 196 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000; 197 198 private static final int FLAG_MASK_FOCUSABILITY = 0x60000; 199 200 /** 201 * This view will get focus before any of its descendants. 202 */ 203 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000; 204 205 /** 206 * This view will get focus only if none of its descendants want it. 207 */ 208 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000; 209 210 /** 211 * This view will block any of its descendants from getting focus, even 212 * if they are focusable. 213 */ 214 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000; 215 216 /** 217 * Used to map between enum in attrubutes and flag values. 218 */ 219 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS = 220 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, 221 FOCUS_BLOCK_DESCENDANTS}; 222 223 /** 224 * When set, this ViewGroup should not intercept touch events. 225 */ 226 private static final int FLAG_DISALLOW_INTERCEPT = 0x80000; 227 228 /** 229 * Indicates which types of drawing caches are to be kept in memory. 230 * This field should be made private, so it is hidden from the SDK. 231 * {@hide} 232 */ 233 protected int mPersistentDrawingCache; 234 235 /** 236 * Used to indicate that no drawing cache should be kept in memory. 237 */ 238 public static final int PERSISTENT_NO_CACHE = 0x0; 239 240 /** 241 * Used to indicate that the animation drawing cache should be kept in memory. 242 */ 243 public static final int PERSISTENT_ANIMATION_CACHE = 0x1; 244 245 /** 246 * Used to indicate that the scrolling drawing cache should be kept in memory. 247 */ 248 public static final int PERSISTENT_SCROLLING_CACHE = 0x2; 249 250 /** 251 * Used to indicate that all drawing caches should be kept in memory. 252 */ 253 public static final int PERSISTENT_ALL_CACHES = 0x3; 254 255 /** 256 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL 257 * are set at the same time. 258 */ 259 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL; 260 261 // Index of the child's left position in the mLocation array 262 private static final int CHILD_LEFT_INDEX = 0; 263 // Index of the child's top position in the mLocation array 264 private static final int CHILD_TOP_INDEX = 1; 265 266 // Child views of this ViewGroup 267 private View[] mChildren; 268 // Number of valid children in the mChildren array, the rest should be null or not 269 // considered as children 270 private int mChildrenCount; 271 272 private static final int ARRAY_INITIAL_CAPACITY = 12; 273 private static final int ARRAY_CAPACITY_INCREMENT = 12; 274 275 // Used to draw cached views 276 private final Paint mCachePaint = new Paint(); 277 278 public ViewGroup(Context context) { 279 super(context); 280 initViewGroup(); 281 } 282 283 public ViewGroup(Context context, AttributeSet attrs) { 284 super(context, attrs); 285 initViewGroup(); 286 initFromAttributes(context, attrs); 287 } 288 289 public ViewGroup(Context context, AttributeSet attrs, int defStyle) { 290 super(context, attrs, defStyle); 291 initViewGroup(); 292 initFromAttributes(context, attrs); 293 } 294 295 private void initViewGroup() { 296 // ViewGroup doesn't draw by default 297 setFlags(WILL_NOT_DRAW, DRAW_MASK); 298 mGroupFlags |= FLAG_CLIP_CHILDREN; 299 mGroupFlags |= FLAG_CLIP_TO_PADDING; 300 mGroupFlags |= FLAG_ANIMATION_DONE; 301 mGroupFlags |= FLAG_ANIMATION_CACHE; 302 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; 303 304 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); 305 306 mChildren = new View[ARRAY_INITIAL_CAPACITY]; 307 mChildrenCount = 0; 308 309 mCachePaint.setDither(false); 310 311 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; 312 } 313 314 private void initFromAttributes(Context context, AttributeSet attrs) { 315 TypedArray a = context.obtainStyledAttributes(attrs, 316 R.styleable.ViewGroup); 317 318 final int N = a.getIndexCount(); 319 for (int i = 0; i < N; i++) { 320 int attr = a.getIndex(i); 321 switch (attr) { 322 case R.styleable.ViewGroup_clipChildren: 323 setClipChildren(a.getBoolean(attr, true)); 324 break; 325 case R.styleable.ViewGroup_clipToPadding: 326 setClipToPadding(a.getBoolean(attr, true)); 327 break; 328 case R.styleable.ViewGroup_animationCache: 329 setAnimationCacheEnabled(a.getBoolean(attr, true)); 330 break; 331 case R.styleable.ViewGroup_persistentDrawingCache: 332 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE)); 333 break; 334 case R.styleable.ViewGroup_addStatesFromChildren: 335 setAddStatesFromChildren(a.getBoolean(attr, false)); 336 break; 337 case R.styleable.ViewGroup_alwaysDrawnWithCache: 338 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true)); 339 break; 340 case R.styleable.ViewGroup_layoutAnimation: 341 int id = a.getResourceId(attr, -1); 342 if (id > 0) { 343 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id)); 344 } 345 break; 346 case R.styleable.ViewGroup_descendantFocusability: 347 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]); 348 break; 349 } 350 } 351 352 a.recycle(); 353 } 354 355 /** 356 * Gets the descendant focusability of this view group. The descendant 357 * focusability defines the relationship between this view group and its 358 * descendants when looking for a view to take focus in 359 * {@link #requestFocus(int, android.graphics.Rect)}. 360 * 361 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 362 * {@link #FOCUS_BLOCK_DESCENDANTS}. 363 */ 364 @ViewDebug.ExportedProperty(mapping = { 365 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"), 366 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"), 367 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS") 368 }) 369 public int getDescendantFocusability() { 370 return mGroupFlags & FLAG_MASK_FOCUSABILITY; 371 } 372 373 /** 374 * Set the descendant focusability of this view group. This defines the relationship 375 * between this view group and its descendants when looking for a view to 376 * take focus in {@link #requestFocus(int, android.graphics.Rect)}. 377 * 378 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 379 * {@link #FOCUS_BLOCK_DESCENDANTS}. 380 */ 381 public void setDescendantFocusability(int focusability) { 382 switch (focusability) { 383 case FOCUS_BEFORE_DESCENDANTS: 384 case FOCUS_AFTER_DESCENDANTS: 385 case FOCUS_BLOCK_DESCENDANTS: 386 break; 387 default: 388 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, " 389 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS"); 390 } 391 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY; 392 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY); 393 } 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override 399 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) { 400 if (mFocused != null) { 401 mFocused.unFocus(); 402 mFocused = null; 403 } 404 super.handleFocusGainInternal(direction, previouslyFocusedRect); 405 } 406 407 /** 408 * {@inheritDoc} 409 */ 410 public void requestChildFocus(View child, View focused) { 411 if (DBG) { 412 System.out.println(this + " requestChildFocus()"); 413 } 414 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) { 415 return; 416 } 417 418 // Unfocus us, if necessary 419 super.unFocus(); 420 421 // We had a previous notion of who had focus. Clear it. 422 if (mFocused != child) { 423 if (mFocused != null) { 424 mFocused.unFocus(); 425 } 426 427 mFocused = child; 428 } 429 if (mParent != null) { 430 mParent.requestChildFocus(this, focused); 431 } 432 } 433 434 /** 435 * {@inheritDoc} 436 */ 437 public void focusableViewAvailable(View v) { 438 if (mParent != null 439 // shortcut: don't report a new focusable view if we block our descendants from 440 // getting focus 441 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) 442 // shortcut: don't report a new focusable view if we already are focused 443 // (and we don't prefer our descendants) 444 // 445 // note: knowing that mFocused is non-null is not a good enough reason 446 // to break the traversal since in that case we'd actually have to find 447 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and 448 // an ancestor of v; this will get checked for at ViewRoot 449 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) { 450 mParent.focusableViewAvailable(v); 451 } 452 } 453 454 /** 455 * {@inheritDoc} 456 */ 457 public boolean showContextMenuForChild(View originalView) { 458 return mParent != null && mParent.showContextMenuForChild(originalView); 459 } 460 461 /** 462 * Find the nearest view in the specified direction that wants to take 463 * focus. 464 * 465 * @param focused The view that currently has focus 466 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and 467 * FOCUS_RIGHT, or 0 for not applicable. 468 */ 469 public View focusSearch(View focused, int direction) { 470 if (isRootNamespace()) { 471 // root namespace means we should consider ourselves the top of the 472 // tree for focus searching; otherwise we could be focus searching 473 // into other tabs. see LocalActivityManager and TabHost for more info 474 return FocusFinder.getInstance().findNextFocus(this, focused, direction); 475 } else if (mParent != null) { 476 return mParent.focusSearch(focused, direction); 477 } 478 return null; 479 } 480 481 /** 482 * {@inheritDoc} 483 */ 484 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 485 return false; 486 } 487 488 /** 489 * {@inheritDoc} 490 */ 491 @Override 492 public boolean dispatchUnhandledMove(View focused, int direction) { 493 return mFocused != null && 494 mFocused.dispatchUnhandledMove(focused, direction); 495 } 496 497 /** 498 * {@inheritDoc} 499 */ 500 public void clearChildFocus(View child) { 501 if (DBG) { 502 System.out.println(this + " clearChildFocus()"); 503 } 504 505 mFocused = null; 506 if (mParent != null) { 507 mParent.clearChildFocus(this); 508 } 509 } 510 511 /** 512 * {@inheritDoc} 513 */ 514 @Override 515 public void clearFocus() { 516 super.clearFocus(); 517 518 // clear any child focus if it exists 519 if (mFocused != null) { 520 mFocused.clearFocus(); 521 } 522 } 523 524 /** 525 * {@inheritDoc} 526 */ 527 @Override 528 void unFocus() { 529 if (DBG) { 530 System.out.println(this + " unFocus()"); 531 } 532 533 super.unFocus(); 534 if (mFocused != null) { 535 mFocused.unFocus(); 536 } 537 mFocused = null; 538 } 539 540 /** 541 * Returns the focused child of this view, if any. The child may have focus 542 * or contain focus. 543 * 544 * @return the focused child or null. 545 */ 546 public View getFocusedChild() { 547 return mFocused; 548 } 549 550 /** 551 * Returns true if this view has or contains focus 552 * 553 * @return true if this view has or contains focus 554 */ 555 @Override 556 public boolean hasFocus() { 557 return (mPrivateFlags & FOCUSED) != 0 || mFocused != null; 558 } 559 560 /* 561 * (non-Javadoc) 562 * 563 * @see android.view.View#findFocus() 564 */ 565 @Override 566 public View findFocus() { 567 if (DBG) { 568 System.out.println("Find focus in " + this + ": flags=" 569 + isFocused() + ", child=" + mFocused); 570 } 571 572 if (isFocused()) { 573 return this; 574 } 575 576 if (mFocused != null) { 577 return mFocused.findFocus(); 578 } 579 return null; 580 } 581 582 /** 583 * {@inheritDoc} 584 */ 585 @Override 586 public boolean hasFocusable() { 587 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) { 588 return false; 589 } 590 591 if (isFocusable()) { 592 return true; 593 } 594 595 final int descendantFocusability = getDescendantFocusability(); 596 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 597 final int count = mChildrenCount; 598 final View[] children = mChildren; 599 600 for (int i = 0; i < count; i++) { 601 final View child = children[i]; 602 if (child.hasFocusable()) { 603 return true; 604 } 605 } 606 } 607 608 return false; 609 } 610 611 /** 612 * {@inheritDoc} 613 */ 614 @Override 615 public void addFocusables(ArrayList<View> views, int direction) { 616 addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); 617 } 618 619 /** 620 * {@inheritDoc} 621 */ 622 @Override 623 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 624 final int focusableCount = views.size(); 625 626 final int descendantFocusability = getDescendantFocusability(); 627 628 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 629 final int count = mChildrenCount; 630 final View[] children = mChildren; 631 632 for (int i = 0; i < count; i++) { 633 final View child = children[i]; 634 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 635 child.addFocusables(views, direction, focusableMode); 636 } 637 } 638 } 639 640 // we add ourselves (if focusable) in all cases except for when we are 641 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is 642 // to avoid the focus search finding layouts when a more precise search 643 // among the focusable children would be more interesting. 644 if ( 645 descendantFocusability != FOCUS_AFTER_DESCENDANTS || 646 // No focusable descendants 647 (focusableCount == views.size())) { 648 super.addFocusables(views, direction, focusableMode); 649 } 650 } 651 652 /** 653 * {@inheritDoc} 654 */ 655 @Override 656 public void dispatchWindowFocusChanged(boolean hasFocus) { 657 super.dispatchWindowFocusChanged(hasFocus); 658 final int count = mChildrenCount; 659 final View[] children = mChildren; 660 for (int i = 0; i < count; i++) { 661 children[i].dispatchWindowFocusChanged(hasFocus); 662 } 663 } 664 665 /** 666 * {@inheritDoc} 667 */ 668 @Override 669 public void addTouchables(ArrayList<View> views) { 670 super.addTouchables(views); 671 672 final int count = mChildrenCount; 673 final View[] children = mChildren; 674 675 for (int i = 0; i < count; i++) { 676 final View child = children[i]; 677 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 678 child.addTouchables(views); 679 } 680 } 681 } 682 683 /** 684 * {@inheritDoc} 685 */ 686 @Override 687 protected void dispatchVisibilityChanged(View changedView, int visibility) { 688 super.dispatchVisibilityChanged(changedView, visibility); 689 final int count = mChildrenCount; 690 final View[] children = mChildren; 691 for (int i = 0; i < count; i++) { 692 children[i].dispatchVisibilityChanged(changedView, visibility); 693 } 694 } 695 696 /** 697 * {@inheritDoc} 698 */ 699 @Override 700 public void dispatchWindowVisibilityChanged(int visibility) { 701 super.dispatchWindowVisibilityChanged(visibility); 702 final int count = mChildrenCount; 703 final View[] children = mChildren; 704 for (int i = 0; i < count; i++) { 705 children[i].dispatchWindowVisibilityChanged(visibility); 706 } 707 } 708 709 /** 710 * {@inheritDoc} 711 */ 712 public void recomputeViewAttributes(View child) { 713 ViewParent parent = mParent; 714 if (parent != null) parent.recomputeViewAttributes(this); 715 } 716 717 @Override 718 void dispatchCollectViewAttributes(int visibility) { 719 visibility |= mViewFlags&VISIBILITY_MASK; 720 super.dispatchCollectViewAttributes(visibility); 721 final int count = mChildrenCount; 722 final View[] children = mChildren; 723 for (int i = 0; i < count; i++) { 724 children[i].dispatchCollectViewAttributes(visibility); 725 } 726 } 727 728 /** 729 * {@inheritDoc} 730 */ 731 public void bringChildToFront(View child) { 732 int index = indexOfChild(child); 733 if (index >= 0) { 734 removeFromArray(index); 735 addInArray(child, mChildrenCount); 736 child.mParent = this; 737 } 738 } 739 740 /** 741 * {@inheritDoc} 742 */ 743 @Override 744 public boolean dispatchKeyEventPreIme(KeyEvent event) { 745 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 746 return super.dispatchKeyEventPreIme(event); 747 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 748 return mFocused.dispatchKeyEventPreIme(event); 749 } 750 return false; 751 } 752 753 /** 754 * {@inheritDoc} 755 */ 756 @Override 757 public boolean dispatchKeyEvent(KeyEvent event) { 758 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 759 return super.dispatchKeyEvent(event); 760 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 761 return mFocused.dispatchKeyEvent(event); 762 } 763 return false; 764 } 765 766 /** 767 * {@inheritDoc} 768 */ 769 @Override 770 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 771 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 772 return super.dispatchKeyShortcutEvent(event); 773 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 774 return mFocused.dispatchKeyShortcutEvent(event); 775 } 776 return false; 777 } 778 779 /** 780 * {@inheritDoc} 781 */ 782 @Override 783 public boolean dispatchTrackballEvent(MotionEvent event) { 784 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 785 return super.dispatchTrackballEvent(event); 786 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 787 return mFocused.dispatchTrackballEvent(event); 788 } 789 return false; 790 } 791 792 /** 793 * {@inheritDoc} 794 */ 795 @Override 796 public boolean dispatchTouchEvent(MotionEvent ev) { 797 final int action = ev.getAction(); 798 final float xf = ev.getX(); 799 final float yf = ev.getY(); 800 final float scrolledXFloat = xf + mScrollX; 801 final float scrolledYFloat = yf + mScrollY; 802 final Rect frame = mTempRect; 803 804 boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 805 806 if (action == MotionEvent.ACTION_DOWN) { 807 if (mMotionTarget != null) { 808 // this is weird, we got a pen down, but we thought it was 809 // already down! 810 // XXX: We should probably send an ACTION_UP to the current 811 // target. 812 mMotionTarget = null; 813 } 814 // If we're disallowing intercept or if we're allowing and we didn't 815 // intercept 816 if (disallowIntercept || !onInterceptTouchEvent(ev)) { 817 // reset this event's action (just to protect ourselves) 818 ev.setAction(MotionEvent.ACTION_DOWN); 819 // We know we want to dispatch the event down, find a child 820 // who can handle it, start with the front-most child. 821 final int scrolledXInt = (int) scrolledXFloat; 822 final int scrolledYInt = (int) scrolledYFloat; 823 final View[] children = mChildren; 824 final int count = mChildrenCount; 825 for (int i = count - 1; i >= 0; i--) { 826 final View child = children[i]; 827 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE 828 || child.getAnimation() != null) { 829 child.getHitRect(frame); 830 if (frame.contains(scrolledXInt, scrolledYInt)) { 831 // offset the event to the view's coordinate system 832 final float xc = scrolledXFloat - child.mLeft; 833 final float yc = scrolledYFloat - child.mTop; 834 ev.setLocation(xc, yc); 835 if (child.dispatchTouchEvent(ev)) { 836 // Event handled, we have a target now. 837 mMotionTarget = child; 838 return true; 839 } 840 // The event didn't get handled, try the next view. 841 // Don't reset the event's location, it's not 842 // necessary here. 843 } 844 } 845 } 846 } 847 } 848 849 boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || 850 (action == MotionEvent.ACTION_CANCEL); 851 852 if (isUpOrCancel) { 853 // Note, we've already copied the previous state to our local 854 // variable, so this takes effect on the next event 855 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 856 } 857 858 // The event wasn't an ACTION_DOWN, dispatch it to our target if 859 // we have one. 860 final View target = mMotionTarget; 861 if (target == null) { 862 // We don't have a target, this means we're handling the 863 // event as a regular view. 864 ev.setLocation(xf, yf); 865 return super.dispatchTouchEvent(ev); 866 } 867 868 // if have a target, see if we're allowed to and want to intercept its 869 // events 870 if (!disallowIntercept && onInterceptTouchEvent(ev)) { 871 final float xc = scrolledXFloat - (float) target.mLeft; 872 final float yc = scrolledYFloat - (float) target.mTop; 873 ev.setAction(MotionEvent.ACTION_CANCEL); 874 ev.setLocation(xc, yc); 875 if (!target.dispatchTouchEvent(ev)) { 876 // target didn't handle ACTION_CANCEL. not much we can do 877 // but they should have. 878 } 879 // clear the target 880 mMotionTarget = null; 881 // Don't dispatch this event to our own view, because we already 882 // saw it when intercepting; we just want to give the following 883 // event to the normal onTouchEvent(). 884 return true; 885 } 886 887 if (isUpOrCancel) { 888 mMotionTarget = null; 889 } 890 891 // finally offset the event to the target's coordinate system and 892 // dispatch the event. 893 final float xc = scrolledXFloat - (float) target.mLeft; 894 final float yc = scrolledYFloat - (float) target.mTop; 895 ev.setLocation(xc, yc); 896 897 return target.dispatchTouchEvent(ev); 898 } 899 900 /** 901 * {@inheritDoc} 902 */ 903 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 904 905 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { 906 // We're already in this state, assume our ancestors are too 907 return; 908 } 909 910 if (disallowIntercept) { 911 mGroupFlags |= FLAG_DISALLOW_INTERCEPT; 912 } else { 913 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 914 } 915 916 // Pass it up to our parent 917 if (mParent != null) { 918 mParent.requestDisallowInterceptTouchEvent(disallowIntercept); 919 } 920 } 921 922 /** 923 * Implement this method to intercept all touch screen motion events. This 924 * allows you to watch events as they are dispatched to your children, and 925 * take ownership of the current gesture at any point. 926 * 927 * <p>Using this function takes some care, as it has a fairly complicated 928 * interaction with {@link View#onTouchEvent(MotionEvent) 929 * View.onTouchEvent(MotionEvent)}, and using it requires implementing 930 * that method as well as this one in the correct way. Events will be 931 * received in the following order: 932 * 933 * <ol> 934 * <li> You will receive the down event here. 935 * <li> The down event will be handled either by a child of this view 936 * group, or given to your own onTouchEvent() method to handle; this means 937 * you should implement onTouchEvent() to return true, so you will 938 * continue to see the rest of the gesture (instead of looking for 939 * a parent view to handle it). Also, by returning true from 940 * onTouchEvent(), you will not receive any following 941 * events in onInterceptTouchEvent() and all touch processing must 942 * happen in onTouchEvent() like normal. 943 * <li> For as long as you return false from this function, each following 944 * event (up to and including the final up) will be delivered first here 945 * and then to the target's onTouchEvent(). 946 * <li> If you return true from here, you will not receive any 947 * following events: the target view will receive the same event but 948 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further 949 * events will be delivered to your onTouchEvent() method and no longer 950 * appear here. 951 * </ol> 952 * 953 * @param ev The motion event being dispatched down the hierarchy. 954 * @return Return true to steal motion events from the children and have 955 * them dispatched to this ViewGroup through onTouchEvent(). 956 * The current target will receive an ACTION_CANCEL event, and no further 957 * messages will be delivered here. 958 */ 959 public boolean onInterceptTouchEvent(MotionEvent ev) { 960 return false; 961 } 962 963 /** 964 * {@inheritDoc} 965 * 966 * Looks for a view to give focus to respecting the setting specified by 967 * {@link #getDescendantFocusability()}. 968 * 969 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to 970 * find focus within the children of this group when appropriate. 971 * 972 * @see #FOCUS_BEFORE_DESCENDANTS 973 * @see #FOCUS_AFTER_DESCENDANTS 974 * @see #FOCUS_BLOCK_DESCENDANTS 975 * @see #onRequestFocusInDescendants 976 */ 977 @Override 978 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 979 if (DBG) { 980 System.out.println(this + " ViewGroup.requestFocus direction=" 981 + direction); 982 } 983 int descendantFocusability = getDescendantFocusability(); 984 985 switch (descendantFocusability) { 986 case FOCUS_BLOCK_DESCENDANTS: 987 return super.requestFocus(direction, previouslyFocusedRect); 988 case FOCUS_BEFORE_DESCENDANTS: { 989 final boolean took = super.requestFocus(direction, previouslyFocusedRect); 990 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); 991 } 992 case FOCUS_AFTER_DESCENDANTS: { 993 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); 994 return took ? took : super.requestFocus(direction, previouslyFocusedRect); 995 } 996 default: 997 throw new IllegalStateException("descendant focusability must be " 998 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " 999 + "but is " + descendantFocusability); 1000 } 1001 } 1002 1003 /** 1004 * Look for a descendant to call {@link View#requestFocus} on. 1005 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)} 1006 * when it wants to request focus within its children. Override this to 1007 * customize how your {@link ViewGroup} requests focus within its children. 1008 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT 1009 * @param previouslyFocusedRect The rectangle (in this View's coordinate system) 1010 * to give a finer grained hint about where focus is coming from. May be null 1011 * if there is no hint. 1012 * @return Whether focus was taken. 1013 */ 1014 @SuppressWarnings({"ConstantConditions"}) 1015 protected boolean onRequestFocusInDescendants(int direction, 1016 Rect previouslyFocusedRect) { 1017 int index; 1018 int increment; 1019 int end; 1020 int count = mChildrenCount; 1021 if ((direction & FOCUS_FORWARD) != 0) { 1022 index = 0; 1023 increment = 1; 1024 end = count; 1025 } else { 1026 index = count - 1; 1027 increment = -1; 1028 end = -1; 1029 } 1030 final View[] children = mChildren; 1031 for (int i = index; i != end; i += increment) { 1032 View child = children[i]; 1033 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1034 if (child.requestFocus(direction, previouslyFocusedRect)) { 1035 return true; 1036 } 1037 } 1038 } 1039 return false; 1040 } 1041 1042 /** 1043 * {@inheritDoc} 1044 */ 1045 @Override 1046 void dispatchAttachedToWindow(AttachInfo info, int visibility) { 1047 super.dispatchAttachedToWindow(info, visibility); 1048 visibility |= mViewFlags & VISIBILITY_MASK; 1049 final int count = mChildrenCount; 1050 final View[] children = mChildren; 1051 for (int i = 0; i < count; i++) { 1052 children[i].dispatchAttachedToWindow(info, visibility); 1053 } 1054 } 1055 1056 @Override 1057 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1058 boolean populated = false; 1059 for (int i = 0, count = getChildCount(); i < count; i++) { 1060 populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event); 1061 } 1062 return populated; 1063 } 1064 1065 /** 1066 * {@inheritDoc} 1067 */ 1068 @Override 1069 void dispatchDetachedFromWindow() { 1070 // If we still have a motion target, we are still in the process of 1071 // dispatching motion events to a child; we need to get rid of that 1072 // child to avoid dispatching events to it after the window is torn 1073 // down. To make sure we keep the child in a consistent state, we 1074 // first send it an ACTION_CANCEL motion event. 1075 if (mMotionTarget != null) { 1076 final long now = SystemClock.uptimeMillis(); 1077 final MotionEvent event = MotionEvent.obtain(now, now, 1078 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 1079 mMotionTarget.dispatchTouchEvent(event); 1080 event.recycle(); 1081 mMotionTarget = null; 1082 } 1083 1084 final int count = mChildrenCount; 1085 final View[] children = mChildren; 1086 for (int i = 0; i < count; i++) { 1087 children[i].dispatchDetachedFromWindow(); 1088 } 1089 super.dispatchDetachedFromWindow(); 1090 } 1091 1092 /** 1093 * {@inheritDoc} 1094 */ 1095 @Override 1096 public void setPadding(int left, int top, int right, int bottom) { 1097 super.setPadding(left, top, right, bottom); 1098 1099 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) { 1100 mGroupFlags |= FLAG_PADDING_NOT_NULL; 1101 } else { 1102 mGroupFlags &= ~FLAG_PADDING_NOT_NULL; 1103 } 1104 } 1105 1106 /** 1107 * {@inheritDoc} 1108 */ 1109 @Override 1110 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 1111 super.dispatchSaveInstanceState(container); 1112 final int count = mChildrenCount; 1113 final View[] children = mChildren; 1114 for (int i = 0; i < count; i++) { 1115 children[i].dispatchSaveInstanceState(container); 1116 } 1117 } 1118 1119 /** 1120 * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view, 1121 * not to its children. For use when overriding 1122 * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze 1123 * their own state but not the state of their children. 1124 * 1125 * @param container the container 1126 */ 1127 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { 1128 super.dispatchSaveInstanceState(container); 1129 } 1130 1131 /** 1132 * {@inheritDoc} 1133 */ 1134 @Override 1135 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 1136 super.dispatchRestoreInstanceState(container); 1137 final int count = mChildrenCount; 1138 final View[] children = mChildren; 1139 for (int i = 0; i < count; i++) { 1140 children[i].dispatchRestoreInstanceState(container); 1141 } 1142 } 1143 1144 /** 1145 * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view, 1146 * not to its children. For use when overriding 1147 * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw 1148 * their own state but not the state of their children. 1149 * 1150 * @param container the container 1151 */ 1152 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { 1153 super.dispatchRestoreInstanceState(container); 1154 } 1155 1156 /** 1157 * Enables or disables the drawing cache for each child of this view group. 1158 * 1159 * @param enabled true to enable the cache, false to dispose of it 1160 */ 1161 protected void setChildrenDrawingCacheEnabled(boolean enabled) { 1162 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) { 1163 final View[] children = mChildren; 1164 final int count = mChildrenCount; 1165 for (int i = 0; i < count; i++) { 1166 children[i].setDrawingCacheEnabled(enabled); 1167 } 1168 } 1169 } 1170 1171 @Override 1172 protected void onAnimationStart() { 1173 super.onAnimationStart(); 1174 1175 // When this ViewGroup's animation starts, build the cache for the children 1176 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 1177 final int count = mChildrenCount; 1178 final View[] children = mChildren; 1179 1180 for (int i = 0; i < count; i++) { 1181 final View child = children[i]; 1182 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1183 child.setDrawingCacheEnabled(true); 1184 child.buildDrawingCache(true); 1185 } 1186 } 1187 1188 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 1189 } 1190 } 1191 1192 @Override 1193 protected void onAnimationEnd() { 1194 super.onAnimationEnd(); 1195 1196 // When this ViewGroup's animation ends, destroy the cache of the children 1197 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 1198 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 1199 1200 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 1201 setChildrenDrawingCacheEnabled(false); 1202 } 1203 } 1204 } 1205 1206 /** 1207 * {@inheritDoc} 1208 */ 1209 @Override 1210 protected void dispatchDraw(Canvas canvas) { 1211 final int count = mChildrenCount; 1212 final View[] children = mChildren; 1213 int flags = mGroupFlags; 1214 1215 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { 1216 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 1217 1218 for (int i = 0; i < count; i++) { 1219 final View child = children[i]; 1220 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1221 final LayoutParams params = child.getLayoutParams(); 1222 attachLayoutAnimationParameters(child, params, i, count); 1223 bindLayoutAnimation(child); 1224 if (cache) { 1225 child.setDrawingCacheEnabled(true); 1226 child.buildDrawingCache(true); 1227 } 1228 } 1229 } 1230 1231 final LayoutAnimationController controller = mLayoutAnimationController; 1232 if (controller.willOverlap()) { 1233 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE; 1234 } 1235 1236 controller.start(); 1237 1238 mGroupFlags &= ~FLAG_RUN_ANIMATION; 1239 mGroupFlags &= ~FLAG_ANIMATION_DONE; 1240 1241 if (cache) { 1242 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 1243 } 1244 1245 if (mAnimationListener != null) { 1246 mAnimationListener.onAnimationStart(controller.getAnimation()); 1247 } 1248 } 1249 1250 int saveCount = 0; 1251 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 1252 if (clipToPadding) { 1253 saveCount = canvas.save(); 1254 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, 1255 mScrollX + mRight - mLeft - mPaddingRight, 1256 mScrollY + mBottom - mTop - mPaddingBottom); 1257 1258 } 1259 1260 // We will draw our child's animation, let's reset the flag 1261 mPrivateFlags &= ~DRAW_ANIMATION; 1262 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; 1263 1264 boolean more = false; 1265 final long drawingTime = getDrawingTime(); 1266 1267 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { 1268 for (int i = 0; i < count; i++) { 1269 final View child = children[i]; 1270 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 1271 more |= drawChild(canvas, child, drawingTime); 1272 } 1273 } 1274 } else { 1275 for (int i = 0; i < count; i++) { 1276 final View child = children[getChildDrawingOrder(count, i)]; 1277 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 1278 more |= drawChild(canvas, child, drawingTime); 1279 } 1280 } 1281 } 1282 1283 // Draw any disappearing views that have animations 1284 if (mDisappearingChildren != null) { 1285 final ArrayList<View> disappearingChildren = mDisappearingChildren; 1286 final int disappearingCount = disappearingChildren.size() - 1; 1287 // Go backwards -- we may delete as animations finish 1288 for (int i = disappearingCount; i >= 0; i--) { 1289 final View child = disappearingChildren.get(i); 1290 more |= drawChild(canvas, child, drawingTime); 1291 } 1292 } 1293 1294 if (clipToPadding) { 1295 canvas.restoreToCount(saveCount); 1296 } 1297 1298 // mGroupFlags might have been updated by drawChild() 1299 flags = mGroupFlags; 1300 1301 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { 1302 invalidate(); 1303 } 1304 1305 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && 1306 mLayoutAnimationController.isDone() && !more) { 1307 // We want to erase the drawing cache and notify the listener after the 1308 // next frame is drawn because one extra invalidate() is caused by 1309 // drawChild() after the animation is over 1310 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; 1311 final Runnable end = new Runnable() { 1312 public void run() { 1313 notifyAnimationListener(); 1314 } 1315 }; 1316 post(end); 1317 } 1318 } 1319 1320 /** 1321 * Returns the index of the child to draw for this iteration. Override this 1322 * if you want to change the drawing order of children. By default, it 1323 * returns i. 1324 * <p> 1325 * NOTE: In order for this method to be called, you must enable child ordering 1326 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}. 1327 * 1328 * @param i The current iteration. 1329 * @return The index of the child to draw this iteration. 1330 * 1331 * @see #setChildrenDrawingOrderEnabled(boolean) 1332 * @see #isChildrenDrawingOrderEnabled() 1333 */ 1334 protected int getChildDrawingOrder(int childCount, int i) { 1335 return i; 1336 } 1337 1338 private void notifyAnimationListener() { 1339 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; 1340 mGroupFlags |= FLAG_ANIMATION_DONE; 1341 1342 if (mAnimationListener != null) { 1343 final Runnable end = new Runnable() { 1344 public void run() { 1345 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation()); 1346 } 1347 }; 1348 post(end); 1349 } 1350 1351 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 1352 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 1353 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 1354 setChildrenDrawingCacheEnabled(false); 1355 } 1356 } 1357 1358 invalidate(); 1359 } 1360 1361 /** 1362 * Draw one child of this View Group. This method is responsible for getting 1363 * the canvas in the right state. This includes clipping, translating so 1364 * that the child's scrolled origin is at 0, 0, and applying any animation 1365 * transformations. 1366 * 1367 * @param canvas The canvas on which to draw the child 1368 * @param child Who to draw 1369 * @param drawingTime The time at which draw is occuring 1370 * @return True if an invalidate() was issued 1371 */ 1372 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 1373 boolean more = false; 1374 1375 final int cl = child.mLeft; 1376 final int ct = child.mTop; 1377 final int cr = child.mRight; 1378 final int cb = child.mBottom; 1379 1380 final int flags = mGroupFlags; 1381 1382 if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) { 1383 if (mChildTransformation != null) { 1384 mChildTransformation.clear(); 1385 } 1386 mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION; 1387 } 1388 1389 Transformation transformToApply = null; 1390 final Animation a = child.getAnimation(); 1391 boolean concatMatrix = false; 1392 1393 if (a != null) { 1394 if (mInvalidateRegion == null) { 1395 mInvalidateRegion = new RectF(); 1396 } 1397 final RectF region = mInvalidateRegion; 1398 1399 final boolean initialized = a.isInitialized(); 1400 if (!initialized) { 1401 a.initialize(cr - cl, cb - ct, getWidth(), getHeight()); 1402 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct); 1403 child.onAnimationStart(); 1404 } 1405 1406 if (mChildTransformation == null) { 1407 mChildTransformation = new Transformation(); 1408 } 1409 more = a.getTransformation(drawingTime, mChildTransformation); 1410 transformToApply = mChildTransformation; 1411 1412 concatMatrix = a.willChangeTransformationMatrix(); 1413 1414 if (more) { 1415 if (!a.willChangeBounds()) { 1416 if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) == 1417 FLAG_OPTIMIZE_INVALIDATE) { 1418 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 1419 } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) { 1420 // The child need to draw an animation, potentially offscreen, so 1421 // make sure we do not cancel invalidate requests 1422 mPrivateFlags |= DRAW_ANIMATION; 1423 invalidate(cl, ct, cr, cb); 1424 } 1425 } else { 1426 a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, transformToApply); 1427 1428 // The child need to draw an animation, potentially offscreen, so 1429 // make sure we do not cancel invalidate requests 1430 mPrivateFlags |= DRAW_ANIMATION; 1431 1432 final int left = cl + (int) region.left; 1433 final int top = ct + (int) region.top; 1434 invalidate(left, top, left + (int) region.width(), top + (int) region.height()); 1435 } 1436 } 1437 } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == 1438 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) { 1439 if (mChildTransformation == null) { 1440 mChildTransformation = new Transformation(); 1441 } 1442 final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation); 1443 if (hasTransform) { 1444 final int transformType = mChildTransformation.getTransformationType(); 1445 transformToApply = transformType != Transformation.TYPE_IDENTITY ? 1446 mChildTransformation : null; 1447 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0; 1448 } 1449 } 1450 1451 // Sets the flag as early as possible to allow draw() implementations 1452 // to call invalidate() successfully when doing animations 1453 child.mPrivateFlags |= DRAWN; 1454 1455 if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && 1456 (child.mPrivateFlags & DRAW_ANIMATION) == 0) { 1457 return more; 1458 } 1459 1460 child.computeScroll(); 1461 1462 final int sx = child.mScrollX; 1463 final int sy = child.mScrollY; 1464 1465 boolean scalingRequired = false; 1466 Bitmap cache = null; 1467 if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || 1468 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { 1469 cache = child.getDrawingCache(true); 1470 if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; 1471 } 1472 1473 final boolean hasNoCache = cache == null; 1474 1475 final int restoreTo = canvas.save(); 1476 if (hasNoCache) { 1477 canvas.translate(cl - sx, ct - sy); 1478 } else { 1479 canvas.translate(cl, ct); 1480 if (scalingRequired) { 1481 // mAttachInfo cannot be null, otherwise scalingRequired == false 1482 final float scale = 1.0f / mAttachInfo.mApplicationScale; 1483 canvas.scale(scale, scale); 1484 } 1485 } 1486 1487 float alpha = 1.0f; 1488 1489 if (transformToApply != null) { 1490 if (concatMatrix) { 1491 int transX = 0; 1492 int transY = 0; 1493 if (hasNoCache) { 1494 transX = -sx; 1495 transY = -sy; 1496 } 1497 // Undo the scroll translation, apply the transformation matrix, 1498 // then redo the scroll translate to get the correct result. 1499 canvas.translate(-transX, -transY); 1500 canvas.concat(transformToApply.getMatrix()); 1501 canvas.translate(transX, transY); 1502 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 1503 } 1504 1505 alpha = transformToApply.getAlpha(); 1506 if (alpha < 1.0f) { 1507 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 1508 } 1509 1510 if (alpha < 1.0f && hasNoCache) { 1511 final int multipliedAlpha = (int) (255 * alpha); 1512 if (!child.onSetAlpha(multipliedAlpha)) { 1513 canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha, 1514 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); 1515 } else { 1516 child.mPrivateFlags |= ALPHA_SET; 1517 } 1518 } 1519 } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) { 1520 child.onSetAlpha(255); 1521 } 1522 1523 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 1524 if (hasNoCache) { 1525 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); 1526 } else { 1527 if (!scalingRequired) { 1528 canvas.clipRect(0, 0, cr - cl, cb - ct); 1529 } else { 1530 canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); 1531 } 1532 } 1533 } 1534 1535 if (hasNoCache) { 1536 // Fast path for layouts with no backgrounds 1537 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 1538 if (ViewDebug.TRACE_HIERARCHY) { 1539 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); 1540 } 1541 child.mPrivateFlags &= ~DIRTY_MASK; 1542 child.dispatchDraw(canvas); 1543 } else { 1544 child.draw(canvas); 1545 } 1546 } else { 1547 final Paint cachePaint = mCachePaint; 1548 if (alpha < 1.0f) { 1549 cachePaint.setAlpha((int) (alpha * 255)); 1550 mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE; 1551 } else if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) { 1552 cachePaint.setAlpha(255); 1553 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; 1554 } 1555 if (Config.DEBUG && ViewDebug.profileDrawing) { 1556 EventLog.writeEvent(60003, hashCode()); 1557 } 1558 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); 1559 } 1560 1561 canvas.restoreToCount(restoreTo); 1562 1563 if (a != null && !more) { 1564 child.onSetAlpha(255); 1565 finishAnimatingView(child, a); 1566 } 1567 1568 return more; 1569 } 1570 1571 /** 1572 * By default, children are clipped to their bounds before drawing. This 1573 * allows view groups to override this behavior for animations, etc. 1574 * 1575 * @param clipChildren true to clip children to their bounds, 1576 * false otherwise 1577 * @attr ref android.R.styleable#ViewGroup_clipChildren 1578 */ 1579 public void setClipChildren(boolean clipChildren) { 1580 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); 1581 } 1582 1583 /** 1584 * By default, children are clipped to the padding of the ViewGroup. This 1585 * allows view groups to override this behavior 1586 * 1587 * @param clipToPadding true to clip children to the padding of the 1588 * group, false otherwise 1589 * @attr ref android.R.styleable#ViewGroup_clipToPadding 1590 */ 1591 public void setClipToPadding(boolean clipToPadding) { 1592 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding); 1593 } 1594 1595 /** 1596 * {@inheritDoc} 1597 */ 1598 @Override 1599 public void dispatchSetSelected(boolean selected) { 1600 final View[] children = mChildren; 1601 final int count = mChildrenCount; 1602 for (int i = 0; i < count; i++) { 1603 children[i].setSelected(selected); 1604 } 1605 } 1606 1607 @Override 1608 protected void dispatchSetPressed(boolean pressed) { 1609 final View[] children = mChildren; 1610 final int count = mChildrenCount; 1611 for (int i = 0; i < count; i++) { 1612 children[i].setPressed(pressed); 1613 } 1614 } 1615 1616 /** 1617 * When this property is set to true, this ViewGroup supports static transformations on 1618 * children; this causes 1619 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 1620 * invoked when a child is drawn. 1621 * 1622 * Any subclass overriding 1623 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 1624 * set this property to true. 1625 * 1626 * @param enabled True to enable static transformations on children, false otherwise. 1627 * 1628 * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS 1629 */ 1630 protected void setStaticTransformationsEnabled(boolean enabled) { 1631 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled); 1632 } 1633 1634 /** 1635 * {@inheritDoc} 1636 * 1637 * @see #setStaticTransformationsEnabled(boolean) 1638 */ 1639 protected boolean getChildStaticTransformation(View child, Transformation t) { 1640 return false; 1641 } 1642 1643 /** 1644 * {@hide} 1645 */ 1646 @Override 1647 protected View findViewTraversal(int id) { 1648 if (id == mID) { 1649 return this; 1650 } 1651 1652 final View[] where = mChildren; 1653 final int len = mChildrenCount; 1654 1655 for (int i = 0; i < len; i++) { 1656 View v = where[i]; 1657 1658 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 1659 v = v.findViewById(id); 1660 1661 if (v != null) { 1662 return v; 1663 } 1664 } 1665 } 1666 1667 return null; 1668 } 1669 1670 /** 1671 * {@hide} 1672 */ 1673 @Override 1674 protected View findViewWithTagTraversal(Object tag) { 1675 if (tag != null && tag.equals(mTag)) { 1676 return this; 1677 } 1678 1679 final View[] where = mChildren; 1680 final int len = mChildrenCount; 1681 1682 for (int i = 0; i < len; i++) { 1683 View v = where[i]; 1684 1685 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 1686 v = v.findViewWithTag(tag); 1687 1688 if (v != null) { 1689 return v; 1690 } 1691 } 1692 } 1693 1694 return null; 1695 } 1696 1697 /** 1698 * Adds a child view. If no layout parameters are already set on the child, the 1699 * default parameters for this ViewGroup are set on the child. 1700 * 1701 * @param child the child view to add 1702 * 1703 * @see #generateDefaultLayoutParams() 1704 */ 1705 public void addView(View child) { 1706 addView(child, -1); 1707 } 1708 1709 /** 1710 * Adds a child view. If no layout parameters are already set on the child, the 1711 * default parameters for this ViewGroup are set on the child. 1712 * 1713 * @param child the child view to add 1714 * @param index the position at which to add the child 1715 * 1716 * @see #generateDefaultLayoutParams() 1717 */ 1718 public void addView(View child, int index) { 1719 LayoutParams params = child.getLayoutParams(); 1720 if (params == null) { 1721 params = generateDefaultLayoutParams(); 1722 if (params == null) { 1723 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); 1724 } 1725 } 1726 addView(child, index, params); 1727 } 1728 1729 /** 1730 * Adds a child view with this ViewGroup's default layout parameters and the 1731 * specified width and height. 1732 * 1733 * @param child the child view to add 1734 */ 1735 public void addView(View child, int width, int height) { 1736 final LayoutParams params = generateDefaultLayoutParams(); 1737 params.width = width; 1738 params.height = height; 1739 addView(child, -1, params); 1740 } 1741 1742 /** 1743 * Adds a child view with the specified layout parameters. 1744 * 1745 * @param child the child view to add 1746 * @param params the layout parameters to set on the child 1747 */ 1748 public void addView(View child, LayoutParams params) { 1749 addView(child, -1, params); 1750 } 1751 1752 /** 1753 * Adds a child view with the specified layout parameters. 1754 * 1755 * @param child the child view to add 1756 * @param index the position at which to add the child 1757 * @param params the layout parameters to set on the child 1758 */ 1759 public void addView(View child, int index, LayoutParams params) { 1760 if (DBG) { 1761 System.out.println(this + " addView"); 1762 } 1763 1764 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 1765 // therefore, we call requestLayout() on ourselves before, so that the child's request 1766 // will be blocked at our level 1767 requestLayout(); 1768 invalidate(); 1769 addViewInner(child, index, params, false); 1770 } 1771 1772 /** 1773 * {@inheritDoc} 1774 */ 1775 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 1776 if (!checkLayoutParams(params)) { 1777 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this); 1778 } 1779 if (view.mParent != this) { 1780 throw new IllegalArgumentException("Given view not a child of " + this); 1781 } 1782 view.setLayoutParams(params); 1783 } 1784 1785 /** 1786 * {@inheritDoc} 1787 */ 1788 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1789 return p != null; 1790 } 1791 1792 /** 1793 * Interface definition for a callback to be invoked when the hierarchy 1794 * within this view changed. The hierarchy changes whenever a child is added 1795 * to or removed from this view. 1796 */ 1797 public interface OnHierarchyChangeListener { 1798 /** 1799 * Called when a new child is added to a parent view. 1800 * 1801 * @param parent the view in which a child was added 1802 * @param child the new child view added in the hierarchy 1803 */ 1804 void onChildViewAdded(View parent, View child); 1805 1806 /** 1807 * Called when a child is removed from a parent view. 1808 * 1809 * @param parent the view from which the child was removed 1810 * @param child the child removed from the hierarchy 1811 */ 1812 void onChildViewRemoved(View parent, View child); 1813 } 1814 1815 /** 1816 * Register a callback to be invoked when a child is added to or removed 1817 * from this view. 1818 * 1819 * @param listener the callback to invoke on hierarchy change 1820 */ 1821 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 1822 mOnHierarchyChangeListener = listener; 1823 } 1824 1825 /** 1826 * Adds a view during layout. This is useful if in your onLayout() method, 1827 * you need to add more views (as does the list view for example). 1828 * 1829 * If index is negative, it means put it at the end of the list. 1830 * 1831 * @param child the view to add to the group 1832 * @param index the index at which the child must be added 1833 * @param params the layout parameters to associate with the child 1834 * @return true if the child was added, false otherwise 1835 */ 1836 protected boolean addViewInLayout(View child, int index, LayoutParams params) { 1837 return addViewInLayout(child, index, params, false); 1838 } 1839 1840 /** 1841 * Adds a view during layout. This is useful if in your onLayout() method, 1842 * you need to add more views (as does the list view for example). 1843 * 1844 * If index is negative, it means put it at the end of the list. 1845 * 1846 * @param child the view to add to the group 1847 * @param index the index at which the child must be added 1848 * @param params the layout parameters to associate with the child 1849 * @param preventRequestLayout if true, calling this method will not trigger a 1850 * layout request on child 1851 * @return true if the child was added, false otherwise 1852 */ 1853 protected boolean addViewInLayout(View child, int index, LayoutParams params, 1854 boolean preventRequestLayout) { 1855 child.mParent = null; 1856 addViewInner(child, index, params, preventRequestLayout); 1857 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; 1858 return true; 1859 } 1860 1861 /** 1862 * Prevents the specified child to be laid out during the next layout pass. 1863 * 1864 * @param child the child on which to perform the cleanup 1865 */ 1866 protected void cleanupLayoutState(View child) { 1867 child.mPrivateFlags &= ~View.FORCE_LAYOUT; 1868 } 1869 1870 private void addViewInner(View child, int index, LayoutParams params, 1871 boolean preventRequestLayout) { 1872 1873 if (child.getParent() != null) { 1874 throw new IllegalStateException("The specified child already has a parent. " + 1875 "You must call removeView() on the child's parent first."); 1876 } 1877 1878 if (!checkLayoutParams(params)) { 1879 params = generateLayoutParams(params); 1880 } 1881 1882 if (preventRequestLayout) { 1883 child.mLayoutParams = params; 1884 } else { 1885 child.setLayoutParams(params); 1886 } 1887 1888 if (index < 0) { 1889 index = mChildrenCount; 1890 } 1891 1892 addInArray(child, index); 1893 1894 // tell our children 1895 if (preventRequestLayout) { 1896 child.assignParent(this); 1897 } else { 1898 child.mParent = this; 1899 } 1900 1901 if (child.hasFocus()) { 1902 requestChildFocus(child, child.findFocus()); 1903 } 1904 1905 AttachInfo ai = mAttachInfo; 1906 if (ai != null) { 1907 boolean lastKeepOn = ai.mKeepScreenOn; 1908 ai.mKeepScreenOn = false; 1909 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 1910 if (ai.mKeepScreenOn) { 1911 needGlobalAttributesUpdate(true); 1912 } 1913 ai.mKeepScreenOn = lastKeepOn; 1914 } 1915 1916 if (mOnHierarchyChangeListener != null) { 1917 mOnHierarchyChangeListener.onChildViewAdded(this, child); 1918 } 1919 1920 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 1921 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 1922 } 1923 } 1924 1925 private void addInArray(View child, int index) { 1926 View[] children = mChildren; 1927 final int count = mChildrenCount; 1928 final int size = children.length; 1929 if (index == count) { 1930 if (size == count) { 1931 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 1932 System.arraycopy(children, 0, mChildren, 0, size); 1933 children = mChildren; 1934 } 1935 children[mChildrenCount++] = child; 1936 } else if (index < count) { 1937 if (size == count) { 1938 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 1939 System.arraycopy(children, 0, mChildren, 0, index); 1940 System.arraycopy(children, index, mChildren, index + 1, count - index); 1941 children = mChildren; 1942 } else { 1943 System.arraycopy(children, index, children, index + 1, count - index); 1944 } 1945 children[index] = child; 1946 mChildrenCount++; 1947 } else { 1948 throw new IndexOutOfBoundsException("index=" + index + " count=" + count); 1949 } 1950 } 1951 1952 // This method also sets the child's mParent to null 1953 private void removeFromArray(int index) { 1954 final View[] children = mChildren; 1955 children[index].mParent = null; 1956 final int count = mChildrenCount; 1957 if (index == count - 1) { 1958 children[--mChildrenCount] = null; 1959 } else if (index >= 0 && index < count) { 1960 System.arraycopy(children, index + 1, children, index, count - index - 1); 1961 children[--mChildrenCount] = null; 1962 } else { 1963 throw new IndexOutOfBoundsException(); 1964 } 1965 } 1966 1967 // This method also sets the children's mParent to null 1968 private void removeFromArray(int start, int count) { 1969 final View[] children = mChildren; 1970 final int childrenCount = mChildrenCount; 1971 1972 start = Math.max(0, start); 1973 final int end = Math.min(childrenCount, start + count); 1974 1975 if (start == end) { 1976 return; 1977 } 1978 1979 if (end == childrenCount) { 1980 for (int i = start; i < end; i++) { 1981 children[i].mParent = null; 1982 children[i] = null; 1983 } 1984 } else { 1985 for (int i = start; i < end; i++) { 1986 children[i].mParent = null; 1987 } 1988 1989 // Since we're looping above, we might as well do the copy, but is arraycopy() 1990 // faster than the extra 2 bounds checks we would do in the loop? 1991 System.arraycopy(children, end, children, start, childrenCount - end); 1992 1993 for (int i = childrenCount - (end - start); i < childrenCount; i++) { 1994 children[i] = null; 1995 } 1996 } 1997 1998 mChildrenCount -= (end - start); 1999 } 2000 2001 private void bindLayoutAnimation(View child) { 2002 Animation a = mLayoutAnimationController.getAnimationForView(child); 2003 child.setAnimation(a); 2004 } 2005 2006 /** 2007 * Subclasses should override this method to set layout animation 2008 * parameters on the supplied child. 2009 * 2010 * @param child the child to associate with animation parameters 2011 * @param params the child's layout parameters which hold the animation 2012 * parameters 2013 * @param index the index of the child in the view group 2014 * @param count the number of children in the view group 2015 */ 2016 protected void attachLayoutAnimationParameters(View child, 2017 LayoutParams params, int index, int count) { 2018 LayoutAnimationController.AnimationParameters animationParams = 2019 params.layoutAnimationParameters; 2020 if (animationParams == null) { 2021 animationParams = new LayoutAnimationController.AnimationParameters(); 2022 params.layoutAnimationParameters = animationParams; 2023 } 2024 2025 animationParams.count = count; 2026 animationParams.index = index; 2027 } 2028 2029 /** 2030 * {@inheritDoc} 2031 */ 2032 public void removeView(View view) { 2033 removeViewInternal(view); 2034 requestLayout(); 2035 invalidate(); 2036 } 2037 2038 /** 2039 * Removes a view during layout. This is useful if in your onLayout() method, 2040 * you need to remove more views. 2041 * 2042 * @param view the view to remove from the group 2043 */ 2044 public void removeViewInLayout(View view) { 2045 removeViewInternal(view); 2046 } 2047 2048 /** 2049 * Removes a range of views during layout. This is useful if in your onLayout() method, 2050 * you need to remove more views. 2051 * 2052 * @param start the index of the first view to remove from the group 2053 * @param count the number of views to remove from the group 2054 */ 2055 public void removeViewsInLayout(int start, int count) { 2056 removeViewsInternal(start, count); 2057 } 2058 2059 /** 2060 * Removes the view at the specified position in the group. 2061 * 2062 * @param index the position in the group of the view to remove 2063 */ 2064 public void removeViewAt(int index) { 2065 removeViewInternal(index, getChildAt(index)); 2066 requestLayout(); 2067 invalidate(); 2068 } 2069 2070 /** 2071 * Removes the specified range of views from the group. 2072 * 2073 * @param start the first position in the group of the range of views to remove 2074 * @param count the number of views to remove 2075 */ 2076 public void removeViews(int start, int count) { 2077 removeViewsInternal(start, count); 2078 requestLayout(); 2079 invalidate(); 2080 } 2081 2082 private void removeViewInternal(View view) { 2083 final int index = indexOfChild(view); 2084 if (index >= 0) { 2085 removeViewInternal(index, view); 2086 } 2087 } 2088 2089 private void removeViewInternal(int index, View view) { 2090 boolean clearChildFocus = false; 2091 if (view == mFocused) { 2092 view.clearFocusForRemoval(); 2093 clearChildFocus = true; 2094 } 2095 2096 if (view.getAnimation() != null) { 2097 addDisappearingView(view); 2098 } else if (view.mAttachInfo != null) { 2099 view.dispatchDetachedFromWindow(); 2100 } 2101 2102 if (mOnHierarchyChangeListener != null) { 2103 mOnHierarchyChangeListener.onChildViewRemoved(this, view); 2104 } 2105 2106 needGlobalAttributesUpdate(false); 2107 2108 removeFromArray(index); 2109 2110 if (clearChildFocus) { 2111 clearChildFocus(view); 2112 } 2113 } 2114 2115 private void removeViewsInternal(int start, int count) { 2116 final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener; 2117 final boolean notifyListener = onHierarchyChangeListener != null; 2118 final View focused = mFocused; 2119 final boolean detach = mAttachInfo != null; 2120 View clearChildFocus = null; 2121 2122 final View[] children = mChildren; 2123 final int end = start + count; 2124 2125 for (int i = start; i < end; i++) { 2126 final View view = children[i]; 2127 2128 if (view == focused) { 2129 view.clearFocusForRemoval(); 2130 clearChildFocus = view; 2131 } 2132 2133 if (view.getAnimation() != null) { 2134 addDisappearingView(view); 2135 } else if (detach) { 2136 view.dispatchDetachedFromWindow(); 2137 } 2138 2139 needGlobalAttributesUpdate(false); 2140 2141 if (notifyListener) { 2142 onHierarchyChangeListener.onChildViewRemoved(this, view); 2143 } 2144 } 2145 2146 removeFromArray(start, count); 2147 2148 if (clearChildFocus != null) { 2149 clearChildFocus(clearChildFocus); 2150 } 2151 } 2152 2153 /** 2154 * Call this method to remove all child views from the 2155 * ViewGroup. 2156 */ 2157 public void removeAllViews() { 2158 removeAllViewsInLayout(); 2159 requestLayout(); 2160 invalidate(); 2161 } 2162 2163 /** 2164 * Called by a ViewGroup subclass to remove child views from itself, 2165 * when it must first know its size on screen before it can calculate how many 2166 * child views it will render. An example is a Gallery or a ListView, which 2167 * may "have" 50 children, but actually only render the number of children 2168 * that can currently fit inside the object on screen. Do not call 2169 * this method unless you are extending ViewGroup and understand the 2170 * view measuring and layout pipeline. 2171 */ 2172 public void removeAllViewsInLayout() { 2173 final int count = mChildrenCount; 2174 if (count <= 0) { 2175 return; 2176 } 2177 2178 final View[] children = mChildren; 2179 mChildrenCount = 0; 2180 2181 final OnHierarchyChangeListener listener = mOnHierarchyChangeListener; 2182 final boolean notify = listener != null; 2183 final View focused = mFocused; 2184 final boolean detach = mAttachInfo != null; 2185 View clearChildFocus = null; 2186 2187 needGlobalAttributesUpdate(false); 2188 2189 for (int i = count - 1; i >= 0; i--) { 2190 final View view = children[i]; 2191 2192 if (view == focused) { 2193 view.clearFocusForRemoval(); 2194 clearChildFocus = view; 2195 } 2196 2197 if (view.getAnimation() != null) { 2198 addDisappearingView(view); 2199 } else if (detach) { 2200 view.dispatchDetachedFromWindow(); 2201 } 2202 2203 if (notify) { 2204 listener.onChildViewRemoved(this, view); 2205 } 2206 2207 view.mParent = null; 2208 children[i] = null; 2209 } 2210 2211 if (clearChildFocus != null) { 2212 clearChildFocus(clearChildFocus); 2213 } 2214 } 2215 2216 /** 2217 * Finishes the removal of a detached view. This method will dispatch the detached from 2218 * window event and notify the hierarchy change listener. 2219 * 2220 * @param child the child to be definitely removed from the view hierarchy 2221 * @param animate if true and the view has an animation, the view is placed in the 2222 * disappearing views list, otherwise, it is detached from the window 2223 * 2224 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 2225 * @see #detachAllViewsFromParent() 2226 * @see #detachViewFromParent(View) 2227 * @see #detachViewFromParent(int) 2228 */ 2229 protected void removeDetachedView(View child, boolean animate) { 2230 if (child == mFocused) { 2231 child.clearFocus(); 2232 } 2233 2234 if (animate && child.getAnimation() != null) { 2235 addDisappearingView(child); 2236 } else if (child.mAttachInfo != null) { 2237 child.dispatchDetachedFromWindow(); 2238 } 2239 2240 if (mOnHierarchyChangeListener != null) { 2241 mOnHierarchyChangeListener.onChildViewRemoved(this, child); 2242 } 2243 } 2244 2245 /** 2246 * Attaches a view to this view group. Attaching a view assigns this group as the parent, 2247 * sets the layout parameters and puts the view in the list of children so it can be retrieved 2248 * by calling {@link #getChildAt(int)}. 2249 * 2250 * This method should be called only for view which were detached from their parent. 2251 * 2252 * @param child the child to attach 2253 * @param index the index at which the child should be attached 2254 * @param params the layout parameters of the child 2255 * 2256 * @see #removeDetachedView(View, boolean) 2257 * @see #detachAllViewsFromParent() 2258 * @see #detachViewFromParent(View) 2259 * @see #detachViewFromParent(int) 2260 */ 2261 protected void attachViewToParent(View child, int index, LayoutParams params) { 2262 child.mLayoutParams = params; 2263 2264 if (index < 0) { 2265 index = mChildrenCount; 2266 } 2267 2268 addInArray(child, index); 2269 2270 child.mParent = this; 2271 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; 2272 2273 if (child.hasFocus()) { 2274 requestChildFocus(child, child.findFocus()); 2275 } 2276 } 2277 2278 /** 2279 * Detaches a view from its parent. Detaching a view should be temporary and followed 2280 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 2281 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 2282 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 2283 * 2284 * @param child the child to detach 2285 * 2286 * @see #detachViewFromParent(int) 2287 * @see #detachViewsFromParent(int, int) 2288 * @see #detachAllViewsFromParent() 2289 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 2290 * @see #removeDetachedView(View, boolean) 2291 */ 2292 protected void detachViewFromParent(View child) { 2293 removeFromArray(indexOfChild(child)); 2294 } 2295 2296 /** 2297 * Detaches a view from its parent. Detaching a view should be temporary and followed 2298 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 2299 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 2300 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 2301 * 2302 * @param index the index of the child to detach 2303 * 2304 * @see #detachViewFromParent(View) 2305 * @see #detachAllViewsFromParent() 2306 * @see #detachViewsFromParent(int, int) 2307 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 2308 * @see #removeDetachedView(View, boolean) 2309 */ 2310 protected void detachViewFromParent(int index) { 2311 removeFromArray(index); 2312 } 2313 2314 /** 2315 * Detaches a range of view from their parent. Detaching a view should be temporary and followed 2316 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 2317 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its 2318 * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 2319 * 2320 * @param start the first index of the childrend range to detach 2321 * @param count the number of children to detach 2322 * 2323 * @see #detachViewFromParent(View) 2324 * @see #detachViewFromParent(int) 2325 * @see #detachAllViewsFromParent() 2326 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 2327 * @see #removeDetachedView(View, boolean) 2328 */ 2329 protected void detachViewsFromParent(int start, int count) { 2330 removeFromArray(start, count); 2331 } 2332 2333 /** 2334 * Detaches all views from the parent. Detaching a view should be temporary and followed 2335 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 2336 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 2337 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 2338 * 2339 * @see #detachViewFromParent(View) 2340 * @see #detachViewFromParent(int) 2341 * @see #detachViewsFromParent(int, int) 2342 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 2343 * @see #removeDetachedView(View, boolean) 2344 */ 2345 protected void detachAllViewsFromParent() { 2346 final int count = mChildrenCount; 2347 if (count <= 0) { 2348 return; 2349 } 2350 2351 final View[] children = mChildren; 2352 mChildrenCount = 0; 2353 2354 for (int i = count - 1; i >= 0; i--) { 2355 children[i].mParent = null; 2356 children[i] = null; 2357 } 2358 } 2359 2360 /** 2361 * Don't call or override this method. It is used for the implementation of 2362 * the view hierarchy. 2363 */ 2364 public final void invalidateChild(View child, final Rect dirty) { 2365 if (ViewDebug.TRACE_HIERARCHY) { 2366 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD); 2367 } 2368 2369 ViewParent parent = this; 2370 2371 final AttachInfo attachInfo = mAttachInfo; 2372 if (attachInfo != null) { 2373 final int[] location = attachInfo.mInvalidateChildLocation; 2374 location[CHILD_LEFT_INDEX] = child.mLeft; 2375 location[CHILD_TOP_INDEX] = child.mTop; 2376 2377 // If the child is drawing an animation, we want to copy this flag onto 2378 // ourselves and the parent to make sure the invalidate request goes 2379 // through 2380 final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; 2381 2382 // Check whether the child that requests the invalidate is fully opaque 2383 final boolean isOpaque = child.isOpaque() && !drawAnimation && 2384 child.getAnimation() != null; 2385 // Mark the child as dirty, using the appropriate flag 2386 // Make sure we do not set both flags at the same time 2387 final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; 2388 2389 do { 2390 View view = null; 2391 if (parent instanceof View) { 2392 view = (View) parent; 2393 } 2394 2395 if (drawAnimation) { 2396 if (view != null) { 2397 view.mPrivateFlags |= DRAW_ANIMATION; 2398 } else if (parent instanceof ViewRoot) { 2399 ((ViewRoot) parent).mIsAnimating = true; 2400 } 2401 } 2402 2403 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 2404 // flag coming from the child that initiated the invalidate 2405 if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { 2406 view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; 2407 } 2408 2409 parent = parent.invalidateChildInParent(location, dirty); 2410 } while (parent != null); 2411 } 2412 } 2413 2414 /** 2415 * Don't call or override this method. It is used for the implementation of 2416 * the view hierarchy. 2417 * 2418 * This implementation returns null if this ViewGroup does not have a parent, 2419 * if this ViewGroup is already fully invalidated or if the dirty rectangle 2420 * does not intersect with this ViewGroup's bounds. 2421 */ 2422 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 2423 if (ViewDebug.TRACE_HIERARCHY) { 2424 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT); 2425 } 2426 2427 if ((mPrivateFlags & DRAWN) == DRAWN) { 2428 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != 2429 FLAG_OPTIMIZE_INVALIDATE) { 2430 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, 2431 location[CHILD_TOP_INDEX] - mScrollY); 2432 2433 final int left = mLeft; 2434 final int top = mTop; 2435 2436 if (dirty.intersect(0, 0, mRight - left, mBottom - top) || 2437 (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { 2438 mPrivateFlags &= ~DRAWING_CACHE_VALID; 2439 2440 location[CHILD_LEFT_INDEX] = left; 2441 location[CHILD_TOP_INDEX] = top; 2442 2443 return mParent; 2444 } 2445 } else { 2446 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID; 2447 2448 location[CHILD_LEFT_INDEX] = mLeft; 2449 location[CHILD_TOP_INDEX] = mTop; 2450 2451 dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX], 2452 mBottom - location[CHILD_TOP_INDEX]); 2453 2454 return mParent; 2455 } 2456 } 2457 2458 return null; 2459 } 2460 2461 /** 2462 * Offset a rectangle that is in a descendant's coordinate 2463 * space into our coordinate space. 2464 * @param descendant A descendant of this view 2465 * @param rect A rectangle defined in descendant's coordinate space. 2466 */ 2467 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { 2468 offsetRectBetweenParentAndChild(descendant, rect, true, false); 2469 } 2470 2471 /** 2472 * Offset a rectangle that is in our coordinate space into an ancestor's 2473 * coordinate space. 2474 * @param descendant A descendant of this view 2475 * @param rect A rectangle defined in descendant's coordinate space. 2476 */ 2477 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { 2478 offsetRectBetweenParentAndChild(descendant, rect, false, false); 2479 } 2480 2481 /** 2482 * Helper method that offsets a rect either from parent to descendant or 2483 * descendant to parent. 2484 */ 2485 void offsetRectBetweenParentAndChild(View descendant, Rect rect, 2486 boolean offsetFromChildToParent, boolean clipToBounds) { 2487 2488 // already in the same coord system :) 2489 if (descendant == this) { 2490 return; 2491 } 2492 2493 ViewParent theParent = descendant.mParent; 2494 2495 // search and offset up to the parent 2496 while ((theParent != null) 2497 && (theParent instanceof View) 2498 && (theParent != this)) { 2499 2500 if (offsetFromChildToParent) { 2501 rect.offset(descendant.mLeft - descendant.mScrollX, 2502 descendant.mTop - descendant.mScrollY); 2503 if (clipToBounds) { 2504 View p = (View) theParent; 2505 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 2506 } 2507 } else { 2508 if (clipToBounds) { 2509 View p = (View) theParent; 2510 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 2511 } 2512 rect.offset(descendant.mScrollX - descendant.mLeft, 2513 descendant.mScrollY - descendant.mTop); 2514 } 2515 2516 descendant = (View) theParent; 2517 theParent = descendant.mParent; 2518 } 2519 2520 // now that we are up to this view, need to offset one more time 2521 // to get into our coordinate space 2522 if (theParent == this) { 2523 if (offsetFromChildToParent) { 2524 rect.offset(descendant.mLeft - descendant.mScrollX, 2525 descendant.mTop - descendant.mScrollY); 2526 } else { 2527 rect.offset(descendant.mScrollX - descendant.mLeft, 2528 descendant.mScrollY - descendant.mTop); 2529 } 2530 } else { 2531 throw new IllegalArgumentException("parameter must be a descendant of this view"); 2532 } 2533 } 2534 2535 /** 2536 * Offset the vertical location of all children of this view by the specified number of pixels. 2537 * 2538 * @param offset the number of pixels to offset 2539 * 2540 * @hide 2541 */ 2542 public void offsetChildrenTopAndBottom(int offset) { 2543 final int count = mChildrenCount; 2544 final View[] children = mChildren; 2545 2546 for (int i = 0; i < count; i++) { 2547 final View v = children[i]; 2548 v.mTop += offset; 2549 v.mBottom += offset; 2550 } 2551 } 2552 2553 /** 2554 * {@inheritDoc} 2555 */ 2556 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 2557 int dx = child.mLeft - mScrollX; 2558 int dy = child.mTop - mScrollY; 2559 if (offset != null) { 2560 offset.x += dx; 2561 offset.y += dy; 2562 } 2563 r.offset(dx, dy); 2564 return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) && 2565 (mParent == null || mParent.getChildVisibleRect(this, r, offset)); 2566 } 2567 2568 /** 2569 * {@inheritDoc} 2570 */ 2571 @Override 2572 protected abstract void onLayout(boolean changed, 2573 int l, int t, int r, int b); 2574 2575 /** 2576 * Indicates whether the view group has the ability to animate its children 2577 * after the first layout. 2578 * 2579 * @return true if the children can be animated, false otherwise 2580 */ 2581 protected boolean canAnimate() { 2582 return mLayoutAnimationController != null; 2583 } 2584 2585 /** 2586 * Runs the layout animation. Calling this method triggers a relayout of 2587 * this view group. 2588 */ 2589 public void startLayoutAnimation() { 2590 if (mLayoutAnimationController != null) { 2591 mGroupFlags |= FLAG_RUN_ANIMATION; 2592 requestLayout(); 2593 } 2594 } 2595 2596 /** 2597 * Schedules the layout animation to be played after the next layout pass 2598 * of this view group. This can be used to restart the layout animation 2599 * when the content of the view group changes or when the activity is 2600 * paused and resumed. 2601 */ 2602 public void scheduleLayoutAnimation() { 2603 mGroupFlags |= FLAG_RUN_ANIMATION; 2604 } 2605 2606 /** 2607 * Sets the layout animation controller used to animate the group's 2608 * children after the first layout. 2609 * 2610 * @param controller the animation controller 2611 */ 2612 public void setLayoutAnimation(LayoutAnimationController controller) { 2613 mLayoutAnimationController = controller; 2614 if (mLayoutAnimationController != null) { 2615 mGroupFlags |= FLAG_RUN_ANIMATION; 2616 } 2617 } 2618 2619 /** 2620 * Returns the layout animation controller used to animate the group's 2621 * children. 2622 * 2623 * @return the current animation controller 2624 */ 2625 public LayoutAnimationController getLayoutAnimation() { 2626 return mLayoutAnimationController; 2627 } 2628 2629 /** 2630 * Indicates whether the children's drawing cache is used during a layout 2631 * animation. By default, the drawing cache is enabled but this will prevent 2632 * nested layout animations from working. To nest animations, you must disable 2633 * the cache. 2634 * 2635 * @return true if the animation cache is enabled, false otherwise 2636 * 2637 * @see #setAnimationCacheEnabled(boolean) 2638 * @see View#setDrawingCacheEnabled(boolean) 2639 */ 2640 @ViewDebug.ExportedProperty 2641 public boolean isAnimationCacheEnabled() { 2642 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 2643 } 2644 2645 /** 2646 * Enables or disables the children's drawing cache during a layout animation. 2647 * By default, the drawing cache is enabled but this will prevent nested 2648 * layout animations from working. To nest animations, you must disable the 2649 * cache. 2650 * 2651 * @param enabled true to enable the animation cache, false otherwise 2652 * 2653 * @see #isAnimationCacheEnabled() 2654 * @see View#setDrawingCacheEnabled(boolean) 2655 */ 2656 public void setAnimationCacheEnabled(boolean enabled) { 2657 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); 2658 } 2659 2660 /** 2661 * Indicates whether this ViewGroup will always try to draw its children using their 2662 * drawing cache. By default this property is enabled. 2663 * 2664 * @return true if the animation cache is enabled, false otherwise 2665 * 2666 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 2667 * @see #setChildrenDrawnWithCacheEnabled(boolean) 2668 * @see View#setDrawingCacheEnabled(boolean) 2669 */ 2670 @ViewDebug.ExportedProperty 2671 public boolean isAlwaysDrawnWithCacheEnabled() { 2672 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; 2673 } 2674 2675 /** 2676 * Indicates whether this ViewGroup will always try to draw its children using their 2677 * drawing cache. This property can be set to true when the cache rendering is 2678 * slightly different from the children's normal rendering. Renderings can be different, 2679 * for instance, when the cache's quality is set to low. 2680 * 2681 * When this property is disabled, the ViewGroup will use the drawing cache of its 2682 * children only when asked to. It's usually the task of subclasses to tell ViewGroup 2683 * when to start using the drawing cache and when to stop using it. 2684 * 2685 * @param always true to always draw with the drawing cache, false otherwise 2686 * 2687 * @see #isAlwaysDrawnWithCacheEnabled() 2688 * @see #setChildrenDrawnWithCacheEnabled(boolean) 2689 * @see View#setDrawingCacheEnabled(boolean) 2690 * @see View#setDrawingCacheQuality(int) 2691 */ 2692 public void setAlwaysDrawnWithCacheEnabled(boolean always) { 2693 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); 2694 } 2695 2696 /** 2697 * Indicates whether the ViewGroup is currently drawing its children using 2698 * their drawing cache. 2699 * 2700 * @return true if children should be drawn with their cache, false otherwise 2701 * 2702 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 2703 * @see #setChildrenDrawnWithCacheEnabled(boolean) 2704 */ 2705 @ViewDebug.ExportedProperty 2706 protected boolean isChildrenDrawnWithCacheEnabled() { 2707 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; 2708 } 2709 2710 /** 2711 * Tells the ViewGroup to draw its children using their drawing cache. This property 2712 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache 2713 * will be used only if it has been enabled. 2714 * 2715 * Subclasses should call this method to start and stop using the drawing cache when 2716 * they perform performance sensitive operations, like scrolling or animating. 2717 * 2718 * @param enabled true if children should be drawn with their cache, false otherwise 2719 * 2720 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 2721 * @see #isChildrenDrawnWithCacheEnabled() 2722 */ 2723 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { 2724 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); 2725 } 2726 2727 /** 2728 * Indicates whether the ViewGroup is drawing its children in the order defined by 2729 * {@link #getChildDrawingOrder(int, int)}. 2730 * 2731 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, 2732 * false otherwise 2733 * 2734 * @see #setChildrenDrawingOrderEnabled(boolean) 2735 * @see #getChildDrawingOrder(int, int) 2736 */ 2737 @ViewDebug.ExportedProperty 2738 protected boolean isChildrenDrawingOrderEnabled() { 2739 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; 2740 } 2741 2742 /** 2743 * Tells the ViewGroup whether to draw its children in the order defined by the method 2744 * {@link #getChildDrawingOrder(int, int)}. 2745 * 2746 * @param enabled true if the order of the children when drawing is determined by 2747 * {@link #getChildDrawingOrder(int, int)}, false otherwise 2748 * 2749 * @see #isChildrenDrawingOrderEnabled() 2750 * @see #getChildDrawingOrder(int, int) 2751 */ 2752 protected void setChildrenDrawingOrderEnabled(boolean enabled) { 2753 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); 2754 } 2755 2756 private void setBooleanFlag(int flag, boolean value) { 2757 if (value) { 2758 mGroupFlags |= flag; 2759 } else { 2760 mGroupFlags &= ~flag; 2761 } 2762 } 2763 2764 /** 2765 * Returns an integer indicating what types of drawing caches are kept in memory. 2766 * 2767 * @see #setPersistentDrawingCache(int) 2768 * @see #setAnimationCacheEnabled(boolean) 2769 * 2770 * @return one or a combination of {@link #PERSISTENT_NO_CACHE}, 2771 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 2772 * and {@link #PERSISTENT_ALL_CACHES} 2773 */ 2774 @ViewDebug.ExportedProperty(mapping = { 2775 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), 2776 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ANIMATION"), 2777 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), 2778 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") 2779 }) 2780 public int getPersistentDrawingCache() { 2781 return mPersistentDrawingCache; 2782 } 2783 2784 /** 2785 * Indicates what types of drawing caches should be kept in memory after 2786 * they have been created. 2787 * 2788 * @see #getPersistentDrawingCache() 2789 * @see #setAnimationCacheEnabled(boolean) 2790 * 2791 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE}, 2792 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 2793 * and {@link #PERSISTENT_ALL_CACHES} 2794 */ 2795 public void setPersistentDrawingCache(int drawingCacheToKeep) { 2796 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; 2797 } 2798 2799 /** 2800 * Returns a new set of layout parameters based on the supplied attributes set. 2801 * 2802 * @param attrs the attributes to build the layout parameters from 2803 * 2804 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 2805 * of its descendants 2806 */ 2807 public LayoutParams generateLayoutParams(AttributeSet attrs) { 2808 return new LayoutParams(getContext(), attrs); 2809 } 2810 2811 /** 2812 * Returns a safe set of layout parameters based on the supplied layout params. 2813 * When a ViewGroup is passed a View whose layout params do not pass the test of 2814 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method 2815 * is invoked. This method should return a new set of layout params suitable for 2816 * this ViewGroup, possibly by copying the appropriate attributes from the 2817 * specified set of layout params. 2818 * 2819 * @param p The layout parameters to convert into a suitable set of layout parameters 2820 * for this ViewGroup. 2821 * 2822 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 2823 * of its descendants 2824 */ 2825 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 2826 return p; 2827 } 2828 2829 /** 2830 * Returns a set of default layout parameters. These parameters are requested 2831 * when the View passed to {@link #addView(View)} has no layout parameters 2832 * already set. If null is returned, an exception is thrown from addView. 2833 * 2834 * @return a set of default layout parameters or null 2835 */ 2836 protected LayoutParams generateDefaultLayoutParams() { 2837 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 2838 } 2839 2840 /** 2841 * @hide 2842 */ 2843 @Override 2844 protected boolean dispatchConsistencyCheck(int consistency) { 2845 boolean result = super.dispatchConsistencyCheck(consistency); 2846 2847 final int count = mChildrenCount; 2848 final View[] children = mChildren; 2849 for (int i = 0; i < count; i++) { 2850 if (!children[i].dispatchConsistencyCheck(consistency)) result = false; 2851 } 2852 2853 return result; 2854 } 2855 2856 /** 2857 * @hide 2858 */ 2859 @Override 2860 protected boolean onConsistencyCheck(int consistency) { 2861 boolean result = super.onConsistencyCheck(consistency); 2862 2863 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; 2864 final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; 2865 2866 if (checkLayout) { 2867 final int count = mChildrenCount; 2868 final View[] children = mChildren; 2869 for (int i = 0; i < count; i++) { 2870 if (children[i].getParent() != this) { 2871 result = false; 2872 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 2873 "View " + children[i] + " has no parent/a parent that is not " + this); 2874 } 2875 } 2876 } 2877 2878 if (checkDrawing) { 2879 // If this group is dirty, check that the parent is dirty as well 2880 if ((mPrivateFlags & DIRTY_MASK) != 0) { 2881 final ViewParent parent = getParent(); 2882 if (parent != null && !(parent instanceof ViewRoot)) { 2883 if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { 2884 result = false; 2885 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 2886 "ViewGroup " + this + " is dirty but its parent is not: " + this); 2887 } 2888 } 2889 } 2890 } 2891 2892 return result; 2893 } 2894 2895 /** 2896 * {@inheritDoc} 2897 */ 2898 @Override 2899 protected void debug(int depth) { 2900 super.debug(depth); 2901 String output; 2902 2903 if (mFocused != null) { 2904 output = debugIndent(depth); 2905 output += "mFocused"; 2906 Log.d(VIEW_LOG_TAG, output); 2907 } 2908 if (mChildrenCount != 0) { 2909 output = debugIndent(depth); 2910 output += "{"; 2911 Log.d(VIEW_LOG_TAG, output); 2912 } 2913 int count = mChildrenCount; 2914 for (int i = 0; i < count; i++) { 2915 View child = mChildren[i]; 2916 child.debug(depth + 1); 2917 } 2918 2919 if (mChildrenCount != 0) { 2920 output = debugIndent(depth); 2921 output += "}"; 2922 Log.d(VIEW_LOG_TAG, output); 2923 } 2924 } 2925 2926 /** 2927 * Returns the position in the group of the specified child view. 2928 * 2929 * @param child the view for which to get the position 2930 * @return a positive integer representing the position of the view in the 2931 * group, or -1 if the view does not exist in the group 2932 */ 2933 public int indexOfChild(View child) { 2934 final int count = mChildrenCount; 2935 final View[] children = mChildren; 2936 for (int i = 0; i < count; i++) { 2937 if (children[i] == child) { 2938 return i; 2939 } 2940 } 2941 return -1; 2942 } 2943 2944 /** 2945 * Returns the number of children in the group. 2946 * 2947 * @return a positive integer representing the number of children in 2948 * the group 2949 */ 2950 public int getChildCount() { 2951 return mChildrenCount; 2952 } 2953 2954 /** 2955 * Returns the view at the specified position in the group. 2956 * 2957 * @param index the position at which to get the view from 2958 * @return the view at the specified position or null if the position 2959 * does not exist within the group 2960 */ 2961 public View getChildAt(int index) { 2962 try { 2963 return mChildren[index]; 2964 } catch (IndexOutOfBoundsException ex) { 2965 return null; 2966 } 2967 } 2968 2969 /** 2970 * Ask all of the children of this view to measure themselves, taking into 2971 * account both the MeasureSpec requirements for this view and its padding. 2972 * We skip children that are in the GONE state The heavy lifting is done in 2973 * getChildMeasureSpec. 2974 * 2975 * @param widthMeasureSpec The width requirements for this view 2976 * @param heightMeasureSpec The height requirements for this view 2977 */ 2978 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 2979 final int size = mChildrenCount; 2980 final View[] children = mChildren; 2981 for (int i = 0; i < size; ++i) { 2982 final View child = children[i]; 2983 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 2984 measureChild(child, widthMeasureSpec, heightMeasureSpec); 2985 } 2986 } 2987 } 2988 2989 /** 2990 * Ask one of the children of this view to measure itself, taking into 2991 * account both the MeasureSpec requirements for this view and its padding. 2992 * The heavy lifting is done in getChildMeasureSpec. 2993 * 2994 * @param child The child to measure 2995 * @param parentWidthMeasureSpec The width requirements for this view 2996 * @param parentHeightMeasureSpec The height requirements for this view 2997 */ 2998 protected void measureChild(View child, int parentWidthMeasureSpec, 2999 int parentHeightMeasureSpec) { 3000 final LayoutParams lp = child.getLayoutParams(); 3001 3002 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 3003 mPaddingLeft + mPaddingRight, lp.width); 3004 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 3005 mPaddingTop + mPaddingBottom, lp.height); 3006 3007 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 3008 } 3009 3010 /** 3011 * Ask one of the children of this view to measure itself, taking into 3012 * account both the MeasureSpec requirements for this view and its padding 3013 * and margins. The child must have MarginLayoutParams The heavy lifting is 3014 * done in getChildMeasureSpec. 3015 * 3016 * @param child The child to measure 3017 * @param parentWidthMeasureSpec The width requirements for this view 3018 * @param widthUsed Extra space that has been used up by the parent 3019 * horizontally (possibly by other children of the parent) 3020 * @param parentHeightMeasureSpec The height requirements for this view 3021 * @param heightUsed Extra space that has been used up by the parent 3022 * vertically (possibly by other children of the parent) 3023 */ 3024 protected void measureChildWithMargins(View child, 3025 int parentWidthMeasureSpec, int widthUsed, 3026 int parentHeightMeasureSpec, int heightUsed) { 3027 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 3028 3029 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 3030 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 3031 + widthUsed, lp.width); 3032 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 3033 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 3034 + heightUsed, lp.height); 3035 3036 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 3037 } 3038 3039 /** 3040 * Does the hard part of measureChildren: figuring out the MeasureSpec to 3041 * pass to a particular child. This method figures out the right MeasureSpec 3042 * for one dimension (height or width) of one child view. 3043 * 3044 * The goal is to combine information from our MeasureSpec with the 3045 * LayoutParams of the child to get the best possible results. For example, 3046 * if the this view knows its size (because its MeasureSpec has a mode of 3047 * EXACTLY), and the child has indicated in its LayoutParams that it wants 3048 * to be the same size as the parent, the parent should ask the child to 3049 * layout given an exact size. 3050 * 3051 * @param spec The requirements for this view 3052 * @param padding The padding of this view for the current dimension and 3053 * margins, if applicable 3054 * @param childDimension How big the child wants to be in the current 3055 * dimension 3056 * @return a MeasureSpec integer for the child 3057 */ 3058 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 3059 int specMode = MeasureSpec.getMode(spec); 3060 int specSize = MeasureSpec.getSize(spec); 3061 3062 int size = Math.max(0, specSize - padding); 3063 3064 int resultSize = 0; 3065 int resultMode = 0; 3066 3067 switch (specMode) { 3068 // Parent has imposed an exact size on us 3069 case MeasureSpec.EXACTLY: 3070 if (childDimension >= 0) { 3071 resultSize = childDimension; 3072 resultMode = MeasureSpec.EXACTLY; 3073 } else if (childDimension == LayoutParams.FILL_PARENT) { 3074 // Child wants to be our size. So be it. 3075 resultSize = size; 3076 resultMode = MeasureSpec.EXACTLY; 3077 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 3078 // Child wants to determine its own size. It can't be 3079 // bigger than us. 3080 resultSize = size; 3081 resultMode = MeasureSpec.AT_MOST; 3082 } 3083 break; 3084 3085 // Parent has imposed a maximum size on us 3086 case MeasureSpec.AT_MOST: 3087 if (childDimension >= 0) { 3088 // Child wants a specific size... so be it 3089 resultSize = childDimension; 3090 resultMode = MeasureSpec.EXACTLY; 3091 } else if (childDimension == LayoutParams.FILL_PARENT) { 3092 // Child wants to be our size, but our size is not fixed. 3093 // Constrain child to not be bigger than us. 3094 resultSize = size; 3095 resultMode = MeasureSpec.AT_MOST; 3096 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 3097 // Child wants to determine its own size. It can't be 3098 // bigger than us. 3099 resultSize = size; 3100 resultMode = MeasureSpec.AT_MOST; 3101 } 3102 break; 3103 3104 // Parent asked to see how big we want to be 3105 case MeasureSpec.UNSPECIFIED: 3106 if (childDimension >= 0) { 3107 // Child wants a specific size... let him have it 3108 resultSize = childDimension; 3109 resultMode = MeasureSpec.EXACTLY; 3110 } else if (childDimension == LayoutParams.FILL_PARENT) { 3111 // Child wants to be our size... find out how big it should 3112 // be 3113 resultSize = 0; 3114 resultMode = MeasureSpec.UNSPECIFIED; 3115 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 3116 // Child wants to determine its own size.... find out how 3117 // big it should be 3118 resultSize = 0; 3119 resultMode = MeasureSpec.UNSPECIFIED; 3120 } 3121 break; 3122 } 3123 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 3124 } 3125 3126 3127 /** 3128 * Removes any pending animations for views that have been removed. Call 3129 * this if you don't want animations for exiting views to stack up. 3130 */ 3131 public void clearDisappearingChildren() { 3132 if (mDisappearingChildren != null) { 3133 mDisappearingChildren.clear(); 3134 } 3135 } 3136 3137 /** 3138 * Add a view which is removed from mChildren but still needs animation 3139 * 3140 * @param v View to add 3141 */ 3142 private void addDisappearingView(View v) { 3143 ArrayList<View> disappearingChildren = mDisappearingChildren; 3144 3145 if (disappearingChildren == null) { 3146 disappearingChildren = mDisappearingChildren = new ArrayList<View>(); 3147 } 3148 3149 disappearingChildren.add(v); 3150 } 3151 3152 /** 3153 * Cleanup a view when its animation is done. This may mean removing it from 3154 * the list of disappearing views. 3155 * 3156 * @param view The view whose animation has finished 3157 * @param animation The animation, cannot be null 3158 */ 3159 private void finishAnimatingView(final View view, Animation animation) { 3160 final ArrayList<View> disappearingChildren = mDisappearingChildren; 3161 if (disappearingChildren != null) { 3162 if (disappearingChildren.contains(view)) { 3163 disappearingChildren.remove(view); 3164 3165 if (view.mAttachInfo != null) { 3166 view.dispatchDetachedFromWindow(); 3167 } 3168 3169 view.clearAnimation(); 3170 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 3171 } 3172 } 3173 3174 if (animation != null && !animation.getFillAfter()) { 3175 view.clearAnimation(); 3176 } 3177 3178 if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) { 3179 view.onAnimationEnd(); 3180 // Should be performed by onAnimationEnd() but this avoid an infinite loop, 3181 // so we'd rather be safe than sorry 3182 view.mPrivateFlags &= ~ANIMATION_STARTED; 3183 // Draw one more frame after the animation is done 3184 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 3185 } 3186 } 3187 3188 /** 3189 * {@inheritDoc} 3190 */ 3191 @Override 3192 public boolean gatherTransparentRegion(Region region) { 3193 // If no transparent regions requested, we are always opaque. 3194 final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0; 3195 if (meOpaque && region == null) { 3196 // The caller doesn't care about the region, so stop now. 3197 return true; 3198 } 3199 super.gatherTransparentRegion(region); 3200 final View[] children = mChildren; 3201 final int count = mChildrenCount; 3202 boolean noneOfTheChildrenAreTransparent = true; 3203 for (int i = 0; i < count; i++) { 3204 final View child = children[i]; 3205 if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) { 3206 if (!child.gatherTransparentRegion(region)) { 3207 noneOfTheChildrenAreTransparent = false; 3208 } 3209 } 3210 } 3211 return meOpaque || noneOfTheChildrenAreTransparent; 3212 } 3213 3214 /** 3215 * {@inheritDoc} 3216 */ 3217 public void requestTransparentRegion(View child) { 3218 if (child != null) { 3219 child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS; 3220 if (mParent != null) { 3221 mParent.requestTransparentRegion(this); 3222 } 3223 } 3224 } 3225 3226 3227 @Override 3228 protected boolean fitSystemWindows(Rect insets) { 3229 boolean done = super.fitSystemWindows(insets); 3230 if (!done) { 3231 final int count = mChildrenCount; 3232 final View[] children = mChildren; 3233 for (int i = 0; i < count; i++) { 3234 done = children[i].fitSystemWindows(insets); 3235 if (done) { 3236 break; 3237 } 3238 } 3239 } 3240 return done; 3241 } 3242 3243 /** 3244 * Returns the animation listener to which layout animation events are 3245 * sent. 3246 * 3247 * @return an {@link android.view.animation.Animation.AnimationListener} 3248 */ 3249 public Animation.AnimationListener getLayoutAnimationListener() { 3250 return mAnimationListener; 3251 } 3252 3253 @Override 3254 protected void drawableStateChanged() { 3255 super.drawableStateChanged(); 3256 3257 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { 3258 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 3259 throw new IllegalStateException("addStateFromChildren cannot be enabled if a" 3260 + " child has duplicateParentState set to true"); 3261 } 3262 3263 final View[] children = mChildren; 3264 final int count = mChildrenCount; 3265 3266 for (int i = 0; i < count; i++) { 3267 final View child = children[i]; 3268 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { 3269 child.refreshDrawableState(); 3270 } 3271 } 3272 } 3273 } 3274 3275 @Override 3276 protected int[] onCreateDrawableState(int extraSpace) { 3277 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { 3278 return super.onCreateDrawableState(extraSpace); 3279 } 3280 3281 int need = 0; 3282 int n = getChildCount(); 3283 for (int i = 0; i < n; i++) { 3284 int[] childState = getChildAt(i).getDrawableState(); 3285 3286 if (childState != null) { 3287 need += childState.length; 3288 } 3289 } 3290 3291 int[] state = super.onCreateDrawableState(extraSpace + need); 3292 3293 for (int i = 0; i < n; i++) { 3294 int[] childState = getChildAt(i).getDrawableState(); 3295 3296 if (childState != null) { 3297 state = mergeDrawableStates(state, childState); 3298 } 3299 } 3300 3301 return state; 3302 } 3303 3304 /** 3305 * Sets whether this ViewGroup's drawable states also include 3306 * its children's drawable states. This is used, for example, to 3307 * make a group appear to be focused when its child EditText or button 3308 * is focused. 3309 */ 3310 public void setAddStatesFromChildren(boolean addsStates) { 3311 if (addsStates) { 3312 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN; 3313 } else { 3314 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN; 3315 } 3316 3317 refreshDrawableState(); 3318 } 3319 3320 /** 3321 * Returns whether this ViewGroup's drawable states also include 3322 * its children's drawable states. This is used, for example, to 3323 * make a group appear to be focused when its child EditText or button 3324 * is focused. 3325 */ 3326 public boolean addStatesFromChildren() { 3327 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0; 3328 } 3329 3330 /** 3331 * If {link #addStatesFromChildren} is true, refreshes this group's 3332 * drawable state (to include the states from its children). 3333 */ 3334 public void childDrawableStateChanged(View child) { 3335 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 3336 refreshDrawableState(); 3337 } 3338 } 3339 3340 /** 3341 * Specifies the animation listener to which layout animation events must 3342 * be sent. Only 3343 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)} 3344 * and 3345 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)} 3346 * are invoked. 3347 * 3348 * @param animationListener the layout animation listener 3349 */ 3350 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { 3351 mAnimationListener = animationListener; 3352 } 3353 3354 /** 3355 * LayoutParams are used by views to tell their parents how they want to be 3356 * laid out. See 3357 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 3358 * for a list of all child view attributes that this class supports. 3359 * 3360 * <p> 3361 * The base LayoutParams class just describes how big the view wants to be 3362 * for both width and height. For each dimension, it can specify one of: 3363 * <ul> 3364 * <li> an exact number 3365 * <li>FILL_PARENT, which means the view wants to be as big as its parent 3366 * (minus padding) 3367 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 3368 * to enclose its content (plus padding) 3369 * </ul> 3370 * There are subclasses of LayoutParams for different subclasses of 3371 * ViewGroup. For example, AbsoluteLayout has its own subclass of 3372 * LayoutParams which adds an X and Y value. 3373 * 3374 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 3375 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 3376 */ 3377 public static class LayoutParams { 3378 /** 3379 * Special value for the height or width requested by a View. 3380 * FILL_PARENT means that the view wants to fill the available space 3381 * within the parent, taking the parent's padding into account. 3382 */ 3383 public static final int FILL_PARENT = -1; 3384 3385 /** 3386 * Special value for the height or width requested by a View. 3387 * WRAP_CONTENT means that the view wants to be just large enough to fit 3388 * its own internal content, taking its own padding into account. 3389 */ 3390 public static final int WRAP_CONTENT = -2; 3391 3392 /** 3393 * Information about how wide the view wants to be. Can be an exact 3394 * size, or one of the constants FILL_PARENT or WRAP_CONTENT. 3395 */ 3396 @ViewDebug.ExportedProperty(mapping = { 3397 @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"), 3398 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 3399 }) 3400 public int width; 3401 3402 /** 3403 * Information about how tall the view wants to be. Can be an exact 3404 * size, or one of the constants FILL_PARENT or WRAP_CONTENT. 3405 */ 3406 @ViewDebug.ExportedProperty(mapping = { 3407 @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"), 3408 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 3409 }) 3410 public int height; 3411 3412 /** 3413 * Used to animate layouts. 3414 */ 3415 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 3416 3417 /** 3418 * Creates a new set of layout parameters. The values are extracted from 3419 * the supplied attributes set and context. The XML attributes mapped 3420 * to this set of layout parameters are: 3421 * 3422 * <ul> 3423 * <li><code>layout_width</code>: the width, either an exact value, 3424 * {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li> 3425 * <li><code>layout_height</code>: the height, either an exact value, 3426 * {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li> 3427 * </ul> 3428 * 3429 * @param c the application environment 3430 * @param attrs the set of attributes from which to extract the layout 3431 * parameters' values 3432 */ 3433 public LayoutParams(Context c, AttributeSet attrs) { 3434 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 3435 setBaseAttributes(a, 3436 R.styleable.ViewGroup_Layout_layout_width, 3437 R.styleable.ViewGroup_Layout_layout_height); 3438 a.recycle(); 3439 } 3440 3441 /** 3442 * Creates a new set of layout parameters with the specified width 3443 * and height. 3444 * 3445 * @param width the width, either {@link #FILL_PARENT}, 3446 * {@link #WRAP_CONTENT} or a fixed size in pixels 3447 * @param height the height, either {@link #FILL_PARENT}, 3448 * {@link #WRAP_CONTENT} or a fixed size in pixels 3449 */ 3450 public LayoutParams(int width, int height) { 3451 this.width = width; 3452 this.height = height; 3453 } 3454 3455 /** 3456 * Copy constructor. Clones the width and height values of the source. 3457 * 3458 * @param source The layout params to copy from. 3459 */ 3460 public LayoutParams(LayoutParams source) { 3461 this.width = source.width; 3462 this.height = source.height; 3463 } 3464 3465 /** 3466 * Used internally by MarginLayoutParams. 3467 * @hide 3468 */ 3469 LayoutParams() { 3470 } 3471 3472 /** 3473 * Extracts the layout parameters from the supplied attributes. 3474 * 3475 * @param a the style attributes to extract the parameters from 3476 * @param widthAttr the identifier of the width attribute 3477 * @param heightAttr the identifier of the height attribute 3478 */ 3479 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 3480 width = a.getLayoutDimension(widthAttr, "layout_width"); 3481 height = a.getLayoutDimension(heightAttr, "layout_height"); 3482 } 3483 3484 /** 3485 * Returns a String representation of this set of layout parameters. 3486 * 3487 * @param output the String to prepend to the internal representation 3488 * @return a String with the following format: output + 3489 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 3490 * 3491 * @hide 3492 */ 3493 public String debug(String output) { 3494 return output + "ViewGroup.LayoutParams={ width=" 3495 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 3496 } 3497 3498 /** 3499 * Converts the specified size to a readable String. 3500 * 3501 * @param size the size to convert 3502 * @return a String instance representing the supplied size 3503 * 3504 * @hide 3505 */ 3506 protected static String sizeToString(int size) { 3507 if (size == WRAP_CONTENT) { 3508 return "wrap-content"; 3509 } 3510 if (size == FILL_PARENT) { 3511 return "fill-parent"; 3512 } 3513 return String.valueOf(size); 3514 } 3515 } 3516 3517 /** 3518 * Per-child layout information for layouts that support margins. 3519 * See 3520 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes} 3521 * for a list of all child view attributes that this class supports. 3522 */ 3523 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 3524 /** 3525 * The left margin in pixels of the child. 3526 */ 3527 @ViewDebug.ExportedProperty 3528 public int leftMargin; 3529 3530 /** 3531 * The top margin in pixels of the child. 3532 */ 3533 @ViewDebug.ExportedProperty 3534 public int topMargin; 3535 3536 /** 3537 * The right margin in pixels of the child. 3538 */ 3539 @ViewDebug.ExportedProperty 3540 public int rightMargin; 3541 3542 /** 3543 * The bottom margin in pixels of the child. 3544 */ 3545 @ViewDebug.ExportedProperty 3546 public int bottomMargin; 3547 3548 /** 3549 * Creates a new set of layout parameters. The values are extracted from 3550 * the supplied attributes set and context. 3551 * 3552 * @param c the application environment 3553 * @param attrs the set of attributes from which to extract the layout 3554 * parameters' values 3555 */ 3556 public MarginLayoutParams(Context c, AttributeSet attrs) { 3557 super(); 3558 3559 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); 3560 setBaseAttributes(a, 3561 R.styleable.ViewGroup_MarginLayout_layout_width, 3562 R.styleable.ViewGroup_MarginLayout_layout_height); 3563 3564 int margin = a.getDimensionPixelSize( 3565 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); 3566 if (margin >= 0) { 3567 leftMargin = margin; 3568 topMargin = margin; 3569 rightMargin= margin; 3570 bottomMargin = margin; 3571 } else { 3572 leftMargin = a.getDimensionPixelSize( 3573 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0); 3574 topMargin = a.getDimensionPixelSize( 3575 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0); 3576 rightMargin = a.getDimensionPixelSize( 3577 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0); 3578 bottomMargin = a.getDimensionPixelSize( 3579 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0); 3580 } 3581 3582 a.recycle(); 3583 } 3584 3585 /** 3586 * {@inheritDoc} 3587 */ 3588 public MarginLayoutParams(int width, int height) { 3589 super(width, height); 3590 } 3591 3592 /** 3593 * Copy constructor. Clones the width, height and margin values of the source. 3594 * 3595 * @param source The layout params to copy from. 3596 */ 3597 public MarginLayoutParams(MarginLayoutParams source) { 3598 this.width = source.width; 3599 this.height = source.height; 3600 3601 this.leftMargin = source.leftMargin; 3602 this.topMargin = source.topMargin; 3603 this.rightMargin = source.rightMargin; 3604 this.bottomMargin = source.bottomMargin; 3605 } 3606 3607 /** 3608 * {@inheritDoc} 3609 */ 3610 public MarginLayoutParams(LayoutParams source) { 3611 super(source); 3612 } 3613 3614 /** 3615 * Sets the margins, in pixels. 3616 * 3617 * @param left the left margin size 3618 * @param top the top margin size 3619 * @param right the right margin size 3620 * @param bottom the bottom margin size 3621 * 3622 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 3623 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 3624 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 3625 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 3626 */ 3627 public void setMargins(int left, int top, int right, int bottom) { 3628 leftMargin = left; 3629 topMargin = top; 3630 rightMargin = right; 3631 bottomMargin = bottom; 3632 } 3633 } 3634} 3635