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