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