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