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