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