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