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