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