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