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