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