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