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