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