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