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