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