ViewGroup.java revision 4e96efe2edb73a8fbe4b89e85e03327da9796d80
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view; 18 19import android.animation.LayoutTransition; 20import android.view.animation.AlphaAnimation; 21import com.android.internal.R; 22import com.android.internal.util.Predicate; 23 24import android.content.Context; 25import android.content.res.Configuration; 26import android.content.res.TypedArray; 27import android.graphics.Bitmap; 28import android.graphics.Canvas; 29import android.graphics.Matrix; 30import android.graphics.Paint; 31import android.graphics.PointF; 32import android.graphics.Rect; 33import android.graphics.RectF; 34import android.graphics.Region; 35import android.os.Build; 36import android.os.Parcelable; 37import android.os.SystemClock; 38import android.util.AttributeSet; 39import android.util.Log; 40import android.util.SparseArray; 41import android.view.accessibility.AccessibilityEvent; 42import android.view.animation.Animation; 43import android.view.animation.AnimationUtils; 44import android.view.animation.LayoutAnimationController; 45import android.view.animation.Transformation; 46 47import java.util.ArrayList; 48import java.util.HashSet; 49 50/** 51 * <p> 52 * A <code>ViewGroup</code> is a special view that can contain other views 53 * (called children.) The view group is the base class for layouts and views 54 * containers. This class also defines the 55 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base 56 * class for layouts parameters. 57 * </p> 58 * 59 * <p> 60 * Also see {@link LayoutParams} for layout attributes. 61 * </p> 62 * 63 * @attr ref android.R.styleable#ViewGroup_clipChildren 64 * @attr ref android.R.styleable#ViewGroup_clipToPadding 65 * @attr ref android.R.styleable#ViewGroup_layoutAnimation 66 * @attr ref android.R.styleable#ViewGroup_animationCache 67 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache 68 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache 69 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren 70 * @attr ref android.R.styleable#ViewGroup_descendantFocusability 71 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 72 */ 73public abstract class ViewGroup extends View implements ViewParent, ViewManager { 74 75 private static final boolean DBG = false; 76 77 /** 78 * Views which have been hidden or removed which need to be animated on 79 * their way out. 80 * This field should be made private, so it is hidden from the SDK. 81 * {@hide} 82 */ 83 protected ArrayList<View> mDisappearingChildren; 84 85 /** 86 * Listener used to propagate events indicating when children are added 87 * and/or removed from a view group. 88 * This field should be made private, so it is hidden from the SDK. 89 * {@hide} 90 */ 91 protected OnHierarchyChangeListener mOnHierarchyChangeListener; 92 93 // The view contained within this ViewGroup that has or contains focus. 94 private View mFocused; 95 96 /** 97 * A Transformation used when drawing children, to 98 * apply on the child being drawn. 99 */ 100 private final Transformation mChildTransformation = new Transformation(); 101 102 /** 103 * Used to track the current invalidation region. 104 */ 105 private RectF mInvalidateRegion; 106 107 /** 108 * A Transformation used to calculate a correct 109 * invalidation area when the application is autoscaled. 110 */ 111 private Transformation mInvalidationTransformation; 112 113 // View currently under an ongoing drag 114 private View mCurrentDragView; 115 116 // Metadata about the ongoing drag 117 private DragEvent mCurrentDrag; 118 private HashSet<View> mDragNotifiedChildren; 119 120 // Does this group have a child that can accept the current drag payload? 121 private boolean mChildAcceptsDrag; 122 123 // Used during drag dispatch 124 private final PointF mLocalPoint = new PointF(); 125 126 // Layout animation 127 private LayoutAnimationController mLayoutAnimationController; 128 private Animation.AnimationListener mAnimationListener; 129 130 // First touch target in the linked list of touch targets. 131 private TouchTarget mFirstTouchTarget; 132 133 // Temporary arrays for splitting pointers. 134 private int[] mTmpPointerIndexMap; 135 private int[] mTmpPointerIds; 136 private MotionEvent.PointerCoords[] mTmpPointerCoords; 137 138 // For debugging only. You can see these in hierarchyviewer. 139 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) 140 @ViewDebug.ExportedProperty(category = "events") 141 private long mLastTouchDownTime; 142 @ViewDebug.ExportedProperty(category = "events") 143 private int mLastTouchDownIndex = -1; 144 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) 145 @ViewDebug.ExportedProperty(category = "events") 146 private float mLastTouchDownX; 147 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) 148 @ViewDebug.ExportedProperty(category = "events") 149 private float mLastTouchDownY; 150 151 /** 152 * Internal flags. 153 * 154 * This field should be made private, so it is hidden from the SDK. 155 * {@hide} 156 */ 157 protected int mGroupFlags; 158 159 // When set, ViewGroup invalidates only the child's rectangle 160 // Set by default 161 private static final int FLAG_CLIP_CHILDREN = 0x1; 162 163 // When set, ViewGroup excludes the padding area from the invalidate rectangle 164 // Set by default 165 private static final int FLAG_CLIP_TO_PADDING = 0x2; 166 167 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when 168 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set 169 private static final int FLAG_INVALIDATE_REQUIRED = 0x4; 170 171 // When set, dispatchDraw() will run the layout animation and unset the flag 172 private static final int FLAG_RUN_ANIMATION = 0x8; 173 174 // When set, there is either no layout animation on the ViewGroup or the layout 175 // animation is over 176 // Set by default 177 private static final int FLAG_ANIMATION_DONE = 0x10; 178 179 // If set, this ViewGroup has padding; if unset there is no padding and we don't need 180 // to clip it, even if FLAG_CLIP_TO_PADDING is set 181 private static final int FLAG_PADDING_NOT_NULL = 0x20; 182 183 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation 184 // Set by default 185 private static final int FLAG_ANIMATION_CACHE = 0x40; 186 187 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a 188 // layout animation; this avoid clobbering the hierarchy 189 // Automatically set when the layout animation starts, depending on the animation's 190 // characteristics 191 private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80; 192 193 // When set, the next call to drawChild() will clear mChildTransformation's matrix 194 private static final int FLAG_CLEAR_TRANSFORMATION = 0x100; 195 196 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes 197 // the children's Bitmap caches if necessary 198 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set) 199 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200; 200 201 /** 202 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)} 203 * to get the index of the child to draw for that iteration. 204 * 205 * @hide 206 */ 207 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; 208 209 /** 210 * When set, this ViewGroup supports static transformations on children; this causes 211 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 212 * invoked when a child is drawn. 213 * 214 * Any subclass overriding 215 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 216 * set this flags in {@link #mGroupFlags}. 217 * 218 * {@hide} 219 */ 220 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; 221 222 // When the previous drawChild() invocation used an alpha value that was lower than 223 // 1.0 and set it in mCachePaint 224 private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000; 225 226 /** 227 * When set, this ViewGroup's drawable states also include those 228 * of its children. 229 */ 230 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000; 231 232 /** 233 * When set, this ViewGroup tries to always draw its children using their drawing cache. 234 */ 235 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; 236 237 /** 238 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to 239 * draw its children with their drawing cache. 240 */ 241 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; 242 243 /** 244 * When set, this group will go through its list of children to notify them of 245 * any drawable state change. 246 */ 247 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000; 248 249 private static final int FLAG_MASK_FOCUSABILITY = 0x60000; 250 251 /** 252 * This view will get focus before any of its descendants. 253 */ 254 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000; 255 256 /** 257 * This view will get focus only if none of its descendants want it. 258 */ 259 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000; 260 261 /** 262 * This view will block any of its descendants from getting focus, even 263 * if they are focusable. 264 */ 265 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000; 266 267 /** 268 * Used to map between enum in attrubutes and flag values. 269 */ 270 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS = 271 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, 272 FOCUS_BLOCK_DESCENDANTS}; 273 274 /** 275 * When set, this ViewGroup should not intercept touch events. 276 * {@hide} 277 */ 278 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000; 279 280 /** 281 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate. 282 */ 283 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000; 284 285 /** 286 * Indicates which types of drawing caches are to be kept in memory. 287 * This field should be made private, so it is hidden from the SDK. 288 * {@hide} 289 */ 290 protected int mPersistentDrawingCache; 291 292 /** 293 * Used to indicate that no drawing cache should be kept in memory. 294 */ 295 public static final int PERSISTENT_NO_CACHE = 0x0; 296 297 /** 298 * Used to indicate that the animation drawing cache should be kept in memory. 299 */ 300 public static final int PERSISTENT_ANIMATION_CACHE = 0x1; 301 302 /** 303 * Used to indicate that the scrolling drawing cache should be kept in memory. 304 */ 305 public static final int PERSISTENT_SCROLLING_CACHE = 0x2; 306 307 /** 308 * Used to indicate that all drawing caches should be kept in memory. 309 */ 310 public static final int PERSISTENT_ALL_CACHES = 0x3; 311 312 /** 313 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL 314 * are set at the same time. 315 */ 316 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL; 317 318 // Index of the child's left position in the mLocation array 319 private static final int CHILD_LEFT_INDEX = 0; 320 // Index of the child's top position in the mLocation array 321 private static final int CHILD_TOP_INDEX = 1; 322 323 // Child views of this ViewGroup 324 private View[] mChildren; 325 // Number of valid children in the mChildren array, the rest should be null or not 326 // considered as children 327 328 private boolean mLayoutSuppressed = false; 329 330 private int mChildrenCount; 331 332 private static final int ARRAY_INITIAL_CAPACITY = 12; 333 private static final int ARRAY_CAPACITY_INCREMENT = 12; 334 335 // Used to draw cached views 336 private final Paint mCachePaint = new Paint(); 337 338 // Used to animate add/remove changes in layout 339 private LayoutTransition mTransition; 340 341 // The set of views that are currently being transitioned. This list is used to track views 342 // being removed that should not actually be removed from the parent yet because they are 343 // being animated. 344 private ArrayList<View> mTransitioningViews; 345 346 // List of children changing visibility. This is used to potentially keep rendering 347 // views during a transition when they otherwise would have become gone/invisible 348 private ArrayList<View> mVisibilityChangingChildren; 349 350 // Indicates whether this container will use its children layers to draw 351 @ViewDebug.ExportedProperty(category = "drawing") 352 private boolean mDrawLayers = true; 353 354 public ViewGroup(Context context) { 355 super(context); 356 initViewGroup(); 357 } 358 359 public ViewGroup(Context context, AttributeSet attrs) { 360 super(context, attrs); 361 initViewGroup(); 362 initFromAttributes(context, attrs); 363 } 364 365 public ViewGroup(Context context, AttributeSet attrs, int defStyle) { 366 super(context, attrs, defStyle); 367 initViewGroup(); 368 initFromAttributes(context, attrs); 369 } 370 371 private void initViewGroup() { 372 // ViewGroup doesn't draw by default 373 setFlags(WILL_NOT_DRAW, DRAW_MASK); 374 mGroupFlags |= FLAG_CLIP_CHILDREN; 375 mGroupFlags |= FLAG_CLIP_TO_PADDING; 376 mGroupFlags |= FLAG_ANIMATION_DONE; 377 mGroupFlags |= FLAG_ANIMATION_CACHE; 378 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; 379 380 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { 381 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 382 } 383 384 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); 385 386 mChildren = new View[ARRAY_INITIAL_CAPACITY]; 387 mChildrenCount = 0; 388 389 mCachePaint.setDither(false); 390 391 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; 392 } 393 394 private void initFromAttributes(Context context, AttributeSet attrs) { 395 TypedArray a = context.obtainStyledAttributes(attrs, 396 R.styleable.ViewGroup); 397 398 final int N = a.getIndexCount(); 399 for (int i = 0; i < N; i++) { 400 int attr = a.getIndex(i); 401 switch (attr) { 402 case R.styleable.ViewGroup_clipChildren: 403 setClipChildren(a.getBoolean(attr, true)); 404 break; 405 case R.styleable.ViewGroup_clipToPadding: 406 setClipToPadding(a.getBoolean(attr, true)); 407 break; 408 case R.styleable.ViewGroup_animationCache: 409 setAnimationCacheEnabled(a.getBoolean(attr, true)); 410 break; 411 case R.styleable.ViewGroup_persistentDrawingCache: 412 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE)); 413 break; 414 case R.styleable.ViewGroup_addStatesFromChildren: 415 setAddStatesFromChildren(a.getBoolean(attr, false)); 416 break; 417 case R.styleable.ViewGroup_alwaysDrawnWithCache: 418 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true)); 419 break; 420 case R.styleable.ViewGroup_layoutAnimation: 421 int id = a.getResourceId(attr, -1); 422 if (id > 0) { 423 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id)); 424 } 425 break; 426 case R.styleable.ViewGroup_descendantFocusability: 427 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]); 428 break; 429 case R.styleable.ViewGroup_splitMotionEvents: 430 setMotionEventSplittingEnabled(a.getBoolean(attr, false)); 431 break; 432 case R.styleable.ViewGroup_animateLayoutChanges: 433 boolean animateLayoutChanges = a.getBoolean(attr, false); 434 if (animateLayoutChanges) { 435 setLayoutTransition(new LayoutTransition()); 436 } 437 break; 438 } 439 } 440 441 a.recycle(); 442 } 443 444 /** 445 * Gets the descendant focusability of this view group. The descendant 446 * focusability defines the relationship between this view group and its 447 * descendants when looking for a view to take focus in 448 * {@link #requestFocus(int, android.graphics.Rect)}. 449 * 450 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 451 * {@link #FOCUS_BLOCK_DESCENDANTS}. 452 */ 453 @ViewDebug.ExportedProperty(category = "focus", mapping = { 454 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"), 455 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"), 456 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS") 457 }) 458 public int getDescendantFocusability() { 459 return mGroupFlags & FLAG_MASK_FOCUSABILITY; 460 } 461 462 /** 463 * Set the descendant focusability of this view group. This defines the relationship 464 * between this view group and its descendants when looking for a view to 465 * take focus in {@link #requestFocus(int, android.graphics.Rect)}. 466 * 467 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 468 * {@link #FOCUS_BLOCK_DESCENDANTS}. 469 */ 470 public void setDescendantFocusability(int focusability) { 471 switch (focusability) { 472 case FOCUS_BEFORE_DESCENDANTS: 473 case FOCUS_AFTER_DESCENDANTS: 474 case FOCUS_BLOCK_DESCENDANTS: 475 break; 476 default: 477 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, " 478 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS"); 479 } 480 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY; 481 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY); 482 } 483 484 /** 485 * {@inheritDoc} 486 */ 487 @Override 488 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) { 489 if (mFocused != null) { 490 mFocused.unFocus(); 491 mFocused = null; 492 } 493 super.handleFocusGainInternal(direction, previouslyFocusedRect); 494 } 495 496 /** 497 * {@inheritDoc} 498 */ 499 public void requestChildFocus(View child, View focused) { 500 if (DBG) { 501 System.out.println(this + " requestChildFocus()"); 502 } 503 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) { 504 return; 505 } 506 507 // Unfocus us, if necessary 508 super.unFocus(); 509 510 // We had a previous notion of who had focus. Clear it. 511 if (mFocused != child) { 512 if (mFocused != null) { 513 mFocused.unFocus(); 514 } 515 516 mFocused = child; 517 } 518 if (mParent != null) { 519 mParent.requestChildFocus(this, focused); 520 } 521 } 522 523 /** 524 * {@inheritDoc} 525 */ 526 public void focusableViewAvailable(View v) { 527 if (mParent != null 528 // shortcut: don't report a new focusable view if we block our descendants from 529 // getting focus 530 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) 531 // shortcut: don't report a new focusable view if we already are focused 532 // (and we don't prefer our descendants) 533 // 534 // note: knowing that mFocused is non-null is not a good enough reason 535 // to break the traversal since in that case we'd actually have to find 536 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and 537 // an ancestor of v; this will get checked for at ViewRoot 538 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) { 539 mParent.focusableViewAvailable(v); 540 } 541 } 542 543 /** 544 * {@inheritDoc} 545 */ 546 public boolean showContextMenuForChild(View originalView) { 547 return mParent != null && mParent.showContextMenuForChild(originalView); 548 } 549 550 /** 551 * {@inheritDoc} 552 */ 553 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 554 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null; 555 } 556 557 /** 558 * Find the nearest view in the specified direction that wants to take 559 * focus. 560 * 561 * @param focused The view that currently has focus 562 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and 563 * FOCUS_RIGHT, or 0 for not applicable. 564 */ 565 public View focusSearch(View focused, int direction) { 566 if (isRootNamespace()) { 567 // root namespace means we should consider ourselves the top of the 568 // tree for focus searching; otherwise we could be focus searching 569 // into other tabs. see LocalActivityManager and TabHost for more info 570 return FocusFinder.getInstance().findNextFocus(this, focused, direction); 571 } else if (mParent != null) { 572 return mParent.focusSearch(focused, direction); 573 } 574 return null; 575 } 576 577 /** 578 * {@inheritDoc} 579 */ 580 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 581 return false; 582 } 583 584 /** 585 * {@inheritDoc} 586 */ 587 @Override 588 public boolean dispatchUnhandledMove(View focused, int direction) { 589 return mFocused != null && 590 mFocused.dispatchUnhandledMove(focused, direction); 591 } 592 593 /** 594 * {@inheritDoc} 595 */ 596 public void clearChildFocus(View child) { 597 if (DBG) { 598 System.out.println(this + " clearChildFocus()"); 599 } 600 601 mFocused = null; 602 if (mParent != null) { 603 mParent.clearChildFocus(this); 604 } 605 } 606 607 /** 608 * {@inheritDoc} 609 */ 610 @Override 611 public void clearFocus() { 612 super.clearFocus(); 613 614 // clear any child focus if it exists 615 if (mFocused != null) { 616 mFocused.clearFocus(); 617 } 618 } 619 620 /** 621 * {@inheritDoc} 622 */ 623 @Override 624 void unFocus() { 625 if (DBG) { 626 System.out.println(this + " unFocus()"); 627 } 628 629 super.unFocus(); 630 if (mFocused != null) { 631 mFocused.unFocus(); 632 } 633 mFocused = null; 634 } 635 636 /** 637 * Returns the focused child of this view, if any. The child may have focus 638 * or contain focus. 639 * 640 * @return the focused child or null. 641 */ 642 public View getFocusedChild() { 643 return mFocused; 644 } 645 646 /** 647 * Returns true if this view has or contains focus 648 * 649 * @return true if this view has or contains focus 650 */ 651 @Override 652 public boolean hasFocus() { 653 return (mPrivateFlags & FOCUSED) != 0 || mFocused != null; 654 } 655 656 /* 657 * (non-Javadoc) 658 * 659 * @see android.view.View#findFocus() 660 */ 661 @Override 662 public View findFocus() { 663 if (DBG) { 664 System.out.println("Find focus in " + this + ": flags=" 665 + isFocused() + ", child=" + mFocused); 666 } 667 668 if (isFocused()) { 669 return this; 670 } 671 672 if (mFocused != null) { 673 return mFocused.findFocus(); 674 } 675 return null; 676 } 677 678 /** 679 * {@inheritDoc} 680 */ 681 @Override 682 public boolean hasFocusable() { 683 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) { 684 return false; 685 } 686 687 if (isFocusable()) { 688 return true; 689 } 690 691 final int descendantFocusability = getDescendantFocusability(); 692 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 693 final int count = mChildrenCount; 694 final View[] children = mChildren; 695 696 for (int i = 0; i < count; i++) { 697 final View child = children[i]; 698 if (child.hasFocusable()) { 699 return true; 700 } 701 } 702 } 703 704 return false; 705 } 706 707 /** 708 * {@inheritDoc} 709 */ 710 @Override 711 public void addFocusables(ArrayList<View> views, int direction) { 712 addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); 713 } 714 715 /** 716 * {@inheritDoc} 717 */ 718 @Override 719 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 720 final int focusableCount = views.size(); 721 722 final int descendantFocusability = getDescendantFocusability(); 723 724 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 725 final int count = mChildrenCount; 726 final View[] children = mChildren; 727 728 for (int i = 0; i < count; i++) { 729 final View child = children[i]; 730 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 731 child.addFocusables(views, direction, focusableMode); 732 } 733 } 734 } 735 736 // we add ourselves (if focusable) in all cases except for when we are 737 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is 738 // to avoid the focus search finding layouts when a more precise search 739 // among the focusable children would be more interesting. 740 if ( 741 descendantFocusability != FOCUS_AFTER_DESCENDANTS || 742 // No focusable descendants 743 (focusableCount == views.size())) { 744 super.addFocusables(views, direction, focusableMode); 745 } 746 } 747 748 /** 749 * {@inheritDoc} 750 */ 751 @Override 752 public void dispatchWindowFocusChanged(boolean hasFocus) { 753 super.dispatchWindowFocusChanged(hasFocus); 754 final int count = mChildrenCount; 755 final View[] children = mChildren; 756 for (int i = 0; i < count; i++) { 757 children[i].dispatchWindowFocusChanged(hasFocus); 758 } 759 } 760 761 /** 762 * {@inheritDoc} 763 */ 764 @Override 765 public void addTouchables(ArrayList<View> views) { 766 super.addTouchables(views); 767 768 final int count = mChildrenCount; 769 final View[] children = mChildren; 770 771 for (int i = 0; i < count; i++) { 772 final View child = children[i]; 773 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 774 child.addTouchables(views); 775 } 776 } 777 } 778 779 /** 780 * {@inheritDoc} 781 */ 782 @Override 783 public void dispatchDisplayHint(int hint) { 784 super.dispatchDisplayHint(hint); 785 final int count = mChildrenCount; 786 final View[] children = mChildren; 787 for (int i = 0; i < count; i++) { 788 children[i].dispatchDisplayHint(hint); 789 } 790 } 791 792 /** 793 * @hide 794 * @param child 795 * @param visibility 796 */ 797 void onChildVisibilityChanged(View child, int visibility) { 798 if (mTransition != null) { 799 if (visibility == VISIBLE) { 800 mTransition.showChild(this, child); 801 } else { 802 mTransition.hideChild(this, child); 803 } 804 if (visibility != VISIBLE) { 805 // Only track this on disappearing views - appearing views are already visible 806 // and don't need special handling during drawChild() 807 if (mVisibilityChangingChildren == null) { 808 mVisibilityChangingChildren = new ArrayList<View>(); 809 } 810 mVisibilityChangingChildren.add(child); 811 if (mTransitioningViews != null && mTransitioningViews.contains(child)) { 812 addDisappearingView(child); 813 } 814 } 815 } 816 817 // in all cases, for drags 818 if (mCurrentDrag != null) { 819 if (visibility == VISIBLE) { 820 notifyChildOfDrag(child); 821 } 822 } 823 } 824 825 /** 826 * {@inheritDoc} 827 */ 828 @Override 829 protected void dispatchVisibilityChanged(View changedView, int visibility) { 830 super.dispatchVisibilityChanged(changedView, visibility); 831 final int count = mChildrenCount; 832 final View[] children = mChildren; 833 for (int i = 0; i < count; i++) { 834 children[i].dispatchVisibilityChanged(changedView, visibility); 835 } 836 } 837 838 /** 839 * {@inheritDoc} 840 */ 841 @Override 842 public void dispatchWindowVisibilityChanged(int visibility) { 843 super.dispatchWindowVisibilityChanged(visibility); 844 final int count = mChildrenCount; 845 final View[] children = mChildren; 846 for (int i = 0; i < count; i++) { 847 children[i].dispatchWindowVisibilityChanged(visibility); 848 } 849 } 850 851 /** 852 * {@inheritDoc} 853 */ 854 @Override 855 public void dispatchConfigurationChanged(Configuration newConfig) { 856 super.dispatchConfigurationChanged(newConfig); 857 final int count = mChildrenCount; 858 final View[] children = mChildren; 859 for (int i = 0; i < count; i++) { 860 children[i].dispatchConfigurationChanged(newConfig); 861 } 862 } 863 864 /** 865 * {@inheritDoc} 866 */ 867 public void recomputeViewAttributes(View child) { 868 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) { 869 ViewParent parent = mParent; 870 if (parent != null) parent.recomputeViewAttributes(this); 871 } 872 } 873 874 @Override 875 void dispatchCollectViewAttributes(int visibility) { 876 visibility |= mViewFlags&VISIBILITY_MASK; 877 super.dispatchCollectViewAttributes(visibility); 878 final int count = mChildrenCount; 879 final View[] children = mChildren; 880 for (int i = 0; i < count; i++) { 881 children[i].dispatchCollectViewAttributes(visibility); 882 } 883 } 884 885 /** 886 * {@inheritDoc} 887 */ 888 public void bringChildToFront(View child) { 889 int index = indexOfChild(child); 890 if (index >= 0) { 891 removeFromArray(index); 892 addInArray(child, mChildrenCount); 893 child.mParent = this; 894 } 895 } 896 897 /** 898 * {@inheritDoc} 899 * 900 * !!! TODO: write real docs 901 */ 902 @Override 903 public boolean dispatchDragEvent(DragEvent event) { 904 boolean retval = false; 905 final float tx = event.mX; 906 final float ty = event.mY; 907 908 ViewRoot root = getViewRoot(); 909 910 // Dispatch down the view hierarchy 911 switch (event.mAction) { 912 case DragEvent.ACTION_DRAG_STARTED: { 913 // clear state to recalculate which views we drag over 914 mCurrentDragView = null; 915 916 // Set up our tracking of drag-started notifications 917 mCurrentDrag = DragEvent.obtain(event); 918 if (mDragNotifiedChildren == null) { 919 mDragNotifiedChildren = new HashSet<View>(); 920 } else { 921 mDragNotifiedChildren.clear(); 922 } 923 924 // Now dispatch down to our children, caching the responses 925 mChildAcceptsDrag = false; 926 final int count = mChildrenCount; 927 final View[] children = mChildren; 928 for (int i = 0; i < count; i++) { 929 final View child = children[i]; 930 if (child.getVisibility() == VISIBLE) { 931 final boolean handled = notifyChildOfDrag(children[i]); 932 if (handled) { 933 mChildAcceptsDrag = true; 934 } 935 } 936 } 937 938 // Return HANDLED if one of our children can accept the drag 939 if (mChildAcceptsDrag) { 940 retval = true; 941 } 942 } break; 943 944 case DragEvent.ACTION_DRAG_ENDED: { 945 // Release the bookkeeping now that the drag lifecycle has ended 946 if (mDragNotifiedChildren != null) { 947 for (View child : mDragNotifiedChildren) { 948 // If a child was notified about an ongoing drag, it's told that it's over 949 child.dispatchDragEvent(event); 950 } 951 952 mDragNotifiedChildren.clear(); 953 mCurrentDrag.recycle(); 954 mCurrentDrag = null; 955 } 956 957 // We consider drag-ended to have been handled if one of our children 958 // had offered to handle the drag. 959 if (mChildAcceptsDrag) { 960 retval = true; 961 } 962 } break; 963 964 case DragEvent.ACTION_DRAG_LOCATION: { 965 // Find the [possibly new] drag target 966 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); 967 968 // If we've changed apparent drag target, tell the view root which view 969 // we're over now [for purposes of the eventual drag-recipient-changed 970 // notifications to the framework] and tell the new target that the drag 971 // has entered its bounds. The root will see setDragFocus() calls all 972 // the way down to the final leaf view that is handling the LOCATION event 973 // before reporting the new potential recipient to the framework. 974 if (mCurrentDragView != target) { 975 root.setDragFocus(target); 976 977 final int action = event.mAction; 978 // If we've dragged off of a child view, send it the EXITED message 979 if (mCurrentDragView != null) { 980 event.mAction = DragEvent.ACTION_DRAG_EXITED; 981 mCurrentDragView.dispatchDragEvent(event); 982 } 983 mCurrentDragView = target; 984 985 // If we've dragged over a new child view, send it the ENTERED message 986 if (target != null) { 987 event.mAction = DragEvent.ACTION_DRAG_ENTERED; 988 target.dispatchDragEvent(event); 989 } 990 event.mAction = action; // restore the event's original state 991 } 992 993 // Dispatch the actual drag location notice, localized into its coordinates 994 if (target != null) { 995 event.mX = mLocalPoint.x; 996 event.mY = mLocalPoint.y; 997 998 retval = target.dispatchDragEvent(event); 999 1000 event.mX = tx; 1001 event.mY = ty; 1002 } 1003 } break; 1004 1005 /* Entered / exited dispatch 1006 * 1007 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is 1008 * that we're about to get the corresponding LOCATION event, which we will use to 1009 * determine which of our children is the new target; at that point we will 1010 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup]. 1011 * 1012 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the 1013 * drag has left this ViewGroup, we know by definition that every contained subview 1014 * is also no longer under the drag point. 1015 */ 1016 1017 case DragEvent.ACTION_DRAG_EXITED: { 1018 if (mCurrentDragView != null) { 1019 mCurrentDragView.dispatchDragEvent(event); 1020 mCurrentDragView = null; 1021 } 1022 } break; 1023 1024 case DragEvent.ACTION_DROP: { 1025 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event); 1026 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); 1027 if (target != null) { 1028 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target); 1029 event.mX = mLocalPoint.x; 1030 event.mY = mLocalPoint.y; 1031 retval = target.dispatchDragEvent(event); 1032 event.mX = tx; 1033 event.mY = ty; 1034 } else { 1035 if (ViewDebug.DEBUG_DRAG) { 1036 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view"); 1037 } 1038 } 1039 } break; 1040 } 1041 1042 // If none of our children could handle the event, try here 1043 if (!retval) { 1044 // Call up to the View implementation that dispatches to installed listeners 1045 retval = super.dispatchDragEvent(event); 1046 } 1047 return retval; 1048 } 1049 1050 // Find the frontmost child view that lies under the given point, and calculate 1051 // the position within its own local coordinate system. 1052 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) { 1053 final int count = mChildrenCount; 1054 final View[] children = mChildren; 1055 for (int i = count - 1; i >= 0; i--) { 1056 final View child = children[i]; 1057 if (!child.mCanAcceptDrop) { 1058 continue; 1059 } 1060 1061 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) { 1062 return child; 1063 } 1064 } 1065 return null; 1066 } 1067 1068 boolean notifyChildOfDrag(View child) { 1069 if (ViewDebug.DEBUG_DRAG) { 1070 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child); 1071 } 1072 1073 if (! mDragNotifiedChildren.contains(child)) { 1074 mDragNotifiedChildren.add(child); 1075 child.mCanAcceptDrop = child.dispatchDragEvent(mCurrentDrag); 1076 } 1077 return child.mCanAcceptDrop; 1078 } 1079 1080 @Override 1081 public void dispatchSystemUiVisibilityChanged(int visible) { 1082 super.dispatchSystemUiVisibilityChanged(visible); 1083 1084 final int count = mChildrenCount; 1085 final View[] children = mChildren; 1086 for (int i=0; i <count; i++) { 1087 final View child = children[i]; 1088 child.dispatchSystemUiVisibilityChanged(visible); 1089 } 1090 } 1091 1092 /** 1093 * {@inheritDoc} 1094 */ 1095 @Override 1096 public boolean dispatchKeyEventPreIme(KeyEvent event) { 1097 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1098 return super.dispatchKeyEventPreIme(event); 1099 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1100 return mFocused.dispatchKeyEventPreIme(event); 1101 } 1102 return false; 1103 } 1104 1105 /** 1106 * {@inheritDoc} 1107 */ 1108 @Override 1109 public boolean dispatchKeyEvent(KeyEvent event) { 1110 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1111 return super.dispatchKeyEvent(event); 1112 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1113 return mFocused.dispatchKeyEvent(event); 1114 } 1115 return false; 1116 } 1117 1118 /** 1119 * {@inheritDoc} 1120 */ 1121 @Override 1122 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 1123 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1124 return super.dispatchKeyShortcutEvent(event); 1125 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1126 return mFocused.dispatchKeyShortcutEvent(event); 1127 } 1128 return false; 1129 } 1130 1131 /** 1132 * {@inheritDoc} 1133 */ 1134 @Override 1135 public boolean dispatchTrackballEvent(MotionEvent event) { 1136 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1137 return super.dispatchTrackballEvent(event); 1138 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1139 return mFocused.dispatchTrackballEvent(event); 1140 } 1141 return false; 1142 } 1143 1144 /** 1145 * {@inheritDoc} 1146 */ 1147 @Override 1148 public boolean dispatchGenericMotionEvent(MotionEvent event) { 1149 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { 1150 return super.dispatchGenericMotionEvent(event); 1151 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { 1152 return mFocused.dispatchGenericMotionEvent(event); 1153 } 1154 return false; 1155 } 1156 1157 /** 1158 * {@inheritDoc} 1159 */ 1160 @Override 1161 public boolean dispatchTouchEvent(MotionEvent ev) { 1162 if (!onFilterTouchEventForSecurity(ev)) { 1163 return false; 1164 } 1165 1166 final int action = ev.getAction(); 1167 final int actionMasked = action & MotionEvent.ACTION_MASK; 1168 1169 // Handle an initial down. 1170 if (actionMasked == MotionEvent.ACTION_DOWN) { 1171 // Throw away all previous state when starting a new touch gesture. 1172 // The framework may have dropped the up or cancel event for the previous gesture 1173 // due to an app switch, ANR, or some other state change. 1174 cancelAndClearTouchTargets(ev); 1175 resetTouchState(); 1176 } 1177 1178 // Check for interception. 1179 final boolean intercepted; 1180 if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { 1181 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 1182 if (!disallowIntercept) { 1183 intercepted = onInterceptTouchEvent(ev); 1184 ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it 1185 } else { 1186 intercepted = false; 1187 } 1188 } else { 1189 intercepted = true; 1190 } 1191 1192 // Check for cancelation. 1193 final boolean canceled = resetCancelNextUpFlag(this) 1194 || actionMasked == MotionEvent.ACTION_CANCEL; 1195 1196 // Update list of touch targets for pointer down, if needed. 1197 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; 1198 TouchTarget newTouchTarget = null; 1199 boolean alreadyDispatchedToNewTouchTarget = false; 1200 if (!canceled && !intercepted) { 1201 if (actionMasked == MotionEvent.ACTION_DOWN 1202 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)) { 1203 final int actionIndex = ev.getActionIndex(); // always 0 for down 1204 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) 1205 : TouchTarget.ALL_POINTER_IDS; 1206 1207 // Clean up earlier touch targets for this pointer id in case they 1208 // have become out of sync. 1209 removePointersFromTouchTargets(idBitsToAssign); 1210 1211 final int childrenCount = mChildrenCount; 1212 if (childrenCount != 0) { 1213 // Find a child that can receive the event. Scan children from front to back. 1214 final View[] children = mChildren; 1215 final float x = ev.getX(actionIndex); 1216 final float y = ev.getY(actionIndex); 1217 1218 for (int i = childrenCount - 1; i >= 0; i--) { 1219 final View child = children[i]; 1220 if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE 1221 && child.getAnimation() == null) { 1222 // Skip invisible child unless it is animating. 1223 continue; 1224 } 1225 1226 if (!isTransformedTouchPointInView(x, y, child, null)) { 1227 // New pointer is out of child's bounds. 1228 continue; 1229 } 1230 1231 newTouchTarget = getTouchTarget(child); 1232 if (newTouchTarget != null) { 1233 // Child is already receiving touch within its bounds. 1234 // Give it the new pointer in addition to the ones it is handling. 1235 newTouchTarget.pointerIdBits |= idBitsToAssign; 1236 break; 1237 } 1238 1239 resetCancelNextUpFlag(child); 1240 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 1241 // Child wants to receive touch within its bounds. 1242 mLastTouchDownTime = ev.getDownTime(); 1243 mLastTouchDownIndex = i; 1244 mLastTouchDownX = ev.getX(); 1245 mLastTouchDownY = ev.getY(); 1246 newTouchTarget = addTouchTarget(child, idBitsToAssign); 1247 alreadyDispatchedToNewTouchTarget = true; 1248 break; 1249 } 1250 } 1251 } 1252 1253 if (newTouchTarget == null && mFirstTouchTarget != null) { 1254 // Did not find a child to receive the event. 1255 // Assign the pointer to the least recently added target. 1256 newTouchTarget = mFirstTouchTarget; 1257 while (newTouchTarget.next != null) { 1258 newTouchTarget = newTouchTarget.next; 1259 } 1260 newTouchTarget.pointerIdBits |= idBitsToAssign; 1261 } 1262 } 1263 } 1264 1265 // Dispatch to touch targets. 1266 boolean handled = false; 1267 if (mFirstTouchTarget == null) { 1268 // No touch targets so treat this as an ordinary view. 1269 handled = dispatchTransformedTouchEvent(ev, canceled, null, 1270 TouchTarget.ALL_POINTER_IDS); 1271 } else { 1272 // Dispatch to touch targets, excluding the new touch target if we already 1273 // dispatched to it. Cancel touch targets if necessary. 1274 TouchTarget predecessor = null; 1275 TouchTarget target = mFirstTouchTarget; 1276 while (target != null) { 1277 final TouchTarget next = target.next; 1278 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { 1279 handled = true; 1280 } else { 1281 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; 1282 if (dispatchTransformedTouchEvent(ev, cancelChild, 1283 target.child, target.pointerIdBits)) { 1284 handled = true; 1285 } 1286 if (cancelChild) { 1287 if (predecessor == null) { 1288 mFirstTouchTarget = next; 1289 } else { 1290 predecessor.next = next; 1291 } 1292 target.recycle(); 1293 target = next; 1294 continue; 1295 } 1296 } 1297 predecessor = target; 1298 target = next; 1299 } 1300 } 1301 1302 // Update list of touch targets for pointer up or cancel, if needed. 1303 if (canceled || actionMasked == MotionEvent.ACTION_UP) { 1304 resetTouchState(); 1305 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { 1306 final int actionIndex = ev.getActionIndex(); 1307 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); 1308 removePointersFromTouchTargets(idBitsToRemove); 1309 } 1310 1311 return handled; 1312 } 1313 1314 /** 1315 * Resets all touch state in preparation for a new cycle. 1316 */ 1317 private void resetTouchState() { 1318 clearTouchTargets(); 1319 resetCancelNextUpFlag(this); 1320 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 1321 } 1322 1323 /** 1324 * Resets the cancel next up flag. 1325 * Returns true if the flag was previously set. 1326 */ 1327 private boolean resetCancelNextUpFlag(View view) { 1328 if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { 1329 view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 1330 return true; 1331 } 1332 return false; 1333 } 1334 1335 /** 1336 * Clears all touch targets. 1337 */ 1338 private void clearTouchTargets() { 1339 TouchTarget target = mFirstTouchTarget; 1340 if (target != null) { 1341 do { 1342 TouchTarget next = target.next; 1343 target.recycle(); 1344 target = next; 1345 } while (target != null); 1346 mFirstTouchTarget = null; 1347 } 1348 } 1349 1350 /** 1351 * Cancels and clears all touch targets. 1352 */ 1353 private void cancelAndClearTouchTargets(MotionEvent event) { 1354 if (mFirstTouchTarget != null) { 1355 boolean syntheticEvent = false; 1356 if (event == null) { 1357 final long now = SystemClock.uptimeMillis(); 1358 event = MotionEvent.obtain(now, now, 1359 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 1360 syntheticEvent = true; 1361 } 1362 1363 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 1364 resetCancelNextUpFlag(target.child); 1365 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits); 1366 } 1367 clearTouchTargets(); 1368 1369 if (syntheticEvent) { 1370 event.recycle(); 1371 } 1372 } 1373 } 1374 1375 /** 1376 * Gets the touch target for specified child view. 1377 * Returns null if not found. 1378 */ 1379 private TouchTarget getTouchTarget(View child) { 1380 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 1381 if (target.child == child) { 1382 return target; 1383 } 1384 } 1385 return null; 1386 } 1387 1388 /** 1389 * Adds a touch target for specified child to the beginning of the list. 1390 * Assumes the target child is not already present. 1391 */ 1392 private TouchTarget addTouchTarget(View child, int pointerIdBits) { 1393 TouchTarget target = TouchTarget.obtain(child, pointerIdBits); 1394 target.next = mFirstTouchTarget; 1395 mFirstTouchTarget = target; 1396 return target; 1397 } 1398 1399 /** 1400 * Removes the pointer ids from consideration. 1401 */ 1402 private void removePointersFromTouchTargets(int pointerIdBits) { 1403 TouchTarget predecessor = null; 1404 TouchTarget target = mFirstTouchTarget; 1405 while (target != null) { 1406 final TouchTarget next = target.next; 1407 if ((target.pointerIdBits & pointerIdBits) != 0) { 1408 target.pointerIdBits &= ~pointerIdBits; 1409 if (target.pointerIdBits == 0) { 1410 if (predecessor == null) { 1411 mFirstTouchTarget = next; 1412 } else { 1413 predecessor.next = next; 1414 } 1415 target.recycle(); 1416 target = next; 1417 continue; 1418 } 1419 } 1420 predecessor = target; 1421 target = next; 1422 } 1423 } 1424 1425 /** 1426 * Returns true if a child view contains the specified point when transformed 1427 * into its coordinate space. 1428 * Child must not be null. 1429 * @hide 1430 */ 1431 protected boolean isTransformedTouchPointInView(float x, float y, View child, 1432 PointF outLocalPoint) { 1433 float localX = x + mScrollX - child.mLeft; 1434 float localY = y + mScrollY - child.mTop; 1435 if (! child.hasIdentityMatrix() && mAttachInfo != null) { 1436 final float[] localXY = mAttachInfo.mTmpTransformLocation; 1437 localXY[0] = localX; 1438 localXY[1] = localY; 1439 child.getInverseMatrix().mapPoints(localXY); 1440 localX = localXY[0]; 1441 localY = localXY[1]; 1442 } 1443 final boolean isInView = child.pointInView(localX, localY); 1444 if (isInView && outLocalPoint != null) { 1445 outLocalPoint.set(localX, localY); 1446 } 1447 return isInView; 1448 } 1449 1450 /** 1451 * Transforms a motion event into the coordinate space of a particular child view, 1452 * filters out irrelevant pointer ids, and overrides its action if necessary. 1453 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 1454 */ 1455 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, 1456 View child, int desiredPointerIdBits) { 1457 final boolean handled; 1458 1459 // Canceling motions is a special case. We don't need to perform any transformations 1460 // or filtering. The important part is the action, not the contents. 1461 final int oldAction = event.getAction(); 1462 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { 1463 event.setAction(MotionEvent.ACTION_CANCEL); 1464 if (child == null) { 1465 handled = super.dispatchTouchEvent(event); 1466 } else { 1467 handled = child.dispatchTouchEvent(event); 1468 } 1469 event.setAction(oldAction); 1470 return handled; 1471 } 1472 1473 // Calculate the number of pointers to deliver. 1474 final int oldPointerCount = event.getPointerCount(); 1475 int newPointerCount = 0; 1476 if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) { 1477 newPointerCount = oldPointerCount; 1478 } else { 1479 for (int i = 0; i < oldPointerCount; i++) { 1480 final int pointerId = event.getPointerId(i); 1481 final int pointerIdBit = 1 << pointerId; 1482 if ((pointerIdBit & desiredPointerIdBits) != 0) { 1483 newPointerCount += 1; 1484 } 1485 } 1486 } 1487 1488 // If for some reason we ended up in an inconsistent state where it looks like we 1489 // might produce a motion event with no pointers in it, then drop the event. 1490 if (newPointerCount == 0) { 1491 return false; 1492 } 1493 1494 // If the number of pointers is the same and we don't need to perform any fancy 1495 // irreversible transformations, then we can reuse the motion event for this 1496 // dispatch as long as we are careful to revert any changes we make. 1497 final boolean reuse = newPointerCount == oldPointerCount 1498 && (child == null || child.hasIdentityMatrix()); 1499 if (reuse) { 1500 if (child == null) { 1501 handled = super.dispatchTouchEvent(event); 1502 } else { 1503 final float offsetX = mScrollX - child.mLeft; 1504 final float offsetY = mScrollY - child.mTop; 1505 event.offsetLocation(offsetX, offsetY); 1506 1507 handled = child.dispatchTouchEvent(event); 1508 1509 event.offsetLocation(-offsetX, -offsetY); 1510 } 1511 return handled; 1512 } 1513 1514 // Make a copy of the event. 1515 // If the number of pointers is different, then we need to filter out irrelevant pointers 1516 // as we make a copy of the motion event. 1517 MotionEvent transformedEvent; 1518 if (newPointerCount == oldPointerCount) { 1519 transformedEvent = MotionEvent.obtain(event); 1520 } else { 1521 growTmpPointerArrays(newPointerCount); 1522 final int[] newPointerIndexMap = mTmpPointerIndexMap; 1523 final int[] newPointerIds = mTmpPointerIds; 1524 final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords; 1525 1526 int newPointerIndex = 0; 1527 int oldPointerIndex = 0; 1528 while (newPointerIndex < newPointerCount) { 1529 final int pointerId = event.getPointerId(oldPointerIndex); 1530 final int pointerIdBits = 1 << pointerId; 1531 if ((pointerIdBits & desiredPointerIdBits) != 0) { 1532 newPointerIndexMap[newPointerIndex] = oldPointerIndex; 1533 newPointerIds[newPointerIndex] = pointerId; 1534 if (newPointerCoords[newPointerIndex] == null) { 1535 newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords(); 1536 } 1537 1538 newPointerIndex += 1; 1539 } 1540 oldPointerIndex += 1; 1541 } 1542 1543 final int newAction; 1544 if (cancel) { 1545 newAction = MotionEvent.ACTION_CANCEL; 1546 } else { 1547 final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK; 1548 if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN 1549 || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) { 1550 final int changedPointerId = event.getPointerId( 1551 (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK) 1552 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT); 1553 final int changedPointerIdBits = 1 << changedPointerId; 1554 if ((changedPointerIdBits & desiredPointerIdBits) != 0) { 1555 if (newPointerCount == 1) { 1556 // The first/last pointer went down/up. 1557 newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN 1558 ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP; 1559 } else { 1560 // A secondary pointer went down/up. 1561 int newChangedPointerIndex = 0; 1562 while (newPointerIds[newChangedPointerIndex] != changedPointerId) { 1563 newChangedPointerIndex += 1; 1564 } 1565 newAction = oldMaskedAction | (newChangedPointerIndex 1566 << MotionEvent.ACTION_POINTER_INDEX_SHIFT); 1567 } 1568 } else { 1569 // An unrelated pointer changed. 1570 newAction = MotionEvent.ACTION_MOVE; 1571 } 1572 } else { 1573 // Simple up/down/cancel/move motion action. 1574 newAction = oldMaskedAction; 1575 } 1576 } 1577 1578 transformedEvent = null; 1579 final int historySize = event.getHistorySize(); 1580 for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) { 1581 for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) { 1582 final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex]; 1583 oldPointerIndex = newPointerIndexMap[newPointerIndex]; 1584 if (historyIndex != historySize) { 1585 event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c); 1586 } else { 1587 event.getPointerCoords(oldPointerIndex, c); 1588 } 1589 } 1590 1591 final long eventTime; 1592 if (historyIndex != historySize) { 1593 eventTime = event.getHistoricalEventTime(historyIndex); 1594 } else { 1595 eventTime = event.getEventTime(); 1596 } 1597 1598 if (transformedEvent == null) { 1599 transformedEvent = MotionEvent.obtain( 1600 event.getDownTime(), eventTime, newAction, 1601 newPointerCount, newPointerIds, newPointerCoords, 1602 event.getMetaState(), event.getXPrecision(), event.getYPrecision(), 1603 event.getDeviceId(), event.getEdgeFlags(), event.getSource(), 1604 event.getFlags()); 1605 } else { 1606 transformedEvent.addBatch(eventTime, newPointerCoords, 0); 1607 } 1608 } 1609 } 1610 1611 // Perform any necessary transformations and dispatch. 1612 if (child == null) { 1613 handled = super.dispatchTouchEvent(transformedEvent); 1614 } else { 1615 final float offsetX = mScrollX - child.mLeft; 1616 final float offsetY = mScrollY - child.mTop; 1617 transformedEvent.offsetLocation(offsetX, offsetY); 1618 if (! child.hasIdentityMatrix()) { 1619 transformedEvent.transform(child.getInverseMatrix()); 1620 } 1621 1622 handled = child.dispatchTouchEvent(transformedEvent); 1623 } 1624 1625 // Done. 1626 transformedEvent.recycle(); 1627 return handled; 1628 } 1629 1630 /** 1631 * Enlarge the temporary pointer arrays for splitting pointers. 1632 * May discard contents (but keeps PointerCoords objects to avoid reallocating them). 1633 */ 1634 private void growTmpPointerArrays(int desiredCapacity) { 1635 final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords; 1636 int capacity; 1637 if (oldTmpPointerCoords != null) { 1638 capacity = oldTmpPointerCoords.length; 1639 if (desiredCapacity <= capacity) { 1640 return; 1641 } 1642 } else { 1643 capacity = 4; 1644 } 1645 1646 while (capacity < desiredCapacity) { 1647 capacity *= 2; 1648 } 1649 1650 mTmpPointerIndexMap = new int[capacity]; 1651 mTmpPointerIds = new int[capacity]; 1652 mTmpPointerCoords = new MotionEvent.PointerCoords[capacity]; 1653 1654 if (oldTmpPointerCoords != null) { 1655 System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0, 1656 oldTmpPointerCoords.length); 1657 } 1658 } 1659 1660 /** 1661 * Enable or disable the splitting of MotionEvents to multiple children during touch event 1662 * dispatch. This behavior is enabled by default for applications that target an 1663 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer. 1664 * 1665 * <p>When this option is enabled MotionEvents may be split and dispatched to different child 1666 * views depending on where each pointer initially went down. This allows for user interactions 1667 * such as scrolling two panes of content independently, chording of buttons, and performing 1668 * independent gestures on different pieces of content. 1669 * 1670 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple 1671 * child views. <code>false</code> to only allow one child view to be the target of 1672 * any MotionEvent received by this ViewGroup. 1673 */ 1674 public void setMotionEventSplittingEnabled(boolean split) { 1675 // TODO Applications really shouldn't change this setting mid-touch event, 1676 // but perhaps this should handle that case and send ACTION_CANCELs to any child views 1677 // with gestures in progress when this is changed. 1678 if (split) { 1679 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 1680 } else { 1681 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS; 1682 } 1683 } 1684 1685 /** 1686 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 1687 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 1688 */ 1689 public boolean isMotionEventSplittingEnabled() { 1690 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS; 1691 } 1692 1693 /** 1694 * {@inheritDoc} 1695 */ 1696 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 1697 1698 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { 1699 // We're already in this state, assume our ancestors are too 1700 return; 1701 } 1702 1703 if (disallowIntercept) { 1704 mGroupFlags |= FLAG_DISALLOW_INTERCEPT; 1705 } else { 1706 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 1707 } 1708 1709 // Pass it up to our parent 1710 if (mParent != null) { 1711 mParent.requestDisallowInterceptTouchEvent(disallowIntercept); 1712 } 1713 } 1714 1715 /** 1716 * Implement this method to intercept all touch screen motion events. This 1717 * allows you to watch events as they are dispatched to your children, and 1718 * take ownership of the current gesture at any point. 1719 * 1720 * <p>Using this function takes some care, as it has a fairly complicated 1721 * interaction with {@link View#onTouchEvent(MotionEvent) 1722 * View.onTouchEvent(MotionEvent)}, and using it requires implementing 1723 * that method as well as this one in the correct way. Events will be 1724 * received in the following order: 1725 * 1726 * <ol> 1727 * <li> You will receive the down event here. 1728 * <li> The down event will be handled either by a child of this view 1729 * group, or given to your own onTouchEvent() method to handle; this means 1730 * you should implement onTouchEvent() to return true, so you will 1731 * continue to see the rest of the gesture (instead of looking for 1732 * a parent view to handle it). Also, by returning true from 1733 * onTouchEvent(), you will not receive any following 1734 * events in onInterceptTouchEvent() and all touch processing must 1735 * happen in onTouchEvent() like normal. 1736 * <li> For as long as you return false from this function, each following 1737 * event (up to and including the final up) will be delivered first here 1738 * and then to the target's onTouchEvent(). 1739 * <li> If you return true from here, you will not receive any 1740 * following events: the target view will receive the same event but 1741 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further 1742 * events will be delivered to your onTouchEvent() method and no longer 1743 * appear here. 1744 * </ol> 1745 * 1746 * @param ev The motion event being dispatched down the hierarchy. 1747 * @return Return true to steal motion events from the children and have 1748 * them dispatched to this ViewGroup through onTouchEvent(). 1749 * The current target will receive an ACTION_CANCEL event, and no further 1750 * messages will be delivered here. 1751 */ 1752 public boolean onInterceptTouchEvent(MotionEvent ev) { 1753 return false; 1754 } 1755 1756 /** 1757 * {@inheritDoc} 1758 * 1759 * Looks for a view to give focus to respecting the setting specified by 1760 * {@link #getDescendantFocusability()}. 1761 * 1762 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to 1763 * find focus within the children of this group when appropriate. 1764 * 1765 * @see #FOCUS_BEFORE_DESCENDANTS 1766 * @see #FOCUS_AFTER_DESCENDANTS 1767 * @see #FOCUS_BLOCK_DESCENDANTS 1768 * @see #onRequestFocusInDescendants 1769 */ 1770 @Override 1771 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 1772 if (DBG) { 1773 System.out.println(this + " ViewGroup.requestFocus direction=" 1774 + direction); 1775 } 1776 int descendantFocusability = getDescendantFocusability(); 1777 1778 switch (descendantFocusability) { 1779 case FOCUS_BLOCK_DESCENDANTS: 1780 return super.requestFocus(direction, previouslyFocusedRect); 1781 case FOCUS_BEFORE_DESCENDANTS: { 1782 final boolean took = super.requestFocus(direction, previouslyFocusedRect); 1783 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); 1784 } 1785 case FOCUS_AFTER_DESCENDANTS: { 1786 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); 1787 return took ? took : super.requestFocus(direction, previouslyFocusedRect); 1788 } 1789 default: 1790 throw new IllegalStateException("descendant focusability must be " 1791 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " 1792 + "but is " + descendantFocusability); 1793 } 1794 } 1795 1796 /** 1797 * Look for a descendant to call {@link View#requestFocus} on. 1798 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)} 1799 * when it wants to request focus within its children. Override this to 1800 * customize how your {@link ViewGroup} requests focus within its children. 1801 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT 1802 * @param previouslyFocusedRect The rectangle (in this View's coordinate system) 1803 * to give a finer grained hint about where focus is coming from. May be null 1804 * if there is no hint. 1805 * @return Whether focus was taken. 1806 */ 1807 @SuppressWarnings({"ConstantConditions"}) 1808 protected boolean onRequestFocusInDescendants(int direction, 1809 Rect previouslyFocusedRect) { 1810 int index; 1811 int increment; 1812 int end; 1813 int count = mChildrenCount; 1814 if ((direction & FOCUS_FORWARD) != 0) { 1815 index = 0; 1816 increment = 1; 1817 end = count; 1818 } else { 1819 index = count - 1; 1820 increment = -1; 1821 end = -1; 1822 } 1823 final View[] children = mChildren; 1824 for (int i = index; i != end; i += increment) { 1825 View child = children[i]; 1826 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1827 if (child.requestFocus(direction, previouslyFocusedRect)) { 1828 return true; 1829 } 1830 } 1831 } 1832 return false; 1833 } 1834 1835 /** 1836 * {@inheritDoc} 1837 * 1838 * @hide 1839 */ 1840 @Override 1841 public void dispatchStartTemporaryDetach() { 1842 super.dispatchStartTemporaryDetach(); 1843 final int count = mChildrenCount; 1844 final View[] children = mChildren; 1845 for (int i = 0; i < count; i++) { 1846 children[i].dispatchStartTemporaryDetach(); 1847 } 1848 } 1849 1850 /** 1851 * {@inheritDoc} 1852 * 1853 * @hide 1854 */ 1855 @Override 1856 public void dispatchFinishTemporaryDetach() { 1857 super.dispatchFinishTemporaryDetach(); 1858 final int count = mChildrenCount; 1859 final View[] children = mChildren; 1860 for (int i = 0; i < count; i++) { 1861 children[i].dispatchFinishTemporaryDetach(); 1862 } 1863 } 1864 1865 /** 1866 * {@inheritDoc} 1867 */ 1868 @Override 1869 void dispatchAttachedToWindow(AttachInfo info, int visibility) { 1870 super.dispatchAttachedToWindow(info, visibility); 1871 visibility |= mViewFlags & VISIBILITY_MASK; 1872 final int count = mChildrenCount; 1873 final View[] children = mChildren; 1874 for (int i = 0; i < count; i++) { 1875 children[i].dispatchAttachedToWindow(info, visibility); 1876 } 1877 } 1878 1879 @Override 1880 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1881 boolean populated = false; 1882 for (int i = 0, count = getChildCount(); i < count; i++) { 1883 populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event); 1884 } 1885 return populated; 1886 } 1887 1888 /** 1889 * {@inheritDoc} 1890 */ 1891 @Override 1892 void dispatchDetachedFromWindow() { 1893 // If we still have a touch target, we are still in the process of 1894 // dispatching motion events to a child; we need to get rid of that 1895 // child to avoid dispatching events to it after the window is torn 1896 // down. To make sure we keep the child in a consistent state, we 1897 // first send it an ACTION_CANCEL motion event. 1898 cancelAndClearTouchTargets(null); 1899 1900 // In case view is detached while transition is running 1901 mLayoutSuppressed = false; 1902 1903 // Tear down our drag tracking 1904 mDragNotifiedChildren = null; 1905 if (mCurrentDrag != null) { 1906 mCurrentDrag.recycle(); 1907 mCurrentDrag = null; 1908 } 1909 1910 final int count = mChildrenCount; 1911 final View[] children = mChildren; 1912 for (int i = 0; i < count; i++) { 1913 children[i].dispatchDetachedFromWindow(); 1914 } 1915 super.dispatchDetachedFromWindow(); 1916 } 1917 1918 /** 1919 * {@inheritDoc} 1920 */ 1921 @Override 1922 public void setPadding(int left, int top, int right, int bottom) { 1923 super.setPadding(left, top, right, bottom); 1924 1925 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) { 1926 mGroupFlags |= FLAG_PADDING_NOT_NULL; 1927 } else { 1928 mGroupFlags &= ~FLAG_PADDING_NOT_NULL; 1929 } 1930 } 1931 1932 /** 1933 * {@inheritDoc} 1934 */ 1935 @Override 1936 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 1937 super.dispatchSaveInstanceState(container); 1938 final int count = mChildrenCount; 1939 final View[] children = mChildren; 1940 for (int i = 0; i < count; i++) { 1941 View c = children[i]; 1942 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 1943 c.dispatchSaveInstanceState(container); 1944 } 1945 } 1946 } 1947 1948 /** 1949 * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view, 1950 * not to its children. For use when overriding 1951 * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze 1952 * their own state but not the state of their children. 1953 * 1954 * @param container the container 1955 */ 1956 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { 1957 super.dispatchSaveInstanceState(container); 1958 } 1959 1960 /** 1961 * {@inheritDoc} 1962 */ 1963 @Override 1964 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 1965 super.dispatchRestoreInstanceState(container); 1966 final int count = mChildrenCount; 1967 final View[] children = mChildren; 1968 for (int i = 0; i < count; i++) { 1969 View c = children[i]; 1970 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 1971 c.dispatchRestoreInstanceState(container); 1972 } 1973 } 1974 } 1975 1976 /** 1977 * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view, 1978 * not to its children. For use when overriding 1979 * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw 1980 * their own state but not the state of their children. 1981 * 1982 * @param container the container 1983 */ 1984 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { 1985 super.dispatchRestoreInstanceState(container); 1986 } 1987 1988 /** 1989 * Enables or disables the drawing cache for each child of this view group. 1990 * 1991 * @param enabled true to enable the cache, false to dispose of it 1992 */ 1993 protected void setChildrenDrawingCacheEnabled(boolean enabled) { 1994 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) { 1995 final View[] children = mChildren; 1996 final int count = mChildrenCount; 1997 for (int i = 0; i < count; i++) { 1998 children[i].setDrawingCacheEnabled(enabled); 1999 } 2000 } 2001 } 2002 2003 @Override 2004 protected void onAnimationStart() { 2005 super.onAnimationStart(); 2006 2007 // When this ViewGroup's animation starts, build the cache for the children 2008 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2009 final int count = mChildrenCount; 2010 final View[] children = mChildren; 2011 final boolean buildCache = !isHardwareAccelerated(); 2012 2013 for (int i = 0; i < count; i++) { 2014 final View child = children[i]; 2015 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2016 child.setDrawingCacheEnabled(true); 2017 if (buildCache) { 2018 child.buildDrawingCache(true); 2019 } 2020 } 2021 } 2022 2023 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 2024 } 2025 } 2026 2027 @Override 2028 protected void onAnimationEnd() { 2029 super.onAnimationEnd(); 2030 2031 // When this ViewGroup's animation ends, destroy the cache of the children 2032 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2033 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 2034 2035 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 2036 setChildrenDrawingCacheEnabled(false); 2037 } 2038 } 2039 } 2040 2041 @Override 2042 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) { 2043 int count = mChildrenCount; 2044 int[] visibilities = null; 2045 2046 if (skipChildren) { 2047 visibilities = new int[count]; 2048 for (int i = 0; i < count; i++) { 2049 View child = getChildAt(i); 2050 visibilities[i] = child.getVisibility(); 2051 if (visibilities[i] == View.VISIBLE) { 2052 child.setVisibility(INVISIBLE); 2053 } 2054 } 2055 } 2056 2057 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren); 2058 2059 if (skipChildren) { 2060 for (int i = 0; i < count; i++) { 2061 getChildAt(i).setVisibility(visibilities[i]); 2062 } 2063 } 2064 2065 return b; 2066 } 2067 2068 /** 2069 * {@inheritDoc} 2070 */ 2071 @Override 2072 protected void dispatchDraw(Canvas canvas) { 2073 final int count = mChildrenCount; 2074 final View[] children = mChildren; 2075 int flags = mGroupFlags; 2076 2077 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { 2078 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 2079 2080 final boolean buildCache = !isHardwareAccelerated(); 2081 for (int i = 0; i < count; i++) { 2082 final View child = children[i]; 2083 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2084 final LayoutParams params = child.getLayoutParams(); 2085 attachLayoutAnimationParameters(child, params, i, count); 2086 bindLayoutAnimation(child); 2087 if (cache) { 2088 child.setDrawingCacheEnabled(true); 2089 if (buildCache) { 2090 child.buildDrawingCache(true); 2091 } 2092 } 2093 } 2094 } 2095 2096 final LayoutAnimationController controller = mLayoutAnimationController; 2097 if (controller.willOverlap()) { 2098 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE; 2099 } 2100 2101 controller.start(); 2102 2103 mGroupFlags &= ~FLAG_RUN_ANIMATION; 2104 mGroupFlags &= ~FLAG_ANIMATION_DONE; 2105 2106 if (cache) { 2107 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 2108 } 2109 2110 if (mAnimationListener != null) { 2111 mAnimationListener.onAnimationStart(controller.getAnimation()); 2112 } 2113 } 2114 2115 int saveCount = 0; 2116 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 2117 if (clipToPadding) { 2118 saveCount = canvas.save(); 2119 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, 2120 mScrollX + mRight - mLeft - mPaddingRight, 2121 mScrollY + mBottom - mTop - mPaddingBottom); 2122 2123 } 2124 2125 // We will draw our child's animation, let's reset the flag 2126 mPrivateFlags &= ~DRAW_ANIMATION; 2127 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; 2128 2129 boolean more = false; 2130 final long drawingTime = getDrawingTime(); 2131 2132 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { 2133 for (int i = 0; i < count; i++) { 2134 final View child = children[i]; 2135 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 2136 more |= drawChild(canvas, child, drawingTime); 2137 } 2138 } 2139 } else { 2140 for (int i = 0; i < count; i++) { 2141 final View child = children[getChildDrawingOrder(count, i)]; 2142 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 2143 more |= drawChild(canvas, child, drawingTime); 2144 } 2145 } 2146 } 2147 2148 // Draw any disappearing views that have animations 2149 if (mDisappearingChildren != null) { 2150 final ArrayList<View> disappearingChildren = mDisappearingChildren; 2151 final int disappearingCount = disappearingChildren.size() - 1; 2152 // Go backwards -- we may delete as animations finish 2153 for (int i = disappearingCount; i >= 0; i--) { 2154 final View child = disappearingChildren.get(i); 2155 more |= drawChild(canvas, child, drawingTime); 2156 } 2157 } 2158 2159 if (clipToPadding) { 2160 canvas.restoreToCount(saveCount); 2161 } 2162 2163 // mGroupFlags might have been updated by drawChild() 2164 flags = mGroupFlags; 2165 2166 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { 2167 invalidate(true); 2168 } 2169 2170 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && 2171 mLayoutAnimationController.isDone() && !more) { 2172 // We want to erase the drawing cache and notify the listener after the 2173 // next frame is drawn because one extra invalidate() is caused by 2174 // drawChild() after the animation is over 2175 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; 2176 final Runnable end = new Runnable() { 2177 public void run() { 2178 notifyAnimationListener(); 2179 } 2180 }; 2181 post(end); 2182 } 2183 } 2184 2185 /** 2186 * Returns the index of the child to draw for this iteration. Override this 2187 * if you want to change the drawing order of children. By default, it 2188 * returns i. 2189 * <p> 2190 * NOTE: In order for this method to be called, you must enable child ordering 2191 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}. 2192 * 2193 * @param i The current iteration. 2194 * @return The index of the child to draw this iteration. 2195 * 2196 * @see #setChildrenDrawingOrderEnabled(boolean) 2197 * @see #isChildrenDrawingOrderEnabled() 2198 */ 2199 protected int getChildDrawingOrder(int childCount, int i) { 2200 return i; 2201 } 2202 2203 private void notifyAnimationListener() { 2204 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; 2205 mGroupFlags |= FLAG_ANIMATION_DONE; 2206 2207 if (mAnimationListener != null) { 2208 final Runnable end = new Runnable() { 2209 public void run() { 2210 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation()); 2211 } 2212 }; 2213 post(end); 2214 } 2215 2216 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2217 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 2218 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 2219 setChildrenDrawingCacheEnabled(false); 2220 } 2221 } 2222 2223 invalidate(true); 2224 } 2225 2226 /** 2227 * This method is used to cause children of this ViewGroup to restore or recreate their 2228 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need 2229 * to recreate its own display list, which would happen if it went through the normal 2230 * draw/dispatchDraw mechanisms. 2231 * 2232 * @hide 2233 */ 2234 @Override 2235 protected void dispatchGetDisplayList() { 2236 final int count = mChildrenCount; 2237 final View[] children = mChildren; 2238 for (int i = 0; i < count; i++) { 2239 final View child = children[i]; 2240 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED; 2241 child.mPrivateFlags &= ~INVALIDATED; 2242 child.getDisplayList(); 2243 child.mRecreateDisplayList = false; 2244 } 2245 } 2246 2247 /** 2248 * Draw one child of this View Group. This method is responsible for getting 2249 * the canvas in the right state. This includes clipping, translating so 2250 * that the child's scrolled origin is at 0, 0, and applying any animation 2251 * transformations. 2252 * 2253 * @param canvas The canvas on which to draw the child 2254 * @param child Who to draw 2255 * @param drawingTime The time at which draw is occuring 2256 * @return True if an invalidate() was issued 2257 */ 2258 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 2259 boolean more = false; 2260 2261 final int cl = child.mLeft; 2262 final int ct = child.mTop; 2263 final int cr = child.mRight; 2264 final int cb = child.mBottom; 2265 2266 final boolean childHasIdentityMatrix = child.hasIdentityMatrix(); 2267 2268 final int flags = mGroupFlags; 2269 2270 if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) { 2271 mChildTransformation.clear(); 2272 mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION; 2273 } 2274 2275 Transformation transformToApply = null; 2276 Transformation invalidationTransform; 2277 final Animation a = child.getAnimation(); 2278 boolean concatMatrix = false; 2279 2280 boolean scalingRequired = false; 2281 boolean caching; 2282 int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE; 2283 2284 final boolean hardwareAccelerated = canvas.isHardwareAccelerated(); 2285 if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || 2286 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { 2287 caching = true; 2288 if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; 2289 } else { 2290 caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated; 2291 } 2292 2293 if (a != null) { 2294 final boolean initialized = a.isInitialized(); 2295 if (!initialized) { 2296 a.initialize(cr - cl, cb - ct, getWidth(), getHeight()); 2297 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct); 2298 child.onAnimationStart(); 2299 } 2300 2301 more = a.getTransformation(drawingTime, mChildTransformation, 2302 scalingRequired ? mAttachInfo.mApplicationScale : 1f); 2303 if (scalingRequired && mAttachInfo.mApplicationScale != 1f) { 2304 if (mInvalidationTransformation == null) { 2305 mInvalidationTransformation = new Transformation(); 2306 } 2307 invalidationTransform = mInvalidationTransformation; 2308 a.getTransformation(drawingTime, invalidationTransform, 1f); 2309 } else { 2310 invalidationTransform = mChildTransformation; 2311 } 2312 transformToApply = mChildTransformation; 2313 2314 concatMatrix = a.willChangeTransformationMatrix(); 2315 2316 if (more) { 2317 if (!a.willChangeBounds()) { 2318 if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) == 2319 FLAG_OPTIMIZE_INVALIDATE) { 2320 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 2321 } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) { 2322 // The child need to draw an animation, potentially offscreen, so 2323 // make sure we do not cancel invalidate requests 2324 mPrivateFlags |= DRAW_ANIMATION; 2325 invalidate(cl, ct, cr, cb); 2326 } 2327 } else { 2328 if (mInvalidateRegion == null) { 2329 mInvalidateRegion = new RectF(); 2330 } 2331 final RectF region = mInvalidateRegion; 2332 a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform); 2333 2334 // The child need to draw an animation, potentially offscreen, so 2335 // make sure we do not cancel invalidate requests 2336 mPrivateFlags |= DRAW_ANIMATION; 2337 2338 final int left = cl + (int) region.left; 2339 final int top = ct + (int) region.top; 2340 invalidate(left, top, left + (int) region.width(), top + (int) region.height()); 2341 } 2342 } 2343 } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == 2344 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) { 2345 final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation); 2346 if (hasTransform) { 2347 final int transformType = mChildTransformation.getTransformationType(); 2348 transformToApply = transformType != Transformation.TYPE_IDENTITY ? 2349 mChildTransformation : null; 2350 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0; 2351 } 2352 } 2353 2354 concatMatrix |= !childHasIdentityMatrix; 2355 2356 // Sets the flag as early as possible to allow draw() implementations 2357 // to call invalidate() successfully when doing animations 2358 child.mPrivateFlags |= DRAWN; 2359 2360 if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && 2361 (child.mPrivateFlags & DRAW_ANIMATION) == 0) { 2362 return more; 2363 } 2364 2365 float alpha = child.getAlpha(); 2366 // Bail out early if the view does not need to be drawn 2367 if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 && 2368 !(child instanceof SurfaceView)) { 2369 return more; 2370 } 2371 2372 if (hardwareAccelerated) { 2373 // Clear INVALIDATED flag to allow invalidation to occur during rendering, but 2374 // retain the flag's value temporarily in the mRecreateDisplayList flag 2375 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED; 2376 child.mPrivateFlags &= ~INVALIDATED; 2377 } 2378 2379 child.computeScroll(); 2380 2381 final int sx = child.mScrollX; 2382 final int sy = child.mScrollY; 2383 2384 DisplayList displayList = null; 2385 Bitmap cache = null; 2386 boolean hasDisplayList = false; 2387 if (caching) { 2388 if (!hardwareAccelerated) { 2389 if (layerType != LAYER_TYPE_NONE) { 2390 layerType = LAYER_TYPE_SOFTWARE; 2391 child.buildDrawingCache(true); 2392 } 2393 cache = child.getDrawingCache(true); 2394 } else { 2395 if (layerType == LAYER_TYPE_SOFTWARE) { 2396 child.buildDrawingCache(true); 2397 cache = child.getDrawingCache(true); 2398 } else if (layerType == LAYER_TYPE_NONE) { 2399 // Delay getting the display list until animation-driven alpha values are 2400 // set up and possibly passed on to the view 2401 hasDisplayList = child.canHaveDisplayList(); 2402 } 2403 } 2404 } 2405 2406 final boolean hasNoCache = cache == null || hasDisplayList; 2407 2408 final int restoreTo = canvas.save(); 2409 if (cache == null && !hasDisplayList) { 2410 canvas.translate(cl - sx, ct - sy); 2411 } else { 2412 canvas.translate(cl, ct); 2413 if (scalingRequired) { 2414 // mAttachInfo cannot be null, otherwise scalingRequired == false 2415 final float scale = 1.0f / mAttachInfo.mApplicationScale; 2416 canvas.scale(scale, scale); 2417 } 2418 } 2419 2420 if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) { 2421 if (transformToApply != null || !childHasIdentityMatrix) { 2422 int transX = 0; 2423 int transY = 0; 2424 2425 if (cache == null && !hasDisplayList) { 2426 transX = -sx; 2427 transY = -sy; 2428 } 2429 2430 if (transformToApply != null) { 2431 if (concatMatrix) { 2432 // Undo the scroll translation, apply the transformation matrix, 2433 // then redo the scroll translate to get the correct result. 2434 canvas.translate(-transX, -transY); 2435 canvas.concat(transformToApply.getMatrix()); 2436 canvas.translate(transX, transY); 2437 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 2438 } 2439 2440 float transformAlpha = transformToApply.getAlpha(); 2441 if (transformAlpha < 1.0f) { 2442 alpha *= transformToApply.getAlpha(); 2443 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 2444 } 2445 } 2446 2447 if (!childHasIdentityMatrix) { 2448 canvas.translate(-transX, -transY); 2449 canvas.concat(child.getMatrix()); 2450 canvas.translate(transX, transY); 2451 } 2452 } 2453 2454 if (alpha < 1.0f) { 2455 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; 2456 if (hasNoCache) { 2457 final int multipliedAlpha = (int) (255 * alpha); 2458 if (!child.onSetAlpha(multipliedAlpha)) { 2459 int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; 2460 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN || 2461 layerType != LAYER_TYPE_NONE) { 2462 layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; 2463 } 2464 if (layerType == LAYER_TYPE_NONE) { 2465 canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, 2466 multipliedAlpha, layerFlags); 2467 } 2468 } else { 2469 // Alpha is handled by the child directly, clobber the layer's alpha 2470 child.mPrivateFlags |= ALPHA_SET; 2471 } 2472 } 2473 } 2474 } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) { 2475 child.onSetAlpha(255); 2476 child.mPrivateFlags &= ~ALPHA_SET; 2477 } 2478 2479 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 2480 if (cache == null && !hasDisplayList) { 2481 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); 2482 } else { 2483 if (!scalingRequired || cache == null) { 2484 canvas.clipRect(0, 0, cr - cl, cb - ct); 2485 } else { 2486 canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); 2487 } 2488 } 2489 } 2490 2491 if (hasDisplayList) { 2492 displayList = child.getDisplayList(); 2493 } 2494 2495 if (hasNoCache) { 2496 boolean layerRendered = false; 2497 if (layerType == LAYER_TYPE_HARDWARE) { 2498 final HardwareLayer layer = child.getHardwareLayer(); 2499 if (layer != null && layer.isValid()) { 2500 child.mLayerPaint.setAlpha((int) (alpha * 255)); 2501 ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint); 2502 layerRendered = true; 2503 } else { 2504 canvas.saveLayer(sx, sy, sx + cr - cl, sy + cb - ct, child.mLayerPaint, 2505 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); 2506 } 2507 } 2508 2509 if (!layerRendered) { 2510 if (!hasDisplayList) { 2511 // Fast path for layouts with no backgrounds 2512 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 2513 if (ViewDebug.TRACE_HIERARCHY) { 2514 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); 2515 } 2516 child.mPrivateFlags &= ~DIRTY_MASK; 2517 child.dispatchDraw(canvas); 2518 } else { 2519 child.draw(canvas); 2520 } 2521 } else { 2522 child.mPrivateFlags &= ~DIRTY_MASK; 2523 ((HardwareCanvas) canvas).drawDisplayList(displayList); 2524 } 2525 } 2526 } else if (cache != null) { 2527 child.mPrivateFlags &= ~DIRTY_MASK; 2528 Paint cachePaint; 2529 2530 if (layerType == LAYER_TYPE_NONE) { 2531 cachePaint = mCachePaint; 2532 if (alpha < 1.0f) { 2533 cachePaint.setAlpha((int) (alpha * 255)); 2534 mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE; 2535 } else if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) { 2536 cachePaint.setAlpha(255); 2537 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; 2538 } 2539 } else { 2540 cachePaint = child.mLayerPaint; 2541 cachePaint.setAlpha((int) (alpha * 255)); 2542 } 2543 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); 2544 } 2545 2546 canvas.restoreToCount(restoreTo); 2547 2548 if (a != null && !more) { 2549 if (!hardwareAccelerated && !a.getFillAfter()) { 2550 child.onSetAlpha(255); 2551 } 2552 finishAnimatingView(child, a); 2553 } 2554 2555 if (more && hardwareAccelerated) { 2556 // invalidation is the trigger to recreate display lists, so if we're using 2557 // display lists to render, force an invalidate to allow the animation to 2558 // continue drawing another frame 2559 invalidate(true); 2560 if (a instanceof AlphaAnimation) { 2561 // alpha animations should cause the child to recreate its display list 2562 child.invalidate(true); 2563 } 2564 } 2565 2566 child.mRecreateDisplayList = false; 2567 2568 return more; 2569 } 2570 2571 /** 2572 * 2573 * @param enabled True if children should be drawn with layers, false otherwise. 2574 * 2575 * @hide 2576 */ 2577 public void setChildrenLayersEnabled(boolean enabled) { 2578 mDrawLayers = enabled; 2579 invalidate(true); 2580 } 2581 2582 /** 2583 * By default, children are clipped to their bounds before drawing. This 2584 * allows view groups to override this behavior for animations, etc. 2585 * 2586 * @param clipChildren true to clip children to their bounds, 2587 * false otherwise 2588 * @attr ref android.R.styleable#ViewGroup_clipChildren 2589 */ 2590 public void setClipChildren(boolean clipChildren) { 2591 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); 2592 } 2593 2594 /** 2595 * By default, children are clipped to the padding of the ViewGroup. This 2596 * allows view groups to override this behavior 2597 * 2598 * @param clipToPadding true to clip children to the padding of the 2599 * group, false otherwise 2600 * @attr ref android.R.styleable#ViewGroup_clipToPadding 2601 */ 2602 public void setClipToPadding(boolean clipToPadding) { 2603 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding); 2604 } 2605 2606 /** 2607 * {@inheritDoc} 2608 */ 2609 @Override 2610 public void dispatchSetSelected(boolean selected) { 2611 final View[] children = mChildren; 2612 final int count = mChildrenCount; 2613 for (int i = 0; i < count; i++) { 2614 children[i].setSelected(selected); 2615 } 2616 } 2617 2618 /** 2619 * {@inheritDoc} 2620 */ 2621 @Override 2622 public void dispatchSetActivated(boolean activated) { 2623 final View[] children = mChildren; 2624 final int count = mChildrenCount; 2625 for (int i = 0; i < count; i++) { 2626 children[i].setActivated(activated); 2627 } 2628 } 2629 2630 @Override 2631 protected void dispatchSetPressed(boolean pressed) { 2632 final View[] children = mChildren; 2633 final int count = mChildrenCount; 2634 for (int i = 0; i < count; i++) { 2635 children[i].setPressed(pressed); 2636 } 2637 } 2638 2639 /** 2640 * When this property is set to true, this ViewGroup supports static transformations on 2641 * children; this causes 2642 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 2643 * invoked when a child is drawn. 2644 * 2645 * Any subclass overriding 2646 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 2647 * set this property to true. 2648 * 2649 * @param enabled True to enable static transformations on children, false otherwise. 2650 * 2651 * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS 2652 */ 2653 protected void setStaticTransformationsEnabled(boolean enabled) { 2654 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled); 2655 } 2656 2657 /** 2658 * {@inheritDoc} 2659 * 2660 * @see #setStaticTransformationsEnabled(boolean) 2661 */ 2662 protected boolean getChildStaticTransformation(View child, Transformation t) { 2663 return false; 2664 } 2665 2666 /** 2667 * {@hide} 2668 */ 2669 @Override 2670 protected View findViewTraversal(int id) { 2671 if (id == mID) { 2672 return this; 2673 } 2674 2675 final View[] where = mChildren; 2676 final int len = mChildrenCount; 2677 2678 for (int i = 0; i < len; i++) { 2679 View v = where[i]; 2680 2681 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 2682 v = v.findViewById(id); 2683 2684 if (v != null) { 2685 return v; 2686 } 2687 } 2688 } 2689 2690 return null; 2691 } 2692 2693 /** 2694 * {@hide} 2695 */ 2696 @Override 2697 protected View findViewWithTagTraversal(Object tag) { 2698 if (tag != null && tag.equals(mTag)) { 2699 return this; 2700 } 2701 2702 final View[] where = mChildren; 2703 final int len = mChildrenCount; 2704 2705 for (int i = 0; i < len; i++) { 2706 View v = where[i]; 2707 2708 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 2709 v = v.findViewWithTag(tag); 2710 2711 if (v != null) { 2712 return v; 2713 } 2714 } 2715 } 2716 2717 return null; 2718 } 2719 2720 /** 2721 * {@hide} 2722 */ 2723 @Override 2724 protected View findViewByPredicateTraversal(Predicate<View> predicate) { 2725 if (predicate.apply(this)) { 2726 return this; 2727 } 2728 2729 final View[] where = mChildren; 2730 final int len = mChildrenCount; 2731 2732 for (int i = 0; i < len; i++) { 2733 View v = where[i]; 2734 2735 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { 2736 v = v.findViewByPredicate(predicate); 2737 2738 if (v != null) { 2739 return v; 2740 } 2741 } 2742 } 2743 2744 return null; 2745 } 2746 2747 /** 2748 * Adds a child view. If no layout parameters are already set on the child, the 2749 * default parameters for this ViewGroup are set on the child. 2750 * 2751 * @param child the child view to add 2752 * 2753 * @see #generateDefaultLayoutParams() 2754 */ 2755 public void addView(View child) { 2756 addView(child, -1); 2757 } 2758 2759 /** 2760 * Adds a child view. If no layout parameters are already set on the child, the 2761 * default parameters for this ViewGroup are set on the child. 2762 * 2763 * @param child the child view to add 2764 * @param index the position at which to add the child 2765 * 2766 * @see #generateDefaultLayoutParams() 2767 */ 2768 public void addView(View child, int index) { 2769 LayoutParams params = child.getLayoutParams(); 2770 if (params == null) { 2771 params = generateDefaultLayoutParams(); 2772 if (params == null) { 2773 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); 2774 } 2775 } 2776 addView(child, index, params); 2777 } 2778 2779 /** 2780 * Adds a child view with this ViewGroup's default layout parameters and the 2781 * specified width and height. 2782 * 2783 * @param child the child view to add 2784 */ 2785 public void addView(View child, int width, int height) { 2786 final LayoutParams params = generateDefaultLayoutParams(); 2787 params.width = width; 2788 params.height = height; 2789 addView(child, -1, params); 2790 } 2791 2792 /** 2793 * Adds a child view with the specified layout parameters. 2794 * 2795 * @param child the child view to add 2796 * @param params the layout parameters to set on the child 2797 */ 2798 public void addView(View child, LayoutParams params) { 2799 addView(child, -1, params); 2800 } 2801 2802 /** 2803 * Adds a child view with the specified layout parameters. 2804 * 2805 * @param child the child view to add 2806 * @param index the position at which to add the child 2807 * @param params the layout parameters to set on the child 2808 */ 2809 public void addView(View child, int index, LayoutParams params) { 2810 if (DBG) { 2811 System.out.println(this + " addView"); 2812 } 2813 2814 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 2815 // therefore, we call requestLayout() on ourselves before, so that the child's request 2816 // will be blocked at our level 2817 requestLayout(); 2818 invalidate(true); 2819 addViewInner(child, index, params, false); 2820 } 2821 2822 /** 2823 * {@inheritDoc} 2824 */ 2825 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 2826 if (!checkLayoutParams(params)) { 2827 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this); 2828 } 2829 if (view.mParent != this) { 2830 throw new IllegalArgumentException("Given view not a child of " + this); 2831 } 2832 view.setLayoutParams(params); 2833 } 2834 2835 /** 2836 * {@inheritDoc} 2837 */ 2838 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 2839 return p != null; 2840 } 2841 2842 /** 2843 * Interface definition for a callback to be invoked when the hierarchy 2844 * within this view changed. The hierarchy changes whenever a child is added 2845 * to or removed from this view. 2846 */ 2847 public interface OnHierarchyChangeListener { 2848 /** 2849 * Called when a new child is added to a parent view. 2850 * 2851 * @param parent the view in which a child was added 2852 * @param child the new child view added in the hierarchy 2853 */ 2854 void onChildViewAdded(View parent, View child); 2855 2856 /** 2857 * Called when a child is removed from a parent view. 2858 * 2859 * @param parent the view from which the child was removed 2860 * @param child the child removed from the hierarchy 2861 */ 2862 void onChildViewRemoved(View parent, View child); 2863 } 2864 2865 /** 2866 * Register a callback to be invoked when a child is added to or removed 2867 * from this view. 2868 * 2869 * @param listener the callback to invoke on hierarchy change 2870 */ 2871 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 2872 mOnHierarchyChangeListener = listener; 2873 } 2874 2875 /** 2876 * Adds a view during layout. This is useful if in your onLayout() method, 2877 * you need to add more views (as does the list view for example). 2878 * 2879 * If index is negative, it means put it at the end of the list. 2880 * 2881 * @param child the view to add to the group 2882 * @param index the index at which the child must be added 2883 * @param params the layout parameters to associate with the child 2884 * @return true if the child was added, false otherwise 2885 */ 2886 protected boolean addViewInLayout(View child, int index, LayoutParams params) { 2887 return addViewInLayout(child, index, params, false); 2888 } 2889 2890 /** 2891 * Adds a view during layout. This is useful if in your onLayout() method, 2892 * you need to add more views (as does the list view for example). 2893 * 2894 * If index is negative, it means put it at the end of the list. 2895 * 2896 * @param child the view to add to the group 2897 * @param index the index at which the child must be added 2898 * @param params the layout parameters to associate with the child 2899 * @param preventRequestLayout if true, calling this method will not trigger a 2900 * layout request on child 2901 * @return true if the child was added, false otherwise 2902 */ 2903 protected boolean addViewInLayout(View child, int index, LayoutParams params, 2904 boolean preventRequestLayout) { 2905 child.mParent = null; 2906 addViewInner(child, index, params, preventRequestLayout); 2907 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; 2908 return true; 2909 } 2910 2911 /** 2912 * Prevents the specified child to be laid out during the next layout pass. 2913 * 2914 * @param child the child on which to perform the cleanup 2915 */ 2916 protected void cleanupLayoutState(View child) { 2917 child.mPrivateFlags &= ~View.FORCE_LAYOUT; 2918 } 2919 2920 private void addViewInner(View child, int index, LayoutParams params, 2921 boolean preventRequestLayout) { 2922 2923 if (child.getParent() != null) { 2924 throw new IllegalStateException("The specified child already has a parent. " + 2925 "You must call removeView() on the child's parent first."); 2926 } 2927 2928 if (mTransition != null) { 2929 mTransition.addChild(this, child); 2930 } 2931 2932 if (!checkLayoutParams(params)) { 2933 params = generateLayoutParams(params); 2934 } 2935 2936 if (preventRequestLayout) { 2937 child.mLayoutParams = params; 2938 } else { 2939 child.setLayoutParams(params); 2940 } 2941 2942 if (index < 0) { 2943 index = mChildrenCount; 2944 } 2945 2946 addInArray(child, index); 2947 2948 // tell our children 2949 if (preventRequestLayout) { 2950 child.assignParent(this); 2951 } else { 2952 child.mParent = this; 2953 } 2954 2955 if (child.hasFocus()) { 2956 requestChildFocus(child, child.findFocus()); 2957 } 2958 2959 AttachInfo ai = mAttachInfo; 2960 if (ai != null) { 2961 boolean lastKeepOn = ai.mKeepScreenOn; 2962 ai.mKeepScreenOn = false; 2963 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 2964 if (ai.mKeepScreenOn) { 2965 needGlobalAttributesUpdate(true); 2966 } 2967 ai.mKeepScreenOn = lastKeepOn; 2968 } 2969 2970 if (mOnHierarchyChangeListener != null) { 2971 mOnHierarchyChangeListener.onChildViewAdded(this, child); 2972 } 2973 2974 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 2975 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 2976 } 2977 } 2978 2979 private void addInArray(View child, int index) { 2980 View[] children = mChildren; 2981 final int count = mChildrenCount; 2982 final int size = children.length; 2983 if (index == count) { 2984 if (size == count) { 2985 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 2986 System.arraycopy(children, 0, mChildren, 0, size); 2987 children = mChildren; 2988 } 2989 children[mChildrenCount++] = child; 2990 } else if (index < count) { 2991 if (size == count) { 2992 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 2993 System.arraycopy(children, 0, mChildren, 0, index); 2994 System.arraycopy(children, index, mChildren, index + 1, count - index); 2995 children = mChildren; 2996 } else { 2997 System.arraycopy(children, index, children, index + 1, count - index); 2998 } 2999 children[index] = child; 3000 mChildrenCount++; 3001 if (mLastTouchDownIndex >= index) { 3002 mLastTouchDownIndex++; 3003 } 3004 } else { 3005 throw new IndexOutOfBoundsException("index=" + index + " count=" + count); 3006 } 3007 } 3008 3009 // This method also sets the child's mParent to null 3010 private void removeFromArray(int index) { 3011 final View[] children = mChildren; 3012 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) { 3013 children[index].mParent = null; 3014 } 3015 final int count = mChildrenCount; 3016 if (index == count - 1) { 3017 children[--mChildrenCount] = null; 3018 } else if (index >= 0 && index < count) { 3019 System.arraycopy(children, index + 1, children, index, count - index - 1); 3020 children[--mChildrenCount] = null; 3021 } else { 3022 throw new IndexOutOfBoundsException(); 3023 } 3024 if (mLastTouchDownIndex == index) { 3025 mLastTouchDownTime = 0; 3026 mLastTouchDownIndex = -1; 3027 } else if (mLastTouchDownIndex > index) { 3028 mLastTouchDownIndex--; 3029 } 3030 } 3031 3032 // This method also sets the children's mParent to null 3033 private void removeFromArray(int start, int count) { 3034 final View[] children = mChildren; 3035 final int childrenCount = mChildrenCount; 3036 3037 start = Math.max(0, start); 3038 final int end = Math.min(childrenCount, start + count); 3039 3040 if (start == end) { 3041 return; 3042 } 3043 3044 if (end == childrenCount) { 3045 for (int i = start; i < end; i++) { 3046 children[i].mParent = null; 3047 children[i] = null; 3048 } 3049 } else { 3050 for (int i = start; i < end; i++) { 3051 children[i].mParent = null; 3052 } 3053 3054 // Since we're looping above, we might as well do the copy, but is arraycopy() 3055 // faster than the extra 2 bounds checks we would do in the loop? 3056 System.arraycopy(children, end, children, start, childrenCount - end); 3057 3058 for (int i = childrenCount - (end - start); i < childrenCount; i++) { 3059 children[i] = null; 3060 } 3061 } 3062 3063 mChildrenCount -= (end - start); 3064 } 3065 3066 private void bindLayoutAnimation(View child) { 3067 Animation a = mLayoutAnimationController.getAnimationForView(child); 3068 child.setAnimation(a); 3069 } 3070 3071 /** 3072 * Subclasses should override this method to set layout animation 3073 * parameters on the supplied child. 3074 * 3075 * @param child the child to associate with animation parameters 3076 * @param params the child's layout parameters which hold the animation 3077 * parameters 3078 * @param index the index of the child in the view group 3079 * @param count the number of children in the view group 3080 */ 3081 protected void attachLayoutAnimationParameters(View child, 3082 LayoutParams params, int index, int count) { 3083 LayoutAnimationController.AnimationParameters animationParams = 3084 params.layoutAnimationParameters; 3085 if (animationParams == null) { 3086 animationParams = new LayoutAnimationController.AnimationParameters(); 3087 params.layoutAnimationParameters = animationParams; 3088 } 3089 3090 animationParams.count = count; 3091 animationParams.index = index; 3092 } 3093 3094 /** 3095 * {@inheritDoc} 3096 */ 3097 public void removeView(View view) { 3098 removeViewInternal(view); 3099 requestLayout(); 3100 invalidate(true); 3101 } 3102 3103 /** 3104 * Removes a view during layout. This is useful if in your onLayout() method, 3105 * you need to remove more views. 3106 * 3107 * @param view the view to remove from the group 3108 */ 3109 public void removeViewInLayout(View view) { 3110 removeViewInternal(view); 3111 } 3112 3113 /** 3114 * Removes a range of views during layout. This is useful if in your onLayout() method, 3115 * you need to remove more views. 3116 * 3117 * @param start the index of the first view to remove from the group 3118 * @param count the number of views to remove from the group 3119 */ 3120 public void removeViewsInLayout(int start, int count) { 3121 removeViewsInternal(start, count); 3122 } 3123 3124 /** 3125 * Removes the view at the specified position in the group. 3126 * 3127 * @param index the position in the group of the view to remove 3128 */ 3129 public void removeViewAt(int index) { 3130 removeViewInternal(index, getChildAt(index)); 3131 requestLayout(); 3132 invalidate(true); 3133 } 3134 3135 /** 3136 * Removes the specified range of views from the group. 3137 * 3138 * @param start the first position in the group of the range of views to remove 3139 * @param count the number of views to remove 3140 */ 3141 public void removeViews(int start, int count) { 3142 removeViewsInternal(start, count); 3143 requestLayout(); 3144 invalidate(true); 3145 } 3146 3147 private void removeViewInternal(View view) { 3148 final int index = indexOfChild(view); 3149 if (index >= 0) { 3150 removeViewInternal(index, view); 3151 } 3152 } 3153 3154 private void removeViewInternal(int index, View view) { 3155 3156 if (mTransition != null) { 3157 mTransition.removeChild(this, view); 3158 } 3159 3160 boolean clearChildFocus = false; 3161 if (view == mFocused) { 3162 view.clearFocusForRemoval(); 3163 clearChildFocus = true; 3164 } 3165 3166 if (view.getAnimation() != null || 3167 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3168 addDisappearingView(view); 3169 } else if (view.mAttachInfo != null) { 3170 view.dispatchDetachedFromWindow(); 3171 } 3172 3173 if (mOnHierarchyChangeListener != null) { 3174 mOnHierarchyChangeListener.onChildViewRemoved(this, view); 3175 } 3176 3177 needGlobalAttributesUpdate(false); 3178 3179 removeFromArray(index); 3180 3181 if (clearChildFocus) { 3182 clearChildFocus(view); 3183 } 3184 } 3185 3186 /** 3187 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 3188 * not null, changes in layout which occur because of children being added to or removed from 3189 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 3190 * object. By default, the transition object is null (so layout changes are not animated). 3191 * 3192 * @param transition The LayoutTransition object that will animated changes in layout. A value 3193 * of <code>null</code> means no transition will run on layout changes. 3194 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 3195 */ 3196 public void setLayoutTransition(LayoutTransition transition) { 3197 if (mTransition != null) { 3198 mTransition.removeTransitionListener(mLayoutTransitionListener); 3199 } 3200 mTransition = transition; 3201 if (mTransition != null) { 3202 mTransition.addTransitionListener(mLayoutTransitionListener); 3203 } 3204 } 3205 3206 /** 3207 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 3208 * not null, changes in layout which occur because of children being added to or removed from 3209 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 3210 * object. By default, the transition object is null (so layout changes are not animated). 3211 * 3212 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout. 3213 * A value of <code>null</code> means no transition will run on layout changes. 3214 */ 3215 public LayoutTransition getLayoutTransition() { 3216 return mTransition; 3217 } 3218 3219 private void removeViewsInternal(int start, int count) { 3220 final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener; 3221 final boolean notifyListener = onHierarchyChangeListener != null; 3222 final View focused = mFocused; 3223 final boolean detach = mAttachInfo != null; 3224 View clearChildFocus = null; 3225 3226 final View[] children = mChildren; 3227 final int end = start + count; 3228 3229 for (int i = start; i < end; i++) { 3230 final View view = children[i]; 3231 3232 if (mTransition != null) { 3233 mTransition.removeChild(this, view); 3234 } 3235 3236 if (view == focused) { 3237 view.clearFocusForRemoval(); 3238 clearChildFocus = view; 3239 } 3240 3241 if (view.getAnimation() != null || 3242 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3243 addDisappearingView(view); 3244 } else if (detach) { 3245 view.dispatchDetachedFromWindow(); 3246 } 3247 3248 needGlobalAttributesUpdate(false); 3249 3250 if (notifyListener) { 3251 onHierarchyChangeListener.onChildViewRemoved(this, view); 3252 } 3253 } 3254 3255 removeFromArray(start, count); 3256 3257 if (clearChildFocus != null) { 3258 clearChildFocus(clearChildFocus); 3259 } 3260 } 3261 3262 /** 3263 * Call this method to remove all child views from the 3264 * ViewGroup. 3265 */ 3266 public void removeAllViews() { 3267 removeAllViewsInLayout(); 3268 requestLayout(); 3269 invalidate(true); 3270 } 3271 3272 /** 3273 * Called by a ViewGroup subclass to remove child views from itself, 3274 * when it must first know its size on screen before it can calculate how many 3275 * child views it will render. An example is a Gallery or a ListView, which 3276 * may "have" 50 children, but actually only render the number of children 3277 * that can currently fit inside the object on screen. Do not call 3278 * this method unless you are extending ViewGroup and understand the 3279 * view measuring and layout pipeline. 3280 */ 3281 public void removeAllViewsInLayout() { 3282 final int count = mChildrenCount; 3283 if (count <= 0) { 3284 return; 3285 } 3286 3287 final View[] children = mChildren; 3288 mChildrenCount = 0; 3289 3290 final OnHierarchyChangeListener listener = mOnHierarchyChangeListener; 3291 final boolean notify = listener != null; 3292 final View focused = mFocused; 3293 final boolean detach = mAttachInfo != null; 3294 View clearChildFocus = null; 3295 3296 needGlobalAttributesUpdate(false); 3297 3298 for (int i = count - 1; i >= 0; i--) { 3299 final View view = children[i]; 3300 3301 if (mTransition != null) { 3302 mTransition.removeChild(this, view); 3303 } 3304 3305 if (view == focused) { 3306 view.clearFocusForRemoval(); 3307 clearChildFocus = view; 3308 } 3309 3310 if (view.getAnimation() != null || 3311 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3312 addDisappearingView(view); 3313 } else if (detach) { 3314 view.dispatchDetachedFromWindow(); 3315 } 3316 3317 if (notify) { 3318 listener.onChildViewRemoved(this, view); 3319 } 3320 3321 view.mParent = null; 3322 children[i] = null; 3323 } 3324 3325 if (clearChildFocus != null) { 3326 clearChildFocus(clearChildFocus); 3327 } 3328 } 3329 3330 /** 3331 * Finishes the removal of a detached view. This method will dispatch the detached from 3332 * window event and notify the hierarchy change listener. 3333 * 3334 * @param child the child to be definitely removed from the view hierarchy 3335 * @param animate if true and the view has an animation, the view is placed in the 3336 * disappearing views list, otherwise, it is detached from the window 3337 * 3338 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3339 * @see #detachAllViewsFromParent() 3340 * @see #detachViewFromParent(View) 3341 * @see #detachViewFromParent(int) 3342 */ 3343 protected void removeDetachedView(View child, boolean animate) { 3344 if (mTransition != null) { 3345 mTransition.removeChild(this, child); 3346 } 3347 3348 if (child == mFocused) { 3349 child.clearFocus(); 3350 } 3351 3352 if ((animate && child.getAnimation() != null) || 3353 (mTransitioningViews != null && mTransitioningViews.contains(child))) { 3354 addDisappearingView(child); 3355 } else if (child.mAttachInfo != null) { 3356 child.dispatchDetachedFromWindow(); 3357 } 3358 3359 if (mOnHierarchyChangeListener != null) { 3360 mOnHierarchyChangeListener.onChildViewRemoved(this, child); 3361 } 3362 } 3363 3364 /** 3365 * Attaches a view to this view group. Attaching a view assigns this group as the parent, 3366 * sets the layout parameters and puts the view in the list of children so it can be retrieved 3367 * by calling {@link #getChildAt(int)}. 3368 * 3369 * This method should be called only for view which were detached from their parent. 3370 * 3371 * @param child the child to attach 3372 * @param index the index at which the child should be attached 3373 * @param params the layout parameters of the child 3374 * 3375 * @see #removeDetachedView(View, boolean) 3376 * @see #detachAllViewsFromParent() 3377 * @see #detachViewFromParent(View) 3378 * @see #detachViewFromParent(int) 3379 */ 3380 protected void attachViewToParent(View child, int index, LayoutParams params) { 3381 child.mLayoutParams = params; 3382 3383 if (index < 0) { 3384 index = mChildrenCount; 3385 } 3386 3387 addInArray(child, index); 3388 3389 child.mParent = this; 3390 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | 3391 DRAWN | INVALIDATED; 3392 this.mPrivateFlags |= INVALIDATED; 3393 3394 if (child.hasFocus()) { 3395 requestChildFocus(child, child.findFocus()); 3396 } 3397 } 3398 3399 /** 3400 * Detaches a view from its parent. Detaching a view should be temporary and followed 3401 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3402 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 3403 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3404 * 3405 * @param child the child to detach 3406 * 3407 * @see #detachViewFromParent(int) 3408 * @see #detachViewsFromParent(int, int) 3409 * @see #detachAllViewsFromParent() 3410 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3411 * @see #removeDetachedView(View, boolean) 3412 */ 3413 protected void detachViewFromParent(View child) { 3414 removeFromArray(indexOfChild(child)); 3415 } 3416 3417 /** 3418 * Detaches a view from its parent. Detaching a view should be temporary and followed 3419 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3420 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 3421 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3422 * 3423 * @param index the index of the child to detach 3424 * 3425 * @see #detachViewFromParent(View) 3426 * @see #detachAllViewsFromParent() 3427 * @see #detachViewsFromParent(int, int) 3428 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3429 * @see #removeDetachedView(View, boolean) 3430 */ 3431 protected void detachViewFromParent(int index) { 3432 removeFromArray(index); 3433 } 3434 3435 /** 3436 * Detaches a range of view from their parent. Detaching a view should be temporary and followed 3437 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3438 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its 3439 * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3440 * 3441 * @param start the first index of the childrend range to detach 3442 * @param count the number of children to detach 3443 * 3444 * @see #detachViewFromParent(View) 3445 * @see #detachViewFromParent(int) 3446 * @see #detachAllViewsFromParent() 3447 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3448 * @see #removeDetachedView(View, boolean) 3449 */ 3450 protected void detachViewsFromParent(int start, int count) { 3451 removeFromArray(start, count); 3452 } 3453 3454 /** 3455 * Detaches all views from the parent. Detaching a view should be temporary and followed 3456 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 3457 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, 3458 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}. 3459 * 3460 * @see #detachViewFromParent(View) 3461 * @see #detachViewFromParent(int) 3462 * @see #detachViewsFromParent(int, int) 3463 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3464 * @see #removeDetachedView(View, boolean) 3465 */ 3466 protected void detachAllViewsFromParent() { 3467 final int count = mChildrenCount; 3468 if (count <= 0) { 3469 return; 3470 } 3471 3472 final View[] children = mChildren; 3473 mChildrenCount = 0; 3474 3475 for (int i = count - 1; i >= 0; i--) { 3476 children[i].mParent = null; 3477 children[i] = null; 3478 } 3479 } 3480 3481 /** 3482 * Don't call or override this method. It is used for the implementation of 3483 * the view hierarchy. 3484 */ 3485 public final void invalidateChild(View child, final Rect dirty) { 3486 if (ViewDebug.TRACE_HIERARCHY) { 3487 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD); 3488 } 3489 3490 ViewParent parent = this; 3491 3492 final AttachInfo attachInfo = mAttachInfo; 3493 if (attachInfo != null) { 3494 // If the child is drawing an animation, we want to copy this flag onto 3495 // ourselves and the parent to make sure the invalidate request goes 3496 // through 3497 final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; 3498 3499 if (dirty == null) { 3500 if (child.mLayerType != LAYER_TYPE_NONE) { 3501 mPrivateFlags |= INVALIDATED; 3502 mPrivateFlags &= ~DRAWING_CACHE_VALID; 3503 } 3504 do { 3505 View view = null; 3506 if (parent instanceof View) { 3507 view = (View) parent; 3508 if (view.mLayerType != LAYER_TYPE_NONE && 3509 view.getParent() instanceof View) { 3510 final View grandParent = (View) view.getParent(); 3511 grandParent.mPrivateFlags |= INVALIDATED; 3512 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; 3513 } 3514 if ((view.mPrivateFlags & DIRTY_MASK) != 0) { 3515 // already marked dirty - we're done 3516 break; 3517 } 3518 } 3519 3520 if (drawAnimation) { 3521 if (view != null) { 3522 view.mPrivateFlags |= DRAW_ANIMATION; 3523 } else if (parent instanceof ViewRoot) { 3524 ((ViewRoot) parent).mIsAnimating = true; 3525 } 3526 } 3527 3528 if (parent instanceof ViewRoot) { 3529 ((ViewRoot) parent).invalidate(); 3530 parent = null; 3531 } else if (view != null) { 3532 if ((view.mPrivateFlags & DRAWN) == DRAWN || 3533 (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) { 3534 view.mPrivateFlags &= ~DRAWING_CACHE_VALID; 3535 view.mPrivateFlags |= DIRTY; 3536 parent = view.mParent; 3537 } else { 3538 parent = null; 3539 } 3540 } 3541 } while (parent != null); 3542 } else { 3543 // Check whether the child that requests the invalidate is fully opaque 3544 final boolean isOpaque = child.isOpaque() && !drawAnimation && 3545 child.getAnimation() == null; 3546 // Mark the child as dirty, using the appropriate flag 3547 // Make sure we do not set both flags at the same time 3548 int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; 3549 3550 final int[] location = attachInfo.mInvalidateChildLocation; 3551 location[CHILD_LEFT_INDEX] = child.mLeft; 3552 location[CHILD_TOP_INDEX] = child.mTop; 3553 Matrix childMatrix = child.getMatrix(); 3554 if (!childMatrix.isIdentity()) { 3555 RectF boundingRect = attachInfo.mTmpTransformRect; 3556 boundingRect.set(dirty); 3557 childMatrix.mapRect(boundingRect); 3558 dirty.set((int) boundingRect.left, (int) boundingRect.top, 3559 (int) (boundingRect.right + 0.5f), 3560 (int) (boundingRect.bottom + 0.5f)); 3561 } 3562 3563 if (child.mLayerType != LAYER_TYPE_NONE) { 3564 mPrivateFlags |= INVALIDATED; 3565 mPrivateFlags &= ~DRAWING_CACHE_VALID; 3566 } 3567 do { 3568 View view = null; 3569 if (parent instanceof View) { 3570 view = (View) parent; 3571 if (view.mLayerType != LAYER_TYPE_NONE && 3572 view.getParent() instanceof View) { 3573 final View grandParent = (View) view.getParent(); 3574 grandParent.mPrivateFlags |= INVALIDATED; 3575 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; 3576 } 3577 } 3578 3579 if (drawAnimation) { 3580 if (view != null) { 3581 view.mPrivateFlags |= DRAW_ANIMATION; 3582 } else if (parent instanceof ViewRoot) { 3583 ((ViewRoot) parent).mIsAnimating = true; 3584 } 3585 } 3586 3587 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 3588 // flag coming from the child that initiated the invalidate 3589 if (view != null) { 3590 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && 3591 view.getSolidColor() == 0 && !view.isOpaque()) { 3592 opaqueFlag = DIRTY; 3593 } 3594 if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) { 3595 view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; 3596 } 3597 } 3598 3599 parent = parent.invalidateChildInParent(location, dirty); 3600 if (view != null) { 3601 // Account for transform on current parent 3602 Matrix m = view.getMatrix(); 3603 if (!m.isIdentity()) { 3604 RectF boundingRect = attachInfo.mTmpTransformRect; 3605 boundingRect.set(dirty); 3606 m.mapRect(boundingRect); 3607 dirty.set((int) boundingRect.left, (int) boundingRect.top, 3608 (int) (boundingRect.right + 0.5f), 3609 (int) (boundingRect.bottom + 0.5f)); 3610 } 3611 } 3612 } while (parent != null); 3613 } 3614 } 3615 } 3616 3617 /** 3618 * Don't call or override this method. It is used for the implementation of 3619 * the view hierarchy. 3620 * 3621 * This implementation returns null if this ViewGroup does not have a parent, 3622 * if this ViewGroup is already fully invalidated or if the dirty rectangle 3623 * does not intersect with this ViewGroup's bounds. 3624 */ 3625 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 3626 if (ViewDebug.TRACE_HIERARCHY) { 3627 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT); 3628 } 3629 3630 if ((mPrivateFlags & DRAWN) == DRAWN || 3631 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) { 3632 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != 3633 FLAG_OPTIMIZE_INVALIDATE) { 3634 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, 3635 location[CHILD_TOP_INDEX] - mScrollY); 3636 3637 final int left = mLeft; 3638 final int top = mTop; 3639 3640 if (dirty.intersect(0, 0, mRight - left, mBottom - top) || 3641 (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { 3642 mPrivateFlags &= ~DRAWING_CACHE_VALID; 3643 3644 location[CHILD_LEFT_INDEX] = left; 3645 location[CHILD_TOP_INDEX] = top; 3646 3647 return mParent; 3648 } 3649 } else { 3650 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID; 3651 3652 location[CHILD_LEFT_INDEX] = mLeft; 3653 location[CHILD_TOP_INDEX] = mTop; 3654 3655 dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX], 3656 mBottom - location[CHILD_TOP_INDEX]); 3657 3658 return mParent; 3659 } 3660 } 3661 3662 return null; 3663 } 3664 3665 /** 3666 * Offset a rectangle that is in a descendant's coordinate 3667 * space into our coordinate space. 3668 * @param descendant A descendant of this view 3669 * @param rect A rectangle defined in descendant's coordinate space. 3670 */ 3671 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { 3672 offsetRectBetweenParentAndChild(descendant, rect, true, false); 3673 } 3674 3675 /** 3676 * Offset a rectangle that is in our coordinate space into an ancestor's 3677 * coordinate space. 3678 * @param descendant A descendant of this view 3679 * @param rect A rectangle defined in descendant's coordinate space. 3680 */ 3681 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { 3682 offsetRectBetweenParentAndChild(descendant, rect, false, false); 3683 } 3684 3685 /** 3686 * Helper method that offsets a rect either from parent to descendant or 3687 * descendant to parent. 3688 */ 3689 void offsetRectBetweenParentAndChild(View descendant, Rect rect, 3690 boolean offsetFromChildToParent, boolean clipToBounds) { 3691 3692 // already in the same coord system :) 3693 if (descendant == this) { 3694 return; 3695 } 3696 3697 ViewParent theParent = descendant.mParent; 3698 3699 // search and offset up to the parent 3700 while ((theParent != null) 3701 && (theParent instanceof View) 3702 && (theParent != this)) { 3703 3704 if (offsetFromChildToParent) { 3705 rect.offset(descendant.mLeft - descendant.mScrollX, 3706 descendant.mTop - descendant.mScrollY); 3707 if (clipToBounds) { 3708 View p = (View) theParent; 3709 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 3710 } 3711 } else { 3712 if (clipToBounds) { 3713 View p = (View) theParent; 3714 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 3715 } 3716 rect.offset(descendant.mScrollX - descendant.mLeft, 3717 descendant.mScrollY - descendant.mTop); 3718 } 3719 3720 descendant = (View) theParent; 3721 theParent = descendant.mParent; 3722 } 3723 3724 // now that we are up to this view, need to offset one more time 3725 // to get into our coordinate space 3726 if (theParent == this) { 3727 if (offsetFromChildToParent) { 3728 rect.offset(descendant.mLeft - descendant.mScrollX, 3729 descendant.mTop - descendant.mScrollY); 3730 } else { 3731 rect.offset(descendant.mScrollX - descendant.mLeft, 3732 descendant.mScrollY - descendant.mTop); 3733 } 3734 } else { 3735 throw new IllegalArgumentException("parameter must be a descendant of this view"); 3736 } 3737 } 3738 3739 /** 3740 * Offset the vertical location of all children of this view by the specified number of pixels. 3741 * 3742 * @param offset the number of pixels to offset 3743 * 3744 * @hide 3745 */ 3746 public void offsetChildrenTopAndBottom(int offset) { 3747 final int count = mChildrenCount; 3748 final View[] children = mChildren; 3749 3750 for (int i = 0; i < count; i++) { 3751 final View v = children[i]; 3752 v.mTop += offset; 3753 v.mBottom += offset; 3754 } 3755 } 3756 3757 /** 3758 * {@inheritDoc} 3759 */ 3760 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 3761 int dx = child.mLeft - mScrollX; 3762 int dy = child.mTop - mScrollY; 3763 if (offset != null) { 3764 offset.x += dx; 3765 offset.y += dy; 3766 } 3767 r.offset(dx, dy); 3768 return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) && 3769 (mParent == null || mParent.getChildVisibleRect(this, r, offset)); 3770 } 3771 3772 /** 3773 * {@inheritDoc} 3774 */ 3775 @Override 3776 public final void layout(int l, int t, int r, int b) { 3777 if (mTransition == null || !mTransition.isChangingLayout()) { 3778 super.layout(l, t, r, b); 3779 } else { 3780 // record the fact that we noop'd it; request layout when transition finishes 3781 mLayoutSuppressed = true; 3782 } 3783 } 3784 3785 /** 3786 * {@inheritDoc} 3787 */ 3788 @Override 3789 protected abstract void onLayout(boolean changed, 3790 int l, int t, int r, int b); 3791 3792 /** 3793 * Indicates whether the view group has the ability to animate its children 3794 * after the first layout. 3795 * 3796 * @return true if the children can be animated, false otherwise 3797 */ 3798 protected boolean canAnimate() { 3799 return mLayoutAnimationController != null; 3800 } 3801 3802 /** 3803 * Runs the layout animation. Calling this method triggers a relayout of 3804 * this view group. 3805 */ 3806 public void startLayoutAnimation() { 3807 if (mLayoutAnimationController != null) { 3808 mGroupFlags |= FLAG_RUN_ANIMATION; 3809 requestLayout(); 3810 } 3811 } 3812 3813 /** 3814 * Schedules the layout animation to be played after the next layout pass 3815 * of this view group. This can be used to restart the layout animation 3816 * when the content of the view group changes or when the activity is 3817 * paused and resumed. 3818 */ 3819 public void scheduleLayoutAnimation() { 3820 mGroupFlags |= FLAG_RUN_ANIMATION; 3821 } 3822 3823 /** 3824 * Sets the layout animation controller used to animate the group's 3825 * children after the first layout. 3826 * 3827 * @param controller the animation controller 3828 */ 3829 public void setLayoutAnimation(LayoutAnimationController controller) { 3830 mLayoutAnimationController = controller; 3831 if (mLayoutAnimationController != null) { 3832 mGroupFlags |= FLAG_RUN_ANIMATION; 3833 } 3834 } 3835 3836 /** 3837 * Returns the layout animation controller used to animate the group's 3838 * children. 3839 * 3840 * @return the current animation controller 3841 */ 3842 public LayoutAnimationController getLayoutAnimation() { 3843 return mLayoutAnimationController; 3844 } 3845 3846 /** 3847 * Indicates whether the children's drawing cache is used during a layout 3848 * animation. By default, the drawing cache is enabled but this will prevent 3849 * nested layout animations from working. To nest animations, you must disable 3850 * the cache. 3851 * 3852 * @return true if the animation cache is enabled, false otherwise 3853 * 3854 * @see #setAnimationCacheEnabled(boolean) 3855 * @see View#setDrawingCacheEnabled(boolean) 3856 */ 3857 @ViewDebug.ExportedProperty 3858 public boolean isAnimationCacheEnabled() { 3859 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 3860 } 3861 3862 /** 3863 * Enables or disables the children's drawing cache during a layout animation. 3864 * By default, the drawing cache is enabled but this will prevent nested 3865 * layout animations from working. To nest animations, you must disable the 3866 * cache. 3867 * 3868 * @param enabled true to enable the animation cache, false otherwise 3869 * 3870 * @see #isAnimationCacheEnabled() 3871 * @see View#setDrawingCacheEnabled(boolean) 3872 */ 3873 public void setAnimationCacheEnabled(boolean enabled) { 3874 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); 3875 } 3876 3877 /** 3878 * Indicates whether this ViewGroup will always try to draw its children using their 3879 * drawing cache. By default this property is enabled. 3880 * 3881 * @return true if the animation cache is enabled, false otherwise 3882 * 3883 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 3884 * @see #setChildrenDrawnWithCacheEnabled(boolean) 3885 * @see View#setDrawingCacheEnabled(boolean) 3886 */ 3887 @ViewDebug.ExportedProperty(category = "drawing") 3888 public boolean isAlwaysDrawnWithCacheEnabled() { 3889 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; 3890 } 3891 3892 /** 3893 * Indicates whether this ViewGroup will always try to draw its children using their 3894 * drawing cache. This property can be set to true when the cache rendering is 3895 * slightly different from the children's normal rendering. Renderings can be different, 3896 * for instance, when the cache's quality is set to low. 3897 * 3898 * When this property is disabled, the ViewGroup will use the drawing cache of its 3899 * children only when asked to. It's usually the task of subclasses to tell ViewGroup 3900 * when to start using the drawing cache and when to stop using it. 3901 * 3902 * @param always true to always draw with the drawing cache, false otherwise 3903 * 3904 * @see #isAlwaysDrawnWithCacheEnabled() 3905 * @see #setChildrenDrawnWithCacheEnabled(boolean) 3906 * @see View#setDrawingCacheEnabled(boolean) 3907 * @see View#setDrawingCacheQuality(int) 3908 */ 3909 public void setAlwaysDrawnWithCacheEnabled(boolean always) { 3910 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); 3911 } 3912 3913 /** 3914 * Indicates whether the ViewGroup is currently drawing its children using 3915 * their drawing cache. 3916 * 3917 * @return true if children should be drawn with their cache, false otherwise 3918 * 3919 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 3920 * @see #setChildrenDrawnWithCacheEnabled(boolean) 3921 */ 3922 @ViewDebug.ExportedProperty(category = "drawing") 3923 protected boolean isChildrenDrawnWithCacheEnabled() { 3924 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; 3925 } 3926 3927 /** 3928 * Tells the ViewGroup to draw its children using their drawing cache. This property 3929 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache 3930 * will be used only if it has been enabled. 3931 * 3932 * Subclasses should call this method to start and stop using the drawing cache when 3933 * they perform performance sensitive operations, like scrolling or animating. 3934 * 3935 * @param enabled true if children should be drawn with their cache, false otherwise 3936 * 3937 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 3938 * @see #isChildrenDrawnWithCacheEnabled() 3939 */ 3940 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { 3941 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); 3942 } 3943 3944 /** 3945 * Indicates whether the ViewGroup is drawing its children in the order defined by 3946 * {@link #getChildDrawingOrder(int, int)}. 3947 * 3948 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, 3949 * false otherwise 3950 * 3951 * @see #setChildrenDrawingOrderEnabled(boolean) 3952 * @see #getChildDrawingOrder(int, int) 3953 */ 3954 @ViewDebug.ExportedProperty(category = "drawing") 3955 protected boolean isChildrenDrawingOrderEnabled() { 3956 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; 3957 } 3958 3959 /** 3960 * Tells the ViewGroup whether to draw its children in the order defined by the method 3961 * {@link #getChildDrawingOrder(int, int)}. 3962 * 3963 * @param enabled true if the order of the children when drawing is determined by 3964 * {@link #getChildDrawingOrder(int, int)}, false otherwise 3965 * 3966 * @see #isChildrenDrawingOrderEnabled() 3967 * @see #getChildDrawingOrder(int, int) 3968 */ 3969 protected void setChildrenDrawingOrderEnabled(boolean enabled) { 3970 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); 3971 } 3972 3973 private void setBooleanFlag(int flag, boolean value) { 3974 if (value) { 3975 mGroupFlags |= flag; 3976 } else { 3977 mGroupFlags &= ~flag; 3978 } 3979 } 3980 3981 /** 3982 * Returns an integer indicating what types of drawing caches are kept in memory. 3983 * 3984 * @see #setPersistentDrawingCache(int) 3985 * @see #setAnimationCacheEnabled(boolean) 3986 * 3987 * @return one or a combination of {@link #PERSISTENT_NO_CACHE}, 3988 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 3989 * and {@link #PERSISTENT_ALL_CACHES} 3990 */ 3991 @ViewDebug.ExportedProperty(category = "drawing", mapping = { 3992 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), 3993 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"), 3994 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), 3995 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") 3996 }) 3997 public int getPersistentDrawingCache() { 3998 return mPersistentDrawingCache; 3999 } 4000 4001 /** 4002 * Indicates what types of drawing caches should be kept in memory after 4003 * they have been created. 4004 * 4005 * @see #getPersistentDrawingCache() 4006 * @see #setAnimationCacheEnabled(boolean) 4007 * 4008 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE}, 4009 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 4010 * and {@link #PERSISTENT_ALL_CACHES} 4011 */ 4012 public void setPersistentDrawingCache(int drawingCacheToKeep) { 4013 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; 4014 } 4015 4016 /** 4017 * Returns a new set of layout parameters based on the supplied attributes set. 4018 * 4019 * @param attrs the attributes to build the layout parameters from 4020 * 4021 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 4022 * of its descendants 4023 */ 4024 public LayoutParams generateLayoutParams(AttributeSet attrs) { 4025 return new LayoutParams(getContext(), attrs); 4026 } 4027 4028 /** 4029 * Returns a safe set of layout parameters based on the supplied layout params. 4030 * When a ViewGroup is passed a View whose layout params do not pass the test of 4031 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method 4032 * is invoked. This method should return a new set of layout params suitable for 4033 * this ViewGroup, possibly by copying the appropriate attributes from the 4034 * specified set of layout params. 4035 * 4036 * @param p The layout parameters to convert into a suitable set of layout parameters 4037 * for this ViewGroup. 4038 * 4039 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 4040 * of its descendants 4041 */ 4042 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 4043 return p; 4044 } 4045 4046 /** 4047 * Returns a set of default layout parameters. These parameters are requested 4048 * when the View passed to {@link #addView(View)} has no layout parameters 4049 * already set. If null is returned, an exception is thrown from addView. 4050 * 4051 * @return a set of default layout parameters or null 4052 */ 4053 protected LayoutParams generateDefaultLayoutParams() { 4054 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 4055 } 4056 4057 /** 4058 * @hide 4059 */ 4060 @Override 4061 protected boolean dispatchConsistencyCheck(int consistency) { 4062 boolean result = super.dispatchConsistencyCheck(consistency); 4063 4064 final int count = mChildrenCount; 4065 final View[] children = mChildren; 4066 for (int i = 0; i < count; i++) { 4067 if (!children[i].dispatchConsistencyCheck(consistency)) result = false; 4068 } 4069 4070 return result; 4071 } 4072 4073 /** 4074 * @hide 4075 */ 4076 @Override 4077 protected boolean onConsistencyCheck(int consistency) { 4078 boolean result = super.onConsistencyCheck(consistency); 4079 4080 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; 4081 final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; 4082 4083 if (checkLayout) { 4084 final int count = mChildrenCount; 4085 final View[] children = mChildren; 4086 for (int i = 0; i < count; i++) { 4087 if (children[i].getParent() != this) { 4088 result = false; 4089 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 4090 "View " + children[i] + " has no parent/a parent that is not " + this); 4091 } 4092 } 4093 } 4094 4095 if (checkDrawing) { 4096 // If this group is dirty, check that the parent is dirty as well 4097 if ((mPrivateFlags & DIRTY_MASK) != 0) { 4098 final ViewParent parent = getParent(); 4099 if (parent != null && !(parent instanceof ViewRoot)) { 4100 if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { 4101 result = false; 4102 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 4103 "ViewGroup " + this + " is dirty but its parent is not: " + this); 4104 } 4105 } 4106 } 4107 } 4108 4109 return result; 4110 } 4111 4112 /** 4113 * {@inheritDoc} 4114 */ 4115 @Override 4116 protected void debug(int depth) { 4117 super.debug(depth); 4118 String output; 4119 4120 if (mFocused != null) { 4121 output = debugIndent(depth); 4122 output += "mFocused"; 4123 Log.d(VIEW_LOG_TAG, output); 4124 } 4125 if (mChildrenCount != 0) { 4126 output = debugIndent(depth); 4127 output += "{"; 4128 Log.d(VIEW_LOG_TAG, output); 4129 } 4130 int count = mChildrenCount; 4131 for (int i = 0; i < count; i++) { 4132 View child = mChildren[i]; 4133 child.debug(depth + 1); 4134 } 4135 4136 if (mChildrenCount != 0) { 4137 output = debugIndent(depth); 4138 output += "}"; 4139 Log.d(VIEW_LOG_TAG, output); 4140 } 4141 } 4142 4143 /** 4144 * Returns the position in the group of the specified child view. 4145 * 4146 * @param child the view for which to get the position 4147 * @return a positive integer representing the position of the view in the 4148 * group, or -1 if the view does not exist in the group 4149 */ 4150 public int indexOfChild(View child) { 4151 final int count = mChildrenCount; 4152 final View[] children = mChildren; 4153 for (int i = 0; i < count; i++) { 4154 if (children[i] == child) { 4155 return i; 4156 } 4157 } 4158 return -1; 4159 } 4160 4161 /** 4162 * Returns the number of children in the group. 4163 * 4164 * @return a positive integer representing the number of children in 4165 * the group 4166 */ 4167 public int getChildCount() { 4168 return mChildrenCount; 4169 } 4170 4171 /** 4172 * Returns the view at the specified position in the group. 4173 * 4174 * @param index the position at which to get the view from 4175 * @return the view at the specified position or null if the position 4176 * does not exist within the group 4177 */ 4178 public View getChildAt(int index) { 4179 try { 4180 return mChildren[index]; 4181 } catch (IndexOutOfBoundsException ex) { 4182 return null; 4183 } 4184 } 4185 4186 /** 4187 * Ask all of the children of this view to measure themselves, taking into 4188 * account both the MeasureSpec requirements for this view and its padding. 4189 * We skip children that are in the GONE state The heavy lifting is done in 4190 * getChildMeasureSpec. 4191 * 4192 * @param widthMeasureSpec The width requirements for this view 4193 * @param heightMeasureSpec The height requirements for this view 4194 */ 4195 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 4196 final int size = mChildrenCount; 4197 final View[] children = mChildren; 4198 for (int i = 0; i < size; ++i) { 4199 final View child = children[i]; 4200 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 4201 measureChild(child, widthMeasureSpec, heightMeasureSpec); 4202 } 4203 } 4204 } 4205 4206 /** 4207 * Ask one of the children of this view to measure itself, taking into 4208 * account both the MeasureSpec requirements for this view and its padding. 4209 * The heavy lifting is done in getChildMeasureSpec. 4210 * 4211 * @param child The child to measure 4212 * @param parentWidthMeasureSpec The width requirements for this view 4213 * @param parentHeightMeasureSpec The height requirements for this view 4214 */ 4215 protected void measureChild(View child, int parentWidthMeasureSpec, 4216 int parentHeightMeasureSpec) { 4217 final LayoutParams lp = child.getLayoutParams(); 4218 4219 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 4220 mPaddingLeft + mPaddingRight, lp.width); 4221 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 4222 mPaddingTop + mPaddingBottom, lp.height); 4223 4224 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4225 } 4226 4227 /** 4228 * Ask one of the children of this view to measure itself, taking into 4229 * account both the MeasureSpec requirements for this view and its padding 4230 * and margins. The child must have MarginLayoutParams The heavy lifting is 4231 * done in getChildMeasureSpec. 4232 * 4233 * @param child The child to measure 4234 * @param parentWidthMeasureSpec The width requirements for this view 4235 * @param widthUsed Extra space that has been used up by the parent 4236 * horizontally (possibly by other children of the parent) 4237 * @param parentHeightMeasureSpec The height requirements for this view 4238 * @param heightUsed Extra space that has been used up by the parent 4239 * vertically (possibly by other children of the parent) 4240 */ 4241 protected void measureChildWithMargins(View child, 4242 int parentWidthMeasureSpec, int widthUsed, 4243 int parentHeightMeasureSpec, int heightUsed) { 4244 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 4245 4246 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 4247 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 4248 + widthUsed, lp.width); 4249 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 4250 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 4251 + heightUsed, lp.height); 4252 4253 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4254 } 4255 4256 /** 4257 * Does the hard part of measureChildren: figuring out the MeasureSpec to 4258 * pass to a particular child. This method figures out the right MeasureSpec 4259 * for one dimension (height or width) of one child view. 4260 * 4261 * The goal is to combine information from our MeasureSpec with the 4262 * LayoutParams of the child to get the best possible results. For example, 4263 * if the this view knows its size (because its MeasureSpec has a mode of 4264 * EXACTLY), and the child has indicated in its LayoutParams that it wants 4265 * to be the same size as the parent, the parent should ask the child to 4266 * layout given an exact size. 4267 * 4268 * @param spec The requirements for this view 4269 * @param padding The padding of this view for the current dimension and 4270 * margins, if applicable 4271 * @param childDimension How big the child wants to be in the current 4272 * dimension 4273 * @return a MeasureSpec integer for the child 4274 */ 4275 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 4276 int specMode = MeasureSpec.getMode(spec); 4277 int specSize = MeasureSpec.getSize(spec); 4278 4279 int size = Math.max(0, specSize - padding); 4280 4281 int resultSize = 0; 4282 int resultMode = 0; 4283 4284 switch (specMode) { 4285 // Parent has imposed an exact size on us 4286 case MeasureSpec.EXACTLY: 4287 if (childDimension >= 0) { 4288 resultSize = childDimension; 4289 resultMode = MeasureSpec.EXACTLY; 4290 } else if (childDimension == LayoutParams.MATCH_PARENT) { 4291 // Child wants to be our size. So be it. 4292 resultSize = size; 4293 resultMode = MeasureSpec.EXACTLY; 4294 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 4295 // Child wants to determine its own size. It can't be 4296 // bigger than us. 4297 resultSize = size; 4298 resultMode = MeasureSpec.AT_MOST; 4299 } 4300 break; 4301 4302 // Parent has imposed a maximum size on us 4303 case MeasureSpec.AT_MOST: 4304 if (childDimension >= 0) { 4305 // Child wants a specific size... so be it 4306 resultSize = childDimension; 4307 resultMode = MeasureSpec.EXACTLY; 4308 } else if (childDimension == LayoutParams.MATCH_PARENT) { 4309 // Child wants to be our size, but our size is not fixed. 4310 // Constrain child to not be bigger than us. 4311 resultSize = size; 4312 resultMode = MeasureSpec.AT_MOST; 4313 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 4314 // Child wants to determine its own size. It can't be 4315 // bigger than us. 4316 resultSize = size; 4317 resultMode = MeasureSpec.AT_MOST; 4318 } 4319 break; 4320 4321 // Parent asked to see how big we want to be 4322 case MeasureSpec.UNSPECIFIED: 4323 if (childDimension >= 0) { 4324 // Child wants a specific size... let him have it 4325 resultSize = childDimension; 4326 resultMode = MeasureSpec.EXACTLY; 4327 } else if (childDimension == LayoutParams.MATCH_PARENT) { 4328 // Child wants to be our size... find out how big it should 4329 // be 4330 resultSize = 0; 4331 resultMode = MeasureSpec.UNSPECIFIED; 4332 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 4333 // Child wants to determine its own size.... find out how 4334 // big it should be 4335 resultSize = 0; 4336 resultMode = MeasureSpec.UNSPECIFIED; 4337 } 4338 break; 4339 } 4340 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 4341 } 4342 4343 4344 /** 4345 * Removes any pending animations for views that have been removed. Call 4346 * this if you don't want animations for exiting views to stack up. 4347 */ 4348 public void clearDisappearingChildren() { 4349 if (mDisappearingChildren != null) { 4350 mDisappearingChildren.clear(); 4351 } 4352 } 4353 4354 /** 4355 * Add a view which is removed from mChildren but still needs animation 4356 * 4357 * @param v View to add 4358 */ 4359 private void addDisappearingView(View v) { 4360 ArrayList<View> disappearingChildren = mDisappearingChildren; 4361 4362 if (disappearingChildren == null) { 4363 disappearingChildren = mDisappearingChildren = new ArrayList<View>(); 4364 } 4365 4366 disappearingChildren.add(v); 4367 } 4368 4369 /** 4370 * Cleanup a view when its animation is done. This may mean removing it from 4371 * the list of disappearing views. 4372 * 4373 * @param view The view whose animation has finished 4374 * @param animation The animation, cannot be null 4375 */ 4376 private void finishAnimatingView(final View view, Animation animation) { 4377 final ArrayList<View> disappearingChildren = mDisappearingChildren; 4378 if (disappearingChildren != null) { 4379 if (disappearingChildren.contains(view)) { 4380 disappearingChildren.remove(view); 4381 4382 if (view.mAttachInfo != null) { 4383 view.dispatchDetachedFromWindow(); 4384 } 4385 4386 view.clearAnimation(); 4387 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 4388 } 4389 } 4390 4391 if (animation != null && !animation.getFillAfter()) { 4392 view.clearAnimation(); 4393 } 4394 4395 if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) { 4396 view.onAnimationEnd(); 4397 // Should be performed by onAnimationEnd() but this avoid an infinite loop, 4398 // so we'd rather be safe than sorry 4399 view.mPrivateFlags &= ~ANIMATION_STARTED; 4400 // Draw one more frame after the animation is done 4401 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 4402 } 4403 } 4404 4405 /** 4406 * This method tells the ViewGroup that the given View object, which should have this 4407 * ViewGroup as its parent, 4408 * should be kept around (re-displayed when the ViewGroup draws its children) even if it 4409 * is removed from its parent. This allows animations, such as those used by 4410 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate 4411 * the removal of views. A call to this method should always be accompanied by a later call 4412 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished, 4413 * so that the View finally gets removed. 4414 * 4415 * @param view The View object to be kept visible even if it gets removed from its parent. 4416 */ 4417 public void startViewTransition(View view) { 4418 if (view.mParent == this) { 4419 if (mTransitioningViews == null) { 4420 mTransitioningViews = new ArrayList<View>(); 4421 } 4422 mTransitioningViews.add(view); 4423 } 4424 } 4425 4426 /** 4427 * This method should always be called following an earlier call to 4428 * {@link #startViewTransition(View)}. The given View is finally removed from its parent 4429 * and will no longer be displayed. Note that this method does not perform the functionality 4430 * of removing a view from its parent; it just discontinues the display of a View that 4431 * has previously been removed. 4432 * 4433 * @return view The View object that has been removed but is being kept around in the visible 4434 * hierarchy by an earlier call to {@link #startViewTransition(View)}. 4435 */ 4436 public void endViewTransition(View view) { 4437 if (mTransitioningViews != null) { 4438 mTransitioningViews.remove(view); 4439 final ArrayList<View> disappearingChildren = mDisappearingChildren; 4440 if (disappearingChildren != null && disappearingChildren.contains(view)) { 4441 disappearingChildren.remove(view); 4442 if (mVisibilityChangingChildren != null && 4443 mVisibilityChangingChildren.contains(view)) { 4444 mVisibilityChangingChildren.remove(view); 4445 } else { 4446 if (view.mAttachInfo != null) { 4447 view.dispatchDetachedFromWindow(); 4448 } 4449 if (view.mParent != null) { 4450 view.mParent = null; 4451 } 4452 } 4453 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 4454 } 4455 } 4456 } 4457 4458 private LayoutTransition.TransitionListener mLayoutTransitionListener = 4459 new LayoutTransition.TransitionListener() { 4460 @Override 4461 public void startTransition(LayoutTransition transition, ViewGroup container, 4462 View view, int transitionType) { 4463 // We only care about disappearing items, since we need special logic to keep 4464 // those items visible after they've been 'removed' 4465 if (transitionType == LayoutTransition.DISAPPEARING) { 4466 startViewTransition(view); 4467 } 4468 } 4469 4470 @Override 4471 public void endTransition(LayoutTransition transition, ViewGroup container, 4472 View view, int transitionType) { 4473 if (mLayoutSuppressed && !transition.isChangingLayout()) { 4474 requestLayout(); 4475 mLayoutSuppressed = false; 4476 } 4477 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) { 4478 endViewTransition(view); 4479 } 4480 } 4481 }; 4482 4483 /** 4484 * {@inheritDoc} 4485 */ 4486 @Override 4487 public boolean gatherTransparentRegion(Region region) { 4488 // If no transparent regions requested, we are always opaque. 4489 final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0; 4490 if (meOpaque && region == null) { 4491 // The caller doesn't care about the region, so stop now. 4492 return true; 4493 } 4494 super.gatherTransparentRegion(region); 4495 final View[] children = mChildren; 4496 final int count = mChildrenCount; 4497 boolean noneOfTheChildrenAreTransparent = true; 4498 for (int i = 0; i < count; i++) { 4499 final View child = children[i]; 4500 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 4501 if (!child.gatherTransparentRegion(region)) { 4502 noneOfTheChildrenAreTransparent = false; 4503 } 4504 } 4505 } 4506 return meOpaque || noneOfTheChildrenAreTransparent; 4507 } 4508 4509 /** 4510 * {@inheritDoc} 4511 */ 4512 public void requestTransparentRegion(View child) { 4513 if (child != null) { 4514 child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS; 4515 if (mParent != null) { 4516 mParent.requestTransparentRegion(this); 4517 } 4518 } 4519 } 4520 4521 4522 @Override 4523 protected boolean fitSystemWindows(Rect insets) { 4524 boolean done = super.fitSystemWindows(insets); 4525 if (!done) { 4526 final int count = mChildrenCount; 4527 final View[] children = mChildren; 4528 for (int i = 0; i < count; i++) { 4529 done = children[i].fitSystemWindows(insets); 4530 if (done) { 4531 break; 4532 } 4533 } 4534 } 4535 return done; 4536 } 4537 4538 /** 4539 * Returns the animation listener to which layout animation events are 4540 * sent. 4541 * 4542 * @return an {@link android.view.animation.Animation.AnimationListener} 4543 */ 4544 public Animation.AnimationListener getLayoutAnimationListener() { 4545 return mAnimationListener; 4546 } 4547 4548 @Override 4549 protected void drawableStateChanged() { 4550 super.drawableStateChanged(); 4551 4552 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { 4553 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 4554 throw new IllegalStateException("addStateFromChildren cannot be enabled if a" 4555 + " child has duplicateParentState set to true"); 4556 } 4557 4558 final View[] children = mChildren; 4559 final int count = mChildrenCount; 4560 4561 for (int i = 0; i < count; i++) { 4562 final View child = children[i]; 4563 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { 4564 child.refreshDrawableState(); 4565 } 4566 } 4567 } 4568 } 4569 4570 @Override 4571 public void jumpDrawablesToCurrentState() { 4572 super.jumpDrawablesToCurrentState(); 4573 final View[] children = mChildren; 4574 final int count = mChildrenCount; 4575 for (int i = 0; i < count; i++) { 4576 children[i].jumpDrawablesToCurrentState(); 4577 } 4578 } 4579 4580 @Override 4581 protected int[] onCreateDrawableState(int extraSpace) { 4582 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { 4583 return super.onCreateDrawableState(extraSpace); 4584 } 4585 4586 int need = 0; 4587 int n = getChildCount(); 4588 for (int i = 0; i < n; i++) { 4589 int[] childState = getChildAt(i).getDrawableState(); 4590 4591 if (childState != null) { 4592 need += childState.length; 4593 } 4594 } 4595 4596 int[] state = super.onCreateDrawableState(extraSpace + need); 4597 4598 for (int i = 0; i < n; i++) { 4599 int[] childState = getChildAt(i).getDrawableState(); 4600 4601 if (childState != null) { 4602 state = mergeDrawableStates(state, childState); 4603 } 4604 } 4605 4606 return state; 4607 } 4608 4609 /** 4610 * Sets whether this ViewGroup's drawable states also include 4611 * its children's drawable states. This is used, for example, to 4612 * make a group appear to be focused when its child EditText or button 4613 * is focused. 4614 */ 4615 public void setAddStatesFromChildren(boolean addsStates) { 4616 if (addsStates) { 4617 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN; 4618 } else { 4619 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN; 4620 } 4621 4622 refreshDrawableState(); 4623 } 4624 4625 /** 4626 * Returns whether this ViewGroup's drawable states also include 4627 * its children's drawable states. This is used, for example, to 4628 * make a group appear to be focused when its child EditText or button 4629 * is focused. 4630 */ 4631 public boolean addStatesFromChildren() { 4632 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0; 4633 } 4634 4635 /** 4636 * If {link #addStatesFromChildren} is true, refreshes this group's 4637 * drawable state (to include the states from its children). 4638 */ 4639 public void childDrawableStateChanged(View child) { 4640 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 4641 refreshDrawableState(); 4642 } 4643 } 4644 4645 /** 4646 * Specifies the animation listener to which layout animation events must 4647 * be sent. Only 4648 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)} 4649 * and 4650 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)} 4651 * are invoked. 4652 * 4653 * @param animationListener the layout animation listener 4654 */ 4655 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { 4656 mAnimationListener = animationListener; 4657 } 4658 4659 /** 4660 * LayoutParams are used by views to tell their parents how they want to be 4661 * laid out. See 4662 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 4663 * for a list of all child view attributes that this class supports. 4664 * 4665 * <p> 4666 * The base LayoutParams class just describes how big the view wants to be 4667 * for both width and height. For each dimension, it can specify one of: 4668 * <ul> 4669 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which 4670 * means that the view wants to be as big as its parent (minus padding) 4671 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 4672 * to enclose its content (plus padding) 4673 * <li> an exact number 4674 * </ul> 4675 * There are subclasses of LayoutParams for different subclasses of 4676 * ViewGroup. For example, AbsoluteLayout has its own subclass of 4677 * LayoutParams which adds an X and Y value. 4678 * 4679 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 4680 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 4681 */ 4682 public static class LayoutParams { 4683 /** 4684 * Special value for the height or width requested by a View. 4685 * FILL_PARENT means that the view wants to be as big as its parent, 4686 * minus the parent's padding, if any. This value is deprecated 4687 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 4688 */ 4689 @SuppressWarnings({"UnusedDeclaration"}) 4690 @Deprecated 4691 public static final int FILL_PARENT = -1; 4692 4693 /** 4694 * Special value for the height or width requested by a View. 4695 * MATCH_PARENT means that the view wants to be as big as its parent, 4696 * minus the parent's padding, if any. Introduced in API Level 8. 4697 */ 4698 public static final int MATCH_PARENT = -1; 4699 4700 /** 4701 * Special value for the height or width requested by a View. 4702 * WRAP_CONTENT means that the view wants to be just large enough to fit 4703 * its own internal content, taking its own padding into account. 4704 */ 4705 public static final int WRAP_CONTENT = -2; 4706 4707 /** 4708 * Information about how wide the view wants to be. Can be one of the 4709 * constants FILL_PARENT (replaced by MATCH_PARENT , 4710 * in API Level 8) or WRAP_CONTENT. or an exact size. 4711 */ 4712 @ViewDebug.ExportedProperty(category = "layout", mapping = { 4713 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 4714 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 4715 }) 4716 public int width; 4717 4718 /** 4719 * Information about how tall the view wants to be. Can be one of the 4720 * constants FILL_PARENT (replaced by MATCH_PARENT , 4721 * in API Level 8) or WRAP_CONTENT. or an exact size. 4722 */ 4723 @ViewDebug.ExportedProperty(category = "layout", mapping = { 4724 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 4725 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 4726 }) 4727 public int height; 4728 4729 /** 4730 * Used to animate layouts. 4731 */ 4732 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 4733 4734 /** 4735 * Creates a new set of layout parameters. The values are extracted from 4736 * the supplied attributes set and context. The XML attributes mapped 4737 * to this set of layout parameters are: 4738 * 4739 * <ul> 4740 * <li><code>layout_width</code>: the width, either an exact value, 4741 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 4742 * {@link #MATCH_PARENT} in API Level 8)</li> 4743 * <li><code>layout_height</code>: the height, either an exact value, 4744 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 4745 * {@link #MATCH_PARENT} in API Level 8)</li> 4746 * </ul> 4747 * 4748 * @param c the application environment 4749 * @param attrs the set of attributes from which to extract the layout 4750 * parameters' values 4751 */ 4752 public LayoutParams(Context c, AttributeSet attrs) { 4753 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 4754 setBaseAttributes(a, 4755 R.styleable.ViewGroup_Layout_layout_width, 4756 R.styleable.ViewGroup_Layout_layout_height); 4757 a.recycle(); 4758 } 4759 4760 /** 4761 * Creates a new set of layout parameters with the specified width 4762 * and height. 4763 * 4764 * @param width the width, either {@link #WRAP_CONTENT}, 4765 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 4766 * API Level 8), or a fixed size in pixels 4767 * @param height the height, either {@link #WRAP_CONTENT}, 4768 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 4769 * API Level 8), or a fixed size in pixels 4770 */ 4771 public LayoutParams(int width, int height) { 4772 this.width = width; 4773 this.height = height; 4774 } 4775 4776 /** 4777 * Copy constructor. Clones the width and height values of the source. 4778 * 4779 * @param source The layout params to copy from. 4780 */ 4781 public LayoutParams(LayoutParams source) { 4782 this.width = source.width; 4783 this.height = source.height; 4784 } 4785 4786 /** 4787 * Used internally by MarginLayoutParams. 4788 * @hide 4789 */ 4790 LayoutParams() { 4791 } 4792 4793 /** 4794 * Extracts the layout parameters from the supplied attributes. 4795 * 4796 * @param a the style attributes to extract the parameters from 4797 * @param widthAttr the identifier of the width attribute 4798 * @param heightAttr the identifier of the height attribute 4799 */ 4800 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 4801 width = a.getLayoutDimension(widthAttr, "layout_width"); 4802 height = a.getLayoutDimension(heightAttr, "layout_height"); 4803 } 4804 4805 /** 4806 * Returns a String representation of this set of layout parameters. 4807 * 4808 * @param output the String to prepend to the internal representation 4809 * @return a String with the following format: output + 4810 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 4811 * 4812 * @hide 4813 */ 4814 public String debug(String output) { 4815 return output + "ViewGroup.LayoutParams={ width=" 4816 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 4817 } 4818 4819 /** 4820 * Converts the specified size to a readable String. 4821 * 4822 * @param size the size to convert 4823 * @return a String instance representing the supplied size 4824 * 4825 * @hide 4826 */ 4827 protected static String sizeToString(int size) { 4828 if (size == WRAP_CONTENT) { 4829 return "wrap-content"; 4830 } 4831 if (size == MATCH_PARENT) { 4832 return "match-parent"; 4833 } 4834 return String.valueOf(size); 4835 } 4836 } 4837 4838 /** 4839 * Per-child layout information for layouts that support margins. 4840 * See 4841 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes} 4842 * for a list of all child view attributes that this class supports. 4843 */ 4844 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 4845 /** 4846 * The left margin in pixels of the child. 4847 */ 4848 @ViewDebug.ExportedProperty(category = "layout") 4849 public int leftMargin; 4850 4851 /** 4852 * The top margin in pixels of the child. 4853 */ 4854 @ViewDebug.ExportedProperty(category = "layout") 4855 public int topMargin; 4856 4857 /** 4858 * The right margin in pixels of the child. 4859 */ 4860 @ViewDebug.ExportedProperty(category = "layout") 4861 public int rightMargin; 4862 4863 /** 4864 * The bottom margin in pixels of the child. 4865 */ 4866 @ViewDebug.ExportedProperty(category = "layout") 4867 public int bottomMargin; 4868 4869 /** 4870 * Creates a new set of layout parameters. The values are extracted from 4871 * the supplied attributes set and context. 4872 * 4873 * @param c the application environment 4874 * @param attrs the set of attributes from which to extract the layout 4875 * parameters' values 4876 */ 4877 public MarginLayoutParams(Context c, AttributeSet attrs) { 4878 super(); 4879 4880 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); 4881 setBaseAttributes(a, 4882 R.styleable.ViewGroup_MarginLayout_layout_width, 4883 R.styleable.ViewGroup_MarginLayout_layout_height); 4884 4885 int margin = a.getDimensionPixelSize( 4886 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); 4887 if (margin >= 0) { 4888 leftMargin = margin; 4889 topMargin = margin; 4890 rightMargin= margin; 4891 bottomMargin = margin; 4892 } else { 4893 leftMargin = a.getDimensionPixelSize( 4894 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0); 4895 topMargin = a.getDimensionPixelSize( 4896 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0); 4897 rightMargin = a.getDimensionPixelSize( 4898 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0); 4899 bottomMargin = a.getDimensionPixelSize( 4900 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0); 4901 } 4902 4903 a.recycle(); 4904 } 4905 4906 /** 4907 * {@inheritDoc} 4908 */ 4909 public MarginLayoutParams(int width, int height) { 4910 super(width, height); 4911 } 4912 4913 /** 4914 * Copy constructor. Clones the width, height and margin values of the source. 4915 * 4916 * @param source The layout params to copy from. 4917 */ 4918 public MarginLayoutParams(MarginLayoutParams source) { 4919 this.width = source.width; 4920 this.height = source.height; 4921 4922 this.leftMargin = source.leftMargin; 4923 this.topMargin = source.topMargin; 4924 this.rightMargin = source.rightMargin; 4925 this.bottomMargin = source.bottomMargin; 4926 } 4927 4928 /** 4929 * {@inheritDoc} 4930 */ 4931 public MarginLayoutParams(LayoutParams source) { 4932 super(source); 4933 } 4934 4935 /** 4936 * Sets the margins, in pixels. 4937 * 4938 * @param left the left margin size 4939 * @param top the top margin size 4940 * @param right the right margin size 4941 * @param bottom the bottom margin size 4942 * 4943 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 4944 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 4945 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 4946 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 4947 */ 4948 public void setMargins(int left, int top, int right, int bottom) { 4949 leftMargin = left; 4950 topMargin = top; 4951 rightMargin = right; 4952 bottomMargin = bottom; 4953 } 4954 } 4955 4956 /* Describes a touched view and the ids of the pointers that it has captured. 4957 * 4958 * This code assumes that pointer ids are always in the range 0..31 such that 4959 * it can use a bitfield to track which pointer ids are present. 4960 * As it happens, the lower layers of the input dispatch pipeline also use the 4961 * same trick so the assumption should be safe here... 4962 */ 4963 private static final class TouchTarget { 4964 private static final int MAX_RECYCLED = 32; 4965 private static final Object sRecycleLock = new Object(); 4966 private static TouchTarget sRecycleBin; 4967 private static int sRecycledCount; 4968 4969 public static final int ALL_POINTER_IDS = -1; // all ones 4970 4971 // The touched child view. 4972 public View child; 4973 4974 // The combined bit mask of pointer ids for all pointers captured by the target. 4975 public int pointerIdBits; 4976 4977 // The next target in the target list. 4978 public TouchTarget next; 4979 4980 private TouchTarget() { 4981 } 4982 4983 public static TouchTarget obtain(View child, int pointerIdBits) { 4984 final TouchTarget target; 4985 synchronized (sRecycleLock) { 4986 if (sRecycleBin == null) { 4987 target = new TouchTarget(); 4988 } else { 4989 target = sRecycleBin; 4990 sRecycleBin = target.next; 4991 sRecycledCount--; 4992 target.next = null; 4993 } 4994 } 4995 target.child = child; 4996 target.pointerIdBits = pointerIdBits; 4997 return target; 4998 } 4999 5000 public void recycle() { 5001 synchronized (sRecycleLock) { 5002 if (sRecycledCount < MAX_RECYCLED) { 5003 next = sRecycleBin; 5004 sRecycleBin = this; 5005 sRecycledCount += 1; 5006 } else { 5007 next = null; 5008 } 5009 child = null; 5010 } 5011 } 5012 } 5013} 5014