ViewGroup.java revision 9c17fe693deb0cc84099b619185472f192c2b52d
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.content.Context; 21import android.content.res.Configuration; 22import android.content.res.TypedArray; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Color; 26import android.graphics.Insets; 27import android.graphics.Matrix; 28import android.graphics.Paint; 29import android.graphics.PointF; 30import android.graphics.Rect; 31import android.graphics.RectF; 32import android.graphics.Region; 33import android.os.Build; 34import android.os.Parcelable; 35import android.os.SystemClock; 36import android.util.AttributeSet; 37import android.util.Log; 38import android.util.Pools.SynchronizedPool; 39import android.util.SparseArray; 40import android.view.accessibility.AccessibilityEvent; 41import android.view.accessibility.AccessibilityNodeInfo; 42import android.view.animation.Animation; 43import android.view.animation.AnimationUtils; 44import android.view.animation.LayoutAnimationController; 45import android.view.animation.Transformation; 46 47import com.android.internal.R; 48import com.android.internal.util.Predicate; 49 50import java.util.ArrayList; 51import java.util.Collections; 52import java.util.HashSet; 53 54/** 55 * <p> 56 * A <code>ViewGroup</code> is a special view that can contain other views 57 * (called children.) The view group is the base class for layouts and views 58 * containers. This class also defines the 59 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base 60 * class for layouts parameters. 61 * </p> 62 * 63 * <p> 64 * Also see {@link LayoutParams} for layout attributes. 65 * </p> 66 * 67 * <div class="special reference"> 68 * <h3>Developer Guides</h3> 69 * <p>For more information about creating user interface layouts, read the 70 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer 71 * guide.</p></div> 72 * 73 * <p>Here is a complete implementation of a custom ViewGroup that implements 74 * a simple {@link android.widget.FrameLayout} along with the ability to stack 75 * children in left and right gutters.</p> 76 * 77 * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java 78 * Complete} 79 * 80 * <p>If you are implementing XML layout attributes as shown in the example, this is the 81 * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p> 82 * 83 * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout} 84 * 85 * <p>Finally the layout manager can be used in an XML layout like so:</p> 86 * 87 * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete} 88 * 89 * @attr ref android.R.styleable#ViewGroup_clipChildren 90 * @attr ref android.R.styleable#ViewGroup_clipToPadding 91 * @attr ref android.R.styleable#ViewGroup_layoutAnimation 92 * @attr ref android.R.styleable#ViewGroup_animationCache 93 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache 94 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache 95 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren 96 * @attr ref android.R.styleable#ViewGroup_descendantFocusability 97 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 98 */ 99public abstract class ViewGroup extends View implements ViewParent, ViewManager { 100 private static final String TAG = "ViewGroup"; 101 102 private static final boolean DBG = false; 103 /** @hide */ 104 public static boolean DEBUG_DRAW = false; 105 106 /** 107 * Views which have been hidden or removed which need to be animated on 108 * their way out. 109 * This field should be made private, so it is hidden from the SDK. 110 * {@hide} 111 */ 112 protected ArrayList<View> mDisappearingChildren; 113 114 /** 115 * Listener used to propagate events indicating when children are added 116 * and/or removed from a view group. 117 * This field should be made private, so it is hidden from the SDK. 118 * {@hide} 119 */ 120 protected OnHierarchyChangeListener mOnHierarchyChangeListener; 121 122 // The view contained within this ViewGroup that has or contains focus. 123 private View mFocused; 124 125 /** 126 * A Transformation used when drawing children, to 127 * apply on the child being drawn. 128 */ 129 final Transformation mChildTransformation = new Transformation(); 130 131 /** 132 * Used to track the current invalidation region. 133 */ 134 RectF mInvalidateRegion; 135 136 /** 137 * A Transformation used to calculate a correct 138 * invalidation area when the application is autoscaled. 139 */ 140 Transformation mInvalidationTransformation; 141 142 // View currently under an ongoing drag 143 private View mCurrentDragView; 144 145 // Metadata about the ongoing drag 146 private DragEvent mCurrentDrag; 147 private HashSet<View> mDragNotifiedChildren; 148 149 // Does this group have a child that can accept the current drag payload? 150 private boolean mChildAcceptsDrag; 151 152 // Used during drag dispatch 153 private final PointF mLocalPoint = new PointF(); 154 155 // Layout animation 156 private LayoutAnimationController mLayoutAnimationController; 157 private Animation.AnimationListener mAnimationListener; 158 159 // First touch target in the linked list of touch targets. 160 private TouchTarget mFirstTouchTarget; 161 162 // For debugging only. You can see these in hierarchyviewer. 163 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) 164 @ViewDebug.ExportedProperty(category = "events") 165 private long mLastTouchDownTime; 166 @ViewDebug.ExportedProperty(category = "events") 167 private int mLastTouchDownIndex = -1; 168 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) 169 @ViewDebug.ExportedProperty(category = "events") 170 private float mLastTouchDownX; 171 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) 172 @ViewDebug.ExportedProperty(category = "events") 173 private float mLastTouchDownY; 174 175 // First hover target in the linked list of hover targets. 176 // The hover targets are children which have received ACTION_HOVER_ENTER. 177 // They might not have actually handled the hover event, but we will 178 // continue sending hover events to them as long as the pointer remains over 179 // their bounds and the view group does not intercept hover. 180 private HoverTarget mFirstHoverTarget; 181 182 // True if the view group itself received a hover event. 183 // It might not have actually handled the hover event. 184 private boolean mHoveredSelf; 185 186 /** 187 * Internal flags. 188 * 189 * This field should be made private, so it is hidden from the SDK. 190 * {@hide} 191 */ 192 @ViewDebug.ExportedProperty(flagMapping = { 193 @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN, 194 name = "CLIP_CHILDREN"), 195 @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING, 196 name = "CLIP_TO_PADDING"), 197 @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL, 198 name = "PADDING_NOT_NULL") 199 }) 200 protected int mGroupFlags; 201 202 /** 203 * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 204 */ 205 private int mLayoutMode = DEFAULT_LAYOUT_MODE; 206 207 /** 208 * NOTE: If you change the flags below make sure to reflect the changes 209 * the DisplayList class 210 */ 211 212 // When set, ViewGroup invalidates only the child's rectangle 213 // Set by default 214 static final int FLAG_CLIP_CHILDREN = 0x1; 215 216 // When set, ViewGroup excludes the padding area from the invalidate rectangle 217 // Set by default 218 private static final int FLAG_CLIP_TO_PADDING = 0x2; 219 220 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when 221 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set 222 static final int FLAG_INVALIDATE_REQUIRED = 0x4; 223 224 // When set, dispatchDraw() will run the layout animation and unset the flag 225 private static final int FLAG_RUN_ANIMATION = 0x8; 226 227 // When set, there is either no layout animation on the ViewGroup or the layout 228 // animation is over 229 // Set by default 230 static final int FLAG_ANIMATION_DONE = 0x10; 231 232 // If set, this ViewGroup has padding; if unset there is no padding and we don't need 233 // to clip it, even if FLAG_CLIP_TO_PADDING is set 234 private static final int FLAG_PADDING_NOT_NULL = 0x20; 235 236 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation 237 // Set by default 238 private static final int FLAG_ANIMATION_CACHE = 0x40; 239 240 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a 241 // layout animation; this avoid clobbering the hierarchy 242 // Automatically set when the layout animation starts, depending on the animation's 243 // characteristics 244 static final int FLAG_OPTIMIZE_INVALIDATE = 0x80; 245 246 // When set, the next call to drawChild() will clear mChildTransformation's matrix 247 static final int FLAG_CLEAR_TRANSFORMATION = 0x100; 248 249 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes 250 // the children's Bitmap caches if necessary 251 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set) 252 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200; 253 254 /** 255 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)} 256 * to get the index of the child to draw for that iteration. 257 * 258 * @hide 259 */ 260 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; 261 262 /** 263 * When set, this ViewGroup supports static transformations on children; this causes 264 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 265 * invoked when a child is drawn. 266 * 267 * Any subclass overriding 268 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 269 * set this flags in {@link #mGroupFlags}. 270 * 271 * {@hide} 272 */ 273 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; 274 275 // When the previous drawChild() invocation used an alpha value that was lower than 276 // 1.0 and set it in mCachePaint 277 static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000; 278 279 /** 280 * When set, this ViewGroup's drawable states also include those 281 * of its children. 282 */ 283 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000; 284 285 /** 286 * When set, this ViewGroup tries to always draw its children using their drawing cache. 287 */ 288 static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; 289 290 /** 291 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to 292 * draw its children with their drawing cache. 293 */ 294 static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; 295 296 /** 297 * When set, this group will go through its list of children to notify them of 298 * any drawable state change. 299 */ 300 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000; 301 302 private static final int FLAG_MASK_FOCUSABILITY = 0x60000; 303 304 /** 305 * This view will get focus before any of its descendants. 306 */ 307 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000; 308 309 /** 310 * This view will get focus only if none of its descendants want it. 311 */ 312 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000; 313 314 /** 315 * This view will block any of its descendants from getting focus, even 316 * if they are focusable. 317 */ 318 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000; 319 320 /** 321 * Used to map between enum in attrubutes and flag values. 322 */ 323 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS = 324 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, 325 FOCUS_BLOCK_DESCENDANTS}; 326 327 /** 328 * When set, this ViewGroup should not intercept touch events. 329 * {@hide} 330 */ 331 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000; 332 333 /** 334 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate. 335 */ 336 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000; 337 338 /** 339 * When set, this ViewGroup will not dispatch onAttachedToWindow calls 340 * to children when adding new views. This is used to prevent multiple 341 * onAttached calls when a ViewGroup adds children in its own onAttached method. 342 */ 343 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000; 344 345 /** 346 * Indicates which types of drawing caches are to be kept in memory. 347 * This field should be made private, so it is hidden from the SDK. 348 * {@hide} 349 */ 350 protected int mPersistentDrawingCache; 351 352 /** 353 * Used to indicate that no drawing cache should be kept in memory. 354 */ 355 public static final int PERSISTENT_NO_CACHE = 0x0; 356 357 /** 358 * Used to indicate that the animation drawing cache should be kept in memory. 359 */ 360 public static final int PERSISTENT_ANIMATION_CACHE = 0x1; 361 362 /** 363 * Used to indicate that the scrolling drawing cache should be kept in memory. 364 */ 365 public static final int PERSISTENT_SCROLLING_CACHE = 0x2; 366 367 /** 368 * Used to indicate that all drawing caches should be kept in memory. 369 */ 370 public static final int PERSISTENT_ALL_CACHES = 0x3; 371 372 // Layout Modes 373 374 /** 375 * This constant is a {@link #setLayoutMode(int) layoutMode}. 376 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top}, 377 * {@link #getRight() right} and {@link #getBottom() bottom}. 378 */ 379 public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; 380 381 /** 382 * This constant is a {@link #setLayoutMode(int) layoutMode}. 383 * Optical bounds describe where a widget appears to be. They sit inside the clip 384 * bounds which need to cover a larger area to allow other effects, 385 * such as shadows and glows, to be drawn. 386 */ 387 public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; 388 389 /** @hide */ 390 public static int DEFAULT_LAYOUT_MODE = LAYOUT_MODE_CLIP_BOUNDS; 391 392 /** 393 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL 394 * are set at the same time. 395 */ 396 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL; 397 398 // Index of the child's left position in the mLocation array 399 private static final int CHILD_LEFT_INDEX = 0; 400 // Index of the child's top position in the mLocation array 401 private static final int CHILD_TOP_INDEX = 1; 402 403 // Child views of this ViewGroup 404 private View[] mChildren; 405 // Number of valid children in the mChildren array, the rest should be null or not 406 // considered as children 407 408 private boolean mLayoutSuppressed = false; 409 410 private int mChildrenCount; 411 412 private static final int ARRAY_INITIAL_CAPACITY = 12; 413 private static final int ARRAY_CAPACITY_INCREMENT = 12; 414 415 private static Paint sDebugPaint; 416 private static float[] sDebugLines; 417 418 // Used to draw cached views 419 Paint mCachePaint; 420 421 // Used to animate add/remove changes in layout 422 private LayoutTransition mTransition; 423 424 // The set of views that are currently being transitioned. This list is used to track views 425 // being removed that should not actually be removed from the parent yet because they are 426 // being animated. 427 private ArrayList<View> mTransitioningViews; 428 429 // List of children changing visibility. This is used to potentially keep rendering 430 // views during a transition when they otherwise would have become gone/invisible 431 private ArrayList<View> mVisibilityChangingChildren; 432 433 // Indicates how many of this container's child subtrees contain transient state 434 @ViewDebug.ExportedProperty(category = "layout") 435 private int mChildCountWithTransientState = 0; 436 437 public ViewGroup(Context context) { 438 super(context); 439 initViewGroup(); 440 } 441 442 public ViewGroup(Context context, AttributeSet attrs) { 443 super(context, attrs); 444 initViewGroup(); 445 initFromAttributes(context, attrs); 446 } 447 448 public ViewGroup(Context context, AttributeSet attrs, int defStyle) { 449 super(context, attrs, defStyle); 450 initViewGroup(); 451 initFromAttributes(context, attrs); 452 } 453 454 private boolean debugDraw() { 455 return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout; 456 } 457 458 private void initViewGroup() { 459 // ViewGroup doesn't draw by default 460 if (!debugDraw()) { 461 setFlags(WILL_NOT_DRAW, DRAW_MASK); 462 } 463 mGroupFlags |= FLAG_CLIP_CHILDREN; 464 mGroupFlags |= FLAG_CLIP_TO_PADDING; 465 mGroupFlags |= FLAG_ANIMATION_DONE; 466 mGroupFlags |= FLAG_ANIMATION_CACHE; 467 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; 468 469 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { 470 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 471 } 472 473 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); 474 475 mChildren = new View[ARRAY_INITIAL_CAPACITY]; 476 mChildrenCount = 0; 477 478 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; 479 } 480 481 private void initFromAttributes(Context context, AttributeSet attrs) { 482 TypedArray a = context.obtainStyledAttributes(attrs, 483 R.styleable.ViewGroup); 484 485 final int N = a.getIndexCount(); 486 for (int i = 0; i < N; i++) { 487 int attr = a.getIndex(i); 488 switch (attr) { 489 case R.styleable.ViewGroup_clipChildren: 490 setClipChildren(a.getBoolean(attr, true)); 491 break; 492 case R.styleable.ViewGroup_clipToPadding: 493 setClipToPadding(a.getBoolean(attr, true)); 494 break; 495 case R.styleable.ViewGroup_animationCache: 496 setAnimationCacheEnabled(a.getBoolean(attr, true)); 497 break; 498 case R.styleable.ViewGroup_persistentDrawingCache: 499 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE)); 500 break; 501 case R.styleable.ViewGroup_addStatesFromChildren: 502 setAddStatesFromChildren(a.getBoolean(attr, false)); 503 break; 504 case R.styleable.ViewGroup_alwaysDrawnWithCache: 505 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true)); 506 break; 507 case R.styleable.ViewGroup_layoutAnimation: 508 int id = a.getResourceId(attr, -1); 509 if (id > 0) { 510 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id)); 511 } 512 break; 513 case R.styleable.ViewGroup_descendantFocusability: 514 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]); 515 break; 516 case R.styleable.ViewGroup_splitMotionEvents: 517 setMotionEventSplittingEnabled(a.getBoolean(attr, false)); 518 break; 519 case R.styleable.ViewGroup_animateLayoutChanges: 520 boolean animateLayoutChanges = a.getBoolean(attr, false); 521 if (animateLayoutChanges) { 522 setLayoutTransition(new LayoutTransition()); 523 } 524 break; 525 case R.styleable.ViewGroup_layoutMode: 526 setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE)); 527 break; 528 } 529 } 530 531 a.recycle(); 532 } 533 534 /** 535 * Gets the descendant focusability of this view group. The descendant 536 * focusability defines the relationship between this view group and its 537 * descendants when looking for a view to take focus in 538 * {@link #requestFocus(int, android.graphics.Rect)}. 539 * 540 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 541 * {@link #FOCUS_BLOCK_DESCENDANTS}. 542 */ 543 @ViewDebug.ExportedProperty(category = "focus", mapping = { 544 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"), 545 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"), 546 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS") 547 }) 548 public int getDescendantFocusability() { 549 return mGroupFlags & FLAG_MASK_FOCUSABILITY; 550 } 551 552 /** 553 * Set the descendant focusability of this view group. This defines the relationship 554 * between this view group and its descendants when looking for a view to 555 * take focus in {@link #requestFocus(int, android.graphics.Rect)}. 556 * 557 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, 558 * {@link #FOCUS_BLOCK_DESCENDANTS}. 559 */ 560 public void setDescendantFocusability(int focusability) { 561 switch (focusability) { 562 case FOCUS_BEFORE_DESCENDANTS: 563 case FOCUS_AFTER_DESCENDANTS: 564 case FOCUS_BLOCK_DESCENDANTS: 565 break; 566 default: 567 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, " 568 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS"); 569 } 570 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY; 571 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY); 572 } 573 574 /** 575 * {@inheritDoc} 576 */ 577 @Override 578 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) { 579 if (mFocused != null) { 580 mFocused.unFocus(); 581 mFocused = null; 582 } 583 super.handleFocusGainInternal(direction, previouslyFocusedRect); 584 } 585 586 /** 587 * {@inheritDoc} 588 */ 589 public void requestChildFocus(View child, View focused) { 590 if (DBG) { 591 System.out.println(this + " requestChildFocus()"); 592 } 593 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) { 594 return; 595 } 596 597 // Unfocus us, if necessary 598 super.unFocus(); 599 600 // We had a previous notion of who had focus. Clear it. 601 if (mFocused != child) { 602 if (mFocused != null) { 603 mFocused.unFocus(); 604 } 605 606 mFocused = child; 607 } 608 if (mParent != null) { 609 mParent.requestChildFocus(this, focused); 610 } 611 } 612 613 /** 614 * {@inheritDoc} 615 */ 616 public void focusableViewAvailable(View v) { 617 if (mParent != null 618 // shortcut: don't report a new focusable view if we block our descendants from 619 // getting focus 620 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) 621 // shortcut: don't report a new focusable view if we already are focused 622 // (and we don't prefer our descendants) 623 // 624 // note: knowing that mFocused is non-null is not a good enough reason 625 // to break the traversal since in that case we'd actually have to find 626 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and 627 // an ancestor of v; this will get checked for at ViewAncestor 628 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) { 629 mParent.focusableViewAvailable(v); 630 } 631 } 632 633 /** 634 * {@inheritDoc} 635 */ 636 public boolean showContextMenuForChild(View originalView) { 637 return mParent != null && mParent.showContextMenuForChild(originalView); 638 } 639 640 /** 641 * {@inheritDoc} 642 */ 643 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 644 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null; 645 } 646 647 /** 648 * Find the nearest view in the specified direction that wants to take 649 * focus. 650 * 651 * @param focused The view that currently has focus 652 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and 653 * FOCUS_RIGHT, or 0 for not applicable. 654 */ 655 public View focusSearch(View focused, int direction) { 656 if (isRootNamespace()) { 657 // root namespace means we should consider ourselves the top of the 658 // tree for focus searching; otherwise we could be focus searching 659 // into other tabs. see LocalActivityManager and TabHost for more info 660 return FocusFinder.getInstance().findNextFocus(this, focused, direction); 661 } else if (mParent != null) { 662 return mParent.focusSearch(focused, direction); 663 } 664 return null; 665 } 666 667 /** 668 * {@inheritDoc} 669 */ 670 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 671 return false; 672 } 673 674 /** 675 * {@inheritDoc} 676 */ 677 @Override 678 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 679 ViewParent parent = mParent; 680 if (parent == null) { 681 return false; 682 } 683 final boolean propagate = onRequestSendAccessibilityEvent(child, event); 684 if (!propagate) { 685 return false; 686 } 687 return parent.requestSendAccessibilityEvent(this, event); 688 } 689 690 /** 691 * Called when a child has requested sending an {@link AccessibilityEvent} and 692 * gives an opportunity to its parent to augment the event. 693 * <p> 694 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 695 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 696 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} 697 * is responsible for handling this call. 698 * </p> 699 * 700 * @param child The child which requests sending the event. 701 * @param event The event to be sent. 702 * @return True if the event should be sent. 703 * 704 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent) 705 */ 706 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { 707 if (mAccessibilityDelegate != null) { 708 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event); 709 } else { 710 return onRequestSendAccessibilityEventInternal(child, event); 711 } 712 } 713 714 /** 715 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent) 716 * 717 * Note: Called from the default {@link View.AccessibilityDelegate}. 718 */ 719 boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) { 720 return true; 721 } 722 723 /** 724 * Called when a child view has changed whether or not it is tracking transient state. 725 * 726 * @hide 727 */ 728 public void childHasTransientStateChanged(View child, boolean childHasTransientState) { 729 final boolean oldHasTransientState = hasTransientState(); 730 if (childHasTransientState) { 731 mChildCountWithTransientState++; 732 } else { 733 mChildCountWithTransientState--; 734 } 735 736 final boolean newHasTransientState = hasTransientState(); 737 if (mParent != null && oldHasTransientState != newHasTransientState) { 738 try { 739 mParent.childHasTransientStateChanged(this, newHasTransientState); 740 } catch (AbstractMethodError e) { 741 Log.e(TAG, mParent.getClass().getSimpleName() + 742 " does not fully implement ViewParent", e); 743 } 744 } 745 } 746 747 /** 748 * @hide 749 */ 750 @Override 751 public boolean hasTransientState() { 752 return mChildCountWithTransientState > 0 || super.hasTransientState(); 753 } 754 755 /** 756 * {@inheritDoc} 757 */ 758 @Override 759 public boolean dispatchUnhandledMove(View focused, int direction) { 760 return mFocused != null && 761 mFocused.dispatchUnhandledMove(focused, direction); 762 } 763 764 /** 765 * {@inheritDoc} 766 */ 767 public void clearChildFocus(View child) { 768 if (DBG) { 769 System.out.println(this + " clearChildFocus()"); 770 } 771 772 mFocused = null; 773 if (mParent != null) { 774 mParent.clearChildFocus(this); 775 } 776 } 777 778 /** 779 * {@inheritDoc} 780 */ 781 @Override 782 public void clearFocus() { 783 if (DBG) { 784 System.out.println(this + " clearFocus()"); 785 } 786 if (mFocused == null) { 787 super.clearFocus(); 788 } else { 789 View focused = mFocused; 790 mFocused = null; 791 focused.clearFocus(); 792 } 793 } 794 795 /** 796 * {@inheritDoc} 797 */ 798 @Override 799 void unFocus() { 800 if (DBG) { 801 System.out.println(this + " unFocus()"); 802 } 803 if (mFocused == null) { 804 super.unFocus(); 805 } else { 806 mFocused.unFocus(); 807 mFocused = null; 808 } 809 } 810 811 /** 812 * Returns the focused child of this view, if any. The child may have focus 813 * or contain focus. 814 * 815 * @return the focused child or null. 816 */ 817 public View getFocusedChild() { 818 return mFocused; 819 } 820 821 /** 822 * Returns true if this view has or contains focus 823 * 824 * @return true if this view has or contains focus 825 */ 826 @Override 827 public boolean hasFocus() { 828 return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null; 829 } 830 831 /* 832 * (non-Javadoc) 833 * 834 * @see android.view.View#findFocus() 835 */ 836 @Override 837 public View findFocus() { 838 if (DBG) { 839 System.out.println("Find focus in " + this + ": flags=" 840 + isFocused() + ", child=" + mFocused); 841 } 842 843 if (isFocused()) { 844 return this; 845 } 846 847 if (mFocused != null) { 848 return mFocused.findFocus(); 849 } 850 return null; 851 } 852 853 /** 854 * {@inheritDoc} 855 */ 856 @Override 857 public boolean hasFocusable() { 858 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) { 859 return false; 860 } 861 862 if (isFocusable()) { 863 return true; 864 } 865 866 final int descendantFocusability = getDescendantFocusability(); 867 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 868 final int count = mChildrenCount; 869 final View[] children = mChildren; 870 871 for (int i = 0; i < count; i++) { 872 final View child = children[i]; 873 if (child.hasFocusable()) { 874 return true; 875 } 876 } 877 } 878 879 return false; 880 } 881 882 /** 883 * {@inheritDoc} 884 */ 885 @Override 886 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 887 final int focusableCount = views.size(); 888 889 final int descendantFocusability = getDescendantFocusability(); 890 891 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 892 final int count = mChildrenCount; 893 final View[] children = mChildren; 894 895 for (int i = 0; i < count; i++) { 896 final View child = children[i]; 897 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 898 child.addFocusables(views, direction, focusableMode); 899 } 900 } 901 } 902 903 // we add ourselves (if focusable) in all cases except for when we are 904 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is 905 // to avoid the focus search finding layouts when a more precise search 906 // among the focusable children would be more interesting. 907 if (descendantFocusability != FOCUS_AFTER_DESCENDANTS 908 // No focusable descendants 909 || (focusableCount == views.size())) { 910 super.addFocusables(views, direction, focusableMode); 911 } 912 } 913 914 @Override 915 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) { 916 super.findViewsWithText(outViews, text, flags); 917 final int childrenCount = mChildrenCount; 918 final View[] children = mChildren; 919 for (int i = 0; i < childrenCount; i++) { 920 View child = children[i]; 921 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE 922 && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 923 child.findViewsWithText(outViews, text, flags); 924 } 925 } 926 } 927 928 @Override 929 View findViewByAccessibilityIdTraversal(int accessibilityId) { 930 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId); 931 if (foundView != null) { 932 return foundView; 933 } 934 final int childrenCount = mChildrenCount; 935 final View[] children = mChildren; 936 for (int i = 0; i < childrenCount; i++) { 937 View child = children[i]; 938 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId); 939 if (foundView != null) { 940 return foundView; 941 } 942 } 943 return null; 944 } 945 946 /** 947 * {@inheritDoc} 948 */ 949 @Override 950 public void dispatchWindowFocusChanged(boolean hasFocus) { 951 super.dispatchWindowFocusChanged(hasFocus); 952 final int count = mChildrenCount; 953 final View[] children = mChildren; 954 for (int i = 0; i < count; i++) { 955 children[i].dispatchWindowFocusChanged(hasFocus); 956 } 957 } 958 959 /** 960 * {@inheritDoc} 961 */ 962 @Override 963 public void addTouchables(ArrayList<View> views) { 964 super.addTouchables(views); 965 966 final int count = mChildrenCount; 967 final View[] children = mChildren; 968 969 for (int i = 0; i < count; i++) { 970 final View child = children[i]; 971 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 972 child.addTouchables(views); 973 } 974 } 975 } 976 977 /** 978 * @hide 979 */ 980 @Override 981 public void makeOptionalFitsSystemWindows() { 982 super.makeOptionalFitsSystemWindows(); 983 final int count = mChildrenCount; 984 final View[] children = mChildren; 985 for (int i = 0; i < count; i++) { 986 children[i].makeOptionalFitsSystemWindows(); 987 } 988 } 989 990 /** 991 * {@inheritDoc} 992 */ 993 @Override 994 public void dispatchDisplayHint(int hint) { 995 super.dispatchDisplayHint(hint); 996 final int count = mChildrenCount; 997 final View[] children = mChildren; 998 for (int i = 0; i < count; i++) { 999 children[i].dispatchDisplayHint(hint); 1000 } 1001 } 1002 1003 /** 1004 * Called when a view's visibility has changed. Notify the parent to take any appropriate 1005 * action. 1006 * 1007 * @param child The view whose visibility has changed 1008 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE). 1009 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE). 1010 * @hide 1011 */ 1012 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) { 1013 if (mTransition != null) { 1014 if (newVisibility == VISIBLE) { 1015 mTransition.showChild(this, child, oldVisibility); 1016 } else { 1017 mTransition.hideChild(this, child, newVisibility); 1018 if (mTransitioningViews != null && mTransitioningViews.contains(child)) { 1019 // Only track this on disappearing views - appearing views are already visible 1020 // and don't need special handling during drawChild() 1021 if (mVisibilityChangingChildren == null) { 1022 mVisibilityChangingChildren = new ArrayList<View>(); 1023 } 1024 mVisibilityChangingChildren.add(child); 1025 addDisappearingView(child); 1026 } 1027 } 1028 } 1029 1030 // in all cases, for drags 1031 if (mCurrentDrag != null) { 1032 if (newVisibility == VISIBLE) { 1033 notifyChildOfDrag(child); 1034 } 1035 } 1036 } 1037 1038 /** 1039 * {@inheritDoc} 1040 */ 1041 @Override 1042 protected void dispatchVisibilityChanged(View changedView, int visibility) { 1043 super.dispatchVisibilityChanged(changedView, visibility); 1044 final int count = mChildrenCount; 1045 final View[] children = mChildren; 1046 for (int i = 0; i < count; i++) { 1047 children[i].dispatchVisibilityChanged(changedView, visibility); 1048 } 1049 } 1050 1051 /** 1052 * {@inheritDoc} 1053 */ 1054 @Override 1055 public void dispatchWindowVisibilityChanged(int visibility) { 1056 super.dispatchWindowVisibilityChanged(visibility); 1057 final int count = mChildrenCount; 1058 final View[] children = mChildren; 1059 for (int i = 0; i < count; i++) { 1060 children[i].dispatchWindowVisibilityChanged(visibility); 1061 } 1062 } 1063 1064 /** 1065 * {@inheritDoc} 1066 */ 1067 @Override 1068 public void dispatchConfigurationChanged(Configuration newConfig) { 1069 super.dispatchConfigurationChanged(newConfig); 1070 final int count = mChildrenCount; 1071 final View[] children = mChildren; 1072 for (int i = 0; i < count; i++) { 1073 children[i].dispatchConfigurationChanged(newConfig); 1074 } 1075 } 1076 1077 /** 1078 * {@inheritDoc} 1079 */ 1080 public void recomputeViewAttributes(View child) { 1081 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) { 1082 ViewParent parent = mParent; 1083 if (parent != null) parent.recomputeViewAttributes(this); 1084 } 1085 } 1086 1087 @Override 1088 void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) { 1089 if ((visibility & VISIBILITY_MASK) == VISIBLE) { 1090 super.dispatchCollectViewAttributes(attachInfo, visibility); 1091 final int count = mChildrenCount; 1092 final View[] children = mChildren; 1093 for (int i = 0; i < count; i++) { 1094 final View child = children[i]; 1095 child.dispatchCollectViewAttributes(attachInfo, 1096 visibility | (child.mViewFlags&VISIBILITY_MASK)); 1097 } 1098 } 1099 } 1100 1101 /** 1102 * {@inheritDoc} 1103 */ 1104 public void bringChildToFront(View child) { 1105 int index = indexOfChild(child); 1106 if (index >= 0) { 1107 removeFromArray(index); 1108 addInArray(child, mChildrenCount); 1109 child.mParent = this; 1110 } 1111 } 1112 1113 /** 1114 * {@inheritDoc} 1115 */ 1116 // TODO: Write real docs 1117 @Override 1118 public boolean dispatchDragEvent(DragEvent event) { 1119 boolean retval = false; 1120 final float tx = event.mX; 1121 final float ty = event.mY; 1122 1123 ViewRootImpl root = getViewRootImpl(); 1124 1125 // Dispatch down the view hierarchy 1126 switch (event.mAction) { 1127 case DragEvent.ACTION_DRAG_STARTED: { 1128 // clear state to recalculate which views we drag over 1129 mCurrentDragView = null; 1130 1131 // Set up our tracking of drag-started notifications 1132 mCurrentDrag = DragEvent.obtain(event); 1133 if (mDragNotifiedChildren == null) { 1134 mDragNotifiedChildren = new HashSet<View>(); 1135 } else { 1136 mDragNotifiedChildren.clear(); 1137 } 1138 1139 // Now dispatch down to our children, caching the responses 1140 mChildAcceptsDrag = false; 1141 final int count = mChildrenCount; 1142 final View[] children = mChildren; 1143 for (int i = 0; i < count; i++) { 1144 final View child = children[i]; 1145 child.mPrivateFlags2 &= ~View.DRAG_MASK; 1146 if (child.getVisibility() == VISIBLE) { 1147 final boolean handled = notifyChildOfDrag(children[i]); 1148 if (handled) { 1149 mChildAcceptsDrag = true; 1150 } 1151 } 1152 } 1153 1154 // Return HANDLED if one of our children can accept the drag 1155 if (mChildAcceptsDrag) { 1156 retval = true; 1157 } 1158 } break; 1159 1160 case DragEvent.ACTION_DRAG_ENDED: { 1161 // Release the bookkeeping now that the drag lifecycle has ended 1162 if (mDragNotifiedChildren != null) { 1163 for (View child : mDragNotifiedChildren) { 1164 // If a child was notified about an ongoing drag, it's told that it's over 1165 child.dispatchDragEvent(event); 1166 child.mPrivateFlags2 &= ~View.DRAG_MASK; 1167 child.refreshDrawableState(); 1168 } 1169 1170 mDragNotifiedChildren.clear(); 1171 if (mCurrentDrag != null) { 1172 mCurrentDrag.recycle(); 1173 mCurrentDrag = null; 1174 } 1175 } 1176 1177 // We consider drag-ended to have been handled if one of our children 1178 // had offered to handle the drag. 1179 if (mChildAcceptsDrag) { 1180 retval = true; 1181 } 1182 } break; 1183 1184 case DragEvent.ACTION_DRAG_LOCATION: { 1185 // Find the [possibly new] drag target 1186 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); 1187 1188 // If we've changed apparent drag target, tell the view root which view 1189 // we're over now [for purposes of the eventual drag-recipient-changed 1190 // notifications to the framework] and tell the new target that the drag 1191 // has entered its bounds. The root will see setDragFocus() calls all 1192 // the way down to the final leaf view that is handling the LOCATION event 1193 // before reporting the new potential recipient to the framework. 1194 if (mCurrentDragView != target) { 1195 root.setDragFocus(target); 1196 1197 final int action = event.mAction; 1198 // If we've dragged off of a child view, send it the EXITED message 1199 if (mCurrentDragView != null) { 1200 final View view = mCurrentDragView; 1201 event.mAction = DragEvent.ACTION_DRAG_EXITED; 1202 view.dispatchDragEvent(event); 1203 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; 1204 view.refreshDrawableState(); 1205 } 1206 mCurrentDragView = target; 1207 1208 // If we've dragged over a new child view, send it the ENTERED message 1209 if (target != null) { 1210 event.mAction = DragEvent.ACTION_DRAG_ENTERED; 1211 target.dispatchDragEvent(event); 1212 target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED; 1213 target.refreshDrawableState(); 1214 } 1215 event.mAction = action; // restore the event's original state 1216 } 1217 1218 // Dispatch the actual drag location notice, localized into its coordinates 1219 if (target != null) { 1220 event.mX = mLocalPoint.x; 1221 event.mY = mLocalPoint.y; 1222 1223 retval = target.dispatchDragEvent(event); 1224 1225 event.mX = tx; 1226 event.mY = ty; 1227 } 1228 } break; 1229 1230 /* Entered / exited dispatch 1231 * 1232 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is 1233 * that we're about to get the corresponding LOCATION event, which we will use to 1234 * determine which of our children is the new target; at that point we will 1235 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup]. 1236 * 1237 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the 1238 * drag has left this ViewGroup, we know by definition that every contained subview 1239 * is also no longer under the drag point. 1240 */ 1241 1242 case DragEvent.ACTION_DRAG_EXITED: { 1243 if (mCurrentDragView != null) { 1244 final View view = mCurrentDragView; 1245 view.dispatchDragEvent(event); 1246 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; 1247 view.refreshDrawableState(); 1248 1249 mCurrentDragView = null; 1250 } 1251 } break; 1252 1253 case DragEvent.ACTION_DROP: { 1254 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event); 1255 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); 1256 if (target != null) { 1257 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target); 1258 event.mX = mLocalPoint.x; 1259 event.mY = mLocalPoint.y; 1260 retval = target.dispatchDragEvent(event); 1261 event.mX = tx; 1262 event.mY = ty; 1263 } else { 1264 if (ViewDebug.DEBUG_DRAG) { 1265 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view"); 1266 } 1267 } 1268 } break; 1269 } 1270 1271 // If none of our children could handle the event, try here 1272 if (!retval) { 1273 // Call up to the View implementation that dispatches to installed listeners 1274 retval = super.dispatchDragEvent(event); 1275 } 1276 return retval; 1277 } 1278 1279 // Find the frontmost child view that lies under the given point, and calculate 1280 // the position within its own local coordinate system. 1281 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) { 1282 final int count = mChildrenCount; 1283 final View[] children = mChildren; 1284 for (int i = count - 1; i >= 0; i--) { 1285 final View child = children[i]; 1286 if (!child.canAcceptDrag()) { 1287 continue; 1288 } 1289 1290 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) { 1291 return child; 1292 } 1293 } 1294 return null; 1295 } 1296 1297 boolean notifyChildOfDrag(View child) { 1298 if (ViewDebug.DEBUG_DRAG) { 1299 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child); 1300 } 1301 1302 boolean canAccept = false; 1303 if (! mDragNotifiedChildren.contains(child)) { 1304 mDragNotifiedChildren.add(child); 1305 canAccept = child.dispatchDragEvent(mCurrentDrag); 1306 if (canAccept && !child.canAcceptDrag()) { 1307 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT; 1308 child.refreshDrawableState(); 1309 } 1310 } 1311 return canAccept; 1312 } 1313 1314 @Override 1315 public void dispatchWindowSystemUiVisiblityChanged(int visible) { 1316 super.dispatchWindowSystemUiVisiblityChanged(visible); 1317 1318 final int count = mChildrenCount; 1319 final View[] children = mChildren; 1320 for (int i=0; i <count; i++) { 1321 final View child = children[i]; 1322 child.dispatchWindowSystemUiVisiblityChanged(visible); 1323 } 1324 } 1325 1326 @Override 1327 public void dispatchSystemUiVisibilityChanged(int visible) { 1328 super.dispatchSystemUiVisibilityChanged(visible); 1329 1330 final int count = mChildrenCount; 1331 final View[] children = mChildren; 1332 for (int i=0; i <count; i++) { 1333 final View child = children[i]; 1334 child.dispatchSystemUiVisibilityChanged(visible); 1335 } 1336 } 1337 1338 @Override 1339 boolean updateLocalSystemUiVisibility(int localValue, int localChanges) { 1340 boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges); 1341 1342 final int count = mChildrenCount; 1343 final View[] children = mChildren; 1344 for (int i=0; i <count; i++) { 1345 final View child = children[i]; 1346 changed |= child.updateLocalSystemUiVisibility(localValue, localChanges); 1347 } 1348 return changed; 1349 } 1350 1351 /** 1352 * {@inheritDoc} 1353 */ 1354 @Override 1355 public boolean dispatchKeyEventPreIme(KeyEvent event) { 1356 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) 1357 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { 1358 return super.dispatchKeyEventPreIme(event); 1359 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) 1360 == PFLAG_HAS_BOUNDS) { 1361 return mFocused.dispatchKeyEventPreIme(event); 1362 } 1363 return false; 1364 } 1365 1366 /** 1367 * {@inheritDoc} 1368 */ 1369 @Override 1370 public boolean dispatchKeyEvent(KeyEvent event) { 1371 if (mInputEventConsistencyVerifier != null) { 1372 mInputEventConsistencyVerifier.onKeyEvent(event, 1); 1373 } 1374 1375 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) 1376 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { 1377 if (super.dispatchKeyEvent(event)) { 1378 return true; 1379 } 1380 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) 1381 == PFLAG_HAS_BOUNDS) { 1382 if (mFocused.dispatchKeyEvent(event)) { 1383 return true; 1384 } 1385 } 1386 1387 if (mInputEventConsistencyVerifier != null) { 1388 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); 1389 } 1390 return false; 1391 } 1392 1393 /** 1394 * {@inheritDoc} 1395 */ 1396 @Override 1397 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 1398 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) 1399 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { 1400 return super.dispatchKeyShortcutEvent(event); 1401 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) 1402 == PFLAG_HAS_BOUNDS) { 1403 return mFocused.dispatchKeyShortcutEvent(event); 1404 } 1405 return false; 1406 } 1407 1408 /** 1409 * {@inheritDoc} 1410 */ 1411 @Override 1412 public boolean dispatchTrackballEvent(MotionEvent event) { 1413 if (mInputEventConsistencyVerifier != null) { 1414 mInputEventConsistencyVerifier.onTrackballEvent(event, 1); 1415 } 1416 1417 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) 1418 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { 1419 if (super.dispatchTrackballEvent(event)) { 1420 return true; 1421 } 1422 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) 1423 == PFLAG_HAS_BOUNDS) { 1424 if (mFocused.dispatchTrackballEvent(event)) { 1425 return true; 1426 } 1427 } 1428 1429 if (mInputEventConsistencyVerifier != null) { 1430 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); 1431 } 1432 return false; 1433 } 1434 1435 /** 1436 * {@inheritDoc} 1437 */ 1438 @SuppressWarnings({"ConstantConditions"}) 1439 @Override 1440 protected boolean dispatchHoverEvent(MotionEvent event) { 1441 final int action = event.getAction(); 1442 1443 // First check whether the view group wants to intercept the hover event. 1444 final boolean interceptHover = onInterceptHoverEvent(event); 1445 event.setAction(action); // restore action in case it was changed 1446 1447 MotionEvent eventNoHistory = event; 1448 boolean handled = false; 1449 1450 // Send events to the hovered children and build a new list of hover targets until 1451 // one is found that handles the event. 1452 HoverTarget firstOldHoverTarget = mFirstHoverTarget; 1453 mFirstHoverTarget = null; 1454 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) { 1455 final float x = event.getX(); 1456 final float y = event.getY(); 1457 final int childrenCount = mChildrenCount; 1458 if (childrenCount != 0) { 1459 final View[] children = mChildren; 1460 HoverTarget lastHoverTarget = null; 1461 for (int i = childrenCount - 1; i >= 0; i--) { 1462 final View child = children[i]; 1463 if (!canViewReceivePointerEvents(child) 1464 || !isTransformedTouchPointInView(x, y, child, null)) { 1465 continue; 1466 } 1467 1468 // Obtain a hover target for this child. Dequeue it from the 1469 // old hover target list if the child was previously hovered. 1470 HoverTarget hoverTarget = firstOldHoverTarget; 1471 final boolean wasHovered; 1472 for (HoverTarget predecessor = null; ;) { 1473 if (hoverTarget == null) { 1474 hoverTarget = HoverTarget.obtain(child); 1475 wasHovered = false; 1476 break; 1477 } 1478 1479 if (hoverTarget.child == child) { 1480 if (predecessor != null) { 1481 predecessor.next = hoverTarget.next; 1482 } else { 1483 firstOldHoverTarget = hoverTarget.next; 1484 } 1485 hoverTarget.next = null; 1486 wasHovered = true; 1487 break; 1488 } 1489 1490 predecessor = hoverTarget; 1491 hoverTarget = hoverTarget.next; 1492 } 1493 1494 // Enqueue the hover target onto the new hover target list. 1495 if (lastHoverTarget != null) { 1496 lastHoverTarget.next = hoverTarget; 1497 } else { 1498 lastHoverTarget = hoverTarget; 1499 mFirstHoverTarget = hoverTarget; 1500 } 1501 1502 // Dispatch the event to the child. 1503 if (action == MotionEvent.ACTION_HOVER_ENTER) { 1504 if (!wasHovered) { 1505 // Send the enter as is. 1506 handled |= dispatchTransformedGenericPointerEvent( 1507 event, child); // enter 1508 } 1509 } else if (action == MotionEvent.ACTION_HOVER_MOVE) { 1510 if (!wasHovered) { 1511 // Synthesize an enter from a move. 1512 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); 1513 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER); 1514 handled |= dispatchTransformedGenericPointerEvent( 1515 eventNoHistory, child); // enter 1516 eventNoHistory.setAction(action); 1517 1518 handled |= dispatchTransformedGenericPointerEvent( 1519 eventNoHistory, child); // move 1520 } else { 1521 // Send the move as is. 1522 handled |= dispatchTransformedGenericPointerEvent(event, child); 1523 } 1524 } 1525 if (handled) { 1526 break; 1527 } 1528 } 1529 } 1530 } 1531 1532 // Send exit events to all previously hovered children that are no longer hovered. 1533 while (firstOldHoverTarget != null) { 1534 final View child = firstOldHoverTarget.child; 1535 1536 // Exit the old hovered child. 1537 if (action == MotionEvent.ACTION_HOVER_EXIT) { 1538 // Send the exit as is. 1539 handled |= dispatchTransformedGenericPointerEvent( 1540 event, child); // exit 1541 } else { 1542 // Synthesize an exit from a move or enter. 1543 // Ignore the result because hover focus has moved to a different view. 1544 if (action == MotionEvent.ACTION_HOVER_MOVE) { 1545 dispatchTransformedGenericPointerEvent( 1546 event, child); // move 1547 } 1548 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); 1549 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT); 1550 dispatchTransformedGenericPointerEvent( 1551 eventNoHistory, child); // exit 1552 eventNoHistory.setAction(action); 1553 } 1554 1555 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next; 1556 firstOldHoverTarget.recycle(); 1557 firstOldHoverTarget = nextOldHoverTarget; 1558 } 1559 1560 // Send events to the view group itself if no children have handled it. 1561 boolean newHoveredSelf = !handled; 1562 if (newHoveredSelf == mHoveredSelf) { 1563 if (newHoveredSelf) { 1564 // Send event to the view group as before. 1565 handled |= super.dispatchHoverEvent(event); 1566 } 1567 } else { 1568 if (mHoveredSelf) { 1569 // Exit the view group. 1570 if (action == MotionEvent.ACTION_HOVER_EXIT) { 1571 // Send the exit as is. 1572 handled |= super.dispatchHoverEvent(event); // exit 1573 } else { 1574 // Synthesize an exit from a move or enter. 1575 // Ignore the result because hover focus is moving to a different view. 1576 if (action == MotionEvent.ACTION_HOVER_MOVE) { 1577 super.dispatchHoverEvent(event); // move 1578 } 1579 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); 1580 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT); 1581 super.dispatchHoverEvent(eventNoHistory); // exit 1582 eventNoHistory.setAction(action); 1583 } 1584 mHoveredSelf = false; 1585 } 1586 1587 if (newHoveredSelf) { 1588 // Enter the view group. 1589 if (action == MotionEvent.ACTION_HOVER_ENTER) { 1590 // Send the enter as is. 1591 handled |= super.dispatchHoverEvent(event); // enter 1592 mHoveredSelf = true; 1593 } else if (action == MotionEvent.ACTION_HOVER_MOVE) { 1594 // Synthesize an enter from a move. 1595 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); 1596 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER); 1597 handled |= super.dispatchHoverEvent(eventNoHistory); // enter 1598 eventNoHistory.setAction(action); 1599 1600 handled |= super.dispatchHoverEvent(eventNoHistory); // move 1601 mHoveredSelf = true; 1602 } 1603 } 1604 } 1605 1606 // Recycle the copy of the event that we made. 1607 if (eventNoHistory != event) { 1608 eventNoHistory.recycle(); 1609 } 1610 1611 // Done. 1612 return handled; 1613 } 1614 1615 private void exitHoverTargets() { 1616 if (mHoveredSelf || mFirstHoverTarget != null) { 1617 final long now = SystemClock.uptimeMillis(); 1618 MotionEvent event = MotionEvent.obtain(now, now, 1619 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0); 1620 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 1621 dispatchHoverEvent(event); 1622 event.recycle(); 1623 } 1624 } 1625 1626 private void cancelHoverTarget(View view) { 1627 HoverTarget predecessor = null; 1628 HoverTarget target = mFirstHoverTarget; 1629 while (target != null) { 1630 final HoverTarget next = target.next; 1631 if (target.child == view) { 1632 if (predecessor == null) { 1633 mFirstHoverTarget = next; 1634 } else { 1635 predecessor.next = next; 1636 } 1637 target.recycle(); 1638 1639 final long now = SystemClock.uptimeMillis(); 1640 MotionEvent event = MotionEvent.obtain(now, now, 1641 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0); 1642 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 1643 view.dispatchHoverEvent(event); 1644 event.recycle(); 1645 return; 1646 } 1647 predecessor = target; 1648 target = next; 1649 } 1650 } 1651 1652 /** @hide */ 1653 @Override 1654 protected boolean hasHoveredChild() { 1655 return mFirstHoverTarget != null; 1656 } 1657 1658 @Override 1659 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) { 1660 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true); 1661 try { 1662 final int childrenCount = children.getChildCount(); 1663 for (int i = 0; i < childrenCount; i++) { 1664 View child = children.getChildAt(i); 1665 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 1666 if (child.includeForAccessibility()) { 1667 childrenForAccessibility.add(child); 1668 } else { 1669 child.addChildrenForAccessibility(childrenForAccessibility); 1670 } 1671 } 1672 } 1673 } finally { 1674 children.recycle(); 1675 } 1676 } 1677 1678 /** 1679 * @hide 1680 */ 1681 @Override 1682 public void childAccessibilityStateChanged(View child) { 1683 if (mParent != null) { 1684 mParent.childAccessibilityStateChanged(child); 1685 } 1686 } 1687 1688 /** 1689 * Implement this method to intercept hover events before they are handled 1690 * by child views. 1691 * <p> 1692 * This method is called before dispatching a hover event to a child of 1693 * the view group or to the view group's own {@link #onHoverEvent} to allow 1694 * the view group a chance to intercept the hover event. 1695 * This method can also be used to watch all pointer motions that occur within 1696 * the bounds of the view group even when the pointer is hovering over 1697 * a child of the view group rather than over the view group itself. 1698 * </p><p> 1699 * The view group can prevent its children from receiving hover events by 1700 * implementing this method and returning <code>true</code> to indicate 1701 * that it would like to intercept hover events. The view group must 1702 * continuously return <code>true</code> from {@link #onInterceptHoverEvent} 1703 * for as long as it wishes to continue intercepting hover events from 1704 * its children. 1705 * </p><p> 1706 * Interception preserves the invariant that at most one view can be 1707 * hovered at a time by transferring hover focus from the currently hovered 1708 * child to the view group or vice-versa as needed. 1709 * </p><p> 1710 * If this method returns <code>true</code> and a child is already hovered, then the 1711 * child view will first receive a hover exit event and then the view group 1712 * itself will receive a hover enter event in {@link #onHoverEvent}. 1713 * Likewise, if this method had previously returned <code>true</code> to intercept hover 1714 * events and instead returns <code>false</code> while the pointer is hovering 1715 * within the bounds of one of a child, then the view group will first receive a 1716 * hover exit event in {@link #onHoverEvent} and then the hovered child will 1717 * receive a hover enter event. 1718 * </p><p> 1719 * The default implementation always returns false. 1720 * </p> 1721 * 1722 * @param event The motion event that describes the hover. 1723 * @return True if the view group would like to intercept the hover event 1724 * and prevent its children from receiving it. 1725 */ 1726 public boolean onInterceptHoverEvent(MotionEvent event) { 1727 return false; 1728 } 1729 1730 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) { 1731 if (event.getHistorySize() == 0) { 1732 return event; 1733 } 1734 return MotionEvent.obtainNoHistory(event); 1735 } 1736 1737 /** 1738 * {@inheritDoc} 1739 */ 1740 @Override 1741 protected boolean dispatchGenericPointerEvent(MotionEvent event) { 1742 // Send the event to the child under the pointer. 1743 final int childrenCount = mChildrenCount; 1744 if (childrenCount != 0) { 1745 final View[] children = mChildren; 1746 final float x = event.getX(); 1747 final float y = event.getY(); 1748 1749 final boolean customOrder = isChildrenDrawingOrderEnabled(); 1750 for (int i = childrenCount - 1; i >= 0; i--) { 1751 final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; 1752 final View child = children[childIndex]; 1753 if (!canViewReceivePointerEvents(child) 1754 || !isTransformedTouchPointInView(x, y, child, null)) { 1755 continue; 1756 } 1757 1758 if (dispatchTransformedGenericPointerEvent(event, child)) { 1759 return true; 1760 } 1761 } 1762 } 1763 1764 // No child handled the event. Send it to this view group. 1765 return super.dispatchGenericPointerEvent(event); 1766 } 1767 1768 /** 1769 * {@inheritDoc} 1770 */ 1771 @Override 1772 protected boolean dispatchGenericFocusedEvent(MotionEvent event) { 1773 // Send the event to the focused child or to this view group if it has focus. 1774 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) 1775 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { 1776 return super.dispatchGenericFocusedEvent(event); 1777 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) 1778 == PFLAG_HAS_BOUNDS) { 1779 return mFocused.dispatchGenericMotionEvent(event); 1780 } 1781 return false; 1782 } 1783 1784 /** 1785 * Dispatches a generic pointer event to a child, taking into account 1786 * transformations that apply to the child. 1787 * 1788 * @param event The event to send. 1789 * @param child The view to send the event to. 1790 * @return {@code true} if the child handled the event. 1791 */ 1792 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) { 1793 final float offsetX = mScrollX - child.mLeft; 1794 final float offsetY = mScrollY - child.mTop; 1795 1796 boolean handled; 1797 if (!child.hasIdentityMatrix()) { 1798 MotionEvent transformedEvent = MotionEvent.obtain(event); 1799 transformedEvent.offsetLocation(offsetX, offsetY); 1800 transformedEvent.transform(child.getInverseMatrix()); 1801 handled = child.dispatchGenericMotionEvent(transformedEvent); 1802 transformedEvent.recycle(); 1803 } else { 1804 event.offsetLocation(offsetX, offsetY); 1805 handled = child.dispatchGenericMotionEvent(event); 1806 event.offsetLocation(-offsetX, -offsetY); 1807 } 1808 return handled; 1809 } 1810 1811 /** 1812 * {@inheritDoc} 1813 */ 1814 @Override 1815 public boolean dispatchTouchEvent(MotionEvent ev) { 1816 if (mInputEventConsistencyVerifier != null) { 1817 mInputEventConsistencyVerifier.onTouchEvent(ev, 1); 1818 } 1819 1820 boolean handled = false; 1821 if (onFilterTouchEventForSecurity(ev)) { 1822 final int action = ev.getAction(); 1823 final int actionMasked = action & MotionEvent.ACTION_MASK; 1824 1825 // Handle an initial down. 1826 if (actionMasked == MotionEvent.ACTION_DOWN) { 1827 // Throw away all previous state when starting a new touch gesture. 1828 // The framework may have dropped the up or cancel event for the previous gesture 1829 // due to an app switch, ANR, or some other state change. 1830 cancelAndClearTouchTargets(ev); 1831 resetTouchState(); 1832 } 1833 1834 // Check for interception. 1835 final boolean intercepted; 1836 if (actionMasked == MotionEvent.ACTION_DOWN 1837 || mFirstTouchTarget != null) { 1838 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 1839 if (!disallowIntercept) { 1840 intercepted = onInterceptTouchEvent(ev); 1841 ev.setAction(action); // restore action in case it was changed 1842 } else { 1843 intercepted = false; 1844 } 1845 } else { 1846 // There are no touch targets and this action is not an initial down 1847 // so this view group continues to intercept touches. 1848 intercepted = true; 1849 } 1850 1851 // Check for cancelation. 1852 final boolean canceled = resetCancelNextUpFlag(this) 1853 || actionMasked == MotionEvent.ACTION_CANCEL; 1854 1855 // Update list of touch targets for pointer down, if needed. 1856 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; 1857 TouchTarget newTouchTarget = null; 1858 boolean alreadyDispatchedToNewTouchTarget = false; 1859 if (!canceled && !intercepted) { 1860 if (actionMasked == MotionEvent.ACTION_DOWN 1861 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) 1862 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 1863 final int actionIndex = ev.getActionIndex(); // always 0 for down 1864 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) 1865 : TouchTarget.ALL_POINTER_IDS; 1866 1867 // Clean up earlier touch targets for this pointer id in case they 1868 // have become out of sync. 1869 removePointersFromTouchTargets(idBitsToAssign); 1870 1871 final float x = ev.getX(actionIndex); 1872 final float y = ev.getY(actionIndex); 1873 1874 if (mOverlay != null) { 1875 ViewOverlay overlay = (ViewOverlay) mOverlay; 1876 // Check to see whether the overlay can handle the event 1877 final View child = mOverlay; 1878 if (canViewReceivePointerEvents(child) && 1879 isTransformedTouchPointInView(x, y, child, null)) { 1880 newTouchTarget = getTouchTarget(child); 1881 if (newTouchTarget != null) { 1882 newTouchTarget.pointerIdBits |= idBitsToAssign; 1883 } else { 1884 resetCancelNextUpFlag(child); 1885 if (dispatchTransformedTouchEvent(ev, false, child, 1886 idBitsToAssign)) { 1887 mLastTouchDownTime = ev.getDownTime(); 1888 mLastTouchDownX = ev.getX(); 1889 mLastTouchDownY = ev.getY(); 1890 newTouchTarget = addTouchTarget(child, idBitsToAssign); 1891 alreadyDispatchedToNewTouchTarget = true; 1892 } 1893 } 1894 } 1895 } 1896 1897 final int childrenCount = mChildrenCount; 1898 if (newTouchTarget == null && childrenCount != 0) { 1899 // Find a child that can receive the event. 1900 // Scan children from front to back. 1901 final View[] children = mChildren; 1902 1903 final boolean customOrder = isChildrenDrawingOrderEnabled(); 1904 for (int i = childrenCount - 1; i >= 0; i--) { 1905 final int childIndex = customOrder ? 1906 getChildDrawingOrder(childrenCount, i) : i; 1907 final View child = children[childIndex]; 1908 if (!canViewReceivePointerEvents(child) 1909 || !isTransformedTouchPointInView(x, y, child, null)) { 1910 continue; 1911 } 1912 1913 newTouchTarget = getTouchTarget(child); 1914 if (newTouchTarget != null) { 1915 // Child is already receiving touch within its bounds. 1916 // Give it the new pointer in addition to the ones it is handling. 1917 newTouchTarget.pointerIdBits |= idBitsToAssign; 1918 break; 1919 } 1920 1921 resetCancelNextUpFlag(child); 1922 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 1923 // Child wants to receive touch within its bounds. 1924 mLastTouchDownTime = ev.getDownTime(); 1925 mLastTouchDownIndex = childIndex; 1926 mLastTouchDownX = ev.getX(); 1927 mLastTouchDownY = ev.getY(); 1928 newTouchTarget = addTouchTarget(child, idBitsToAssign); 1929 alreadyDispatchedToNewTouchTarget = true; 1930 break; 1931 } 1932 } 1933 } 1934 1935 if (newTouchTarget == null && mFirstTouchTarget != null) { 1936 // Did not find a child to receive the event. 1937 // Assign the pointer to the least recently added target. 1938 newTouchTarget = mFirstTouchTarget; 1939 while (newTouchTarget.next != null) { 1940 newTouchTarget = newTouchTarget.next; 1941 } 1942 newTouchTarget.pointerIdBits |= idBitsToAssign; 1943 } 1944 } 1945 } 1946 1947 // Dispatch to touch targets. 1948 if (mFirstTouchTarget == null) { 1949 // No touch targets so treat this as an ordinary view. 1950 handled = dispatchTransformedTouchEvent(ev, canceled, null, 1951 TouchTarget.ALL_POINTER_IDS); 1952 } else { 1953 // Dispatch to touch targets, excluding the new touch target if we already 1954 // dispatched to it. Cancel touch targets if necessary. 1955 TouchTarget predecessor = null; 1956 TouchTarget target = mFirstTouchTarget; 1957 while (target != null) { 1958 final TouchTarget next = target.next; 1959 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { 1960 handled = true; 1961 } else { 1962 final boolean cancelChild = resetCancelNextUpFlag(target.child) 1963 || intercepted; 1964 if (dispatchTransformedTouchEvent(ev, cancelChild, 1965 target.child, target.pointerIdBits)) { 1966 handled = true; 1967 } 1968 if (cancelChild) { 1969 if (predecessor == null) { 1970 mFirstTouchTarget = next; 1971 } else { 1972 predecessor.next = next; 1973 } 1974 target.recycle(); 1975 target = next; 1976 continue; 1977 } 1978 } 1979 predecessor = target; 1980 target = next; 1981 } 1982 } 1983 1984 // Update list of touch targets for pointer up or cancel, if needed. 1985 if (canceled 1986 || actionMasked == MotionEvent.ACTION_UP 1987 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 1988 resetTouchState(); 1989 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { 1990 final int actionIndex = ev.getActionIndex(); 1991 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); 1992 removePointersFromTouchTargets(idBitsToRemove); 1993 } 1994 } 1995 1996 if (!handled && mInputEventConsistencyVerifier != null) { 1997 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); 1998 } 1999 return handled; 2000 } 2001 2002 /** 2003 * Resets all touch state in preparation for a new cycle. 2004 */ 2005 private void resetTouchState() { 2006 clearTouchTargets(); 2007 resetCancelNextUpFlag(this); 2008 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 2009 } 2010 2011 /** 2012 * Resets the cancel next up flag. 2013 * Returns true if the flag was previously set. 2014 */ 2015 private static boolean resetCancelNextUpFlag(View view) { 2016 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { 2017 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; 2018 return true; 2019 } 2020 return false; 2021 } 2022 2023 /** 2024 * Clears all touch targets. 2025 */ 2026 private void clearTouchTargets() { 2027 TouchTarget target = mFirstTouchTarget; 2028 if (target != null) { 2029 do { 2030 TouchTarget next = target.next; 2031 target.recycle(); 2032 target = next; 2033 } while (target != null); 2034 mFirstTouchTarget = null; 2035 } 2036 } 2037 2038 /** 2039 * Cancels and clears all touch targets. 2040 */ 2041 private void cancelAndClearTouchTargets(MotionEvent event) { 2042 if (mFirstTouchTarget != null) { 2043 boolean syntheticEvent = false; 2044 if (event == null) { 2045 final long now = SystemClock.uptimeMillis(); 2046 event = MotionEvent.obtain(now, now, 2047 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 2048 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 2049 syntheticEvent = true; 2050 } 2051 2052 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 2053 resetCancelNextUpFlag(target.child); 2054 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits); 2055 } 2056 clearTouchTargets(); 2057 2058 if (syntheticEvent) { 2059 event.recycle(); 2060 } 2061 } 2062 } 2063 2064 /** 2065 * Gets the touch target for specified child view. 2066 * Returns null if not found. 2067 */ 2068 private TouchTarget getTouchTarget(View child) { 2069 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { 2070 if (target.child == child) { 2071 return target; 2072 } 2073 } 2074 return null; 2075 } 2076 2077 /** 2078 * Adds a touch target for specified child to the beginning of the list. 2079 * Assumes the target child is not already present. 2080 */ 2081 private TouchTarget addTouchTarget(View child, int pointerIdBits) { 2082 TouchTarget target = TouchTarget.obtain(child, pointerIdBits); 2083 target.next = mFirstTouchTarget; 2084 mFirstTouchTarget = target; 2085 return target; 2086 } 2087 2088 /** 2089 * Removes the pointer ids from consideration. 2090 */ 2091 private void removePointersFromTouchTargets(int pointerIdBits) { 2092 TouchTarget predecessor = null; 2093 TouchTarget target = mFirstTouchTarget; 2094 while (target != null) { 2095 final TouchTarget next = target.next; 2096 if ((target.pointerIdBits & pointerIdBits) != 0) { 2097 target.pointerIdBits &= ~pointerIdBits; 2098 if (target.pointerIdBits == 0) { 2099 if (predecessor == null) { 2100 mFirstTouchTarget = next; 2101 } else { 2102 predecessor.next = next; 2103 } 2104 target.recycle(); 2105 target = next; 2106 continue; 2107 } 2108 } 2109 predecessor = target; 2110 target = next; 2111 } 2112 } 2113 2114 private void cancelTouchTarget(View view) { 2115 TouchTarget predecessor = null; 2116 TouchTarget target = mFirstTouchTarget; 2117 while (target != null) { 2118 final TouchTarget next = target.next; 2119 if (target.child == view) { 2120 if (predecessor == null) { 2121 mFirstTouchTarget = next; 2122 } else { 2123 predecessor.next = next; 2124 } 2125 target.recycle(); 2126 2127 final long now = SystemClock.uptimeMillis(); 2128 MotionEvent event = MotionEvent.obtain(now, now, 2129 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 2130 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 2131 view.dispatchTouchEvent(event); 2132 event.recycle(); 2133 return; 2134 } 2135 predecessor = target; 2136 target = next; 2137 } 2138 } 2139 2140 /** 2141 * Returns true if a child view can receive pointer events. 2142 * @hide 2143 */ 2144 private static boolean canViewReceivePointerEvents(View child) { 2145 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE 2146 || child.getAnimation() != null; 2147 } 2148 2149 /** 2150 * Returns true if a child view contains the specified point when transformed 2151 * into its coordinate space. 2152 * Child must not be null. 2153 * @hide 2154 */ 2155 protected boolean isTransformedTouchPointInView(float x, float y, View child, 2156 PointF outLocalPoint) { 2157 float localX = x + mScrollX - child.mLeft; 2158 float localY = y + mScrollY - child.mTop; 2159 if (! child.hasIdentityMatrix() && mAttachInfo != null) { 2160 final float[] localXY = mAttachInfo.mTmpTransformLocation; 2161 localXY[0] = localX; 2162 localXY[1] = localY; 2163 child.getInverseMatrix().mapPoints(localXY); 2164 localX = localXY[0]; 2165 localY = localXY[1]; 2166 } 2167 final boolean isInView = child.pointInView(localX, localY); 2168 if (isInView && outLocalPoint != null) { 2169 outLocalPoint.set(localX, localY); 2170 } 2171 return isInView; 2172 } 2173 2174 /** 2175 * Transforms a motion event into the coordinate space of a particular child view, 2176 * filters out irrelevant pointer ids, and overrides its action if necessary. 2177 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 2178 */ 2179 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, 2180 View child, int desiredPointerIdBits) { 2181 final boolean handled; 2182 2183 // Canceling motions is a special case. We don't need to perform any transformations 2184 // or filtering. The important part is the action, not the contents. 2185 final int oldAction = event.getAction(); 2186 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { 2187 event.setAction(MotionEvent.ACTION_CANCEL); 2188 if (child == null) { 2189 handled = super.dispatchTouchEvent(event); 2190 } else { 2191 handled = child.dispatchTouchEvent(event); 2192 } 2193 event.setAction(oldAction); 2194 return handled; 2195 } 2196 2197 // Calculate the number of pointers to deliver. 2198 final int oldPointerIdBits = event.getPointerIdBits(); 2199 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; 2200 2201 // If for some reason we ended up in an inconsistent state where it looks like we 2202 // might produce a motion event with no pointers in it, then drop the event. 2203 if (newPointerIdBits == 0) { 2204 return false; 2205 } 2206 2207 // If the number of pointers is the same and we don't need to perform any fancy 2208 // irreversible transformations, then we can reuse the motion event for this 2209 // dispatch as long as we are careful to revert any changes we make. 2210 // Otherwise we need to make a copy. 2211 final MotionEvent transformedEvent; 2212 if (newPointerIdBits == oldPointerIdBits) { 2213 if (child == null || child.hasIdentityMatrix()) { 2214 if (child == null) { 2215 handled = super.dispatchTouchEvent(event); 2216 } else { 2217 final float offsetX = mScrollX - child.mLeft; 2218 final float offsetY = mScrollY - child.mTop; 2219 event.offsetLocation(offsetX, offsetY); 2220 2221 handled = child.dispatchTouchEvent(event); 2222 2223 event.offsetLocation(-offsetX, -offsetY); 2224 } 2225 return handled; 2226 } 2227 transformedEvent = MotionEvent.obtain(event); 2228 } else { 2229 transformedEvent = event.split(newPointerIdBits); 2230 } 2231 2232 // Perform any necessary transformations and dispatch. 2233 if (child == null) { 2234 handled = super.dispatchTouchEvent(transformedEvent); 2235 } else { 2236 final float offsetX = mScrollX - child.mLeft; 2237 final float offsetY = mScrollY - child.mTop; 2238 transformedEvent.offsetLocation(offsetX, offsetY); 2239 if (! child.hasIdentityMatrix()) { 2240 transformedEvent.transform(child.getInverseMatrix()); 2241 } 2242 2243 handled = child.dispatchTouchEvent(transformedEvent); 2244 } 2245 2246 // Done. 2247 transformedEvent.recycle(); 2248 return handled; 2249 } 2250 2251 /** 2252 * Enable or disable the splitting of MotionEvents to multiple children during touch event 2253 * dispatch. This behavior is enabled by default for applications that target an 2254 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer. 2255 * 2256 * <p>When this option is enabled MotionEvents may be split and dispatched to different child 2257 * views depending on where each pointer initially went down. This allows for user interactions 2258 * such as scrolling two panes of content independently, chording of buttons, and performing 2259 * independent gestures on different pieces of content. 2260 * 2261 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple 2262 * child views. <code>false</code> to only allow one child view to be the target of 2263 * any MotionEvent received by this ViewGroup. 2264 */ 2265 public void setMotionEventSplittingEnabled(boolean split) { 2266 // TODO Applications really shouldn't change this setting mid-touch event, 2267 // but perhaps this should handle that case and send ACTION_CANCELs to any child views 2268 // with gestures in progress when this is changed. 2269 if (split) { 2270 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 2271 } else { 2272 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS; 2273 } 2274 } 2275 2276 /** 2277 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 2278 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children. 2279 */ 2280 public boolean isMotionEventSplittingEnabled() { 2281 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS; 2282 } 2283 2284 /** 2285 * {@inheritDoc} 2286 */ 2287 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 2288 2289 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { 2290 // We're already in this state, assume our ancestors are too 2291 return; 2292 } 2293 2294 if (disallowIntercept) { 2295 mGroupFlags |= FLAG_DISALLOW_INTERCEPT; 2296 } else { 2297 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 2298 } 2299 2300 // Pass it up to our parent 2301 if (mParent != null) { 2302 mParent.requestDisallowInterceptTouchEvent(disallowIntercept); 2303 } 2304 } 2305 2306 /** 2307 * Implement this method to intercept all touch screen motion events. This 2308 * allows you to watch events as they are dispatched to your children, and 2309 * take ownership of the current gesture at any point. 2310 * 2311 * <p>Using this function takes some care, as it has a fairly complicated 2312 * interaction with {@link View#onTouchEvent(MotionEvent) 2313 * View.onTouchEvent(MotionEvent)}, and using it requires implementing 2314 * that method as well as this one in the correct way. Events will be 2315 * received in the following order: 2316 * 2317 * <ol> 2318 * <li> You will receive the down event here. 2319 * <li> The down event will be handled either by a child of this view 2320 * group, or given to your own onTouchEvent() method to handle; this means 2321 * you should implement onTouchEvent() to return true, so you will 2322 * continue to see the rest of the gesture (instead of looking for 2323 * a parent view to handle it). Also, by returning true from 2324 * onTouchEvent(), you will not receive any following 2325 * events in onInterceptTouchEvent() and all touch processing must 2326 * happen in onTouchEvent() like normal. 2327 * <li> For as long as you return false from this function, each following 2328 * event (up to and including the final up) will be delivered first here 2329 * and then to the target's onTouchEvent(). 2330 * <li> If you return true from here, you will not receive any 2331 * following events: the target view will receive the same event but 2332 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further 2333 * events will be delivered to your onTouchEvent() method and no longer 2334 * appear here. 2335 * </ol> 2336 * 2337 * @param ev The motion event being dispatched down the hierarchy. 2338 * @return Return true to steal motion events from the children and have 2339 * them dispatched to this ViewGroup through onTouchEvent(). 2340 * The current target will receive an ACTION_CANCEL event, and no further 2341 * messages will be delivered here. 2342 */ 2343 public boolean onInterceptTouchEvent(MotionEvent ev) { 2344 return false; 2345 } 2346 2347 /** 2348 * {@inheritDoc} 2349 * 2350 * Looks for a view to give focus to respecting the setting specified by 2351 * {@link #getDescendantFocusability()}. 2352 * 2353 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to 2354 * find focus within the children of this group when appropriate. 2355 * 2356 * @see #FOCUS_BEFORE_DESCENDANTS 2357 * @see #FOCUS_AFTER_DESCENDANTS 2358 * @see #FOCUS_BLOCK_DESCENDANTS 2359 * @see #onRequestFocusInDescendants(int, android.graphics.Rect) 2360 */ 2361 @Override 2362 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 2363 if (DBG) { 2364 System.out.println(this + " ViewGroup.requestFocus direction=" 2365 + direction); 2366 } 2367 int descendantFocusability = getDescendantFocusability(); 2368 2369 switch (descendantFocusability) { 2370 case FOCUS_BLOCK_DESCENDANTS: 2371 return super.requestFocus(direction, previouslyFocusedRect); 2372 case FOCUS_BEFORE_DESCENDANTS: { 2373 final boolean took = super.requestFocus(direction, previouslyFocusedRect); 2374 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); 2375 } 2376 case FOCUS_AFTER_DESCENDANTS: { 2377 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); 2378 return took ? took : super.requestFocus(direction, previouslyFocusedRect); 2379 } 2380 default: 2381 throw new IllegalStateException("descendant focusability must be " 2382 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " 2383 + "but is " + descendantFocusability); 2384 } 2385 } 2386 2387 /** 2388 * Look for a descendant to call {@link View#requestFocus} on. 2389 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)} 2390 * when it wants to request focus within its children. Override this to 2391 * customize how your {@link ViewGroup} requests focus within its children. 2392 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT 2393 * @param previouslyFocusedRect The rectangle (in this View's coordinate system) 2394 * to give a finer grained hint about where focus is coming from. May be null 2395 * if there is no hint. 2396 * @return Whether focus was taken. 2397 */ 2398 @SuppressWarnings({"ConstantConditions"}) 2399 protected boolean onRequestFocusInDescendants(int direction, 2400 Rect previouslyFocusedRect) { 2401 int index; 2402 int increment; 2403 int end; 2404 int count = mChildrenCount; 2405 if ((direction & FOCUS_FORWARD) != 0) { 2406 index = 0; 2407 increment = 1; 2408 end = count; 2409 } else { 2410 index = count - 1; 2411 increment = -1; 2412 end = -1; 2413 } 2414 final View[] children = mChildren; 2415 for (int i = index; i != end; i += increment) { 2416 View child = children[i]; 2417 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2418 if (child.requestFocus(direction, previouslyFocusedRect)) { 2419 return true; 2420 } 2421 } 2422 } 2423 return false; 2424 } 2425 2426 /** 2427 * {@inheritDoc} 2428 * 2429 * @hide 2430 */ 2431 @Override 2432 public void dispatchStartTemporaryDetach() { 2433 super.dispatchStartTemporaryDetach(); 2434 final int count = mChildrenCount; 2435 final View[] children = mChildren; 2436 for (int i = 0; i < count; i++) { 2437 children[i].dispatchStartTemporaryDetach(); 2438 } 2439 } 2440 2441 /** 2442 * {@inheritDoc} 2443 * 2444 * @hide 2445 */ 2446 @Override 2447 public void dispatchFinishTemporaryDetach() { 2448 super.dispatchFinishTemporaryDetach(); 2449 final int count = mChildrenCount; 2450 final View[] children = mChildren; 2451 for (int i = 0; i < count; i++) { 2452 children[i].dispatchFinishTemporaryDetach(); 2453 } 2454 } 2455 2456 /** 2457 * {@inheritDoc} 2458 */ 2459 @Override 2460 void dispatchAttachedToWindow(AttachInfo info, int visibility) { 2461 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; 2462 super.dispatchAttachedToWindow(info, visibility); 2463 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; 2464 2465 final int count = mChildrenCount; 2466 final View[] children = mChildren; 2467 for (int i = 0; i < count; i++) { 2468 final View child = children[i]; 2469 child.dispatchAttachedToWindow(info, 2470 visibility | (child.mViewFlags & VISIBILITY_MASK)); 2471 } 2472 } 2473 2474 @Override 2475 void dispatchScreenStateChanged(int screenState) { 2476 super.dispatchScreenStateChanged(screenState); 2477 2478 final int count = mChildrenCount; 2479 final View[] children = mChildren; 2480 for (int i = 0; i < count; i++) { 2481 children[i].dispatchScreenStateChanged(screenState); 2482 } 2483 } 2484 2485 @Override 2486 boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 2487 boolean handled = false; 2488 if (includeForAccessibility()) { 2489 handled = super.dispatchPopulateAccessibilityEventInternal(event); 2490 if (handled) { 2491 return handled; 2492 } 2493 } 2494 // Let our children have a shot in populating the event. 2495 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true); 2496 try { 2497 final int childCount = children.getChildCount(); 2498 for (int i = 0; i < childCount; i++) { 2499 View child = children.getChildAt(i); 2500 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2501 handled = child.dispatchPopulateAccessibilityEvent(event); 2502 if (handled) { 2503 return handled; 2504 } 2505 } 2506 } 2507 } finally { 2508 children.recycle(); 2509 } 2510 return false; 2511 } 2512 2513 @Override 2514 void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { 2515 super.onInitializeAccessibilityNodeInfoInternal(info); 2516 if (mAttachInfo != null) { 2517 ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList; 2518 childrenForAccessibility.clear(); 2519 addChildrenForAccessibility(childrenForAccessibility); 2520 final int childrenForAccessibilityCount = childrenForAccessibility.size(); 2521 for (int i = 0; i < childrenForAccessibilityCount; i++) { 2522 View child = childrenForAccessibility.get(i); 2523 info.addChild(child); 2524 } 2525 childrenForAccessibility.clear(); 2526 } 2527 } 2528 2529 @Override 2530 void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { 2531 super.onInitializeAccessibilityEventInternal(event); 2532 event.setClassName(ViewGroup.class.getName()); 2533 } 2534 2535 /** 2536 * @hide 2537 */ 2538 @Override 2539 public void resetAccessibilityStateChanged() { 2540 super.resetAccessibilityStateChanged(); 2541 View[] children = mChildren; 2542 final int childCount = mChildrenCount; 2543 for (int i = 0; i < childCount; i++) { 2544 View child = children[i]; 2545 child.resetAccessibilityStateChanged(); 2546 } 2547 } 2548 2549 /** 2550 * {@inheritDoc} 2551 */ 2552 @Override 2553 void dispatchDetachedFromWindow() { 2554 // If we still have a touch target, we are still in the process of 2555 // dispatching motion events to a child; we need to get rid of that 2556 // child to avoid dispatching events to it after the window is torn 2557 // down. To make sure we keep the child in a consistent state, we 2558 // first send it an ACTION_CANCEL motion event. 2559 cancelAndClearTouchTargets(null); 2560 2561 // Similarly, set ACTION_EXIT to all hover targets and clear them. 2562 exitHoverTargets(); 2563 2564 // In case view is detached while transition is running 2565 mLayoutSuppressed = false; 2566 2567 // Tear down our drag tracking 2568 mDragNotifiedChildren = null; 2569 if (mCurrentDrag != null) { 2570 mCurrentDrag.recycle(); 2571 mCurrentDrag = null; 2572 } 2573 2574 final int count = mChildrenCount; 2575 final View[] children = mChildren; 2576 for (int i = 0; i < count; i++) { 2577 children[i].dispatchDetachedFromWindow(); 2578 } 2579 super.dispatchDetachedFromWindow(); 2580 } 2581 2582 /** 2583 * @hide 2584 */ 2585 @Override 2586 protected void internalSetPadding(int left, int top, int right, int bottom) { 2587 super.internalSetPadding(left, top, right, bottom); 2588 2589 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) { 2590 mGroupFlags |= FLAG_PADDING_NOT_NULL; 2591 } else { 2592 mGroupFlags &= ~FLAG_PADDING_NOT_NULL; 2593 } 2594 } 2595 2596 /** 2597 * {@inheritDoc} 2598 */ 2599 @Override 2600 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 2601 super.dispatchSaveInstanceState(container); 2602 final int count = mChildrenCount; 2603 final View[] children = mChildren; 2604 for (int i = 0; i < count; i++) { 2605 View c = children[i]; 2606 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 2607 c.dispatchSaveInstanceState(container); 2608 } 2609 } 2610 } 2611 2612 /** 2613 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()} 2614 * to only this view, not to its children. For use when overriding 2615 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow 2616 * subclasses to freeze their own state but not the state of their children. 2617 * 2618 * @param container the container 2619 */ 2620 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { 2621 super.dispatchSaveInstanceState(container); 2622 } 2623 2624 /** 2625 * {@inheritDoc} 2626 */ 2627 @Override 2628 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 2629 super.dispatchRestoreInstanceState(container); 2630 final int count = mChildrenCount; 2631 final View[] children = mChildren; 2632 for (int i = 0; i < count; i++) { 2633 View c = children[i]; 2634 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 2635 c.dispatchRestoreInstanceState(container); 2636 } 2637 } 2638 } 2639 2640 /** 2641 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)} 2642 * to only this view, not to its children. For use when overriding 2643 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow 2644 * subclasses to thaw their own state but not the state of their children. 2645 * 2646 * @param container the container 2647 */ 2648 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { 2649 super.dispatchRestoreInstanceState(container); 2650 } 2651 2652 /** 2653 * Enables or disables the drawing cache for each child of this view group. 2654 * 2655 * @param enabled true to enable the cache, false to dispose of it 2656 */ 2657 protected void setChildrenDrawingCacheEnabled(boolean enabled) { 2658 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) { 2659 final View[] children = mChildren; 2660 final int count = mChildrenCount; 2661 for (int i = 0; i < count; i++) { 2662 children[i].setDrawingCacheEnabled(enabled); 2663 } 2664 } 2665 } 2666 2667 @Override 2668 protected void onAnimationStart() { 2669 super.onAnimationStart(); 2670 2671 // When this ViewGroup's animation starts, build the cache for the children 2672 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2673 final int count = mChildrenCount; 2674 final View[] children = mChildren; 2675 final boolean buildCache = !isHardwareAccelerated(); 2676 2677 for (int i = 0; i < count; i++) { 2678 final View child = children[i]; 2679 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2680 child.setDrawingCacheEnabled(true); 2681 if (buildCache) { 2682 child.buildDrawingCache(true); 2683 } 2684 } 2685 } 2686 2687 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 2688 } 2689 } 2690 2691 @Override 2692 protected void onAnimationEnd() { 2693 super.onAnimationEnd(); 2694 2695 // When this ViewGroup's animation ends, destroy the cache of the children 2696 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 2697 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 2698 2699 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 2700 setChildrenDrawingCacheEnabled(false); 2701 } 2702 } 2703 } 2704 2705 @Override 2706 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) { 2707 int count = mChildrenCount; 2708 int[] visibilities = null; 2709 2710 if (skipChildren) { 2711 visibilities = new int[count]; 2712 for (int i = 0; i < count; i++) { 2713 View child = getChildAt(i); 2714 visibilities[i] = child.getVisibility(); 2715 if (visibilities[i] == View.VISIBLE) { 2716 child.setVisibility(INVISIBLE); 2717 } 2718 } 2719 } 2720 2721 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren); 2722 2723 if (skipChildren) { 2724 for (int i = 0; i < count; i++) { 2725 getChildAt(i).setVisibility(visibilities[i]); 2726 } 2727 } 2728 2729 return b; 2730 } 2731 2732 /** Return true if this ViewGroup is laying out using optical bounds. */ 2733 boolean isLayoutModeOptical() { 2734 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS; 2735 } 2736 2737 Insets computeOpticalInsets() { 2738 if (isLayoutModeOptical()) { 2739 int left = 0; 2740 int top = 0; 2741 int right = 0; 2742 int bottom = 0; 2743 for (int i = 0; i < mChildrenCount; i++) { 2744 View child = getChildAt(i); 2745 if (child.getVisibility() == VISIBLE) { 2746 Insets insets = child.getOpticalInsets(); 2747 left = Math.max(left, insets.left); 2748 top = Math.max(top, insets.top); 2749 right = Math.max(right, insets.right); 2750 bottom = Math.max(bottom, insets.bottom); 2751 } 2752 } 2753 return Insets.of(left, top, right, bottom); 2754 } else { 2755 return Insets.NONE; 2756 } 2757 } 2758 2759 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { 2760 if (x1 != x2 && y1 != y2) { 2761 if (x1 > x2) { 2762 int tmp = x1; x1 = x2; x2 = tmp; 2763 } 2764 if (y1 > y2) { 2765 int tmp = y1; y1 = y2; y2 = tmp; 2766 } 2767 canvas.drawRect(x1, y1, x2, y2, paint); 2768 } 2769 } 2770 2771 private static int sign(int x) { 2772 return (x >= 0) ? 1 : -1; 2773 } 2774 2775 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) { 2776 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); 2777 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); 2778 } 2779 2780 private int dipsToPixels(int dips) { 2781 float scale = getContext().getResources().getDisplayMetrics().density; 2782 return (int) (dips * scale + 0.5f); 2783 } 2784 2785 private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, 2786 int lineLength, int lineWidth) { 2787 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); 2788 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); 2789 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); 2790 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); 2791 } 2792 2793 private static void fillDifference(Canvas canvas, 2794 int x2, int y2, int x3, int y3, 2795 int dx1, int dy1, int dx2, int dy2, Paint paint) { 2796 int x1 = x2 - dx1; 2797 int y1 = y2 - dy1; 2798 2799 int x4 = x3 + dx2; 2800 int y4 = y3 + dy2; 2801 2802 fillRect(canvas, paint, x1, y1, x4, y2); 2803 fillRect(canvas, paint, x1, y2, x2, y3); 2804 fillRect(canvas, paint, x3, y2, x4, y3); 2805 fillRect(canvas, paint, x1, y3, x4, y4); 2806 } 2807 2808 /** 2809 * @hide 2810 */ 2811 protected void onDebugDrawMargins(Canvas canvas, Paint paint) { 2812 for (int i = 0; i < getChildCount(); i++) { 2813 View c = getChildAt(i); 2814 c.getLayoutParams().onDebugDraw(c, canvas, paint); 2815 } 2816 } 2817 2818 /** 2819 * @hide 2820 */ 2821 protected void onDebugDraw(Canvas canvas) { 2822 Paint paint = getDebugPaint(); 2823 2824 // Draw optical bounds 2825 { 2826 paint.setColor(Color.RED); 2827 paint.setStyle(Paint.Style.STROKE); 2828 2829 for (int i = 0; i < getChildCount(); i++) { 2830 View c = getChildAt(i); 2831 Insets insets = c.getOpticalInsets(); 2832 2833 drawRect(canvas, paint, 2834 c.getLeft() + insets.left, 2835 c.getTop() + insets.top, 2836 c.getRight() - insets.right - 1, 2837 c.getBottom() - insets.bottom - 1); 2838 } 2839 } 2840 2841 // Draw margins 2842 { 2843 paint.setColor(Color.argb(63, 255, 0, 255)); 2844 paint.setStyle(Paint.Style.FILL); 2845 2846 onDebugDrawMargins(canvas, paint); 2847 } 2848 2849 // Draw clip bounds 2850 { 2851 paint.setColor(Color.rgb(63, 127, 255)); 2852 paint.setStyle(Paint.Style.FILL); 2853 2854 int lineLength = dipsToPixels(8); 2855 int lineWidth = dipsToPixels(1); 2856 for (int i = 0; i < getChildCount(); i++) { 2857 View c = getChildAt(i); 2858 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), 2859 paint, lineLength, lineWidth); 2860 } 2861 } 2862 } 2863 2864 /** 2865 * {@inheritDoc} 2866 */ 2867 @Override 2868 protected void dispatchDraw(Canvas canvas) { 2869 final int count = mChildrenCount; 2870 final View[] children = mChildren; 2871 int flags = mGroupFlags; 2872 2873 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { 2874 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 2875 2876 final boolean buildCache = !isHardwareAccelerated(); 2877 for (int i = 0; i < count; i++) { 2878 final View child = children[i]; 2879 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { 2880 final LayoutParams params = child.getLayoutParams(); 2881 attachLayoutAnimationParameters(child, params, i, count); 2882 bindLayoutAnimation(child); 2883 if (cache) { 2884 child.setDrawingCacheEnabled(true); 2885 if (buildCache) { 2886 child.buildDrawingCache(true); 2887 } 2888 } 2889 } 2890 } 2891 2892 final LayoutAnimationController controller = mLayoutAnimationController; 2893 if (controller.willOverlap()) { 2894 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE; 2895 } 2896 2897 controller.start(); 2898 2899 mGroupFlags &= ~FLAG_RUN_ANIMATION; 2900 mGroupFlags &= ~FLAG_ANIMATION_DONE; 2901 2902 if (cache) { 2903 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; 2904 } 2905 2906 if (mAnimationListener != null) { 2907 mAnimationListener.onAnimationStart(controller.getAnimation()); 2908 } 2909 } 2910 2911 int saveCount = 0; 2912 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 2913 if (clipToPadding) { 2914 saveCount = canvas.save(); 2915 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, 2916 mScrollX + mRight - mLeft - mPaddingRight, 2917 mScrollY + mBottom - mTop - mPaddingBottom); 2918 2919 } 2920 2921 // We will draw our child's animation, let's reset the flag 2922 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION; 2923 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; 2924 2925 boolean more = false; 2926 final long drawingTime = getDrawingTime(); 2927 2928 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { 2929 for (int i = 0; i < count; i++) { 2930 final View child = children[i]; 2931 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 2932 more |= drawChild(canvas, child, drawingTime); 2933 } 2934 } 2935 } else { 2936 for (int i = 0; i < count; i++) { 2937 final View child = children[getChildDrawingOrder(count, i)]; 2938 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 2939 more |= drawChild(canvas, child, drawingTime); 2940 } 2941 } 2942 } 2943 2944 // Draw any disappearing views that have animations 2945 if (mDisappearingChildren != null) { 2946 final ArrayList<View> disappearingChildren = mDisappearingChildren; 2947 final int disappearingCount = disappearingChildren.size() - 1; 2948 // Go backwards -- we may delete as animations finish 2949 for (int i = disappearingCount; i >= 0; i--) { 2950 final View child = disappearingChildren.get(i); 2951 more |= drawChild(canvas, child, drawingTime); 2952 } 2953 } 2954 2955 if (debugDraw()) { 2956 onDebugDraw(canvas); 2957 } 2958 2959 if (clipToPadding) { 2960 canvas.restoreToCount(saveCount); 2961 } 2962 2963 // mGroupFlags might have been updated by drawChild() 2964 flags = mGroupFlags; 2965 2966 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { 2967 invalidate(true); 2968 } 2969 2970 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && 2971 mLayoutAnimationController.isDone() && !more) { 2972 // We want to erase the drawing cache and notify the listener after the 2973 // next frame is drawn because one extra invalidate() is caused by 2974 // drawChild() after the animation is over 2975 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; 2976 final Runnable end = new Runnable() { 2977 public void run() { 2978 notifyAnimationListener(); 2979 } 2980 }; 2981 post(end); 2982 } 2983 } 2984 2985 /** 2986 * Returns the index of the child to draw for this iteration. Override this 2987 * if you want to change the drawing order of children. By default, it 2988 * returns i. 2989 * <p> 2990 * NOTE: In order for this method to be called, you must enable child ordering 2991 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}. 2992 * 2993 * @param i The current iteration. 2994 * @return The index of the child to draw this iteration. 2995 * 2996 * @see #setChildrenDrawingOrderEnabled(boolean) 2997 * @see #isChildrenDrawingOrderEnabled() 2998 */ 2999 protected int getChildDrawingOrder(int childCount, int i) { 3000 return i; 3001 } 3002 3003 private void notifyAnimationListener() { 3004 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; 3005 mGroupFlags |= FLAG_ANIMATION_DONE; 3006 3007 if (mAnimationListener != null) { 3008 final Runnable end = new Runnable() { 3009 public void run() { 3010 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation()); 3011 } 3012 }; 3013 post(end); 3014 } 3015 3016 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { 3017 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; 3018 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { 3019 setChildrenDrawingCacheEnabled(false); 3020 } 3021 } 3022 3023 invalidate(true); 3024 } 3025 3026 /** 3027 * This method is used to cause children of this ViewGroup to restore or recreate their 3028 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need 3029 * to recreate its own display list, which would happen if it went through the normal 3030 * draw/dispatchDraw mechanisms. 3031 * 3032 * @hide 3033 */ 3034 @Override 3035 protected void dispatchGetDisplayList() { 3036 final int count = mChildrenCount; 3037 final View[] children = mChildren; 3038 for (int i = 0; i < count; i++) { 3039 final View child = children[i]; 3040 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) && 3041 child.hasStaticLayer()) { 3042 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) 3043 == PFLAG_INVALIDATED; 3044 child.mPrivateFlags &= ~PFLAG_INVALIDATED; 3045 child.getDisplayList(); 3046 child.mRecreateDisplayList = false; 3047 } 3048 } 3049 if (mOverlay != null) { 3050 mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED) 3051 == PFLAG_INVALIDATED; 3052 mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED; 3053 mOverlay.getDisplayList(); 3054 mOverlay.mRecreateDisplayList = false; 3055 } 3056 } 3057 3058 /** 3059 * Draw one child of this View Group. This method is responsible for getting 3060 * the canvas in the right state. This includes clipping, translating so 3061 * that the child's scrolled origin is at 0, 0, and applying any animation 3062 * transformations. 3063 * 3064 * @param canvas The canvas on which to draw the child 3065 * @param child Who to draw 3066 * @param drawingTime The time at which draw is occurring 3067 * @return True if an invalidate() was issued 3068 */ 3069 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 3070 return child.draw(canvas, this, drawingTime); 3071 } 3072 3073 /** 3074 * By default, children are clipped to their bounds before drawing. This 3075 * allows view groups to override this behavior for animations, etc. 3076 * 3077 * @param clipChildren true to clip children to their bounds, 3078 * false otherwise 3079 * @attr ref android.R.styleable#ViewGroup_clipChildren 3080 */ 3081 public void setClipChildren(boolean clipChildren) { 3082 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN; 3083 if (clipChildren != previousValue) { 3084 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); 3085 for (int i = 0; i < mChildrenCount; ++i) { 3086 View child = getChildAt(i); 3087 if (child.mDisplayList != null) { 3088 child.mDisplayList.setClipChildren(clipChildren); 3089 } 3090 } 3091 } 3092 } 3093 3094 /** 3095 * By default, children are clipped to the padding of the ViewGroup. This 3096 * allows view groups to override this behavior 3097 * 3098 * @param clipToPadding true to clip children to the padding of the 3099 * group, false otherwise 3100 * @attr ref android.R.styleable#ViewGroup_clipToPadding 3101 */ 3102 public void setClipToPadding(boolean clipToPadding) { 3103 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding); 3104 } 3105 3106 /** 3107 * {@inheritDoc} 3108 */ 3109 @Override 3110 public void dispatchSetSelected(boolean selected) { 3111 final View[] children = mChildren; 3112 final int count = mChildrenCount; 3113 for (int i = 0; i < count; i++) { 3114 children[i].setSelected(selected); 3115 } 3116 } 3117 3118 /** 3119 * {@inheritDoc} 3120 */ 3121 @Override 3122 public void dispatchSetActivated(boolean activated) { 3123 final View[] children = mChildren; 3124 final int count = mChildrenCount; 3125 for (int i = 0; i < count; i++) { 3126 children[i].setActivated(activated); 3127 } 3128 } 3129 3130 @Override 3131 protected void dispatchSetPressed(boolean pressed) { 3132 final View[] children = mChildren; 3133 final int count = mChildrenCount; 3134 for (int i = 0; i < count; i++) { 3135 final View child = children[i]; 3136 // Children that are clickable on their own should not 3137 // show a pressed state when their parent view does. 3138 // Clearing a pressed state always propagates. 3139 if (!pressed || (!child.isClickable() && !child.isLongClickable())) { 3140 child.setPressed(pressed); 3141 } 3142 } 3143 } 3144 3145 /** 3146 * When this property is set to true, this ViewGroup supports static transformations on 3147 * children; this causes 3148 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be 3149 * invoked when a child is drawn. 3150 * 3151 * Any subclass overriding 3152 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should 3153 * set this property to true. 3154 * 3155 * @param enabled True to enable static transformations on children, false otherwise. 3156 * 3157 * @see #getChildStaticTransformation(View, android.view.animation.Transformation) 3158 */ 3159 protected void setStaticTransformationsEnabled(boolean enabled) { 3160 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled); 3161 } 3162 3163 /** 3164 * Sets <code>t</code> to be the static transformation of the child, if set, returning a 3165 * boolean to indicate whether a static transform was set. The default implementation 3166 * simply returns <code>false</code>; subclasses may override this method for different 3167 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true 3168 * for this method to be called. 3169 * 3170 * @param child The child view whose static transform is being requested 3171 * @param t The Transformation which will hold the result 3172 * @return true if the transformation was set, false otherwise 3173 * @see #setStaticTransformationsEnabled(boolean) 3174 */ 3175 protected boolean getChildStaticTransformation(View child, Transformation t) { 3176 return false; 3177 } 3178 3179 /** 3180 * {@hide} 3181 */ 3182 @Override 3183 protected View findViewTraversal(int id) { 3184 if (id == mID) { 3185 return this; 3186 } 3187 3188 final View[] where = mChildren; 3189 final int len = mChildrenCount; 3190 3191 for (int i = 0; i < len; i++) { 3192 View v = where[i]; 3193 3194 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 3195 v = v.findViewById(id); 3196 3197 if (v != null) { 3198 return v; 3199 } 3200 } 3201 } 3202 3203 return null; 3204 } 3205 3206 /** 3207 * {@hide} 3208 */ 3209 @Override 3210 protected View findViewWithTagTraversal(Object tag) { 3211 if (tag != null && tag.equals(mTag)) { 3212 return this; 3213 } 3214 3215 final View[] where = mChildren; 3216 final int len = mChildrenCount; 3217 3218 for (int i = 0; i < len; i++) { 3219 View v = where[i]; 3220 3221 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 3222 v = v.findViewWithTag(tag); 3223 3224 if (v != null) { 3225 return v; 3226 } 3227 } 3228 } 3229 3230 return null; 3231 } 3232 3233 /** 3234 * {@hide} 3235 */ 3236 @Override 3237 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { 3238 if (predicate.apply(this)) { 3239 return this; 3240 } 3241 3242 final View[] where = mChildren; 3243 final int len = mChildrenCount; 3244 3245 for (int i = 0; i < len; i++) { 3246 View v = where[i]; 3247 3248 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 3249 v = v.findViewByPredicate(predicate); 3250 3251 if (v != null) { 3252 return v; 3253 } 3254 } 3255 } 3256 3257 return null; 3258 } 3259 3260 /** 3261 * <p>Adds a child view. If no layout parameters are already set on the child, the 3262 * default parameters for this ViewGroup are set on the child.</p> 3263 * 3264 * <p><strong>Note:</strong> do not invoke this method from 3265 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3266 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3267 * 3268 * @param child the child view to add 3269 * 3270 * @see #generateDefaultLayoutParams() 3271 */ 3272 public void addView(View child) { 3273 addView(child, -1); 3274 } 3275 3276 /** 3277 * Adds a child view. If no layout parameters are already set on the child, the 3278 * default parameters for this ViewGroup are set on the child. 3279 * 3280 * <p><strong>Note:</strong> do not invoke this method from 3281 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3282 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3283 * 3284 * @param child the child view to add 3285 * @param index the position at which to add the child 3286 * 3287 * @see #generateDefaultLayoutParams() 3288 */ 3289 public void addView(View child, int index) { 3290 LayoutParams params = child.getLayoutParams(); 3291 if (params == null) { 3292 params = generateDefaultLayoutParams(); 3293 if (params == null) { 3294 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); 3295 } 3296 } 3297 addView(child, index, params); 3298 } 3299 3300 /** 3301 * Adds a child view with this ViewGroup's default layout parameters and the 3302 * specified width and height. 3303 * 3304 * <p><strong>Note:</strong> do not invoke this method from 3305 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3306 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3307 * 3308 * @param child the child view to add 3309 */ 3310 public void addView(View child, int width, int height) { 3311 final LayoutParams params = generateDefaultLayoutParams(); 3312 params.width = width; 3313 params.height = height; 3314 addView(child, -1, params); 3315 } 3316 3317 /** 3318 * Adds a child view with the specified layout parameters. 3319 * 3320 * <p><strong>Note:</strong> do not invoke this method from 3321 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3322 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3323 * 3324 * @param child the child view to add 3325 * @param params the layout parameters to set on the child 3326 */ 3327 public void addView(View child, LayoutParams params) { 3328 addView(child, -1, params); 3329 } 3330 3331 /** 3332 * Adds a child view with the specified layout parameters. 3333 * 3334 * <p><strong>Note:</strong> do not invoke this method from 3335 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3336 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3337 * 3338 * @param child the child view to add 3339 * @param index the position at which to add the child 3340 * @param params the layout parameters to set on the child 3341 */ 3342 public void addView(View child, int index, LayoutParams params) { 3343 if (DBG) { 3344 System.out.println(this + " addView"); 3345 } 3346 3347 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 3348 // therefore, we call requestLayout() on ourselves before, so that the child's request 3349 // will be blocked at our level 3350 requestLayout(); 3351 invalidate(true); 3352 addViewInner(child, index, params, false); 3353 } 3354 3355 /** 3356 * {@inheritDoc} 3357 */ 3358 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 3359 if (!checkLayoutParams(params)) { 3360 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this); 3361 } 3362 if (view.mParent != this) { 3363 throw new IllegalArgumentException("Given view not a child of " + this); 3364 } 3365 view.setLayoutParams(params); 3366 } 3367 3368 /** 3369 * {@inheritDoc} 3370 */ 3371 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 3372 return p != null; 3373 } 3374 3375 /** 3376 * Interface definition for a callback to be invoked when the hierarchy 3377 * within this view changed. The hierarchy changes whenever a child is added 3378 * to or removed from this view. 3379 */ 3380 public interface OnHierarchyChangeListener { 3381 /** 3382 * Called when a new child is added to a parent view. 3383 * 3384 * @param parent the view in which a child was added 3385 * @param child the new child view added in the hierarchy 3386 */ 3387 void onChildViewAdded(View parent, View child); 3388 3389 /** 3390 * Called when a child is removed from a parent view. 3391 * 3392 * @param parent the view from which the child was removed 3393 * @param child the child removed from the hierarchy 3394 */ 3395 void onChildViewRemoved(View parent, View child); 3396 } 3397 3398 /** 3399 * Register a callback to be invoked when a child is added to or removed 3400 * from this view. 3401 * 3402 * @param listener the callback to invoke on hierarchy change 3403 */ 3404 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 3405 mOnHierarchyChangeListener = listener; 3406 } 3407 3408 /** 3409 * @hide 3410 */ 3411 protected void onViewAdded(View child) { 3412 if (mOnHierarchyChangeListener != null) { 3413 mOnHierarchyChangeListener.onChildViewAdded(this, child); 3414 } 3415 } 3416 3417 /** 3418 * @hide 3419 */ 3420 protected void onViewRemoved(View child) { 3421 if (mOnHierarchyChangeListener != null) { 3422 mOnHierarchyChangeListener.onChildViewRemoved(this, child); 3423 } 3424 } 3425 3426 /** 3427 * Adds a view during layout. This is useful if in your onLayout() method, 3428 * you need to add more views (as does the list view for example). 3429 * 3430 * If index is negative, it means put it at the end of the list. 3431 * 3432 * @param child the view to add to the group 3433 * @param index the index at which the child must be added 3434 * @param params the layout parameters to associate with the child 3435 * @return true if the child was added, false otherwise 3436 */ 3437 protected boolean addViewInLayout(View child, int index, LayoutParams params) { 3438 return addViewInLayout(child, index, params, false); 3439 } 3440 3441 /** 3442 * Adds a view during layout. This is useful if in your onLayout() method, 3443 * you need to add more views (as does the list view for example). 3444 * 3445 * If index is negative, it means put it at the end of the list. 3446 * 3447 * @param child the view to add to the group 3448 * @param index the index at which the child must be added 3449 * @param params the layout parameters to associate with the child 3450 * @param preventRequestLayout if true, calling this method will not trigger a 3451 * layout request on child 3452 * @return true if the child was added, false otherwise 3453 */ 3454 protected boolean addViewInLayout(View child, int index, LayoutParams params, 3455 boolean preventRequestLayout) { 3456 child.mParent = null; 3457 addViewInner(child, index, params, preventRequestLayout); 3458 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; 3459 return true; 3460 } 3461 3462 /** 3463 * Prevents the specified child to be laid out during the next layout pass. 3464 * 3465 * @param child the child on which to perform the cleanup 3466 */ 3467 protected void cleanupLayoutState(View child) { 3468 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT; 3469 } 3470 3471 private void addViewInner(View child, int index, LayoutParams params, 3472 boolean preventRequestLayout) { 3473 3474 if (mTransition != null) { 3475 // Don't prevent other add transitions from completing, but cancel remove 3476 // transitions to let them complete the process before we add to the container 3477 mTransition.cancel(LayoutTransition.DISAPPEARING); 3478 } 3479 3480 if (child.getParent() != null) { 3481 throw new IllegalStateException("The specified child already has a parent. " + 3482 "You must call removeView() on the child's parent first."); 3483 } 3484 3485 if (mTransition != null) { 3486 mTransition.addChild(this, child); 3487 } 3488 3489 if (!checkLayoutParams(params)) { 3490 params = generateLayoutParams(params); 3491 } 3492 3493 if (preventRequestLayout) { 3494 child.mLayoutParams = params; 3495 } else { 3496 child.setLayoutParams(params); 3497 } 3498 3499 if (index < 0) { 3500 index = mChildrenCount; 3501 } 3502 3503 addInArray(child, index); 3504 3505 // tell our children 3506 if (preventRequestLayout) { 3507 child.assignParent(this); 3508 } else { 3509 child.mParent = this; 3510 } 3511 3512 if (child.hasFocus()) { 3513 requestChildFocus(child, child.findFocus()); 3514 } 3515 3516 AttachInfo ai = mAttachInfo; 3517 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { 3518 boolean lastKeepOn = ai.mKeepScreenOn; 3519 ai.mKeepScreenOn = false; 3520 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 3521 if (ai.mKeepScreenOn) { 3522 needGlobalAttributesUpdate(true); 3523 } 3524 ai.mKeepScreenOn = lastKeepOn; 3525 } 3526 3527 if (child.isLayoutDirectionInherited()) { 3528 child.resetRtlProperties(); 3529 } 3530 3531 onViewAdded(child); 3532 3533 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 3534 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 3535 } 3536 3537 if (child.hasTransientState()) { 3538 childHasTransientStateChanged(child, true); 3539 } 3540 } 3541 3542 private void addInArray(View child, int index) { 3543 View[] children = mChildren; 3544 final int count = mChildrenCount; 3545 final int size = children.length; 3546 if (index == count) { 3547 if (size == count) { 3548 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 3549 System.arraycopy(children, 0, mChildren, 0, size); 3550 children = mChildren; 3551 } 3552 children[mChildrenCount++] = child; 3553 } else if (index < count) { 3554 if (size == count) { 3555 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; 3556 System.arraycopy(children, 0, mChildren, 0, index); 3557 System.arraycopy(children, index, mChildren, index + 1, count - index); 3558 children = mChildren; 3559 } else { 3560 System.arraycopy(children, index, children, index + 1, count - index); 3561 } 3562 children[index] = child; 3563 mChildrenCount++; 3564 if (mLastTouchDownIndex >= index) { 3565 mLastTouchDownIndex++; 3566 } 3567 } else { 3568 throw new IndexOutOfBoundsException("index=" + index + " count=" + count); 3569 } 3570 } 3571 3572 // This method also sets the child's mParent to null 3573 private void removeFromArray(int index) { 3574 final View[] children = mChildren; 3575 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) { 3576 children[index].mParent = null; 3577 } 3578 final int count = mChildrenCount; 3579 if (index == count - 1) { 3580 children[--mChildrenCount] = null; 3581 } else if (index >= 0 && index < count) { 3582 System.arraycopy(children, index + 1, children, index, count - index - 1); 3583 children[--mChildrenCount] = null; 3584 } else { 3585 throw new IndexOutOfBoundsException(); 3586 } 3587 if (mLastTouchDownIndex == index) { 3588 mLastTouchDownTime = 0; 3589 mLastTouchDownIndex = -1; 3590 } else if (mLastTouchDownIndex > index) { 3591 mLastTouchDownIndex--; 3592 } 3593 } 3594 3595 // This method also sets the children's mParent to null 3596 private void removeFromArray(int start, int count) { 3597 final View[] children = mChildren; 3598 final int childrenCount = mChildrenCount; 3599 3600 start = Math.max(0, start); 3601 final int end = Math.min(childrenCount, start + count); 3602 3603 if (start == end) { 3604 return; 3605 } 3606 3607 if (end == childrenCount) { 3608 for (int i = start; i < end; i++) { 3609 children[i].mParent = null; 3610 children[i] = null; 3611 } 3612 } else { 3613 for (int i = start; i < end; i++) { 3614 children[i].mParent = null; 3615 } 3616 3617 // Since we're looping above, we might as well do the copy, but is arraycopy() 3618 // faster than the extra 2 bounds checks we would do in the loop? 3619 System.arraycopy(children, end, children, start, childrenCount - end); 3620 3621 for (int i = childrenCount - (end - start); i < childrenCount; i++) { 3622 children[i] = null; 3623 } 3624 } 3625 3626 mChildrenCount -= (end - start); 3627 } 3628 3629 private void bindLayoutAnimation(View child) { 3630 Animation a = mLayoutAnimationController.getAnimationForView(child); 3631 child.setAnimation(a); 3632 } 3633 3634 /** 3635 * Subclasses should override this method to set layout animation 3636 * parameters on the supplied child. 3637 * 3638 * @param child the child to associate with animation parameters 3639 * @param params the child's layout parameters which hold the animation 3640 * parameters 3641 * @param index the index of the child in the view group 3642 * @param count the number of children in the view group 3643 */ 3644 protected void attachLayoutAnimationParameters(View child, 3645 LayoutParams params, int index, int count) { 3646 LayoutAnimationController.AnimationParameters animationParams = 3647 params.layoutAnimationParameters; 3648 if (animationParams == null) { 3649 animationParams = new LayoutAnimationController.AnimationParameters(); 3650 params.layoutAnimationParameters = animationParams; 3651 } 3652 3653 animationParams.count = count; 3654 animationParams.index = index; 3655 } 3656 3657 /** 3658 * {@inheritDoc} 3659 * 3660 * <p><strong>Note:</strong> do not invoke this method from 3661 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3662 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3663 */ 3664 public void removeView(View view) { 3665 removeViewInternal(view); 3666 requestLayout(); 3667 invalidate(true); 3668 } 3669 3670 /** 3671 * Removes a view during layout. This is useful if in your onLayout() method, 3672 * you need to remove more views. 3673 * 3674 * <p><strong>Note:</strong> do not invoke this method from 3675 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3676 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3677 * 3678 * @param view the view to remove from the group 3679 */ 3680 public void removeViewInLayout(View view) { 3681 removeViewInternal(view); 3682 } 3683 3684 /** 3685 * Removes a range of views during layout. This is useful if in your onLayout() method, 3686 * you need to remove more views. 3687 * 3688 * <p><strong>Note:</strong> do not invoke this method from 3689 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3690 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3691 * 3692 * @param start the index of the first view to remove from the group 3693 * @param count the number of views to remove from the group 3694 */ 3695 public void removeViewsInLayout(int start, int count) { 3696 removeViewsInternal(start, count); 3697 } 3698 3699 /** 3700 * Removes the view at the specified position in the group. 3701 * 3702 * <p><strong>Note:</strong> do not invoke this method from 3703 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3704 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3705 * 3706 * @param index the position in the group of the view to remove 3707 */ 3708 public void removeViewAt(int index) { 3709 removeViewInternal(index, getChildAt(index)); 3710 requestLayout(); 3711 invalidate(true); 3712 } 3713 3714 /** 3715 * Removes the specified range of views from the group. 3716 * 3717 * <p><strong>Note:</strong> do not invoke this method from 3718 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3719 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3720 * 3721 * @param start the first position in the group of the range of views to remove 3722 * @param count the number of views to remove 3723 */ 3724 public void removeViews(int start, int count) { 3725 removeViewsInternal(start, count); 3726 requestLayout(); 3727 invalidate(true); 3728 } 3729 3730 private void removeViewInternal(View view) { 3731 final int index = indexOfChild(view); 3732 if (index >= 0) { 3733 removeViewInternal(index, view); 3734 } 3735 } 3736 3737 private void removeViewInternal(int index, View view) { 3738 3739 if (mTransition != null) { 3740 mTransition.removeChild(this, view); 3741 } 3742 3743 boolean clearChildFocus = false; 3744 if (view == mFocused) { 3745 view.unFocus(); 3746 clearChildFocus = true; 3747 } 3748 3749 if (view.isAccessibilityFocused()) { 3750 view.clearAccessibilityFocus(); 3751 } 3752 3753 cancelTouchTarget(view); 3754 cancelHoverTarget(view); 3755 3756 if (view.getAnimation() != null || 3757 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3758 addDisappearingView(view); 3759 } else if (view.mAttachInfo != null) { 3760 view.dispatchDetachedFromWindow(); 3761 } 3762 3763 if (view.hasTransientState()) { 3764 childHasTransientStateChanged(view, false); 3765 } 3766 3767 needGlobalAttributesUpdate(false); 3768 3769 removeFromArray(index); 3770 3771 if (clearChildFocus) { 3772 clearChildFocus(view); 3773 if (!rootViewRequestFocus()) { 3774 notifyGlobalFocusCleared(this); 3775 } 3776 } 3777 3778 onViewRemoved(view); 3779 } 3780 3781 /** 3782 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 3783 * not null, changes in layout which occur because of children being added to or removed from 3784 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 3785 * object. By default, the transition object is null (so layout changes are not animated). 3786 * 3787 * @param transition The LayoutTransition object that will animated changes in layout. A value 3788 * of <code>null</code> means no transition will run on layout changes. 3789 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges 3790 */ 3791 public void setLayoutTransition(LayoutTransition transition) { 3792 if (mTransition != null) { 3793 mTransition.removeTransitionListener(mLayoutTransitionListener); 3794 } 3795 mTransition = transition; 3796 if (mTransition != null) { 3797 mTransition.addTransitionListener(mLayoutTransitionListener); 3798 } 3799 } 3800 3801 /** 3802 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is 3803 * not null, changes in layout which occur because of children being added to or removed from 3804 * the ViewGroup will be animated according to the animations defined in that LayoutTransition 3805 * object. By default, the transition object is null (so layout changes are not animated). 3806 * 3807 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout. 3808 * A value of <code>null</code> means no transition will run on layout changes. 3809 */ 3810 public LayoutTransition getLayoutTransition() { 3811 return mTransition; 3812 } 3813 3814 private void removeViewsInternal(int start, int count) { 3815 final View focused = mFocused; 3816 final boolean detach = mAttachInfo != null; 3817 boolean clearChildFocus = false; 3818 3819 final View[] children = mChildren; 3820 final int end = start + count; 3821 3822 for (int i = start; i < end; i++) { 3823 final View view = children[i]; 3824 3825 if (mTransition != null) { 3826 mTransition.removeChild(this, view); 3827 } 3828 3829 if (view == focused) { 3830 view.unFocus(); 3831 clearChildFocus = true; 3832 } 3833 3834 if (view.isAccessibilityFocused()) { 3835 view.clearAccessibilityFocus(); 3836 } 3837 3838 cancelTouchTarget(view); 3839 cancelHoverTarget(view); 3840 3841 if (view.getAnimation() != null || 3842 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3843 addDisappearingView(view); 3844 } else if (detach) { 3845 view.dispatchDetachedFromWindow(); 3846 } 3847 3848 if (view.hasTransientState()) { 3849 childHasTransientStateChanged(view, false); 3850 } 3851 3852 needGlobalAttributesUpdate(false); 3853 3854 onViewRemoved(view); 3855 } 3856 3857 removeFromArray(start, count); 3858 3859 if (clearChildFocus) { 3860 clearChildFocus(focused); 3861 if (!rootViewRequestFocus()) { 3862 notifyGlobalFocusCleared(focused); 3863 } 3864 } 3865 } 3866 3867 /** 3868 * Call this method to remove all child views from the 3869 * ViewGroup. 3870 * 3871 * <p><strong>Note:</strong> do not invoke this method from 3872 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3873 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3874 */ 3875 public void removeAllViews() { 3876 removeAllViewsInLayout(); 3877 requestLayout(); 3878 invalidate(true); 3879 } 3880 3881 /** 3882 * Called by a ViewGroup subclass to remove child views from itself, 3883 * when it must first know its size on screen before it can calculate how many 3884 * child views it will render. An example is a Gallery or a ListView, which 3885 * may "have" 50 children, but actually only render the number of children 3886 * that can currently fit inside the object on screen. Do not call 3887 * this method unless you are extending ViewGroup and understand the 3888 * view measuring and layout pipeline. 3889 * 3890 * <p><strong>Note:</strong> do not invoke this method from 3891 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, 3892 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> 3893 */ 3894 public void removeAllViewsInLayout() { 3895 final int count = mChildrenCount; 3896 if (count <= 0) { 3897 return; 3898 } 3899 3900 final View[] children = mChildren; 3901 mChildrenCount = 0; 3902 3903 final View focused = mFocused; 3904 final boolean detach = mAttachInfo != null; 3905 boolean clearChildFocus = false; 3906 3907 needGlobalAttributesUpdate(false); 3908 3909 for (int i = count - 1; i >= 0; i--) { 3910 final View view = children[i]; 3911 3912 if (mTransition != null) { 3913 mTransition.removeChild(this, view); 3914 } 3915 3916 if (view == focused) { 3917 view.unFocus(); 3918 clearChildFocus = true; 3919 } 3920 3921 if (view.isAccessibilityFocused()) { 3922 view.clearAccessibilityFocus(); 3923 } 3924 3925 cancelTouchTarget(view); 3926 cancelHoverTarget(view); 3927 3928 if (view.getAnimation() != null || 3929 (mTransitioningViews != null && mTransitioningViews.contains(view))) { 3930 addDisappearingView(view); 3931 } else if (detach) { 3932 view.dispatchDetachedFromWindow(); 3933 } 3934 3935 if (view.hasTransientState()) { 3936 childHasTransientStateChanged(view, false); 3937 } 3938 3939 onViewRemoved(view); 3940 3941 view.mParent = null; 3942 children[i] = null; 3943 } 3944 3945 if (clearChildFocus) { 3946 clearChildFocus(focused); 3947 if (!rootViewRequestFocus()) { 3948 notifyGlobalFocusCleared(focused); 3949 } 3950 } 3951 } 3952 3953 /** 3954 * Finishes the removal of a detached view. This method will dispatch the detached from 3955 * window event and notify the hierarchy change listener. 3956 * <p> 3957 * This method is intended to be lightweight and makes no assumptions about whether the 3958 * parent or child should be redrawn. Proper use of this method will include also making 3959 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls. 3960 * For example, callers can {@link #post(Runnable) post} a {@link Runnable} 3961 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove 3962 * calls are finished, causing layout to be run prior to redrawing the view hierarchy. 3963 * 3964 * @param child the child to be definitely removed from the view hierarchy 3965 * @param animate if true and the view has an animation, the view is placed in the 3966 * disappearing views list, otherwise, it is detached from the window 3967 * 3968 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 3969 * @see #detachAllViewsFromParent() 3970 * @see #detachViewFromParent(View) 3971 * @see #detachViewFromParent(int) 3972 */ 3973 protected void removeDetachedView(View child, boolean animate) { 3974 if (mTransition != null) { 3975 mTransition.removeChild(this, child); 3976 } 3977 3978 if (child == mFocused) { 3979 child.clearFocus(); 3980 } 3981 3982 child.clearAccessibilityFocus(); 3983 3984 cancelTouchTarget(child); 3985 cancelHoverTarget(child); 3986 3987 if ((animate && child.getAnimation() != null) || 3988 (mTransitioningViews != null && mTransitioningViews.contains(child))) { 3989 addDisappearingView(child); 3990 } else if (child.mAttachInfo != null) { 3991 child.dispatchDetachedFromWindow(); 3992 } 3993 3994 if (child.hasTransientState()) { 3995 childHasTransientStateChanged(child, false); 3996 } 3997 3998 onViewRemoved(child); 3999 } 4000 4001 /** 4002 * Attaches a view to this view group. Attaching a view assigns this group as the parent, 4003 * sets the layout parameters and puts the view in the list of children so that 4004 * it can be retrieved by calling {@link #getChildAt(int)}. 4005 * <p> 4006 * This method is intended to be lightweight and makes no assumptions about whether the 4007 * parent or child should be redrawn. Proper use of this method will include also making 4008 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls. 4009 * For example, callers can {@link #post(Runnable) post} a {@link Runnable} 4010 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach 4011 * calls are finished, causing layout to be run prior to redrawing the view hierarchy. 4012 * <p> 4013 * This method should be called only for views which were detached from their parent. 4014 * 4015 * @param child the child to attach 4016 * @param index the index at which the child should be attached 4017 * @param params the layout parameters of the child 4018 * 4019 * @see #removeDetachedView(View, boolean) 4020 * @see #detachAllViewsFromParent() 4021 * @see #detachViewFromParent(View) 4022 * @see #detachViewFromParent(int) 4023 */ 4024 protected void attachViewToParent(View child, int index, LayoutParams params) { 4025 child.mLayoutParams = params; 4026 4027 if (index < 0) { 4028 index = mChildrenCount; 4029 } 4030 4031 addInArray(child, index); 4032 4033 child.mParent = this; 4034 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK 4035 & ~PFLAG_DRAWING_CACHE_VALID) 4036 | PFLAG_DRAWN | PFLAG_INVALIDATED; 4037 this.mPrivateFlags |= PFLAG_INVALIDATED; 4038 4039 if (child.hasFocus()) { 4040 requestChildFocus(child, child.findFocus()); 4041 } 4042 } 4043 4044 /** 4045 * Detaches a view from its parent. Detaching a view should be followed 4046 * either by a call to 4047 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4048 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4049 * temporary; reattachment or removal should happen within the same drawing cycle as 4050 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4051 * call to {@link #getChildAt(int)}. 4052 * 4053 * @param child the child to detach 4054 * 4055 * @see #detachViewFromParent(int) 4056 * @see #detachViewsFromParent(int, int) 4057 * @see #detachAllViewsFromParent() 4058 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4059 * @see #removeDetachedView(View, boolean) 4060 */ 4061 protected void detachViewFromParent(View child) { 4062 removeFromArray(indexOfChild(child)); 4063 } 4064 4065 /** 4066 * Detaches a view from its parent. Detaching a view should be followed 4067 * either by a call to 4068 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4069 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4070 * temporary; reattachment or removal should happen within the same drawing cycle as 4071 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4072 * call to {@link #getChildAt(int)}. 4073 * 4074 * @param index the index of the child to detach 4075 * 4076 * @see #detachViewFromParent(View) 4077 * @see #detachAllViewsFromParent() 4078 * @see #detachViewsFromParent(int, int) 4079 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4080 * @see #removeDetachedView(View, boolean) 4081 */ 4082 protected void detachViewFromParent(int index) { 4083 removeFromArray(index); 4084 } 4085 4086 /** 4087 * Detaches a range of views from their parents. Detaching a view should be followed 4088 * either by a call to 4089 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4090 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4091 * temporary; reattachment or removal should happen within the same drawing cycle as 4092 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4093 * call to {@link #getChildAt(int)}. 4094 * 4095 * @param start the first index of the childrend range to detach 4096 * @param count the number of children to detach 4097 * 4098 * @see #detachViewFromParent(View) 4099 * @see #detachViewFromParent(int) 4100 * @see #detachAllViewsFromParent() 4101 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4102 * @see #removeDetachedView(View, boolean) 4103 */ 4104 protected void detachViewsFromParent(int start, int count) { 4105 removeFromArray(start, count); 4106 } 4107 4108 /** 4109 * Detaches all views from the parent. Detaching a view should be followed 4110 * either by a call to 4111 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)} 4112 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be 4113 * temporary; reattachment or removal should happen within the same drawing cycle as 4114 * detachment. When a view is detached, its parent is null and cannot be retrieved by a 4115 * call to {@link #getChildAt(int)}. 4116 * 4117 * @see #detachViewFromParent(View) 4118 * @see #detachViewFromParent(int) 4119 * @see #detachViewsFromParent(int, int) 4120 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams) 4121 * @see #removeDetachedView(View, boolean) 4122 */ 4123 protected void detachAllViewsFromParent() { 4124 final int count = mChildrenCount; 4125 if (count <= 0) { 4126 return; 4127 } 4128 4129 final View[] children = mChildren; 4130 mChildrenCount = 0; 4131 4132 for (int i = count - 1; i >= 0; i--) { 4133 children[i].mParent = null; 4134 children[i] = null; 4135 } 4136 } 4137 4138 /** 4139 * Don't call or override this method. It is used for the implementation of 4140 * the view hierarchy. 4141 */ 4142 public final void invalidateChild(View child, final Rect dirty) { 4143 ViewParent parent = this; 4144 4145 final AttachInfo attachInfo = mAttachInfo; 4146 if (attachInfo != null) { 4147 // If the child is drawing an animation, we want to copy this flag onto 4148 // ourselves and the parent to make sure the invalidate request goes 4149 // through 4150 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) 4151 == PFLAG_DRAW_ANIMATION; 4152 4153 // Check whether the child that requests the invalidate is fully opaque 4154 // Views being animated or transformed are not considered opaque because we may 4155 // be invalidating their old position and need the parent to paint behind them. 4156 Matrix childMatrix = child.getMatrix(); 4157 final boolean isOpaque = child.isOpaque() && !drawAnimation && 4158 child.getAnimation() == null && childMatrix.isIdentity(); 4159 // Mark the child as dirty, using the appropriate flag 4160 // Make sure we do not set both flags at the same time 4161 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY; 4162 4163 if (child.mLayerType != LAYER_TYPE_NONE) { 4164 mPrivateFlags |= PFLAG_INVALIDATED; 4165 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 4166 child.mLocalDirtyRect.union(dirty); 4167 } 4168 4169 final int[] location = attachInfo.mInvalidateChildLocation; 4170 location[CHILD_LEFT_INDEX] = child.mLeft; 4171 location[CHILD_TOP_INDEX] = child.mTop; 4172 if (!childMatrix.isIdentity() || 4173 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 4174 RectF boundingRect = attachInfo.mTmpTransformRect; 4175 boundingRect.set(dirty); 4176 Matrix transformMatrix; 4177 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 4178 Transformation t = attachInfo.mTmpTransformation; 4179 boolean transformed = getChildStaticTransformation(child, t); 4180 if (transformed) { 4181 transformMatrix = attachInfo.mTmpMatrix; 4182 transformMatrix.set(t.getMatrix()); 4183 if (!childMatrix.isIdentity()) { 4184 transformMatrix.preConcat(childMatrix); 4185 } 4186 } else { 4187 transformMatrix = childMatrix; 4188 } 4189 } else { 4190 transformMatrix = childMatrix; 4191 } 4192 transformMatrix.mapRect(boundingRect); 4193 dirty.set((int) (boundingRect.left - 0.5f), 4194 (int) (boundingRect.top - 0.5f), 4195 (int) (boundingRect.right + 0.5f), 4196 (int) (boundingRect.bottom + 0.5f)); 4197 } 4198 4199 do { 4200 View view = null; 4201 if (parent instanceof View) { 4202 view = (View) parent; 4203 } 4204 4205 if (drawAnimation) { 4206 if (view != null) { 4207 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION; 4208 } else if (parent instanceof ViewRootImpl) { 4209 ((ViewRootImpl) parent).mIsAnimating = true; 4210 } 4211 } 4212 4213 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 4214 // flag coming from the child that initiated the invalidate 4215 if (view != null) { 4216 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && 4217 view.getSolidColor() == 0) { 4218 opaqueFlag = PFLAG_DIRTY; 4219 } 4220 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { 4221 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag; 4222 } 4223 } 4224 4225 parent = parent.invalidateChildInParent(location, dirty); 4226 if (view != null) { 4227 // Account for transform on current parent 4228 Matrix m = view.getMatrix(); 4229 if (!m.isIdentity()) { 4230 RectF boundingRect = attachInfo.mTmpTransformRect; 4231 boundingRect.set(dirty); 4232 m.mapRect(boundingRect); 4233 dirty.set((int) (boundingRect.left - 0.5f), 4234 (int) (boundingRect.top - 0.5f), 4235 (int) (boundingRect.right + 0.5f), 4236 (int) (boundingRect.bottom + 0.5f)); 4237 } 4238 } 4239 } while (parent != null); 4240 } 4241 } 4242 4243 /** 4244 * Don't call or override this method. It is used for the implementation of 4245 * the view hierarchy. 4246 * 4247 * This implementation returns null if this ViewGroup does not have a parent, 4248 * if this ViewGroup is already fully invalidated or if the dirty rectangle 4249 * does not intersect with this ViewGroup's bounds. 4250 */ 4251 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { 4252 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || 4253 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { 4254 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != 4255 FLAG_OPTIMIZE_INVALIDATE) { 4256 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, 4257 location[CHILD_TOP_INDEX] - mScrollY); 4258 4259 final int left = mLeft; 4260 final int top = mTop; 4261 4262 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 4263 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) { 4264 dirty.setEmpty(); 4265 } 4266 } 4267 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 4268 4269 location[CHILD_LEFT_INDEX] = left; 4270 location[CHILD_TOP_INDEX] = top; 4271 4272 if (mLayerType != LAYER_TYPE_NONE) { 4273 mPrivateFlags |= PFLAG_INVALIDATED; 4274 mLocalDirtyRect.union(dirty); 4275 } 4276 4277 return mParent; 4278 4279 } else { 4280 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID; 4281 4282 location[CHILD_LEFT_INDEX] = mLeft; 4283 location[CHILD_TOP_INDEX] = mTop; 4284 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { 4285 dirty.set(0, 0, mRight - mLeft, mBottom - mTop); 4286 } else { 4287 // in case the dirty rect extends outside the bounds of this container 4288 dirty.union(0, 0, mRight - mLeft, mBottom - mTop); 4289 } 4290 4291 if (mLayerType != LAYER_TYPE_NONE) { 4292 mPrivateFlags |= PFLAG_INVALIDATED; 4293 mLocalDirtyRect.union(dirty); 4294 } 4295 4296 return mParent; 4297 } 4298 } 4299 4300 return null; 4301 } 4302 4303 /** 4304 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the 4305 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods 4306 * do; all we want to do here is schedule a traversal with the appropriate dirty rect. 4307 * 4308 * @hide 4309 */ 4310 public void invalidateChildFast(View child, final Rect dirty) { 4311 ViewParent parent = this; 4312 4313 final AttachInfo attachInfo = mAttachInfo; 4314 if (attachInfo != null) { 4315 if (child.mLayerType != LAYER_TYPE_NONE) { 4316 child.mLocalDirtyRect.union(dirty); 4317 } 4318 4319 int left = child.mLeft; 4320 int top = child.mTop; 4321 if (!child.getMatrix().isIdentity()) { 4322 child.transformRect(dirty); 4323 } 4324 4325 do { 4326 if (parent instanceof ViewGroup) { 4327 ViewGroup parentVG = (ViewGroup) parent; 4328 if (parentVG.mLayerType != LAYER_TYPE_NONE) { 4329 // Layered parents should be recreated, not just re-issued 4330 parentVG.invalidate(); 4331 parent = null; 4332 } else { 4333 parent = parentVG.invalidateChildInParentFast(left, top, dirty); 4334 left = parentVG.mLeft; 4335 top = parentVG.mTop; 4336 } 4337 } else { 4338 // Reached the top; this calls into the usual invalidate method in 4339 // ViewRootImpl, which schedules a traversal 4340 final int[] location = attachInfo.mInvalidateChildLocation; 4341 location[0] = left; 4342 location[1] = top; 4343 parent = parent.invalidateChildInParent(location, dirty); 4344 } 4345 } while (parent != null); 4346 } 4347 } 4348 4349 /** 4350 * Quick invalidation method that simply transforms the dirty rect into the parent's 4351 * coordinate system, pruning the invalidation if the parent has already been invalidated. 4352 */ 4353 private ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) { 4354 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || 4355 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { 4356 dirty.offset(left - mScrollX, top - mScrollY); 4357 4358 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 || 4359 dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) { 4360 4361 if (mLayerType != LAYER_TYPE_NONE) { 4362 mLocalDirtyRect.union(dirty); 4363 } 4364 if (!getMatrix().isIdentity()) { 4365 transformRect(dirty); 4366 } 4367 4368 return mParent; 4369 } 4370 } 4371 4372 return null; 4373 } 4374 4375 /** 4376 * Offset a rectangle that is in a descendant's coordinate 4377 * space into our coordinate space. 4378 * @param descendant A descendant of this view 4379 * @param rect A rectangle defined in descendant's coordinate space. 4380 */ 4381 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { 4382 offsetRectBetweenParentAndChild(descendant, rect, true, false); 4383 } 4384 4385 /** 4386 * Offset a rectangle that is in our coordinate space into an ancestor's 4387 * coordinate space. 4388 * @param descendant A descendant of this view 4389 * @param rect A rectangle defined in descendant's coordinate space. 4390 */ 4391 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { 4392 offsetRectBetweenParentAndChild(descendant, rect, false, false); 4393 } 4394 4395 /** 4396 * Helper method that offsets a rect either from parent to descendant or 4397 * descendant to parent. 4398 */ 4399 void offsetRectBetweenParentAndChild(View descendant, Rect rect, 4400 boolean offsetFromChildToParent, boolean clipToBounds) { 4401 4402 // already in the same coord system :) 4403 if (descendant == this) { 4404 return; 4405 } 4406 4407 ViewParent theParent = descendant.mParent; 4408 4409 // search and offset up to the parent 4410 while ((theParent != null) 4411 && (theParent instanceof View) 4412 && (theParent != this)) { 4413 4414 if (offsetFromChildToParent) { 4415 rect.offset(descendant.mLeft - descendant.mScrollX, 4416 descendant.mTop - descendant.mScrollY); 4417 if (clipToBounds) { 4418 View p = (View) theParent; 4419 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 4420 } 4421 } else { 4422 if (clipToBounds) { 4423 View p = (View) theParent; 4424 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop); 4425 } 4426 rect.offset(descendant.mScrollX - descendant.mLeft, 4427 descendant.mScrollY - descendant.mTop); 4428 } 4429 4430 descendant = (View) theParent; 4431 theParent = descendant.mParent; 4432 } 4433 4434 // now that we are up to this view, need to offset one more time 4435 // to get into our coordinate space 4436 if (theParent == this) { 4437 if (offsetFromChildToParent) { 4438 rect.offset(descendant.mLeft - descendant.mScrollX, 4439 descendant.mTop - descendant.mScrollY); 4440 } else { 4441 rect.offset(descendant.mScrollX - descendant.mLeft, 4442 descendant.mScrollY - descendant.mTop); 4443 } 4444 } else { 4445 throw new IllegalArgumentException("parameter must be a descendant of this view"); 4446 } 4447 } 4448 4449 /** 4450 * Offset the vertical location of all children of this view by the specified number of pixels. 4451 * 4452 * @param offset the number of pixels to offset 4453 * 4454 * @hide 4455 */ 4456 public void offsetChildrenTopAndBottom(int offset) { 4457 final int count = mChildrenCount; 4458 final View[] children = mChildren; 4459 4460 for (int i = 0; i < count; i++) { 4461 final View v = children[i]; 4462 v.mTop += offset; 4463 v.mBottom += offset; 4464 if (v.mDisplayList != null) { 4465 v.mDisplayList.offsetTopAndBottom(offset); 4466 invalidateViewProperty(false, false); 4467 } 4468 } 4469 } 4470 4471 /** 4472 * {@inheritDoc} 4473 */ 4474 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 4475 // It doesn't make a whole lot of sense to call this on a view that isn't attached, 4476 // but for some simple tests it can be useful. If we don't have attach info this 4477 // will allocate memory. 4478 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF(); 4479 rect.set(r); 4480 4481 if (!child.hasIdentityMatrix()) { 4482 child.getMatrix().mapRect(rect); 4483 } 4484 4485 int dx = child.mLeft - mScrollX; 4486 int dy = child.mTop - mScrollY; 4487 4488 rect.offset(dx, dy); 4489 4490 if (offset != null) { 4491 if (!child.hasIdentityMatrix()) { 4492 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation 4493 : new float[2]; 4494 position[0] = offset.x; 4495 position[1] = offset.y; 4496 child.getMatrix().mapPoints(position); 4497 offset.x = (int) (position[0] + 0.5f); 4498 offset.y = (int) (position[1] + 0.5f); 4499 } 4500 offset.x += dx; 4501 offset.y += dy; 4502 } 4503 4504 if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) { 4505 if (mParent == null) return true; 4506 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f), 4507 (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f)); 4508 return mParent.getChildVisibleRect(this, r, offset); 4509 } 4510 4511 return false; 4512 } 4513 4514 /** 4515 * {@inheritDoc} 4516 */ 4517 @Override 4518 public final void layout(int l, int t, int r, int b) { 4519 if (mTransition == null || !mTransition.isChangingLayout()) { 4520 if (mTransition != null) { 4521 mTransition.layoutChange(this); 4522 } 4523 super.layout(l, t, r, b); 4524 } else { 4525 // record the fact that we noop'd it; request layout when transition finishes 4526 mLayoutSuppressed = true; 4527 } 4528 } 4529 4530 /** 4531 * {@inheritDoc} 4532 */ 4533 @Override 4534 protected abstract void onLayout(boolean changed, 4535 int l, int t, int r, int b); 4536 4537 /** 4538 * Indicates whether the view group has the ability to animate its children 4539 * after the first layout. 4540 * 4541 * @return true if the children can be animated, false otherwise 4542 */ 4543 protected boolean canAnimate() { 4544 return mLayoutAnimationController != null; 4545 } 4546 4547 /** 4548 * Runs the layout animation. Calling this method triggers a relayout of 4549 * this view group. 4550 */ 4551 public void startLayoutAnimation() { 4552 if (mLayoutAnimationController != null) { 4553 mGroupFlags |= FLAG_RUN_ANIMATION; 4554 requestLayout(); 4555 } 4556 } 4557 4558 /** 4559 * Schedules the layout animation to be played after the next layout pass 4560 * of this view group. This can be used to restart the layout animation 4561 * when the content of the view group changes or when the activity is 4562 * paused and resumed. 4563 */ 4564 public void scheduleLayoutAnimation() { 4565 mGroupFlags |= FLAG_RUN_ANIMATION; 4566 } 4567 4568 /** 4569 * Sets the layout animation controller used to animate the group's 4570 * children after the first layout. 4571 * 4572 * @param controller the animation controller 4573 */ 4574 public void setLayoutAnimation(LayoutAnimationController controller) { 4575 mLayoutAnimationController = controller; 4576 if (mLayoutAnimationController != null) { 4577 mGroupFlags |= FLAG_RUN_ANIMATION; 4578 } 4579 } 4580 4581 /** 4582 * Returns the layout animation controller used to animate the group's 4583 * children. 4584 * 4585 * @return the current animation controller 4586 */ 4587 public LayoutAnimationController getLayoutAnimation() { 4588 return mLayoutAnimationController; 4589 } 4590 4591 /** 4592 * Indicates whether the children's drawing cache is used during a layout 4593 * animation. By default, the drawing cache is enabled but this will prevent 4594 * nested layout animations from working. To nest animations, you must disable 4595 * the cache. 4596 * 4597 * @return true if the animation cache is enabled, false otherwise 4598 * 4599 * @see #setAnimationCacheEnabled(boolean) 4600 * @see View#setDrawingCacheEnabled(boolean) 4601 */ 4602 @ViewDebug.ExportedProperty 4603 public boolean isAnimationCacheEnabled() { 4604 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; 4605 } 4606 4607 /** 4608 * Enables or disables the children's drawing cache during a layout animation. 4609 * By default, the drawing cache is enabled but this will prevent nested 4610 * layout animations from working. To nest animations, you must disable the 4611 * cache. 4612 * 4613 * @param enabled true to enable the animation cache, false otherwise 4614 * 4615 * @see #isAnimationCacheEnabled() 4616 * @see View#setDrawingCacheEnabled(boolean) 4617 */ 4618 public void setAnimationCacheEnabled(boolean enabled) { 4619 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); 4620 } 4621 4622 /** 4623 * Indicates whether this ViewGroup will always try to draw its children using their 4624 * drawing cache. By default this property is enabled. 4625 * 4626 * @return true if the animation cache is enabled, false otherwise 4627 * 4628 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 4629 * @see #setChildrenDrawnWithCacheEnabled(boolean) 4630 * @see View#setDrawingCacheEnabled(boolean) 4631 */ 4632 @ViewDebug.ExportedProperty(category = "drawing") 4633 public boolean isAlwaysDrawnWithCacheEnabled() { 4634 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; 4635 } 4636 4637 /** 4638 * Indicates whether this ViewGroup will always try to draw its children using their 4639 * drawing cache. This property can be set to true when the cache rendering is 4640 * slightly different from the children's normal rendering. Renderings can be different, 4641 * for instance, when the cache's quality is set to low. 4642 * 4643 * When this property is disabled, the ViewGroup will use the drawing cache of its 4644 * children only when asked to. It's usually the task of subclasses to tell ViewGroup 4645 * when to start using the drawing cache and when to stop using it. 4646 * 4647 * @param always true to always draw with the drawing cache, false otherwise 4648 * 4649 * @see #isAlwaysDrawnWithCacheEnabled() 4650 * @see #setChildrenDrawnWithCacheEnabled(boolean) 4651 * @see View#setDrawingCacheEnabled(boolean) 4652 * @see View#setDrawingCacheQuality(int) 4653 */ 4654 public void setAlwaysDrawnWithCacheEnabled(boolean always) { 4655 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); 4656 } 4657 4658 /** 4659 * Indicates whether the ViewGroup is currently drawing its children using 4660 * their drawing cache. 4661 * 4662 * @return true if children should be drawn with their cache, false otherwise 4663 * 4664 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 4665 * @see #setChildrenDrawnWithCacheEnabled(boolean) 4666 */ 4667 @ViewDebug.ExportedProperty(category = "drawing") 4668 protected boolean isChildrenDrawnWithCacheEnabled() { 4669 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; 4670 } 4671 4672 /** 4673 * Tells the ViewGroup to draw its children using their drawing cache. This property 4674 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache 4675 * will be used only if it has been enabled. 4676 * 4677 * Subclasses should call this method to start and stop using the drawing cache when 4678 * they perform performance sensitive operations, like scrolling or animating. 4679 * 4680 * @param enabled true if children should be drawn with their cache, false otherwise 4681 * 4682 * @see #setAlwaysDrawnWithCacheEnabled(boolean) 4683 * @see #isChildrenDrawnWithCacheEnabled() 4684 */ 4685 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { 4686 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); 4687 } 4688 4689 /** 4690 * Indicates whether the ViewGroup is drawing its children in the order defined by 4691 * {@link #getChildDrawingOrder(int, int)}. 4692 * 4693 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, 4694 * false otherwise 4695 * 4696 * @see #setChildrenDrawingOrderEnabled(boolean) 4697 * @see #getChildDrawingOrder(int, int) 4698 */ 4699 @ViewDebug.ExportedProperty(category = "drawing") 4700 protected boolean isChildrenDrawingOrderEnabled() { 4701 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; 4702 } 4703 4704 /** 4705 * Tells the ViewGroup whether to draw its children in the order defined by the method 4706 * {@link #getChildDrawingOrder(int, int)}. 4707 * 4708 * @param enabled true if the order of the children when drawing is determined by 4709 * {@link #getChildDrawingOrder(int, int)}, false otherwise 4710 * 4711 * @see #isChildrenDrawingOrderEnabled() 4712 * @see #getChildDrawingOrder(int, int) 4713 */ 4714 protected void setChildrenDrawingOrderEnabled(boolean enabled) { 4715 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); 4716 } 4717 4718 private void setBooleanFlag(int flag, boolean value) { 4719 if (value) { 4720 mGroupFlags |= flag; 4721 } else { 4722 mGroupFlags &= ~flag; 4723 } 4724 } 4725 4726 /** 4727 * Returns an integer indicating what types of drawing caches are kept in memory. 4728 * 4729 * @see #setPersistentDrawingCache(int) 4730 * @see #setAnimationCacheEnabled(boolean) 4731 * 4732 * @return one or a combination of {@link #PERSISTENT_NO_CACHE}, 4733 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 4734 * and {@link #PERSISTENT_ALL_CACHES} 4735 */ 4736 @ViewDebug.ExportedProperty(category = "drawing", mapping = { 4737 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), 4738 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"), 4739 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), 4740 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") 4741 }) 4742 public int getPersistentDrawingCache() { 4743 return mPersistentDrawingCache; 4744 } 4745 4746 /** 4747 * Indicates what types of drawing caches should be kept in memory after 4748 * they have been created. 4749 * 4750 * @see #getPersistentDrawingCache() 4751 * @see #setAnimationCacheEnabled(boolean) 4752 * 4753 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE}, 4754 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} 4755 * and {@link #PERSISTENT_ALL_CACHES} 4756 */ 4757 public void setPersistentDrawingCache(int drawingCacheToKeep) { 4758 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; 4759 } 4760 4761 /** 4762 * Returns the basis of alignment during layout operations on this view group: 4763 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 4764 * 4765 * @return the layout mode to use during layout operations 4766 * 4767 * @see #setLayoutMode(int) 4768 */ 4769 public int getLayoutMode() { 4770 return mLayoutMode; 4771 } 4772 4773 /** 4774 * Sets the basis of alignment during the layout of this view group. 4775 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or 4776 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. 4777 * <p> 4778 * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}. 4779 * 4780 * @param layoutMode the layout mode to use during layout operations 4781 * 4782 * @see #getLayoutMode() 4783 */ 4784 public void setLayoutMode(int layoutMode) { 4785 if (mLayoutMode != layoutMode) { 4786 mLayoutMode = layoutMode; 4787 requestLayout(); 4788 } 4789 } 4790 4791 /** 4792 * Returns a new set of layout parameters based on the supplied attributes set. 4793 * 4794 * @param attrs the attributes to build the layout parameters from 4795 * 4796 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 4797 * of its descendants 4798 */ 4799 public LayoutParams generateLayoutParams(AttributeSet attrs) { 4800 return new LayoutParams(getContext(), attrs); 4801 } 4802 4803 /** 4804 * Returns a safe set of layout parameters based on the supplied layout params. 4805 * When a ViewGroup is passed a View whose layout params do not pass the test of 4806 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method 4807 * is invoked. This method should return a new set of layout params suitable for 4808 * this ViewGroup, possibly by copying the appropriate attributes from the 4809 * specified set of layout params. 4810 * 4811 * @param p The layout parameters to convert into a suitable set of layout parameters 4812 * for this ViewGroup. 4813 * 4814 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one 4815 * of its descendants 4816 */ 4817 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 4818 return p; 4819 } 4820 4821 /** 4822 * Returns a set of default layout parameters. These parameters are requested 4823 * when the View passed to {@link #addView(View)} has no layout parameters 4824 * already set. If null is returned, an exception is thrown from addView. 4825 * 4826 * @return a set of default layout parameters or null 4827 */ 4828 protected LayoutParams generateDefaultLayoutParams() { 4829 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 4830 } 4831 4832 /** 4833 * {@inheritDoc} 4834 */ 4835 @Override 4836 protected void debug(int depth) { 4837 super.debug(depth); 4838 String output; 4839 4840 if (mFocused != null) { 4841 output = debugIndent(depth); 4842 output += "mFocused"; 4843 Log.d(VIEW_LOG_TAG, output); 4844 } 4845 if (mChildrenCount != 0) { 4846 output = debugIndent(depth); 4847 output += "{"; 4848 Log.d(VIEW_LOG_TAG, output); 4849 } 4850 int count = mChildrenCount; 4851 for (int i = 0; i < count; i++) { 4852 View child = mChildren[i]; 4853 child.debug(depth + 1); 4854 } 4855 4856 if (mChildrenCount != 0) { 4857 output = debugIndent(depth); 4858 output += "}"; 4859 Log.d(VIEW_LOG_TAG, output); 4860 } 4861 } 4862 4863 /** 4864 * Returns the position in the group of the specified child view. 4865 * 4866 * @param child the view for which to get the position 4867 * @return a positive integer representing the position of the view in the 4868 * group, or -1 if the view does not exist in the group 4869 */ 4870 public int indexOfChild(View child) { 4871 final int count = mChildrenCount; 4872 final View[] children = mChildren; 4873 for (int i = 0; i < count; i++) { 4874 if (children[i] == child) { 4875 return i; 4876 } 4877 } 4878 return -1; 4879 } 4880 4881 /** 4882 * Returns the number of children in the group. 4883 * 4884 * @return a positive integer representing the number of children in 4885 * the group 4886 */ 4887 public int getChildCount() { 4888 return mChildrenCount; 4889 } 4890 4891 /** 4892 * Returns the view at the specified position in the group. 4893 * 4894 * @param index the position at which to get the view from 4895 * @return the view at the specified position or null if the position 4896 * does not exist within the group 4897 */ 4898 public View getChildAt(int index) { 4899 if (index < 0 || index >= mChildrenCount) { 4900 return null; 4901 } 4902 return mChildren[index]; 4903 } 4904 4905 /** 4906 * Ask all of the children of this view to measure themselves, taking into 4907 * account both the MeasureSpec requirements for this view and its padding. 4908 * We skip children that are in the GONE state The heavy lifting is done in 4909 * getChildMeasureSpec. 4910 * 4911 * @param widthMeasureSpec The width requirements for this view 4912 * @param heightMeasureSpec The height requirements for this view 4913 */ 4914 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 4915 final int size = mChildrenCount; 4916 final View[] children = mChildren; 4917 for (int i = 0; i < size; ++i) { 4918 final View child = children[i]; 4919 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 4920 measureChild(child, widthMeasureSpec, heightMeasureSpec); 4921 } 4922 } 4923 } 4924 4925 /** 4926 * Ask one of the children of this view to measure itself, taking into 4927 * account both the MeasureSpec requirements for this view and its padding. 4928 * The heavy lifting is done in getChildMeasureSpec. 4929 * 4930 * @param child The child to measure 4931 * @param parentWidthMeasureSpec The width requirements for this view 4932 * @param parentHeightMeasureSpec The height requirements for this view 4933 */ 4934 protected void measureChild(View child, int parentWidthMeasureSpec, 4935 int parentHeightMeasureSpec) { 4936 final LayoutParams lp = child.getLayoutParams(); 4937 4938 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 4939 mPaddingLeft + mPaddingRight, lp.width); 4940 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 4941 mPaddingTop + mPaddingBottom, lp.height); 4942 4943 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4944 } 4945 4946 /** 4947 * Ask one of the children of this view to measure itself, taking into 4948 * account both the MeasureSpec requirements for this view and its padding 4949 * and margins. The child must have MarginLayoutParams The heavy lifting is 4950 * done in getChildMeasureSpec. 4951 * 4952 * @param child The child to measure 4953 * @param parentWidthMeasureSpec The width requirements for this view 4954 * @param widthUsed Extra space that has been used up by the parent 4955 * horizontally (possibly by other children of the parent) 4956 * @param parentHeightMeasureSpec The height requirements for this view 4957 * @param heightUsed Extra space that has been used up by the parent 4958 * vertically (possibly by other children of the parent) 4959 */ 4960 protected void measureChildWithMargins(View child, 4961 int parentWidthMeasureSpec, int widthUsed, 4962 int parentHeightMeasureSpec, int heightUsed) { 4963 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 4964 4965 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 4966 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 4967 + widthUsed, lp.width); 4968 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 4969 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 4970 + heightUsed, lp.height); 4971 4972 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4973 } 4974 4975 /** 4976 * Does the hard part of measureChildren: figuring out the MeasureSpec to 4977 * pass to a particular child. This method figures out the right MeasureSpec 4978 * for one dimension (height or width) of one child view. 4979 * 4980 * The goal is to combine information from our MeasureSpec with the 4981 * LayoutParams of the child to get the best possible results. For example, 4982 * if the this view knows its size (because its MeasureSpec has a mode of 4983 * EXACTLY), and the child has indicated in its LayoutParams that it wants 4984 * to be the same size as the parent, the parent should ask the child to 4985 * layout given an exact size. 4986 * 4987 * @param spec The requirements for this view 4988 * @param padding The padding of this view for the current dimension and 4989 * margins, if applicable 4990 * @param childDimension How big the child wants to be in the current 4991 * dimension 4992 * @return a MeasureSpec integer for the child 4993 */ 4994 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 4995 int specMode = MeasureSpec.getMode(spec); 4996 int specSize = MeasureSpec.getSize(spec); 4997 4998 int size = Math.max(0, specSize - padding); 4999 5000 int resultSize = 0; 5001 int resultMode = 0; 5002 5003 switch (specMode) { 5004 // Parent has imposed an exact size on us 5005 case MeasureSpec.EXACTLY: 5006 if (childDimension >= 0) { 5007 resultSize = childDimension; 5008 resultMode = MeasureSpec.EXACTLY; 5009 } else if (childDimension == LayoutParams.MATCH_PARENT) { 5010 // Child wants to be our size. So be it. 5011 resultSize = size; 5012 resultMode = MeasureSpec.EXACTLY; 5013 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 5014 // Child wants to determine its own size. It can't be 5015 // bigger than us. 5016 resultSize = size; 5017 resultMode = MeasureSpec.AT_MOST; 5018 } 5019 break; 5020 5021 // Parent has imposed a maximum size on us 5022 case MeasureSpec.AT_MOST: 5023 if (childDimension >= 0) { 5024 // Child wants a specific size... so be it 5025 resultSize = childDimension; 5026 resultMode = MeasureSpec.EXACTLY; 5027 } else if (childDimension == LayoutParams.MATCH_PARENT) { 5028 // Child wants to be our size, but our size is not fixed. 5029 // Constrain child to not be bigger than us. 5030 resultSize = size; 5031 resultMode = MeasureSpec.AT_MOST; 5032 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 5033 // Child wants to determine its own size. It can't be 5034 // bigger than us. 5035 resultSize = size; 5036 resultMode = MeasureSpec.AT_MOST; 5037 } 5038 break; 5039 5040 // Parent asked to see how big we want to be 5041 case MeasureSpec.UNSPECIFIED: 5042 if (childDimension >= 0) { 5043 // Child wants a specific size... let him have it 5044 resultSize = childDimension; 5045 resultMode = MeasureSpec.EXACTLY; 5046 } else if (childDimension == LayoutParams.MATCH_PARENT) { 5047 // Child wants to be our size... find out how big it should 5048 // be 5049 resultSize = 0; 5050 resultMode = MeasureSpec.UNSPECIFIED; 5051 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 5052 // Child wants to determine its own size.... find out how 5053 // big it should be 5054 resultSize = 0; 5055 resultMode = MeasureSpec.UNSPECIFIED; 5056 } 5057 break; 5058 } 5059 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 5060 } 5061 5062 5063 /** 5064 * Removes any pending animations for views that have been removed. Call 5065 * this if you don't want animations for exiting views to stack up. 5066 */ 5067 public void clearDisappearingChildren() { 5068 if (mDisappearingChildren != null) { 5069 mDisappearingChildren.clear(); 5070 invalidate(); 5071 } 5072 } 5073 5074 /** 5075 * Add a view which is removed from mChildren but still needs animation 5076 * 5077 * @param v View to add 5078 */ 5079 private void addDisappearingView(View v) { 5080 ArrayList<View> disappearingChildren = mDisappearingChildren; 5081 5082 if (disappearingChildren == null) { 5083 disappearingChildren = mDisappearingChildren = new ArrayList<View>(); 5084 } 5085 5086 disappearingChildren.add(v); 5087 } 5088 5089 /** 5090 * Cleanup a view when its animation is done. This may mean removing it from 5091 * the list of disappearing views. 5092 * 5093 * @param view The view whose animation has finished 5094 * @param animation The animation, cannot be null 5095 */ 5096 void finishAnimatingView(final View view, Animation animation) { 5097 final ArrayList<View> disappearingChildren = mDisappearingChildren; 5098 if (disappearingChildren != null) { 5099 if (disappearingChildren.contains(view)) { 5100 disappearingChildren.remove(view); 5101 5102 if (view.mAttachInfo != null) { 5103 view.dispatchDetachedFromWindow(); 5104 } 5105 5106 view.clearAnimation(); 5107 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 5108 } 5109 } 5110 5111 if (animation != null && !animation.getFillAfter()) { 5112 view.clearAnimation(); 5113 } 5114 5115 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) { 5116 view.onAnimationEnd(); 5117 // Should be performed by onAnimationEnd() but this avoid an infinite loop, 5118 // so we'd rather be safe than sorry 5119 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED; 5120 // Draw one more frame after the animation is done 5121 mGroupFlags |= FLAG_INVALIDATE_REQUIRED; 5122 } 5123 } 5124 5125 /** 5126 * Utility function called by View during invalidation to determine whether a view that 5127 * is invisible or gone should still be invalidated because it is being transitioned (and 5128 * therefore still needs to be drawn). 5129 */ 5130 boolean isViewTransitioning(View view) { 5131 return (mTransitioningViews != null && mTransitioningViews.contains(view)); 5132 } 5133 5134 /** 5135 * This method tells the ViewGroup that the given View object, which should have this 5136 * ViewGroup as its parent, 5137 * should be kept around (re-displayed when the ViewGroup draws its children) even if it 5138 * is removed from its parent. This allows animations, such as those used by 5139 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate 5140 * the removal of views. A call to this method should always be accompanied by a later call 5141 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished, 5142 * so that the View finally gets removed. 5143 * 5144 * @param view The View object to be kept visible even if it gets removed from its parent. 5145 */ 5146 public void startViewTransition(View view) { 5147 if (view.mParent == this) { 5148 if (mTransitioningViews == null) { 5149 mTransitioningViews = new ArrayList<View>(); 5150 } 5151 mTransitioningViews.add(view); 5152 } 5153 } 5154 5155 /** 5156 * This method should always be called following an earlier call to 5157 * {@link #startViewTransition(View)}. The given View is finally removed from its parent 5158 * and will no longer be displayed. Note that this method does not perform the functionality 5159 * of removing a view from its parent; it just discontinues the display of a View that 5160 * has previously been removed. 5161 * 5162 * @return view The View object that has been removed but is being kept around in the visible 5163 * hierarchy by an earlier call to {@link #startViewTransition(View)}. 5164 */ 5165 public void endViewTransition(View view) { 5166 if (mTransitioningViews != null) { 5167 mTransitioningViews.remove(view); 5168 final ArrayList<View> disappearingChildren = mDisappearingChildren; 5169 if (disappearingChildren != null && disappearingChildren.contains(view)) { 5170 disappearingChildren.remove(view); 5171 if (mVisibilityChangingChildren != null && 5172 mVisibilityChangingChildren.contains(view)) { 5173 mVisibilityChangingChildren.remove(view); 5174 } else { 5175 if (view.mAttachInfo != null) { 5176 view.dispatchDetachedFromWindow(); 5177 } 5178 if (view.mParent != null) { 5179 view.mParent = null; 5180 } 5181 } 5182 invalidate(); 5183 } 5184 } 5185 } 5186 5187 private LayoutTransition.TransitionListener mLayoutTransitionListener = 5188 new LayoutTransition.TransitionListener() { 5189 @Override 5190 public void startTransition(LayoutTransition transition, ViewGroup container, 5191 View view, int transitionType) { 5192 // We only care about disappearing items, since we need special logic to keep 5193 // those items visible after they've been 'removed' 5194 if (transitionType == LayoutTransition.DISAPPEARING) { 5195 startViewTransition(view); 5196 } 5197 } 5198 5199 @Override 5200 public void endTransition(LayoutTransition transition, ViewGroup container, 5201 View view, int transitionType) { 5202 if (mLayoutSuppressed && !transition.isChangingLayout()) { 5203 requestLayout(); 5204 mLayoutSuppressed = false; 5205 } 5206 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) { 5207 endViewTransition(view); 5208 } 5209 } 5210 }; 5211 5212 /** 5213 * {@inheritDoc} 5214 */ 5215 @Override 5216 public boolean gatherTransparentRegion(Region region) { 5217 // If no transparent regions requested, we are always opaque. 5218 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0; 5219 if (meOpaque && region == null) { 5220 // The caller doesn't care about the region, so stop now. 5221 return true; 5222 } 5223 super.gatherTransparentRegion(region); 5224 final View[] children = mChildren; 5225 final int count = mChildrenCount; 5226 boolean noneOfTheChildrenAreTransparent = true; 5227 for (int i = 0; i < count; i++) { 5228 final View child = children[i]; 5229 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 5230 if (!child.gatherTransparentRegion(region)) { 5231 noneOfTheChildrenAreTransparent = false; 5232 } 5233 } 5234 } 5235 return meOpaque || noneOfTheChildrenAreTransparent; 5236 } 5237 5238 /** 5239 * {@inheritDoc} 5240 */ 5241 public void requestTransparentRegion(View child) { 5242 if (child != null) { 5243 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; 5244 if (mParent != null) { 5245 mParent.requestTransparentRegion(this); 5246 } 5247 } 5248 } 5249 5250 5251 @Override 5252 protected boolean fitSystemWindows(Rect insets) { 5253 boolean done = super.fitSystemWindows(insets); 5254 if (!done) { 5255 final int count = mChildrenCount; 5256 final View[] children = mChildren; 5257 for (int i = 0; i < count; i++) { 5258 done = children[i].fitSystemWindows(insets); 5259 if (done) { 5260 break; 5261 } 5262 } 5263 } 5264 return done; 5265 } 5266 5267 /** 5268 * Returns the animation listener to which layout animation events are 5269 * sent. 5270 * 5271 * @return an {@link android.view.animation.Animation.AnimationListener} 5272 */ 5273 public Animation.AnimationListener getLayoutAnimationListener() { 5274 return mAnimationListener; 5275 } 5276 5277 @Override 5278 protected void drawableStateChanged() { 5279 super.drawableStateChanged(); 5280 5281 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { 5282 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 5283 throw new IllegalStateException("addStateFromChildren cannot be enabled if a" 5284 + " child has duplicateParentState set to true"); 5285 } 5286 5287 final View[] children = mChildren; 5288 final int count = mChildrenCount; 5289 5290 for (int i = 0; i < count; i++) { 5291 final View child = children[i]; 5292 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { 5293 child.refreshDrawableState(); 5294 } 5295 } 5296 } 5297 } 5298 5299 @Override 5300 public void jumpDrawablesToCurrentState() { 5301 super.jumpDrawablesToCurrentState(); 5302 final View[] children = mChildren; 5303 final int count = mChildrenCount; 5304 for (int i = 0; i < count; i++) { 5305 children[i].jumpDrawablesToCurrentState(); 5306 } 5307 } 5308 5309 @Override 5310 protected int[] onCreateDrawableState(int extraSpace) { 5311 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { 5312 return super.onCreateDrawableState(extraSpace); 5313 } 5314 5315 int need = 0; 5316 int n = getChildCount(); 5317 for (int i = 0; i < n; i++) { 5318 int[] childState = getChildAt(i).getDrawableState(); 5319 5320 if (childState != null) { 5321 need += childState.length; 5322 } 5323 } 5324 5325 int[] state = super.onCreateDrawableState(extraSpace + need); 5326 5327 for (int i = 0; i < n; i++) { 5328 int[] childState = getChildAt(i).getDrawableState(); 5329 5330 if (childState != null) { 5331 state = mergeDrawableStates(state, childState); 5332 } 5333 } 5334 5335 return state; 5336 } 5337 5338 /** 5339 * Sets whether this ViewGroup's drawable states also include 5340 * its children's drawable states. This is used, for example, to 5341 * make a group appear to be focused when its child EditText or button 5342 * is focused. 5343 */ 5344 public void setAddStatesFromChildren(boolean addsStates) { 5345 if (addsStates) { 5346 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN; 5347 } else { 5348 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN; 5349 } 5350 5351 refreshDrawableState(); 5352 } 5353 5354 /** 5355 * Returns whether this ViewGroup's drawable states also include 5356 * its children's drawable states. This is used, for example, to 5357 * make a group appear to be focused when its child EditText or button 5358 * is focused. 5359 */ 5360 public boolean addStatesFromChildren() { 5361 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0; 5362 } 5363 5364 /** 5365 * If {@link #addStatesFromChildren} is true, refreshes this group's 5366 * drawable state (to include the states from its children). 5367 */ 5368 public void childDrawableStateChanged(View child) { 5369 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { 5370 refreshDrawableState(); 5371 } 5372 } 5373 5374 /** 5375 * Specifies the animation listener to which layout animation events must 5376 * be sent. Only 5377 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)} 5378 * and 5379 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)} 5380 * are invoked. 5381 * 5382 * @param animationListener the layout animation listener 5383 */ 5384 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { 5385 mAnimationListener = animationListener; 5386 } 5387 5388 /** 5389 * This method is called by LayoutTransition when there are 'changing' animations that need 5390 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who 5391 * starts all pending transitions prior to the drawing phase in the current traversal. 5392 * 5393 * @param transition The LayoutTransition to be started on the next traversal. 5394 * 5395 * @hide 5396 */ 5397 public void requestTransitionStart(LayoutTransition transition) { 5398 ViewRootImpl viewAncestor = getViewRootImpl(); 5399 if (viewAncestor != null) { 5400 viewAncestor.requestTransitionStart(transition); 5401 } 5402 } 5403 5404 /** 5405 * @hide 5406 */ 5407 @Override 5408 public void resolveRtlPropertiesIfNeeded() { 5409 super.resolveRtlPropertiesIfNeeded(); 5410 int count = getChildCount(); 5411 for (int i = 0; i < count; i++) { 5412 final View child = getChildAt(i); 5413 if (child.isLayoutDirectionInherited()) { 5414 child.resolveRtlPropertiesIfNeeded(); 5415 } 5416 } 5417 } 5418 5419 /** 5420 * @hide 5421 */ 5422 @Override 5423 public boolean resolveLayoutDirection() { 5424 final boolean result = super.resolveLayoutDirection(); 5425 if (result) { 5426 int count = getChildCount(); 5427 for (int i = 0; i < count; i++) { 5428 final View child = getChildAt(i); 5429 if (child.isLayoutDirectionInherited()) { 5430 child.resolveLayoutDirection(); 5431 } 5432 } 5433 } 5434 return result; 5435 } 5436 5437 /** 5438 * @hide 5439 */ 5440 @Override 5441 public boolean resolveTextDirection() { 5442 final boolean result = super.resolveTextDirection(); 5443 if (result) { 5444 int count = getChildCount(); 5445 for (int i = 0; i < count; i++) { 5446 final View child = getChildAt(i); 5447 if (child.isTextDirectionInherited()) { 5448 child.resolveTextDirection(); 5449 } 5450 } 5451 } 5452 return result; 5453 } 5454 5455 /** 5456 * @hide 5457 */ 5458 @Override 5459 public boolean resolveTextAlignment() { 5460 final boolean result = super.resolveTextAlignment(); 5461 if (result) { 5462 int count = getChildCount(); 5463 for (int i = 0; i < count; i++) { 5464 final View child = getChildAt(i); 5465 if (child.isTextAlignmentInherited()) { 5466 child.resolveTextAlignment(); 5467 } 5468 } 5469 } 5470 return result; 5471 } 5472 5473 /** 5474 * @hide 5475 */ 5476 @Override 5477 public void resolvePadding() { 5478 super.resolvePadding(); 5479 int count = getChildCount(); 5480 for (int i = 0; i < count; i++) { 5481 final View child = getChildAt(i); 5482 if (child.isLayoutDirectionInherited()) { 5483 child.resolvePadding(); 5484 } 5485 } 5486 } 5487 5488 /** 5489 * @hide 5490 */ 5491 @Override 5492 protected void resolveDrawables() { 5493 super.resolveDrawables(); 5494 int count = getChildCount(); 5495 for (int i = 0; i < count; i++) { 5496 final View child = getChildAt(i); 5497 if (child.isLayoutDirectionInherited()) { 5498 child.resolveDrawables(); 5499 } 5500 } 5501 } 5502 5503 /** 5504 * @hide 5505 */ 5506 @Override 5507 public void resolveLayoutParams() { 5508 super.resolveLayoutParams(); 5509 int count = getChildCount(); 5510 for (int i = 0; i < count; i++) { 5511 final View child = getChildAt(i); 5512 child.resolveLayoutParams(); 5513 } 5514 } 5515 5516 /** 5517 * @hide 5518 */ 5519 @Override 5520 public void resetResolvedLayoutDirection() { 5521 super.resetResolvedLayoutDirection(); 5522 5523 int count = getChildCount(); 5524 for (int i = 0; i < count; i++) { 5525 final View child = getChildAt(i); 5526 if (child.isLayoutDirectionInherited()) { 5527 child.resetResolvedLayoutDirection(); 5528 } 5529 } 5530 } 5531 5532 /** 5533 * @hide 5534 */ 5535 @Override 5536 public void resetResolvedTextDirection() { 5537 super.resetResolvedTextDirection(); 5538 5539 int count = getChildCount(); 5540 for (int i = 0; i < count; i++) { 5541 final View child = getChildAt(i); 5542 if (child.isTextDirectionInherited()) { 5543 child.resetResolvedTextDirection(); 5544 } 5545 } 5546 } 5547 5548 /** 5549 * @hide 5550 */ 5551 @Override 5552 public void resetResolvedTextAlignment() { 5553 super.resetResolvedTextAlignment(); 5554 5555 int count = getChildCount(); 5556 for (int i = 0; i < count; i++) { 5557 final View child = getChildAt(i); 5558 if (child.isTextAlignmentInherited()) { 5559 child.resetResolvedTextAlignment(); 5560 } 5561 } 5562 } 5563 5564 /** 5565 * @hide 5566 */ 5567 @Override 5568 public void resetResolvedPadding() { 5569 super.resetResolvedPadding(); 5570 5571 int count = getChildCount(); 5572 for (int i = 0; i < count; i++) { 5573 final View child = getChildAt(i); 5574 if (child.isLayoutDirectionInherited()) { 5575 child.resetResolvedPadding(); 5576 } 5577 } 5578 } 5579 5580 /** 5581 * @hide 5582 */ 5583 @Override 5584 protected void resetResolvedDrawables() { 5585 super.resetResolvedDrawables(); 5586 5587 int count = getChildCount(); 5588 for (int i = 0; i < count; i++) { 5589 final View child = getChildAt(i); 5590 if (child.isLayoutDirectionInherited()) { 5591 child.resetResolvedDrawables(); 5592 } 5593 } 5594 } 5595 5596 /** 5597 * Return true if the pressed state should be delayed for children or descendants of this 5598 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List. 5599 * This prevents the pressed state from appearing when the user is actually trying to scroll 5600 * the content. 5601 * 5602 * The default implementation returns true for compatibility reasons. Subclasses that do 5603 * not scroll should generally override this method and return false. 5604 */ 5605 public boolean shouldDelayChildPressedState() { 5606 return true; 5607 } 5608 5609 /** @hide */ 5610 protected void onSetLayoutParams(View child, LayoutParams layoutParams) { 5611 } 5612 5613 /** 5614 * LayoutParams are used by views to tell their parents how they want to be 5615 * laid out. See 5616 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 5617 * for a list of all child view attributes that this class supports. 5618 * 5619 * <p> 5620 * The base LayoutParams class just describes how big the view wants to be 5621 * for both width and height. For each dimension, it can specify one of: 5622 * <ul> 5623 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which 5624 * means that the view wants to be as big as its parent (minus padding) 5625 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 5626 * to enclose its content (plus padding) 5627 * <li> an exact number 5628 * </ul> 5629 * There are subclasses of LayoutParams for different subclasses of 5630 * ViewGroup. For example, AbsoluteLayout has its own subclass of 5631 * LayoutParams which adds an X and Y value.</p> 5632 * 5633 * <div class="special reference"> 5634 * <h3>Developer Guides</h3> 5635 * <p>For more information about creating user interface layouts, read the 5636 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer 5637 * guide.</p></div> 5638 * 5639 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 5640 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 5641 */ 5642 public static class LayoutParams { 5643 /** 5644 * Special value for the height or width requested by a View. 5645 * FILL_PARENT means that the view wants to be as big as its parent, 5646 * minus the parent's padding, if any. This value is deprecated 5647 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 5648 */ 5649 @SuppressWarnings({"UnusedDeclaration"}) 5650 @Deprecated 5651 public static final int FILL_PARENT = -1; 5652 5653 /** 5654 * Special value for the height or width requested by a View. 5655 * MATCH_PARENT means that the view wants to be as big as its parent, 5656 * minus the parent's padding, if any. Introduced in API Level 8. 5657 */ 5658 public static final int MATCH_PARENT = -1; 5659 5660 /** 5661 * Special value for the height or width requested by a View. 5662 * WRAP_CONTENT means that the view wants to be just large enough to fit 5663 * its own internal content, taking its own padding into account. 5664 */ 5665 public static final int WRAP_CONTENT = -2; 5666 5667 /** 5668 * Information about how wide the view wants to be. Can be one of the 5669 * constants FILL_PARENT (replaced by MATCH_PARENT , 5670 * in API Level 8) or WRAP_CONTENT. or an exact size. 5671 */ 5672 @ViewDebug.ExportedProperty(category = "layout", mapping = { 5673 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 5674 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 5675 }) 5676 public int width; 5677 5678 /** 5679 * Information about how tall the view wants to be. Can be one of the 5680 * constants FILL_PARENT (replaced by MATCH_PARENT , 5681 * in API Level 8) or WRAP_CONTENT. or an exact size. 5682 */ 5683 @ViewDebug.ExportedProperty(category = "layout", mapping = { 5684 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 5685 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 5686 }) 5687 public int height; 5688 5689 /** 5690 * Used to animate layouts. 5691 */ 5692 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 5693 5694 /** 5695 * Creates a new set of layout parameters. The values are extracted from 5696 * the supplied attributes set and context. The XML attributes mapped 5697 * to this set of layout parameters are: 5698 * 5699 * <ul> 5700 * <li><code>layout_width</code>: the width, either an exact value, 5701 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 5702 * {@link #MATCH_PARENT} in API Level 8)</li> 5703 * <li><code>layout_height</code>: the height, either an exact value, 5704 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 5705 * {@link #MATCH_PARENT} in API Level 8)</li> 5706 * </ul> 5707 * 5708 * @param c the application environment 5709 * @param attrs the set of attributes from which to extract the layout 5710 * parameters' values 5711 */ 5712 public LayoutParams(Context c, AttributeSet attrs) { 5713 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 5714 setBaseAttributes(a, 5715 R.styleable.ViewGroup_Layout_layout_width, 5716 R.styleable.ViewGroup_Layout_layout_height); 5717 a.recycle(); 5718 } 5719 5720 /** 5721 * Creates a new set of layout parameters with the specified width 5722 * and height. 5723 * 5724 * @param width the width, either {@link #WRAP_CONTENT}, 5725 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 5726 * API Level 8), or a fixed size in pixels 5727 * @param height the height, either {@link #WRAP_CONTENT}, 5728 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 5729 * API Level 8), or a fixed size in pixels 5730 */ 5731 public LayoutParams(int width, int height) { 5732 this.width = width; 5733 this.height = height; 5734 } 5735 5736 /** 5737 * Copy constructor. Clones the width and height values of the source. 5738 * 5739 * @param source The layout params to copy from. 5740 */ 5741 public LayoutParams(LayoutParams source) { 5742 this.width = source.width; 5743 this.height = source.height; 5744 } 5745 5746 /** 5747 * Used internally by MarginLayoutParams. 5748 * @hide 5749 */ 5750 LayoutParams() { 5751 } 5752 5753 /** 5754 * Extracts the layout parameters from the supplied attributes. 5755 * 5756 * @param a the style attributes to extract the parameters from 5757 * @param widthAttr the identifier of the width attribute 5758 * @param heightAttr the identifier of the height attribute 5759 */ 5760 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 5761 width = a.getLayoutDimension(widthAttr, "layout_width"); 5762 height = a.getLayoutDimension(heightAttr, "layout_height"); 5763 } 5764 5765 /** 5766 * Resolve layout parameters depending on the layout direction. Subclasses that care about 5767 * layoutDirection changes should override this method. The default implementation does 5768 * nothing. 5769 * 5770 * @param layoutDirection the direction of the layout 5771 * 5772 * {@link View#LAYOUT_DIRECTION_LTR} 5773 * {@link View#LAYOUT_DIRECTION_RTL} 5774 */ 5775 public void resolveLayoutDirection(int layoutDirection) { 5776 } 5777 5778 /** 5779 * Returns a String representation of this set of layout parameters. 5780 * 5781 * @param output the String to prepend to the internal representation 5782 * @return a String with the following format: output + 5783 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 5784 * 5785 * @hide 5786 */ 5787 public String debug(String output) { 5788 return output + "ViewGroup.LayoutParams={ width=" 5789 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 5790 } 5791 5792 /** 5793 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters. 5794 * 5795 * @param view the view that contains these layout parameters 5796 * @param canvas the canvas on which to draw 5797 * 5798 * @hide 5799 */ 5800 public void onDebugDraw(View view, Canvas canvas, Paint paint) { 5801 } 5802 5803 /** 5804 * Converts the specified size to a readable String. 5805 * 5806 * @param size the size to convert 5807 * @return a String instance representing the supplied size 5808 * 5809 * @hide 5810 */ 5811 protected static String sizeToString(int size) { 5812 if (size == WRAP_CONTENT) { 5813 return "wrap-content"; 5814 } 5815 if (size == MATCH_PARENT) { 5816 return "match-parent"; 5817 } 5818 return String.valueOf(size); 5819 } 5820 } 5821 5822 /** 5823 * Per-child layout information for layouts that support margins. 5824 * See 5825 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes} 5826 * for a list of all child view attributes that this class supports. 5827 */ 5828 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 5829 /** 5830 * The left margin in pixels of the child. 5831 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5832 * to this field. 5833 */ 5834 @ViewDebug.ExportedProperty(category = "layout") 5835 public int leftMargin; 5836 5837 /** 5838 * The top margin in pixels of the child. 5839 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5840 * to this field. 5841 */ 5842 @ViewDebug.ExportedProperty(category = "layout") 5843 public int topMargin; 5844 5845 /** 5846 * The right margin in pixels of the child. 5847 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5848 * to this field. 5849 */ 5850 @ViewDebug.ExportedProperty(category = "layout") 5851 public int rightMargin; 5852 5853 /** 5854 * The bottom margin in pixels of the child. 5855 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5856 * to this field. 5857 */ 5858 @ViewDebug.ExportedProperty(category = "layout") 5859 public int bottomMargin; 5860 5861 /** 5862 * The start margin in pixels of the child. 5863 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5864 * to this field. 5865 */ 5866 @ViewDebug.ExportedProperty(category = "layout") 5867 private int startMargin = DEFAULT_RELATIVE; 5868 5869 /** 5870 * The end margin in pixels of the child. 5871 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 5872 * to this field. 5873 */ 5874 @ViewDebug.ExportedProperty(category = "layout") 5875 private int endMargin = DEFAULT_RELATIVE; 5876 5877 /** 5878 * The default start and end margin. 5879 * @hide 5880 */ 5881 public static final int DEFAULT_RELATIVE = Integer.MIN_VALUE; 5882 5883 private int initialLeftMargin; 5884 private int initialRightMargin; 5885 5886 private static int LAYOUT_DIRECTION_UNDEFINED = -1; 5887 5888 // Layout direction undefined by default 5889 private int layoutDirection = LAYOUT_DIRECTION_UNDEFINED; 5890 5891 /** 5892 * Creates a new set of layout parameters. The values are extracted from 5893 * the supplied attributes set and context. 5894 * 5895 * @param c the application environment 5896 * @param attrs the set of attributes from which to extract the layout 5897 * parameters' values 5898 */ 5899 public MarginLayoutParams(Context c, AttributeSet attrs) { 5900 super(); 5901 5902 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); 5903 setBaseAttributes(a, 5904 R.styleable.ViewGroup_MarginLayout_layout_width, 5905 R.styleable.ViewGroup_MarginLayout_layout_height); 5906 5907 int margin = a.getDimensionPixelSize( 5908 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); 5909 if (margin >= 0) { 5910 leftMargin = margin; 5911 topMargin = margin; 5912 rightMargin= margin; 5913 bottomMargin = margin; 5914 } else { 5915 leftMargin = a.getDimensionPixelSize( 5916 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0); 5917 topMargin = a.getDimensionPixelSize( 5918 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0); 5919 rightMargin = a.getDimensionPixelSize( 5920 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0); 5921 bottomMargin = a.getDimensionPixelSize( 5922 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0); 5923 startMargin = a.getDimensionPixelSize( 5924 R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE); 5925 endMargin = a.getDimensionPixelSize( 5926 R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE); 5927 } 5928 5929 initialLeftMargin = leftMargin; 5930 initialRightMargin = rightMargin; 5931 5932 a.recycle(); 5933 } 5934 5935 /** 5936 * {@inheritDoc} 5937 */ 5938 public MarginLayoutParams(int width, int height) { 5939 super(width, height); 5940 } 5941 5942 /** 5943 * Copy constructor. Clones the width, height and margin values of the source. 5944 * 5945 * @param source The layout params to copy from. 5946 */ 5947 public MarginLayoutParams(MarginLayoutParams source) { 5948 this.width = source.width; 5949 this.height = source.height; 5950 5951 this.leftMargin = source.leftMargin; 5952 this.topMargin = source.topMargin; 5953 this.rightMargin = source.rightMargin; 5954 this.bottomMargin = source.bottomMargin; 5955 this.startMargin = source.startMargin; 5956 this.endMargin = source.endMargin; 5957 5958 this.initialLeftMargin = source.leftMargin; 5959 this.initialRightMargin = source.rightMargin; 5960 5961 setLayoutDirection(source.layoutDirection); 5962 } 5963 5964 /** 5965 * {@inheritDoc} 5966 */ 5967 public MarginLayoutParams(LayoutParams source) { 5968 super(source); 5969 } 5970 5971 /** 5972 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs 5973 * to be done so that the new margins are taken into account. Left and right margins may be 5974 * overriden by {@link android.view.View#requestLayout()} depending on layout direction. 5975 * 5976 * @param left the left margin size 5977 * @param top the top margin size 5978 * @param right the right margin size 5979 * @param bottom the bottom margin size 5980 * 5981 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft 5982 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 5983 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight 5984 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 5985 */ 5986 public void setMargins(int left, int top, int right, int bottom) { 5987 leftMargin = left; 5988 topMargin = top; 5989 rightMargin = right; 5990 bottomMargin = bottom; 5991 initialLeftMargin = left; 5992 initialRightMargin = right; 5993 } 5994 5995 /** 5996 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()} 5997 * needs to be done so that the new relative margins are taken into account. Left and right 5998 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout 5999 * direction. 6000 * 6001 * @param start the start margin size 6002 * @param top the top margin size 6003 * @param end the right margin size 6004 * @param bottom the bottom margin size 6005 * 6006 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6007 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop 6008 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6009 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom 6010 * 6011 * @hide 6012 */ 6013 public void setMarginsRelative(int start, int top, int end, int bottom) { 6014 startMargin = start; 6015 topMargin = top; 6016 endMargin = end; 6017 bottomMargin = bottom; 6018 initialLeftMargin = 0; 6019 initialRightMargin = 0; 6020 } 6021 6022 /** 6023 * Sets the relative start margin. 6024 * 6025 * @param start the start margin size 6026 * 6027 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6028 */ 6029 public void setMarginStart(int start) { 6030 startMargin = start; 6031 } 6032 6033 /** 6034 * Returns the start margin in pixels. 6035 * 6036 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6037 * 6038 * @return the start margin in pixels. 6039 */ 6040 public int getMarginStart() { 6041 if (startMargin != DEFAULT_RELATIVE) return startMargin; 6042 switch(layoutDirection) { 6043 case View.LAYOUT_DIRECTION_RTL: 6044 return rightMargin; 6045 case View.LAYOUT_DIRECTION_LTR: 6046 default: 6047 return leftMargin; 6048 } 6049 } 6050 6051 /** 6052 * Sets the relative end margin. 6053 * 6054 * @param end the end margin size 6055 * 6056 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6057 */ 6058 public void setMarginEnd(int end) { 6059 endMargin = end; 6060 } 6061 6062 /** 6063 * Returns the end margin in pixels. 6064 * 6065 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6066 * 6067 * @return the end margin in pixels. 6068 */ 6069 public int getMarginEnd() { 6070 if (endMargin != DEFAULT_RELATIVE) return endMargin; 6071 switch(layoutDirection) { 6072 case View.LAYOUT_DIRECTION_RTL: 6073 return leftMargin; 6074 case View.LAYOUT_DIRECTION_LTR: 6075 default: 6076 return rightMargin; 6077 } 6078 } 6079 6080 /** 6081 * Check if margins are relative. 6082 * 6083 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart 6084 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd 6085 * 6086 * @return true if either marginStart or marginEnd has been set. 6087 */ 6088 public boolean isMarginRelative() { 6089 return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE); 6090 } 6091 6092 /** 6093 * Set the layout direction 6094 * @param layoutDirection the layout direction. 6095 * Should be either {@link View#LAYOUT_DIRECTION_LTR} 6096 * or {@link View#LAYOUT_DIRECTION_RTL}. 6097 */ 6098 public void setLayoutDirection(int layoutDirection) { 6099 if (layoutDirection != View.LAYOUT_DIRECTION_LTR && 6100 layoutDirection != View.LAYOUT_DIRECTION_RTL) return; 6101 this.layoutDirection = layoutDirection; 6102 } 6103 6104 /** 6105 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or 6106 * {@link View#LAYOUT_DIRECTION_RTL}. 6107 * 6108 * @return the layout direction. 6109 */ 6110 public int getLayoutDirection() { 6111 return layoutDirection; 6112 } 6113 6114 /** 6115 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins 6116 * may be overridden depending on layout direction. 6117 */ 6118 @Override 6119 public void resolveLayoutDirection(int layoutDirection) { 6120 setLayoutDirection(layoutDirection); 6121 6122 if (!isMarginRelative()) return; 6123 6124 switch(layoutDirection) { 6125 case View.LAYOUT_DIRECTION_RTL: 6126 leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialLeftMargin; 6127 rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialRightMargin; 6128 break; 6129 case View.LAYOUT_DIRECTION_LTR: 6130 default: 6131 leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialLeftMargin; 6132 rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialRightMargin; 6133 break; 6134 } 6135 } 6136 6137 /** 6138 * @hide 6139 */ 6140 public boolean isLayoutRtl() { 6141 return (layoutDirection == View.LAYOUT_DIRECTION_RTL); 6142 } 6143 6144 /** 6145 * @hide 6146 */ 6147 @Override 6148 public void onDebugDraw(View view, Canvas canvas, Paint paint) { 6149 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; 6150 6151 fillDifference(canvas, 6152 view.getLeft() + oi.left, 6153 view.getTop() + oi.top, 6154 view.getRight() - oi.right, 6155 view.getBottom() - oi.bottom, 6156 leftMargin, 6157 topMargin, 6158 rightMargin, 6159 bottomMargin, 6160 paint); 6161 } 6162 } 6163 6164 /* Describes a touched view and the ids of the pointers that it has captured. 6165 * 6166 * This code assumes that pointer ids are always in the range 0..31 such that 6167 * it can use a bitfield to track which pointer ids are present. 6168 * As it happens, the lower layers of the input dispatch pipeline also use the 6169 * same trick so the assumption should be safe here... 6170 */ 6171 private static final class TouchTarget { 6172 private static final int MAX_RECYCLED = 32; 6173 private static final Object sRecycleLock = new Object(); 6174 private static TouchTarget sRecycleBin; 6175 private static int sRecycledCount; 6176 6177 public static final int ALL_POINTER_IDS = -1; // all ones 6178 6179 // The touched child view. 6180 public View child; 6181 6182 // The combined bit mask of pointer ids for all pointers captured by the target. 6183 public int pointerIdBits; 6184 6185 // The next target in the target list. 6186 public TouchTarget next; 6187 6188 private TouchTarget() { 6189 } 6190 6191 public static TouchTarget obtain(View child, int pointerIdBits) { 6192 final TouchTarget target; 6193 synchronized (sRecycleLock) { 6194 if (sRecycleBin == null) { 6195 target = new TouchTarget(); 6196 } else { 6197 target = sRecycleBin; 6198 sRecycleBin = target.next; 6199 sRecycledCount--; 6200 target.next = null; 6201 } 6202 } 6203 target.child = child; 6204 target.pointerIdBits = pointerIdBits; 6205 return target; 6206 } 6207 6208 public void recycle() { 6209 synchronized (sRecycleLock) { 6210 if (sRecycledCount < MAX_RECYCLED) { 6211 next = sRecycleBin; 6212 sRecycleBin = this; 6213 sRecycledCount += 1; 6214 } else { 6215 next = null; 6216 } 6217 child = null; 6218 } 6219 } 6220 } 6221 6222 /* Describes a hovered view. */ 6223 private static final class HoverTarget { 6224 private static final int MAX_RECYCLED = 32; 6225 private static final Object sRecycleLock = new Object(); 6226 private static HoverTarget sRecycleBin; 6227 private static int sRecycledCount; 6228 6229 // The hovered child view. 6230 public View child; 6231 6232 // The next target in the target list. 6233 public HoverTarget next; 6234 6235 private HoverTarget() { 6236 } 6237 6238 public static HoverTarget obtain(View child) { 6239 final HoverTarget target; 6240 synchronized (sRecycleLock) { 6241 if (sRecycleBin == null) { 6242 target = new HoverTarget(); 6243 } else { 6244 target = sRecycleBin; 6245 sRecycleBin = target.next; 6246 sRecycledCount--; 6247 target.next = null; 6248 } 6249 } 6250 target.child = child; 6251 return target; 6252 } 6253 6254 public void recycle() { 6255 synchronized (sRecycleLock) { 6256 if (sRecycledCount < MAX_RECYCLED) { 6257 next = sRecycleBin; 6258 sRecycleBin = this; 6259 sRecycledCount += 1; 6260 } else { 6261 next = null; 6262 } 6263 child = null; 6264 } 6265 } 6266 } 6267 6268 /** 6269 * Pooled class that orderes the children of a ViewGroup from start 6270 * to end based on how they are laid out and the layout direction. 6271 */ 6272 static class ChildListForAccessibility { 6273 6274 private static final int MAX_POOL_SIZE = 32; 6275 6276 private static final SynchronizedPool<ChildListForAccessibility> sPool = 6277 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE); 6278 6279 private final ArrayList<View> mChildren = new ArrayList<View>(); 6280 6281 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>(); 6282 6283 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) { 6284 ChildListForAccessibility list = sPool.acquire(); 6285 if (list == null) { 6286 list = new ChildListForAccessibility(); 6287 } 6288 list.init(parent, sort); 6289 return list; 6290 } 6291 6292 public void recycle() { 6293 clear(); 6294 sPool.release(this); 6295 } 6296 6297 public int getChildCount() { 6298 return mChildren.size(); 6299 } 6300 6301 public View getChildAt(int index) { 6302 return mChildren.get(index); 6303 } 6304 6305 public int getChildIndex(View child) { 6306 return mChildren.indexOf(child); 6307 } 6308 6309 private void init(ViewGroup parent, boolean sort) { 6310 ArrayList<View> children = mChildren; 6311 final int childCount = parent.getChildCount(); 6312 for (int i = 0; i < childCount; i++) { 6313 View child = parent.getChildAt(i); 6314 children.add(child); 6315 } 6316 if (sort) { 6317 ArrayList<ViewLocationHolder> holders = mHolders; 6318 for (int i = 0; i < childCount; i++) { 6319 View child = children.get(i); 6320 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child); 6321 holders.add(holder); 6322 } 6323 Collections.sort(holders); 6324 for (int i = 0; i < childCount; i++) { 6325 ViewLocationHolder holder = holders.get(i); 6326 children.set(i, holder.mView); 6327 holder.recycle(); 6328 } 6329 holders.clear(); 6330 } 6331 } 6332 6333 private void clear() { 6334 mChildren.clear(); 6335 } 6336 } 6337 6338 /** 6339 * Pooled class that holds a View and its location with respect to 6340 * a specified root. This enables sorting of views based on their 6341 * coordinates without recomputing the position relative to the root 6342 * on every comparison. 6343 */ 6344 static class ViewLocationHolder implements Comparable<ViewLocationHolder> { 6345 6346 private static final int MAX_POOL_SIZE = 32; 6347 6348 private static final SynchronizedPool<ViewLocationHolder> sPool = 6349 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE); 6350 6351 private final Rect mLocation = new Rect(); 6352 6353 public View mView; 6354 6355 private int mLayoutDirection; 6356 6357 public static ViewLocationHolder obtain(ViewGroup root, View view) { 6358 ViewLocationHolder holder = sPool.acquire(); 6359 if (holder == null) { 6360 holder = new ViewLocationHolder(); 6361 } 6362 holder.init(root, view); 6363 return holder; 6364 } 6365 6366 public void recycle() { 6367 clear(); 6368 sPool.release(this); 6369 } 6370 6371 @Override 6372 public int compareTo(ViewLocationHolder another) { 6373 // This instance is greater than an invalid argument. 6374 if (another == null) { 6375 return 1; 6376 } 6377 if (getClass() != another.getClass()) { 6378 return 1; 6379 } 6380 // First is above second. 6381 if (mLocation.bottom - another.mLocation.top <= 0) { 6382 return -1; 6383 } 6384 // First is below second. 6385 if (mLocation.top - another.mLocation.bottom >= 0) { 6386 return 1; 6387 } 6388 // LTR 6389 if (mLayoutDirection == LAYOUT_DIRECTION_LTR) { 6390 final int leftDifference = mLocation.left - another.mLocation.left; 6391 // First more to the left than second. 6392 if (leftDifference != 0) { 6393 return leftDifference; 6394 } 6395 } else { // RTL 6396 final int rightDifference = mLocation.right - another.mLocation.right; 6397 // First more to the right than second. 6398 if (rightDifference != 0) { 6399 return -rightDifference; 6400 } 6401 } 6402 // Break tie by top. 6403 final int topDiference = mLocation.top - another.mLocation.top; 6404 if (topDiference != 0) { 6405 return topDiference; 6406 } 6407 // Break tie by height. 6408 final int heightDiference = mLocation.height() - another.mLocation.height(); 6409 if (heightDiference != 0) { 6410 return -heightDiference; 6411 } 6412 // Break tie by width. 6413 final int widthDiference = mLocation.width() - another.mLocation.width(); 6414 if (widthDiference != 0) { 6415 return -widthDiference; 6416 } 6417 // Just break the tie somehow. The accessibliity ids are unique 6418 // and stable, hence this is deterministic tie breaking. 6419 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId(); 6420 } 6421 6422 private void init(ViewGroup root, View view) { 6423 Rect viewLocation = mLocation; 6424 view.getDrawingRect(viewLocation); 6425 root.offsetDescendantRectToMyCoords(view, viewLocation); 6426 mView = view; 6427 mLayoutDirection = root.getLayoutDirection(); 6428 } 6429 6430 private void clear() { 6431 mView = null; 6432 mLocation.set(0, 0, 0, 0); 6433 } 6434 } 6435 6436 private static Paint getDebugPaint() { 6437 if (sDebugPaint == null) { 6438 sDebugPaint = new Paint(); 6439 sDebugPaint.setAntiAlias(false); 6440 } 6441 return sDebugPaint; 6442 } 6443 6444 private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { 6445 if (sDebugLines== null) { 6446 sDebugLines = new float[16]; 6447 } 6448 6449 sDebugLines[0] = x1; 6450 sDebugLines[1] = y1; 6451 sDebugLines[2] = x2; 6452 sDebugLines[3] = y1; 6453 6454 sDebugLines[4] = x2; 6455 sDebugLines[5] = y1; 6456 sDebugLines[6] = x2; 6457 sDebugLines[7] = y2; 6458 6459 sDebugLines[8] = x2; 6460 sDebugLines[9] = y2; 6461 sDebugLines[10] = x1; 6462 sDebugLines[11] = y2; 6463 6464 sDebugLines[12] = x1; 6465 sDebugLines[13] = y2; 6466 sDebugLines[14] = x1; 6467 sDebugLines[15] = y1; 6468 6469 canvas.drawLines(sDebugLines, paint); 6470 } 6471} 6472