1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.core.view; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21 import android.animation.ValueAnimator; 22 import android.annotation.SuppressLint; 23 import android.annotation.TargetApi; 24 import android.content.ClipData; 25 import android.content.Context; 26 import android.content.res.ColorStateList; 27 import android.graphics.Matrix; 28 import android.graphics.Paint; 29 import android.graphics.PorterDuff; 30 import android.graphics.Rect; 31 import android.graphics.drawable.Drawable; 32 import android.os.Build; 33 import android.os.Bundle; 34 import android.util.Log; 35 import android.view.Display; 36 import android.view.MotionEvent; 37 import android.view.PointerIcon; 38 import android.view.VelocityTracker; 39 import android.view.View; 40 import android.view.ViewConfiguration; 41 import android.view.ViewGroup; 42 import android.view.ViewParent; 43 import android.view.WindowInsets; 44 import android.view.WindowManager; 45 import android.view.accessibility.AccessibilityEvent; 46 import android.view.accessibility.AccessibilityNodeProvider; 47 48 import androidx.annotation.FloatRange; 49 import androidx.annotation.IdRes; 50 import androidx.annotation.IntDef; 51 import androidx.annotation.NonNull; 52 import androidx.annotation.Nullable; 53 import androidx.annotation.Px; 54 import androidx.annotation.RequiresApi; 55 import androidx.annotation.RestrictTo; 56 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 57 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat; 58 59 import java.lang.annotation.Retention; 60 import java.lang.annotation.RetentionPolicy; 61 import java.lang.reflect.Field; 62 import java.lang.reflect.InvocationTargetException; 63 import java.lang.reflect.Method; 64 import java.util.Collection; 65 import java.util.WeakHashMap; 66 import java.util.concurrent.atomic.AtomicInteger; 67 68 /** 69 * Helper for accessing features in {@link View}. 70 */ 71 public class ViewCompat { 72 private static final String TAG = "ViewCompat"; 73 74 /** @hide */ 75 @RestrictTo(LIBRARY_GROUP) 76 @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN, 77 View.FOCUS_FORWARD, View.FOCUS_BACKWARD}) 78 @Retention(RetentionPolicy.SOURCE) 79 public @interface FocusDirection {} 80 81 /** @hide */ 82 @RestrictTo(LIBRARY_GROUP) 83 @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN}) 84 @Retention(RetentionPolicy.SOURCE) 85 public @interface FocusRealDirection {} 86 87 /** @hide */ 88 @RestrictTo(LIBRARY_GROUP) 89 @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD}) 90 @Retention(RetentionPolicy.SOURCE) 91 public @interface FocusRelativeDirection {} 92 93 @IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER}) 94 @Retention(RetentionPolicy.SOURCE) 95 private @interface OverScroll {} 96 97 /** 98 * Always allow a user to over-scroll this view, provided it is a 99 * view that can scroll. 100 * @deprecated Use {@link View#OVER_SCROLL_ALWAYS} directly. This constant will be removed in 101 * a future release. 102 */ 103 @Deprecated 104 public static final int OVER_SCROLL_ALWAYS = 0; 105 106 /** 107 * Allow a user to over-scroll this view only if the content is large 108 * enough to meaningfully scroll, provided it is a view that can scroll. 109 * @deprecated Use {@link View#OVER_SCROLL_IF_CONTENT_SCROLLS} directly. This constant will be 110 * removed in a future release. 111 */ 112 @Deprecated 113 public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; 114 115 /** 116 * Never allow a user to over-scroll this view. 117 * @deprecated Use {@link View#OVER_SCROLL_NEVER} directly. This constant will be removed in 118 * a future release. 119 */ 120 @Deprecated 121 public static final int OVER_SCROLL_NEVER = 2; 122 123 @TargetApi(Build.VERSION_CODES.O) 124 @IntDef({ 125 View.IMPORTANT_FOR_AUTOFILL_AUTO, 126 View.IMPORTANT_FOR_AUTOFILL_YES, 127 View.IMPORTANT_FOR_AUTOFILL_NO, 128 View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, 129 View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS 130 }) 131 @Retention(RetentionPolicy.SOURCE) 132 private @interface AutofillImportance {} 133 134 @IntDef({ 135 IMPORTANT_FOR_ACCESSIBILITY_AUTO, 136 IMPORTANT_FOR_ACCESSIBILITY_YES, 137 IMPORTANT_FOR_ACCESSIBILITY_NO, 138 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 139 }) 140 @Retention(RetentionPolicy.SOURCE) 141 private @interface ImportantForAccessibility {} 142 143 /** 144 * Automatically determine whether a view is important for accessibility. 145 */ 146 public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000; 147 148 /** 149 * The view is important for accessibility. 150 */ 151 public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001; 152 153 /** 154 * The view is not important for accessibility. 155 */ 156 public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; 157 158 /** 159 * The view is not important for accessibility, nor are any of its 160 * descendant views. 161 */ 162 public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004; 163 164 @IntDef({ 165 ACCESSIBILITY_LIVE_REGION_NONE, 166 ACCESSIBILITY_LIVE_REGION_POLITE, 167 ACCESSIBILITY_LIVE_REGION_ASSERTIVE 168 }) 169 @Retention(RetentionPolicy.SOURCE) 170 private @interface AccessibilityLiveRegion {} 171 172 /** 173 * Live region mode specifying that accessibility services should not 174 * automatically announce changes to this view. This is the default live 175 * region mode for most views. 176 * <p> 177 * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. 178 */ 179 public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000; 180 181 /** 182 * Live region mode specifying that accessibility services should announce 183 * changes to this view. 184 * <p> 185 * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. 186 */ 187 public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001; 188 189 /** 190 * Live region mode specifying that accessibility services should interrupt 191 * ongoing speech to immediately announce changes to this view. 192 * <p> 193 * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. 194 */ 195 public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002; 196 197 @IntDef({View.LAYER_TYPE_NONE, View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE}) 198 @Retention(RetentionPolicy.SOURCE) 199 private @interface LayerType {} 200 201 /** 202 * Indicates that the view does not have a layer. 203 * 204 * @deprecated Use {@link View#LAYER_TYPE_NONE} directly. 205 */ 206 @Deprecated 207 public static final int LAYER_TYPE_NONE = 0; 208 209 /** 210 * <p>Indicates that the view has a software layer. A software layer is backed 211 * by a bitmap and causes the view to be rendered using Android's software 212 * rendering pipeline, even if hardware acceleration is enabled.</p> 213 * 214 * <p>Software layers have various usages:</p> 215 * <p>When the application is not using hardware acceleration, a software layer 216 * is useful to apply a specific color filter and/or blending mode and/or 217 * translucency to a view and all its children.</p> 218 * <p>When the application is using hardware acceleration, a software layer 219 * is useful to render drawing primitives not supported by the hardware 220 * accelerated pipeline. It can also be used to cache a complex view tree 221 * into a texture and reduce the complexity of drawing operations. For instance, 222 * when animating a complex view tree with a translation, a software layer can 223 * be used to render the view tree only once.</p> 224 * <p>Software layers should be avoided when the affected view tree updates 225 * often. Every update will require to re-render the software layer, which can 226 * potentially be slow (particularly when hardware acceleration is turned on 227 * since the layer will have to be uploaded into a hardware texture after every 228 * update.)</p> 229 * 230 * @deprecated Use {@link View#LAYER_TYPE_SOFTWARE} directly. 231 */ 232 @Deprecated 233 public static final int LAYER_TYPE_SOFTWARE = 1; 234 235 /** 236 * <p>Indicates that the view has a hardware layer. A hardware layer is backed 237 * by a hardware specific texture (generally Frame Buffer Objects or FBO on 238 * OpenGL hardware) and causes the view to be rendered using Android's hardware 239 * rendering pipeline, but only if hardware acceleration is turned on for the 240 * view hierarchy. When hardware acceleration is turned off, hardware layers 241 * behave exactly as {@link View#LAYER_TYPE_SOFTWARE software layers}.</p> 242 * 243 * <p>A hardware layer is useful to apply a specific color filter and/or 244 * blending mode and/or translucency to a view and all its children.</p> 245 * <p>A hardware layer can be used to cache a complex view tree into a 246 * texture and reduce the complexity of drawing operations. For instance, 247 * when animating a complex view tree with a translation, a hardware layer can 248 * be used to render the view tree only once.</p> 249 * <p>A hardware layer can also be used to increase the rendering quality when 250 * rotation transformations are applied on a view. It can also be used to 251 * prevent potential clipping issues when applying 3D transforms on a view.</p> 252 * 253 * @deprecated Use {@link View#LAYER_TYPE_HARDWARE} directly. 254 */ 255 @Deprecated 256 public static final int LAYER_TYPE_HARDWARE = 2; 257 258 @IntDef({ 259 LAYOUT_DIRECTION_LTR, 260 LAYOUT_DIRECTION_RTL, 261 LAYOUT_DIRECTION_INHERIT, 262 LAYOUT_DIRECTION_LOCALE}) 263 @Retention(RetentionPolicy.SOURCE) 264 private @interface LayoutDirectionMode {} 265 266 @IntDef({ 267 LAYOUT_DIRECTION_LTR, 268 LAYOUT_DIRECTION_RTL 269 }) 270 @Retention(RetentionPolicy.SOURCE) 271 private @interface ResolvedLayoutDirectionMode {} 272 273 /** 274 * Horizontal layout direction of this view is from Left to Right. 275 */ 276 public static final int LAYOUT_DIRECTION_LTR = 0; 277 278 /** 279 * Horizontal layout direction of this view is from Right to Left. 280 */ 281 public static final int LAYOUT_DIRECTION_RTL = 1; 282 283 /** 284 * Horizontal layout direction of this view is inherited from its parent. 285 * Use with {@link #setLayoutDirection}. 286 */ 287 public static final int LAYOUT_DIRECTION_INHERIT = 2; 288 289 /** 290 * Horizontal layout direction of this view is from deduced from the default language 291 * script for the locale. Use with {@link #setLayoutDirection}. 292 */ 293 public static final int LAYOUT_DIRECTION_LOCALE = 3; 294 295 /** 296 * Bits of {@link #getMeasuredWidthAndState} and 297 * {@link #getMeasuredWidthAndState} that provide the actual measured size. 298 * 299 * @deprecated Use {@link View#MEASURED_SIZE_MASK} directly. 300 */ 301 @Deprecated 302 public static final int MEASURED_SIZE_MASK = 0x00ffffff; 303 304 /** 305 * Bits of {@link #getMeasuredWidthAndState} and 306 * {@link #getMeasuredWidthAndState} that provide the additional state bits. 307 * 308 * @deprecated Use {@link View#MEASURED_STATE_MASK} directly. 309 */ 310 @Deprecated 311 public static final int MEASURED_STATE_MASK = 0xff000000; 312 313 /** 314 * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits 315 * for functions that combine both width and height into a single int, 316 * such as {@link #getMeasuredState} and the childState argument of 317 * {@link #resolveSizeAndState(int, int, int)}. 318 * 319 * @deprecated Use {@link View#MEASURED_HEIGHT_STATE_SHIFT} directly. 320 */ 321 @Deprecated 322 public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; 323 324 /** 325 * Bit of {@link #getMeasuredWidthAndState} and 326 * {@link #getMeasuredWidthAndState} that indicates the measured size 327 * is smaller that the space the view would like to have. 328 * 329 * @deprecated Use {@link View#MEASURED_STATE_TOO_SMALL} directly. 330 */ 331 @Deprecated 332 public static final int MEASURED_STATE_TOO_SMALL = 0x01000000; 333 334 /** 335 * @hide 336 */ 337 @IntDef(value = {SCROLL_AXIS_NONE, SCROLL_AXIS_HORIZONTAL, SCROLL_AXIS_VERTICAL}, flag = true) 338 @Retention(RetentionPolicy.SOURCE) 339 @RestrictTo(LIBRARY_GROUP) 340 public @interface ScrollAxis {} 341 342 /** 343 * Indicates no axis of view scrolling. 344 */ 345 public static final int SCROLL_AXIS_NONE = 0; 346 347 /** 348 * Indicates scrolling along the horizontal axis. 349 */ 350 public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0; 351 352 /** 353 * Indicates scrolling along the vertical axis. 354 */ 355 public static final int SCROLL_AXIS_VERTICAL = 1 << 1; 356 357 /** 358 * @hide 359 */ 360 @IntDef({TYPE_TOUCH, TYPE_NON_TOUCH}) 361 @Retention(RetentionPolicy.SOURCE) 362 @RestrictTo(LIBRARY_GROUP) 363 public @interface NestedScrollType {} 364 365 /** 366 * Indicates that the input type for the gesture is from a user touching the screen. 367 */ 368 public static final int TYPE_TOUCH = 0; 369 370 /** 371 * Indicates that the input type for the gesture is caused by something which is not a user 372 * touching a screen. This is usually from a fling which is settling. 373 */ 374 public static final int TYPE_NON_TOUCH = 1; 375 376 /** @hide */ 377 @RestrictTo(LIBRARY_GROUP) 378 @Retention(RetentionPolicy.SOURCE) 379 @IntDef(flag = true, 380 value = { 381 SCROLL_INDICATOR_TOP, 382 SCROLL_INDICATOR_BOTTOM, 383 SCROLL_INDICATOR_LEFT, 384 SCROLL_INDICATOR_RIGHT, 385 SCROLL_INDICATOR_START, 386 SCROLL_INDICATOR_END, 387 }) 388 public @interface ScrollIndicators {} 389 390 /** 391 * Scroll indicator direction for the top edge of the view. 392 * 393 * @see #setScrollIndicators(View, int) 394 * @see #setScrollIndicators(View, int, int) 395 * @see #getScrollIndicators(View) 396 */ 397 public static final int SCROLL_INDICATOR_TOP = 0x1; 398 399 /** 400 * Scroll indicator direction for the bottom edge of the view. 401 * 402 * @see #setScrollIndicators(View, int) 403 * @see #setScrollIndicators(View, int, int) 404 * @see #getScrollIndicators(View) 405 */ 406 public static final int SCROLL_INDICATOR_BOTTOM = 0x2; 407 408 /** 409 * Scroll indicator direction for the left edge of the view. 410 * 411 * @see #setScrollIndicators(View, int) 412 * @see #setScrollIndicators(View, int, int) 413 * @see #getScrollIndicators(View) 414 */ 415 public static final int SCROLL_INDICATOR_LEFT = 0x4; 416 417 /** 418 * Scroll indicator direction for the right edge of the view. 419 * 420 * @see #setScrollIndicators(View, int) 421 * @see #setScrollIndicators(View, int, int) 422 * @see #getScrollIndicators(View) 423 */ 424 public static final int SCROLL_INDICATOR_RIGHT = 0x8; 425 426 /** 427 * Scroll indicator direction for the starting edge of the view. 428 * 429 * @see #setScrollIndicators(View, int) 430 * @see #setScrollIndicators(View, int, int) 431 * @see #getScrollIndicators(View) 432 */ 433 public static final int SCROLL_INDICATOR_START = 0x10; 434 435 /** 436 * Scroll indicator direction for the ending edge of the view. 437 * 438 * @see #setScrollIndicators(View, int) 439 * @see #setScrollIndicators(View, int, int) 440 * @see #getScrollIndicators(View) 441 */ 442 public static final int SCROLL_INDICATOR_END = 0x20; 443 444 private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); 445 446 private static Field sMinWidthField; 447 private static boolean sMinWidthFieldFetched; 448 private static Field sMinHeightField; 449 private static boolean sMinHeightFieldFetched; 450 451 private static Method sDispatchStartTemporaryDetach; 452 private static Method sDispatchFinishTemporaryDetach; 453 private static boolean sTempDetachBound; 454 455 private static WeakHashMap<View, String> sTransitionNameMap; 456 private static WeakHashMap<View, ViewPropertyAnimatorCompat> sViewPropertyAnimatorMap = null; 457 458 private static Method sChildrenDrawingOrderMethod; 459 private static Field sAccessibilityDelegateField; 460 private static boolean sAccessibilityDelegateCheckFailed = false; 461 462 private static ThreadLocal<Rect> sThreadLocalRect; 463 464 private static Rect getEmptyTempRect() { 465 if (sThreadLocalRect == null) { 466 sThreadLocalRect = new ThreadLocal<>(); 467 } 468 Rect rect = sThreadLocalRect.get(); 469 if (rect == null) { 470 rect = new Rect(); 471 sThreadLocalRect.set(rect); 472 } 473 rect.setEmpty(); 474 return rect; 475 } 476 477 /** 478 * Check if this view can be scrolled horizontally in a certain direction. 479 * 480 * @param view The View against which to invoke the method. 481 * @param direction Negative to check scrolling left, positive to check scrolling right. 482 * @return true if this view can be scrolled in the specified direction, false otherwise. 483 * 484 * @deprecated Use {@link View#canScrollHorizontally(int)} directly. 485 */ 486 @Deprecated 487 public static boolean canScrollHorizontally(View view, int direction) { 488 return view.canScrollHorizontally(direction); 489 } 490 491 /** 492 * Check if this view can be scrolled vertically in a certain direction. 493 * 494 * @param view The View against which to invoke the method. 495 * @param direction Negative to check scrolling up, positive to check scrolling down. 496 * @return true if this view can be scrolled in the specified direction, false otherwise. 497 * 498 * @deprecated Use {@link View#canScrollVertically(int)} directly. 499 */ 500 @Deprecated 501 public static boolean canScrollVertically(View view, int direction) { 502 return view.canScrollVertically(direction); 503 } 504 505 /** 506 * Returns the over-scroll mode for this view. The result will be 507 * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 508 * (allow over-scrolling only if the view content is larger than the container), 509 * or {@link #OVER_SCROLL_NEVER}. 510 * 511 * @param v The View against which to invoke the method. 512 * @return This view's over-scroll mode. 513 * @deprecated Call {@link View#getOverScrollMode()} directly. This method will be 514 * removed in a future release. 515 */ 516 @Deprecated 517 @OverScroll 518 public static int getOverScrollMode(View v) { 519 //noinspection ResourceType 520 return v.getOverScrollMode(); 521 } 522 523 /** 524 * Set the over-scroll mode for this view. Valid over-scroll modes are 525 * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 526 * (allow over-scrolling only if the view content is larger than the container), 527 * or {@link #OVER_SCROLL_NEVER}. 528 * 529 * Setting the over-scroll mode of a view will have an effect only if the 530 * view is capable of scrolling. 531 * 532 * @param v The View against which to invoke the method. 533 * @param overScrollMode The new over-scroll mode for this view. 534 * @deprecated Call {@link View#setOverScrollMode(int)} directly. This method will be 535 * removed in a future release. 536 */ 537 @Deprecated 538 public static void setOverScrollMode(View v, @OverScroll int overScrollMode) { 539 v.setOverScrollMode(overScrollMode); 540 } 541 542 /** 543 * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} 544 * giving a chance to this View to populate the accessibility event with its 545 * text content. While this method is free to modify event 546 * attributes other than text content, doing so should normally be performed in 547 * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}. 548 * <p> 549 * Example: Adding formatted date string to an accessibility event in addition 550 * to the text added by the super implementation: 551 * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 552 * super.onPopulateAccessibilityEvent(event); 553 * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; 554 * String selectedDateUtterance = DateUtils.formatDateTime(mContext, 555 * mCurrentDate.getTimeInMillis(), flags); 556 * event.getText().add(selectedDateUtterance); 557 * }</pre> 558 * <p> 559 * If an {@link AccessibilityDelegateCompat} has been specified via calling 560 * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its 561 * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)} 562 * is responsible for handling this call. 563 * </p> 564 * <p class="note"><strong>Note:</strong> Always call the super implementation before adding 565 * information to the event, in case the default implementation has basic information to add. 566 * </p> 567 * 568 * @param v The View against which to invoke the method. 569 * @param event The accessibility event which to populate. 570 * 571 * @see View#sendAccessibilityEvent(int) 572 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 573 * 574 * @deprecated Call {@link View#onPopulateAccessibilityEvent(AccessibilityEvent)} directly. 575 * This method will be removed in a future release. 576 */ 577 @Deprecated 578 public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 579 v.onPopulateAccessibilityEvent(event); 580 } 581 582 /** 583 * Initializes an {@link AccessibilityEvent} with information about 584 * this View which is the event source. In other words, the source of 585 * an accessibility event is the view whose state change triggered firing 586 * the event. 587 * <p> 588 * Example: Setting the password property of an event in addition 589 * to properties set by the super implementation: 590 * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 591 * super.onInitializeAccessibilityEvent(event); 592 * event.setPassword(true); 593 * }</pre> 594 * <p> 595 * If an {@link AccessibilityDelegateCompat} has been specified via calling 596 * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its 597 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)} 598 * is responsible for handling this call. 599 * 600 * @param v The View against which to invoke the method. 601 * @param event The event to initialize. 602 * 603 * @see View#sendAccessibilityEvent(int) 604 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 605 * 606 * @deprecated Call {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)} directly. 607 * This method will be removed in a future release. 608 */ 609 @Deprecated 610 public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 611 v.onInitializeAccessibilityEvent(event); 612 } 613 614 /** 615 * Initializes an {@link AccessibilityNodeInfoCompat} with information 616 * about this view. The base implementation sets: 617 * <ul> 618 * <li>{@link AccessibilityNodeInfoCompat#setParent(View)},</li> 619 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)},</li> 620 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)},</li> 621 * <li>{@link AccessibilityNodeInfoCompat#setPackageName(CharSequence)},</li> 622 * <li>{@link AccessibilityNodeInfoCompat#setClassName(CharSequence)},</li> 623 * <li>{@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)},</li> 624 * <li>{@link AccessibilityNodeInfoCompat#setEnabled(boolean)},</li> 625 * <li>{@link AccessibilityNodeInfoCompat#setClickable(boolean)},</li> 626 * <li>{@link AccessibilityNodeInfoCompat#setFocusable(boolean)},</li> 627 * <li>{@link AccessibilityNodeInfoCompat#setFocused(boolean)},</li> 628 * <li>{@link AccessibilityNodeInfoCompat#setLongClickable(boolean)},</li> 629 * <li>{@link AccessibilityNodeInfoCompat#setSelected(boolean)},</li> 630 * </ul> 631 * <p> 632 * If an {@link AccessibilityDelegateCompat} has been specified via calling 633 * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its 634 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} 635 * method is responsible for handling this call. 636 * 637 * @param v The View against which to invoke the method. 638 * @param info The instance to initialize. 639 */ 640 public static void onInitializeAccessibilityNodeInfo(@NonNull View v, 641 AccessibilityNodeInfoCompat info) { 642 v.onInitializeAccessibilityNodeInfo(info.unwrap()); 643 } 644 645 /** 646 * Sets a delegate for implementing accessibility support via composition 647 * (as opposed to inheritance). For more details, see 648 * {@link AccessibilityDelegateCompat}. 649 * <p> 650 * <strong>Note:</strong> On platform versions prior to 651 * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on 652 * views in the {@code android.widget.*} package are called <i>before</i> 653 * host methods. This prevents certain properties such as class name from 654 * being modified by overriding 655 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}, 656 * as any changes will be overwritten by the host class. 657 * <p> 658 * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate 659 * methods are called <i>after</i> host methods, which all properties to be 660 * modified without being overwritten by the host class. 661 * 662 * @param delegate the object to which accessibility method calls should be 663 * delegated 664 * @see AccessibilityDelegateCompat 665 */ 666 public static void setAccessibilityDelegate(@NonNull View v, 667 AccessibilityDelegateCompat delegate) { 668 v.setAccessibilityDelegate(delegate == null ? null : delegate.getBridge()); 669 } 670 671 /** 672 * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how 673 * to autofill the view with the user's data. 674 * 675 * <p>Typically, there is only one way to autofill a view, but there could be more than one. 676 * For example, if the application accepts either an username or email address to identify 677 * an user. 678 * 679 * <p>These hints are not validated by the Android System, but passed "as is" to the service. 680 * Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_} 681 * constants such as: 682 * {@link View#AUTOFILL_HINT_USERNAME}, {@link View#AUTOFILL_HINT_PASSWORD}, 683 * {@link View#AUTOFILL_HINT_EMAIL_ADDRESS}, 684 * {@link View#AUTOFILL_HINT_NAME}, 685 * {@link View#AUTOFILL_HINT_PHONE}, 686 * {@link View#AUTOFILL_HINT_POSTAL_ADDRESS}, {@link View#AUTOFILL_HINT_POSTAL_CODE}, 687 * {@link View#AUTOFILL_HINT_CREDIT_CARD_NUMBER}, 688 * {@link View#AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}, 689 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}, 690 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}, 691 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or 692 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}. 693 * 694 * <p>This method is only supported on API >= 26. 695 * On API 25 and below, it is a no-op</p> 696 * 697 * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set. 698 * @attr ref android.R.styleable#View_autofillHints 699 */ 700 public static void setAutofillHints(@NonNull View v, @Nullable String... autofillHints) { 701 if (Build.VERSION.SDK_INT >= 26) { 702 v.setAutofillHints(autofillHints); 703 } 704 } 705 706 /** 707 * Gets the mode for determining whether this view is important for autofill. 708 * 709 * <p>See {@link #setImportantForAutofill(View, int)} and {@link #isImportantForAutofill(View)} 710 * for more info about this mode. 711 * 712 * <p>This method is only supported on API >= 26. 713 * On API 25 and below, it will always return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}.</p> 714 * 715 * @return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to 716 * {@link #setImportantForAutofill(View, int)}. 717 * 718 * @attr ref android.R.styleable#View_importantForAutofill 719 */ 720 @SuppressLint("InlinedApi") 721 public static @AutofillImportance int getImportantForAutofill(@NonNull View v) { 722 if (Build.VERSION.SDK_INT >= 26) { 723 return v.getImportantForAutofill(); 724 } 725 return View.IMPORTANT_FOR_AUTOFILL_AUTO; 726 } 727 728 /** 729 * Sets the mode for determining whether this view is considered important for autofill. 730 * 731 * <p>The platform determines the importance for autofill automatically but you 732 * can use this method to customize the behavior. For example: 733 * 734 * <ol> 735 * <li>When the view contents is irrelevant for autofill (for example, a text field used in a 736 * "Captcha" challenge), it should be {@link View#IMPORTANT_FOR_AUTOFILL_NO}. 737 * <li>When both the view and its children are irrelevant for autofill (for example, the root 738 * view of an activity containing a spreadhseet editor), it should be 739 * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}. 740 * <li>When the view content is relevant for autofill but its children aren't (for example, 741 * a credit card expiration date represented by a custom view that overrides the proper 742 * autofill methods and has 2 children representing the month and year), it should 743 * be {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}. 744 * </ol> 745 * 746 * <p><b>NOTE:</strong> setting the mode as does {@link View#IMPORTANT_FOR_AUTOFILL_NO} or 747 * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and 748 * its children) will be always be considered not important; for example, when the user 749 * explicitly makes an autofill request, all views are considered important. See 750 * {@link #isImportantForAutofill(View)} for more details about how the View's importance for 751 * autofill is used. 752 * 753 * <p>This method is only supported on API >= 26. 754 * On API 25 and below, it is a no-op</p> 755 * 756 * 757 * @param mode {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}, 758 * {@link View#IMPORTANT_FOR_AUTOFILL_YES}, 759 * {@link View#IMPORTANT_FOR_AUTOFILL_NO}, 760 * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, 761 * or {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}. 762 * 763 * @attr ref android.R.styleable#View_importantForAutofill 764 */ 765 public static void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) { 766 if (Build.VERSION.SDK_INT >= 26) { 767 v.setImportantForAutofill(mode); 768 } 769 } 770 771 /** 772 * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode} 773 * associated with this view is considered important for autofill purposes. 774 * 775 * <p>Generally speaking, a view is important for autofill if: 776 * <ol> 777 * <li>The view can be autofilled by an {@link android.service.autofill.AutofillService}. 778 * <li>The view contents can help an {@link android.service.autofill.AutofillService} 779 * determine how other views can be autofilled. 780 * <ol> 781 * 782 * <p>For example, view containers should typically return {@code false} for performance reasons 783 * (since the important info is provided by their children), but if its properties have relevant 784 * information (for example, a resource id called {@code credentials}, it should return 785 * {@code true}. On the other hand, views representing labels or editable fields should 786 * typically return {@code true}, but in some cases they could return {@code false} 787 * (for example, if they're part of a "Captcha" mechanism). 788 * 789 * <p>The value returned by this method depends on the value returned by 790 * {@link #getImportantForAutofill(View)}: 791 * 792 * <ol> 793 * <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_YES} or 794 * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, 795 * then it returns {@code true} 796 * <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_NO} or 797 * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}, 798 * then it returns {@code false} 799 * <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}, 800 * then it uses some simple heuristics that can return {@code true} 801 * in some cases (like a container with a resource id), but {@code false} in most. 802 * <li>otherwise, it returns {@code false}. 803 * </ol> 804 * 805 * <p>When a view is considered important for autofill: 806 * <ul> 807 * <li>The view might automatically trigger an autofill request when focused on. 808 * <li>The contents of the view are included in the {@link android.view.ViewStructure} 809 * used in an autofill request. 810 * </ul> 811 * 812 * <p>On the other hand, when a view is considered not important for autofill: 813 * <ul> 814 * <li>The view never automatically triggers autofill requests, but it can trigger a manual 815 * request through {@link android.view.autofill.AutofillManager#requestAutofill(View)}. 816 * <li>The contents of the view are not included in the {@link android.view.ViewStructure} 817 * used in an autofill request, unless the request has the 818 * {@link View#AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} flag. 819 * </ul> 820 * 821 * <p>This method is only supported on API >= 26. 822 * On API 25 and below, it will always return {@code true}.</p> 823 * 824 * @return whether the view is considered important for autofill. 825 * 826 * @see #setImportantForAutofill(View, int) 827 * @see View#IMPORTANT_FOR_AUTOFILL_AUTO 828 * @see View#IMPORTANT_FOR_AUTOFILL_YES 829 * @see View#IMPORTANT_FOR_AUTOFILL_NO 830 * @see View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS 831 * @see View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS 832 * @see android.view.autofill.AutofillManager#requestAutofill(View) 833 */ 834 public static boolean isImportantForAutofill(@NonNull View v) { 835 if (Build.VERSION.SDK_INT >= 26) { 836 return v.isImportantForAutofill(); 837 } 838 return true; 839 } 840 841 /** 842 * Checks whether provided View has an accessibility delegate attached to it. 843 * 844 * @param v The View instance to check 845 * @return True if the View has an accessibility delegate 846 */ 847 public static boolean hasAccessibilityDelegate(@NonNull View v) { 848 if (sAccessibilityDelegateCheckFailed) { 849 return false; // View implementation might have changed. 850 } 851 if (sAccessibilityDelegateField == null) { 852 try { 853 sAccessibilityDelegateField = View.class 854 .getDeclaredField("mAccessibilityDelegate"); 855 sAccessibilityDelegateField.setAccessible(true); 856 } catch (Throwable t) { 857 sAccessibilityDelegateCheckFailed = true; 858 return false; 859 } 860 } 861 try { 862 return sAccessibilityDelegateField.get(v) != null; 863 } catch (Throwable t) { 864 sAccessibilityDelegateCheckFailed = true; 865 return false; 866 } 867 } 868 869 /** 870 * Indicates whether the view is currently tracking transient state that the 871 * app should not need to concern itself with saving and restoring, but that 872 * the framework should take special note to preserve when possible. 873 * 874 * @param view View to check for transient state 875 * @return true if the view has transient state 876 */ 877 public static boolean hasTransientState(@NonNull View view) { 878 if (Build.VERSION.SDK_INT >= 16) { 879 return view.hasTransientState(); 880 } 881 return false; 882 } 883 884 /** 885 * Set whether this view is currently tracking transient state that the 886 * framework should attempt to preserve when possible. 887 * 888 * @param view View tracking transient state 889 * @param hasTransientState true if this view has transient state 890 */ 891 public static void setHasTransientState(@NonNull View view, boolean hasTransientState) { 892 if (Build.VERSION.SDK_INT >= 16) { 893 view.setHasTransientState(hasTransientState); 894 } 895 } 896 897 /** 898 * <p>Cause an invalidate to happen on the next animation time step, typically the 899 * next display frame.</p> 900 * 901 * <p>This method can be invoked from outside of the UI thread 902 * only when this View is attached to a window.</p> 903 * 904 * @param view View to invalidate 905 */ 906 public static void postInvalidateOnAnimation(@NonNull View view) { 907 if (Build.VERSION.SDK_INT >= 16) { 908 view.postInvalidateOnAnimation(); 909 } else { 910 view.postInvalidate(); 911 } 912 } 913 914 /** 915 * <p>Cause an invalidate of the specified area to happen on the next animation 916 * time step, typically the next display frame.</p> 917 * 918 * <p>This method can be invoked from outside of the UI thread 919 * only when this View is attached to a window.</p> 920 * 921 * @param view View to invalidate 922 * @param left The left coordinate of the rectangle to invalidate. 923 * @param top The top coordinate of the rectangle to invalidate. 924 * @param right The right coordinate of the rectangle to invalidate. 925 * @param bottom The bottom coordinate of the rectangle to invalidate. 926 */ 927 public static void postInvalidateOnAnimation(@NonNull View view, int left, int top, 928 int right, int bottom) { 929 if (Build.VERSION.SDK_INT >= 16) { 930 view.postInvalidateOnAnimation(left, top, right, bottom); 931 } else { 932 view.postInvalidate(left, top, right, bottom); 933 } 934 } 935 936 /** 937 * <p>Causes the Runnable to execute on the next animation time step. 938 * The runnable will be run on the user interface thread.</p> 939 * 940 * <p>This method can be invoked from outside of the UI thread 941 * only when this View is attached to a window.</p> 942 * 943 * @param view View to post this Runnable to 944 * @param action The Runnable that will be executed. 945 */ 946 public static void postOnAnimation(@NonNull View view, Runnable action) { 947 if (Build.VERSION.SDK_INT >= 16) { 948 view.postOnAnimation(action); 949 } else { 950 view.postDelayed(action, ValueAnimator.getFrameDelay()); 951 } 952 } 953 954 /** 955 * <p>Causes the Runnable to execute on the next animation time step, 956 * after the specified amount of time elapses. 957 * The runnable will be run on the user interface thread.</p> 958 * 959 * <p>This method can be invoked from outside of the UI thread 960 * only when this View is attached to a window.</p> 961 * 962 * @param view The view to post this Runnable to 963 * @param action The Runnable that will be executed. 964 * @param delayMillis The delay (in milliseconds) until the Runnable 965 * will be executed. 966 */ 967 public static void postOnAnimationDelayed(@NonNull View view, Runnable action, 968 long delayMillis) { 969 if (Build.VERSION.SDK_INT >= 16) { 970 view.postOnAnimationDelayed(action, delayMillis); 971 } else { 972 view.postDelayed(action, ValueAnimator.getFrameDelay() + delayMillis); 973 } 974 } 975 976 /** 977 * Gets the mode for determining whether this View is important for accessibility 978 * which is if it fires accessibility events and if it is reported to 979 * accessibility services that query the screen. 980 * 981 * @param view The view whose property to get. 982 * @return The mode for determining whether a View is important for accessibility. 983 * 984 * @see #IMPORTANT_FOR_ACCESSIBILITY_YES 985 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO 986 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 987 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO 988 */ 989 @ImportantForAccessibility 990 public static int getImportantForAccessibility(@NonNull View view) { 991 if (Build.VERSION.SDK_INT >= 16) { 992 return view.getImportantForAccessibility(); 993 } 994 return IMPORTANT_FOR_ACCESSIBILITY_AUTO; 995 } 996 997 /** 998 * Sets how to determine whether this view is important for accessibility 999 * which is if it fires accessibility events and if it is reported to 1000 * accessibility services that query the screen. 1001 * <p> 1002 * <em>Note:</em> If the current platform version does not support the 1003 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} mode, then 1004 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO} will be used as it is the 1005 * closest terms of semantics. 1006 * </p> 1007 * 1008 * @param view The view whose property to set. 1009 * @param mode How to determine whether this view is important for accessibility. 1010 * 1011 * @see #IMPORTANT_FOR_ACCESSIBILITY_YES 1012 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO 1013 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 1014 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO 1015 */ 1016 public static void setImportantForAccessibility(@NonNull View view, 1017 @ImportantForAccessibility int mode) { 1018 if (Build.VERSION.SDK_INT >= 19) { 1019 view.setImportantForAccessibility(mode); 1020 } else if (Build.VERSION.SDK_INT >= 16) { 1021 // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS is not available 1022 // on this platform so replace with IMPORTANT_FOR_ACCESSIBILITY_NO 1023 // which is closer semantically. 1024 if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) { 1025 mode = IMPORTANT_FOR_ACCESSIBILITY_NO; 1026 } 1027 //noinspection WrongConstant 1028 view.setImportantForAccessibility(mode); 1029 } 1030 } 1031 1032 /** 1033 * Computes whether this view should be exposed for accessibility. In 1034 * general, views that are interactive or provide information are exposed 1035 * while views that serve only as containers are hidden. 1036 * <p> 1037 * If an ancestor of this view has importance 1038 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, this method 1039 * returns <code>false</code>. 1040 * <p> 1041 * Otherwise, the value is computed according to the view's 1042 * {@link #getImportantForAccessibility(View)} value: 1043 * <ol> 1044 * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_NO} or 1045 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, return <code>false 1046 * </code> 1047 * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_YES}, return <code>true</code> 1048 * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_AUTO}, return <code>true</code> if 1049 * view satisfies any of the following: 1050 * <ul> 1051 * <li>Is actionable, e.g. {@link View#isClickable()}, 1052 * {@link View#isLongClickable()}, or {@link View#isFocusable()} 1053 * <li>Has an {@link AccessibilityDelegateCompat} 1054 * <li>Has an interaction listener, e.g. {@link View.OnTouchListener}, 1055 * {@link View.OnKeyListener}, etc. 1056 * <li>Is an accessibility live region, e.g. 1057 * {@link #getAccessibilityLiveRegion(View)} is not 1058 * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. 1059 * </ul> 1060 * </ol> 1061 * <p> 1062 * <em>Note:</em> Prior to API 21, this method will always return {@code true}. 1063 * 1064 * @return Whether the view is exposed for accessibility. 1065 * @see #setImportantForAccessibility(View, int) 1066 * @see #getImportantForAccessibility(View) 1067 */ 1068 public static boolean isImportantForAccessibility(@NonNull View view) { 1069 if (Build.VERSION.SDK_INT >= 21) { 1070 return view.isImportantForAccessibility(); 1071 } 1072 return true; 1073 } 1074 1075 /** 1076 * Performs the specified accessibility action on the view. For 1077 * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}. 1078 * <p> 1079 * If an {@link AccessibilityDelegateCompat} has been specified via calling 1080 * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its 1081 * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)} 1082 * is responsible for handling this call. 1083 * </p> 1084 * 1085 * @param action The action to perform. 1086 * @param arguments Optional action arguments. 1087 * @return Whether the action was performed. 1088 */ 1089 public static boolean performAccessibilityAction(@NonNull View view, int action, 1090 Bundle arguments) { 1091 if (Build.VERSION.SDK_INT >= 16) { 1092 return view.performAccessibilityAction(action, arguments); 1093 } 1094 return false; 1095 } 1096 1097 /** 1098 * Gets the provider for managing a virtual view hierarchy rooted at this View 1099 * and reported to {@link android.accessibilityservice.AccessibilityService}s 1100 * that explore the window content. 1101 * <p> 1102 * If this method returns an instance, this instance is responsible for managing 1103 * {@link AccessibilityNodeInfoCompat}s describing the virtual sub-tree rooted at 1104 * this View including the one representing the View itself. Similarly the returned 1105 * instance is responsible for performing accessibility actions on any virtual 1106 * view or the root view itself. 1107 * </p> 1108 * <p> 1109 * If an {@link AccessibilityDelegateCompat} has been specified via calling 1110 * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its 1111 * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)} 1112 * is responsible for handling this call. 1113 * </p> 1114 * 1115 * @param view The view whose property to get. 1116 * @return The provider. 1117 * 1118 * @see AccessibilityNodeProviderCompat 1119 */ 1120 public static AccessibilityNodeProviderCompat getAccessibilityNodeProvider(@NonNull View view) { 1121 if (Build.VERSION.SDK_INT >= 16) { 1122 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); 1123 if (provider != null) { 1124 return new AccessibilityNodeProviderCompat(provider); 1125 } 1126 } 1127 return null; 1128 } 1129 1130 /** 1131 * The opacity of the view. This is a value from 0 to 1, where 0 means the view is 1132 * completely transparent and 1 means the view is completely opaque. 1133 * 1134 * <p>By default this is 1.0f. 1135 * @return The opacity of the view. 1136 * 1137 * @deprecated Use {@link View#getAlpha()} directly. 1138 */ 1139 @Deprecated 1140 public static float getAlpha(View view) { 1141 return view.getAlpha(); 1142 } 1143 1144 /** 1145 * <p>Specifies the type of layer backing this view. The layer can be 1146 * {@link View#LAYER_TYPE_NONE disabled}, {@link View#LAYER_TYPE_SOFTWARE software} or 1147 * {@link View#LAYER_TYPE_HARDWARE hardware}.</p> 1148 * 1149 * <p>A layer is associated with an optional {@link android.graphics.Paint} 1150 * instance that controls how the layer is composed on screen. The following 1151 * properties of the paint are taken into account when composing the layer:</p> 1152 * <ul> 1153 * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li> 1154 * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> 1155 * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> 1156 * </ul> 1157 * 1158 * <p>If this view has an alpha value set to < 1.0 by calling 1159 * setAlpha(float), the alpha value of the layer's paint is replaced by 1160 * this view's alpha value. Calling setAlpha(float) is therefore 1161 * equivalent to setting a hardware layer on this view and providing a paint with 1162 * the desired alpha value.<p> 1163 * 1164 * <p>Refer to the documentation of {@link View#LAYER_TYPE_NONE disabled}, 1165 * {@link View#LAYER_TYPE_SOFTWARE software} and {@link View#LAYER_TYPE_HARDWARE hardware} 1166 * for more information on when and how to use layers.</p> 1167 * 1168 * @param view View to set the layer type for 1169 * @param layerType The type of layer to use with this view, must be one of 1170 * {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or 1171 * {@link View#LAYER_TYPE_HARDWARE} 1172 * @param paint The paint used to compose the layer. This argument is optional 1173 * and can be null. It is ignored when the layer type is 1174 * {@link View#LAYER_TYPE_NONE} 1175 * 1176 * @deprecated Use {@link View#setLayerType(int, Paint)} directly. 1177 */ 1178 @Deprecated 1179 public static void setLayerType(View view, @LayerType int layerType, Paint paint) { 1180 view.setLayerType(layerType, paint); 1181 } 1182 1183 /** 1184 * Indicates what type of layer is currently associated with this view. By default 1185 * a view does not have a layer, and the layer type is {@link View#LAYER_TYPE_NONE}. 1186 * Refer to the documentation of 1187 * {@link #setLayerType(android.view.View, int, android.graphics.Paint)} 1188 * for more information on the different types of layers. 1189 * 1190 * @param view The view to fetch the layer type from 1191 * @return {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or 1192 * {@link View#LAYER_TYPE_HARDWARE} 1193 * 1194 * @see #setLayerType(android.view.View, int, android.graphics.Paint) 1195 * @see View#LAYER_TYPE_NONE 1196 * @see View#LAYER_TYPE_SOFTWARE 1197 * @see View#LAYER_TYPE_HARDWARE 1198 * 1199 * @deprecated Use {@link View#getLayerType()} directly. 1200 */ 1201 @Deprecated 1202 @LayerType 1203 public static int getLayerType(View view) { 1204 //noinspection ResourceType 1205 return view.getLayerType(); 1206 } 1207 1208 /** 1209 * Gets the id of a view for which a given view serves as a label for 1210 * accessibility purposes. 1211 * 1212 * @param view The view on which to invoke the corresponding method. 1213 * @return The labeled view id. 1214 */ 1215 public static int getLabelFor(@NonNull View view) { 1216 if (Build.VERSION.SDK_INT >= 17) { 1217 return view.getLabelFor(); 1218 } 1219 return 0; 1220 } 1221 1222 /** 1223 * Sets the id of a view for which a given view serves as a label for 1224 * accessibility purposes. 1225 * 1226 * @param view The view on which to invoke the corresponding method. 1227 * @param labeledId The labeled view id. 1228 */ 1229 public static void setLabelFor(@NonNull View view, @IdRes int labeledId) { 1230 if (Build.VERSION.SDK_INT >= 17) { 1231 view.setLabelFor(labeledId); 1232 } 1233 } 1234 1235 /** 1236 * Updates the {@link Paint} object used with the current layer (used only if the current 1237 * layer type is not set to {@link View#LAYER_TYPE_NONE}). Changed properties of the Paint 1238 * provided to {@link #setLayerType(android.view.View, int, android.graphics.Paint)} 1239 * will be used the next time the View is redrawn, but 1240 * {@link #setLayerPaint(android.view.View, android.graphics.Paint)} 1241 * must be called to ensure that the view gets redrawn immediately. 1242 * 1243 * <p>A layer is associated with an optional {@link android.graphics.Paint} 1244 * instance that controls how the layer is composed on screen. The following 1245 * properties of the paint are taken into account when composing the layer:</p> 1246 * <ul> 1247 * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li> 1248 * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> 1249 * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> 1250 * </ul> 1251 * 1252 * <p>If this view has an alpha value set to < 1.0 by calling 1253 * View#setAlpha(float), the alpha value of the layer's paint is replaced by 1254 * this view's alpha value. Calling View#setAlpha(float) is therefore 1255 * equivalent to setting a hardware layer on this view and providing a paint with 1256 * the desired alpha value.</p> 1257 * 1258 * @param view View to set a layer paint for 1259 * @param paint The paint used to compose the layer. This argument is optional 1260 * and can be null. It is ignored when the layer type is 1261 * {@link View#LAYER_TYPE_NONE} 1262 * 1263 * @see #setLayerType(View, int, android.graphics.Paint) 1264 */ 1265 public static void setLayerPaint(@NonNull View view, Paint paint) { 1266 if (Build.VERSION.SDK_INT >= 17) { 1267 view.setLayerPaint(paint); 1268 } else { 1269 // Make sure the paint is correct; this will be cheap if it's the same 1270 // instance as was used to call setLayerType earlier. 1271 view.setLayerType(view.getLayerType(), paint); 1272 // This is expensive, but the only way to accomplish this before JB-MR1. 1273 view.invalidate(); 1274 } 1275 } 1276 1277 /** 1278 * Returns the resolved layout direction for this view. 1279 * 1280 * @param view View to get layout direction for 1281 * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns 1282 * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. 1283 * 1284 * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version 1285 * is lower than Jellybean MR1 (API 17) 1286 */ 1287 @ResolvedLayoutDirectionMode 1288 public static int getLayoutDirection(@NonNull View view) { 1289 if (Build.VERSION.SDK_INT >= 17) { 1290 return view.getLayoutDirection(); 1291 } 1292 return LAYOUT_DIRECTION_LTR; 1293 } 1294 1295 /** 1296 * Set the layout direction for this view. This will propagate a reset of layout direction 1297 * resolution to the view's children and resolve layout direction for this view. 1298 * 1299 * @param view View to set layout direction for 1300 * @param layoutDirection the layout direction to set. Should be one of: 1301 * 1302 * {@link #LAYOUT_DIRECTION_LTR}, 1303 * {@link #LAYOUT_DIRECTION_RTL}, 1304 * {@link #LAYOUT_DIRECTION_INHERIT}, 1305 * {@link #LAYOUT_DIRECTION_LOCALE}. 1306 * 1307 * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution 1308 * proceeds up the parent chain of the view to get the value. If there is no parent, then it 1309 * will return the default {@link #LAYOUT_DIRECTION_LTR}. 1310 */ 1311 public static void setLayoutDirection(@NonNull View view, 1312 @LayoutDirectionMode int layoutDirection) { 1313 if (Build.VERSION.SDK_INT >= 17) { 1314 view.setLayoutDirection(layoutDirection); 1315 } 1316 } 1317 1318 /** 1319 * Gets the parent for accessibility purposes. Note that the parent for 1320 * accessibility is not necessary the immediate parent. It is the first 1321 * predecessor that is important for accessibility. 1322 * 1323 * @param view View to retrieve parent for 1324 * @return The parent for use in accessibility inspection 1325 */ 1326 public static ViewParent getParentForAccessibility(@NonNull View view) { 1327 if (Build.VERSION.SDK_INT >= 16) { 1328 return view.getParentForAccessibility(); 1329 } 1330 return view.getParent(); 1331 } 1332 1333 /** 1334 * Finds the first descendant view with the given ID, the view itself if the ID matches 1335 * {@link View#getId()}, or throws an IllegalArgumentException if the ID is invalid or there 1336 * is no matching view in the hierarchy. 1337 * <p> 1338 * <strong>Note:</strong> In most cases -- depending on compiler support -- 1339 * the resulting view is automatically cast to the target class type. If 1340 * the target class type is unconstrained, an explicit cast may be 1341 * necessary. 1342 * 1343 * @param id the ID to search for 1344 * @return a view with given ID 1345 * @see View#findViewById(int) 1346 */ 1347 @SuppressWarnings("TypeParameterUnusedInFormals") 1348 @NonNull 1349 public static <T extends View> T requireViewById(@NonNull View view, @IdRes int id) { 1350 // TODO: use and link to View#requireViewById() directly, once available 1351 T targetView = view.findViewById(id); 1352 if (targetView == null) { 1353 throw new IllegalArgumentException("ID does not reference a View inside this View"); 1354 } 1355 return targetView; 1356 } 1357 1358 /** 1359 * Indicates whether this View is opaque. An opaque View guarantees that it will 1360 * draw all the pixels overlapping its bounds using a fully opaque color. 1361 * 1362 * @return True if this View is guaranteed to be fully opaque, false otherwise. 1363 * @deprecated Use {@link View#isOpaque()} directly. This method will be 1364 * removed in a future release. 1365 */ 1366 @Deprecated 1367 public static boolean isOpaque(View view) { 1368 return view.isOpaque(); 1369 } 1370 1371 /** 1372 * Utility to reconcile a desired size and state, with constraints imposed 1373 * by a MeasureSpec. Will take the desired size, unless a different size 1374 * is imposed by the constraints. The returned value is a compound integer, 1375 * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and 1376 * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting 1377 * size is smaller than the size the view wants to be. 1378 * 1379 * @param size How big the view wants to be 1380 * @param measureSpec Constraints imposed by the parent 1381 * @return Size information bit mask as defined by 1382 * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. 1383 * 1384 * @deprecated Use {@link View#resolveSizeAndState(int, int, int)} directly. 1385 */ 1386 @Deprecated 1387 public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { 1388 return View.resolveSizeAndState(size, measureSpec, childMeasuredState); 1389 } 1390 1391 /** 1392 * Return the full width measurement information for this view as computed 1393 * by the most recent call to {@link android.view.View#measure(int, int)}. 1394 * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and 1395 * {@link #MEASURED_STATE_TOO_SMALL}. 1396 * This should be used during measurement and layout calculations only. Use 1397 * {@link android.view.View#getWidth()} to see how wide a view is after layout. 1398 * 1399 * @return The measured width of this view as a bit mask. 1400 * 1401 * @deprecated Use {@link View#getMeasuredWidth()} directly. 1402 */ 1403 @Deprecated 1404 public static int getMeasuredWidthAndState(View view) { 1405 return view.getMeasuredWidthAndState(); 1406 } 1407 1408 /** 1409 * Return the full height measurement information for this view as computed 1410 * by the most recent call to {@link android.view.View#measure(int, int)}. 1411 * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and 1412 * {@link #MEASURED_STATE_TOO_SMALL}. 1413 * This should be used during measurement and layout calculations only. Use 1414 * {@link android.view.View#getHeight()} to see how wide a view is after layout. 1415 * 1416 * @return The measured width of this view as a bit mask. 1417 * 1418 * @deprecated Use {@link View#getMeasuredHeightAndState()} directly. 1419 */ 1420 @Deprecated 1421 public static int getMeasuredHeightAndState(View view) { 1422 return view.getMeasuredHeightAndState(); 1423 } 1424 1425 /** 1426 * Return only the state bits of {@link #getMeasuredWidthAndState} 1427 * and {@link #getMeasuredHeightAndState}, combined into one integer. 1428 * The width component is in the regular bits {@link #MEASURED_STATE_MASK} 1429 * and the height component is at the shifted bits 1430 * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}. 1431 * 1432 * @deprecated Use {@link View#getMeasuredState()} directly. 1433 */ 1434 @Deprecated 1435 public static int getMeasuredState(View view) { 1436 return view.getMeasuredState(); 1437 } 1438 1439 /** 1440 * Merge two states as returned by {@link #getMeasuredState(View)}. 1441 * @param curState The current state as returned from a view or the result 1442 * of combining multiple views. 1443 * @param newState The new view state to combine. 1444 * @return Returns a new integer reflecting the combination of the two 1445 * states. 1446 * 1447 * @deprecated Use {@link View#combineMeasuredStates(int, int)} directly. 1448 */ 1449 @Deprecated 1450 public static int combineMeasuredStates(int curState, int newState) { 1451 return View.combineMeasuredStates(curState, newState); 1452 } 1453 1454 /** 1455 * Gets the live region mode for the specified View. 1456 * 1457 * @param view The view from which to obtain the live region mode 1458 * @return The live region mode for the view. 1459 * 1460 * @see ViewCompat#setAccessibilityLiveRegion(View, int) 1461 */ 1462 @AccessibilityLiveRegion 1463 public static int getAccessibilityLiveRegion(@NonNull View view) { 1464 if (Build.VERSION.SDK_INT >= 19) { 1465 return view.getAccessibilityLiveRegion(); 1466 } 1467 return ACCESSIBILITY_LIVE_REGION_NONE; 1468 } 1469 1470 /** 1471 * Sets the live region mode for the specified view. This indicates to 1472 * accessibility services whether they should automatically notify the user 1473 * about changes to the view's content description or text, or to the 1474 * content descriptions or text of the view's children (where applicable). 1475 * <p> 1476 * For example, in a login screen with a TextView that displays an "incorrect 1477 * password" notification, that view should be marked as a live region with 1478 * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. 1479 * <p> 1480 * To disable change notifications for this view, use 1481 * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region 1482 * mode for most views. 1483 * <p> 1484 * To indicate that the user should be notified of changes, use 1485 * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. 1486 * <p> 1487 * If the view's changes should interrupt ongoing speech and notify the user 1488 * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}. 1489 * 1490 * @param view The view on which to set the live region mode 1491 * @param mode The live region mode for this view, one of: 1492 * <ul> 1493 * <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE} 1494 * <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE} 1495 * <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE} 1496 * </ul> 1497 */ 1498 public static void setAccessibilityLiveRegion(@NonNull View view, 1499 @AccessibilityLiveRegion int mode) { 1500 if (Build.VERSION.SDK_INT >= 19) { 1501 view.setAccessibilityLiveRegion(mode); 1502 } 1503 } 1504 1505 /** 1506 * Returns the start padding of the specified view depending on its resolved layout direction. 1507 * If there are inset and enabled scrollbars, this value may include the space 1508 * required to display the scrollbars as well. 1509 * 1510 * @param view The view to get padding for 1511 * @return the start padding in pixels 1512 */ 1513 @Px 1514 public static int getPaddingStart(@NonNull View view) { 1515 if (Build.VERSION.SDK_INT >= 17) { 1516 return view.getPaddingStart(); 1517 } 1518 return view.getPaddingLeft(); 1519 } 1520 1521 /** 1522 * Returns the end padding of the specified view depending on its resolved layout direction. 1523 * If there are inset and enabled scrollbars, this value may include the space 1524 * required to display the scrollbars as well. 1525 * 1526 * @param view The view to get padding for 1527 * @return the end padding in pixels 1528 */ 1529 @Px 1530 public static int getPaddingEnd(@NonNull View view) { 1531 if (Build.VERSION.SDK_INT >= 17) { 1532 return view.getPaddingEnd(); 1533 } 1534 return view.getPaddingRight(); 1535 } 1536 1537 /** 1538 * Sets the relative padding. The view may add on the space required to display 1539 * the scrollbars, depending on the style and visibility of the scrollbars. 1540 * So the values returned from {@link #getPaddingStart}, {@link View#getPaddingTop}, 1541 * {@link #getPaddingEnd} and {@link View#getPaddingBottom} may be different 1542 * from the values set in this call. 1543 * 1544 * @param view The view on which to set relative padding 1545 * @param start the start padding in pixels 1546 * @param top the top padding in pixels 1547 * @param end the end padding in pixels 1548 * @param bottom the bottom padding in pixels 1549 */ 1550 public static void setPaddingRelative(@NonNull View view, @Px int start, @Px int top, 1551 @Px int end, @Px int bottom) { 1552 if (Build.VERSION.SDK_INT >= 17) { 1553 view.setPaddingRelative(start, top, end, bottom); 1554 } else { 1555 view.setPadding(start, top, end, bottom); 1556 } 1557 } 1558 1559 private static void bindTempDetach() { 1560 try { 1561 sDispatchStartTemporaryDetach = View.class.getDeclaredMethod( 1562 "dispatchStartTemporaryDetach"); 1563 sDispatchFinishTemporaryDetach = View.class.getDeclaredMethod( 1564 "dispatchFinishTemporaryDetach"); 1565 } catch (NoSuchMethodException e) { 1566 Log.e(TAG, "Couldn't find method", e); 1567 } 1568 sTempDetachBound = true; 1569 } 1570 1571 /** 1572 * Notify a view that it is being temporarily detached. 1573 */ 1574 public static void dispatchStartTemporaryDetach(@NonNull View view) { 1575 if (Build.VERSION.SDK_INT >= 24) { 1576 view.dispatchStartTemporaryDetach(); 1577 } else { 1578 if (!sTempDetachBound) { 1579 bindTempDetach(); 1580 } 1581 if (sDispatchStartTemporaryDetach != null) { 1582 try { 1583 sDispatchStartTemporaryDetach.invoke(view); 1584 } catch (Exception e) { 1585 Log.d(TAG, "Error calling dispatchStartTemporaryDetach", e); 1586 } 1587 } else { 1588 // Try this instead 1589 view.onStartTemporaryDetach(); 1590 } 1591 } 1592 } 1593 1594 /** 1595 * Notify a view that its temporary detach has ended; the view is now reattached. 1596 */ 1597 public static void dispatchFinishTemporaryDetach(@NonNull View view) { 1598 if (Build.VERSION.SDK_INT >= 24) { 1599 view.dispatchFinishTemporaryDetach(); 1600 } else { 1601 if (!sTempDetachBound) { 1602 bindTempDetach(); 1603 } 1604 if (sDispatchFinishTemporaryDetach != null) { 1605 try { 1606 sDispatchFinishTemporaryDetach.invoke(view); 1607 } catch (Exception e) { 1608 Log.d(TAG, "Error calling dispatchFinishTemporaryDetach", e); 1609 } 1610 } else { 1611 // Try this instead 1612 view.onFinishTemporaryDetach(); 1613 } 1614 } 1615 } 1616 1617 /** 1618 * The horizontal location of this view relative to its {@link View#getLeft() left} position. 1619 * This position is post-layout, in addition to wherever the object's 1620 * layout placed it. 1621 * 1622 * @return The horizontal position of this view relative to its left position, in pixels. 1623 * 1624 * @deprecated Use {@link View#getTranslationX()} directly. 1625 */ 1626 @Deprecated 1627 public static float getTranslationX(View view) { 1628 return view.getTranslationX(); 1629 } 1630 1631 /** 1632 * The vertical location of this view relative to its {@link View#getTop() top} position. 1633 * This position is post-layout, in addition to wherever the object's 1634 * layout placed it. 1635 * 1636 * @return The vertical position of this view relative to its top position, in pixels. 1637 * 1638 * @deprecated Use {@link View#getTranslationY()} directly. 1639 */ 1640 @Deprecated 1641 public static float getTranslationY(View view) { 1642 return view.getTranslationY(); 1643 } 1644 1645 /** 1646 * The transform matrix of this view, which is calculated based on the current 1647 * rotation, scale, and pivot properties. 1648 * <p> 1649 * 1650 * @param view The view whose Matrix will be returned 1651 * @return The current transform matrix for the view 1652 * 1653 * @see #getRotation(View) 1654 * @see #getScaleX(View) 1655 * @see #getScaleY(View) 1656 * @see #getPivotX(View) 1657 * @see #getPivotY(View) 1658 * 1659 * @deprecated Use {@link View#getMatrix()} directly. 1660 */ 1661 @Deprecated 1662 @Nullable 1663 public static Matrix getMatrix(View view) { 1664 return view.getMatrix(); 1665 } 1666 1667 /** 1668 * Returns the minimum width of the view. 1669 * 1670 * <p>Prior to API 16, this method may return 0 on some platforms.</p> 1671 * 1672 * @return the minimum width the view will try to be. 1673 */ 1674 public static int getMinimumWidth(@NonNull View view) { 1675 if (Build.VERSION.SDK_INT >= 16) { 1676 return view.getMinimumWidth(); 1677 } 1678 1679 if (!sMinWidthFieldFetched) { 1680 try { 1681 sMinWidthField = View.class.getDeclaredField("mMinWidth"); 1682 sMinWidthField.setAccessible(true); 1683 } catch (NoSuchFieldException e) { 1684 // Couldn't find the field. Abort! 1685 } 1686 sMinWidthFieldFetched = true; 1687 } 1688 1689 if (sMinWidthField != null) { 1690 try { 1691 return (int) sMinWidthField.get(view); 1692 } catch (Exception e) { 1693 // Field get failed. Oh well... 1694 } 1695 } 1696 1697 // We failed, return 0 1698 return 0; 1699 } 1700 1701 /** 1702 * Returns the minimum height of the view. 1703 * 1704 * <p>Prior to API 16, this method may return 0 on some platforms.</p> 1705 * 1706 * @return the minimum height the view will try to be. 1707 */ 1708 public static int getMinimumHeight(@NonNull View view) { 1709 if (Build.VERSION.SDK_INT >= 16) { 1710 return view.getMinimumHeight(); 1711 } 1712 1713 if (!sMinHeightFieldFetched) { 1714 try { 1715 sMinHeightField = View.class.getDeclaredField("mMinHeight"); 1716 sMinHeightField.setAccessible(true); 1717 } catch (NoSuchFieldException e) { 1718 // Couldn't find the field. Abort! 1719 } 1720 sMinHeightFieldFetched = true; 1721 } 1722 1723 if (sMinHeightField != null) { 1724 try { 1725 return (int) sMinHeightField.get(view); 1726 } catch (Exception e) { 1727 // Field get failed. Oh well... 1728 } 1729 } 1730 1731 // We failed, return 0 1732 return 0; 1733 } 1734 1735 /** 1736 * This method returns a ViewPropertyAnimator object, which can be used to animate 1737 * specific properties on this View. 1738 * 1739 * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View. 1740 */ 1741 @NonNull 1742 public static ViewPropertyAnimatorCompat animate(@NonNull View view) { 1743 if (sViewPropertyAnimatorMap == null) { 1744 sViewPropertyAnimatorMap = new WeakHashMap<>(); 1745 } 1746 ViewPropertyAnimatorCompat vpa = sViewPropertyAnimatorMap.get(view); 1747 if (vpa == null) { 1748 vpa = new ViewPropertyAnimatorCompat(view); 1749 sViewPropertyAnimatorMap.put(view, vpa); 1750 } 1751 return vpa; 1752 } 1753 1754 /** 1755 * Sets the horizontal location of this view relative to its left position. 1756 * This effectively positions the object post-layout, in addition to wherever the object's 1757 * layout placed it. 1758 * 1759 * @param value The horizontal position of this view relative to its left position, 1760 * in pixels. 1761 * 1762 * @deprecated Use {@link View#setTranslationX(float)} directly. 1763 */ 1764 @Deprecated 1765 public static void setTranslationX(View view, float value) { 1766 view.setTranslationX(value); 1767 } 1768 1769 /** 1770 * Sets the vertical location of this view relative to its top position. 1771 * This effectively positions the object post-layout, in addition to wherever the object's 1772 * layout placed it. 1773 * 1774 * @param value The vertical position of this view relative to its top position, 1775 * in pixels. 1776 * 1777 * @attr name android:translationY 1778 * 1779 * @deprecated Use {@link View#setTranslationY(float)} directly. 1780 */ 1781 @Deprecated 1782 public static void setTranslationY(View view, float value) { 1783 view.setTranslationY(value); 1784 } 1785 1786 /** 1787 * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is 1788 * completely transparent and 1 means the view is completely opaque.</p> 1789 * 1790 * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant 1791 * performance implications, especially for large views. It is best to use the alpha property 1792 * sparingly and transiently, as in the case of fading animations.</p> 1793 * 1794 * @param value The opacity of the view. 1795 * 1796 * @deprecated Use {@link View#setAlpha(float)} directly. 1797 */ 1798 @Deprecated 1799 public static void setAlpha(View view, @FloatRange(from=0.0, to=1.0) float value) { 1800 view.setAlpha(value); 1801 } 1802 1803 /** 1804 * Sets the visual x position of this view, in pixels. This is equivalent to setting the 1805 * {@link #setTranslationX(View, float) translationX} property to be the difference between 1806 * the x value passed in and the current left property of the view as determined 1807 * by the layout bounds. 1808 * 1809 * @param value The visual x position of this view, in pixels. 1810 * 1811 * @deprecated Use {@link View#setX(float)} directly. 1812 */ 1813 @Deprecated 1814 public static void setX(View view, float value) { 1815 view.setX(value); 1816 } 1817 1818 /** 1819 * Sets the visual y position of this view, in pixels. This is equivalent to setting the 1820 * {@link #setTranslationY(View, float) translationY} property to be the difference between 1821 * the y value passed in and the current top property of the view as determined by the 1822 * layout bounds. 1823 * 1824 * @param value The visual y position of this view, in pixels. 1825 * 1826 * @deprecated Use {@link View#setY(float)} directly. 1827 */ 1828 @Deprecated 1829 public static void setY(View view, float value) { 1830 view.setY(value); 1831 } 1832 1833 /** 1834 * Sets the degrees that the view is rotated around the pivot point. Increasing values 1835 * result in clockwise rotation. 1836 * 1837 * @param value The degrees of rotation. 1838 * 1839 * @deprecated Use {@link View#setRotation(float)} directly. 1840 */ 1841 @Deprecated 1842 public static void setRotation(View view, float value) { 1843 view.setRotation(value); 1844 } 1845 1846 /** 1847 * Sets the degrees that the view is rotated around the horizontal axis through the pivot point. 1848 * Increasing values result in clockwise rotation from the viewpoint of looking down the 1849 * x axis. 1850 * 1851 * @param value The degrees of X rotation. 1852 * 1853 * @deprecated Use {@link View#setRotationX(float)} directly. 1854 */ 1855 @Deprecated 1856 public static void setRotationX(View view, float value) { 1857 view.setRotationX(value); 1858 } 1859 1860 /** 1861 * Sets the degrees that the view is rotated around the vertical axis through the pivot point. 1862 * Increasing values result in counter-clockwise rotation from the viewpoint of looking 1863 * down the y axis. 1864 * 1865 * @param value The degrees of Y rotation. 1866 * 1867 * @deprecated Use {@link View#setRotationY(float)} directly. 1868 */ 1869 @Deprecated 1870 public static void setRotationY(View view, float value) { 1871 view.setRotationY(value); 1872 } 1873 1874 /** 1875 * Sets the amount that the view is scaled in x around the pivot point, as a proportion of 1876 * the view's unscaled width. A value of 1 means that no scaling is applied. 1877 * 1878 * @param value The scaling factor. 1879 * 1880 * @deprecated Use {@link View#setScaleX(float)} directly. 1881 */ 1882 @Deprecated 1883 public static void setScaleX(View view, float value) { 1884 view.setScaleX(value); 1885 } 1886 1887 /** 1888 * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of 1889 * the view's unscaled width. A value of 1 means that no scaling is applied. 1890 * 1891 * @param value The scaling factor. 1892 * 1893 * @deprecated Use {@link View#setScaleY(float)} directly. 1894 */ 1895 @Deprecated 1896 public static void setScaleY(View view, float value) { 1897 view.setScaleY(value); 1898 } 1899 1900 /** 1901 * The x location of the point around which the view is 1902 * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}. 1903 * 1904 * @deprecated Use {@link View#getPivotX()} directly. 1905 */ 1906 @Deprecated 1907 public static float getPivotX(View view) { 1908 return view.getPivotX(); 1909 } 1910 1911 /** 1912 * Sets the x location of the point around which the view is 1913 * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}. 1914 * By default, the pivot point is centered on the object. 1915 * Setting this property disables this behavior and causes the view to use only the 1916 * explicitly set pivotX and pivotY values. 1917 * 1918 * @param value The x location of the pivot point. 1919 * 1920 * @deprecated Use {@link View#setPivotX(float)} directly. 1921 */ 1922 @Deprecated 1923 public static void setPivotX(View view, float value) { 1924 view.setPivotX(value); 1925 } 1926 1927 /** 1928 * The y location of the point around which the view is {@link #setRotation(View, 1929 * float) rotated} and {@link #setScaleY(View, float) scaled}. 1930 * 1931 * @return The y location of the pivot point. 1932 * 1933 * @deprecated Use {@link View#getPivotY()} directly. 1934 */ 1935 @Deprecated 1936 public static float getPivotY(View view) { 1937 return view.getPivotY(); 1938 } 1939 1940 /** 1941 * Sets the y location of the point around which the view is 1942 * {@link #setRotation(View, float) rotated} and {@link #setScaleY(View, float) scaled}. 1943 * By default, the pivot point is centered on the object. 1944 * Setting this property disables this behavior and causes the view to use only the 1945 * explicitly set pivotX and pivotY values. 1946 * 1947 * @param value The y location of the pivot point. 1948 * 1949 * @deprecated Use {@link View#setPivotX(float)} directly. 1950 */ 1951 @Deprecated 1952 public static void setPivotY(View view, float value) { 1953 view.setPivotY(value); 1954 } 1955 1956 /** 1957 * @deprecated Use {@link View#getRotation()} directly. 1958 */ 1959 @Deprecated 1960 public static float getRotation(View view) { 1961 return view.getRotation(); 1962 } 1963 1964 /** 1965 * @deprecated Use {@link View#getRotationX()} directly. 1966 */ 1967 @Deprecated 1968 public static float getRotationX(View view) { 1969 return view.getRotationX(); 1970 } 1971 1972 /** 1973 * @deprecated Use {@link View#getRotationY()} directly. 1974 */ 1975 @Deprecated 1976 public static float getRotationY(View view) { 1977 return view.getRotationY(); 1978 } 1979 1980 /** 1981 * @deprecated Use {@link View#getScaleX()} directly. 1982 */ 1983 @Deprecated 1984 public static float getScaleX(View view) { 1985 return view.getScaleX(); 1986 } 1987 1988 /** 1989 * @deprecated Use {@link View#getScaleY()} directly. 1990 */ 1991 @Deprecated 1992 public static float getScaleY(View view) { 1993 return view.getScaleY(); 1994 } 1995 1996 /** 1997 * @deprecated Use {@link View#getX()} directly. 1998 */ 1999 @Deprecated 2000 public static float getX(View view) { 2001 return view.getX(); 2002 } 2003 2004 /** 2005 * @deprecated Use {@link View#getY()} directly. 2006 */ 2007 @Deprecated 2008 public static float getY(View view) { 2009 return view.getY(); 2010 } 2011 2012 /** 2013 * Sets the base elevation of this view, in pixels. 2014 */ 2015 public static void setElevation(@NonNull View view, float elevation) { 2016 if (Build.VERSION.SDK_INT >= 21) { 2017 view.setElevation(elevation); 2018 } 2019 } 2020 2021 /** 2022 * The base elevation of this view relative to its parent, in pixels. 2023 * 2024 * @return The base depth position of the view, in pixels. 2025 */ 2026 public static float getElevation(@NonNull View view) { 2027 if (Build.VERSION.SDK_INT >= 21) { 2028 return view.getElevation(); 2029 } 2030 return 0f; 2031 } 2032 2033 /** 2034 * Sets the depth location of this view relative to its {@link #getElevation(View) elevation}. 2035 */ 2036 public static void setTranslationZ(@NonNull View view, float translationZ) { 2037 if (Build.VERSION.SDK_INT >= 21) { 2038 view.setTranslationZ(translationZ); 2039 } 2040 } 2041 2042 /** 2043 * The depth location of this view relative to its {@link #getElevation(View) elevation}. 2044 * 2045 * @return The depth of this view relative to its elevation. 2046 */ 2047 public static float getTranslationZ(@NonNull View view) { 2048 if (Build.VERSION.SDK_INT >= 21) { 2049 return view.getTranslationZ(); 2050 } 2051 return 0f; 2052 } 2053 2054 /** 2055 * Sets the name of the View to be used to identify Views in Transitions. 2056 * Names should be unique in the View hierarchy. 2057 * 2058 * @param view The View against which to invoke the method. 2059 * @param transitionName The name of the View to uniquely identify it for Transitions. 2060 */ 2061 public static void setTransitionName(@NonNull View view, String transitionName) { 2062 if (Build.VERSION.SDK_INT >= 21) { 2063 view.setTransitionName(transitionName); 2064 } else { 2065 if (sTransitionNameMap == null) { 2066 sTransitionNameMap = new WeakHashMap<>(); 2067 } 2068 sTransitionNameMap.put(view, transitionName); 2069 } 2070 } 2071 2072 /** 2073 * Returns the name of the View to be used to identify Views in Transitions. 2074 * Names should be unique in the View hierarchy. 2075 * 2076 * <p>This returns null if the View has not been given a name.</p> 2077 * 2078 * @param view The View against which to invoke the method. 2079 * @return The name used of the View to be used to identify Views in Transitions or null 2080 * if no name has been given. 2081 */ 2082 @Nullable 2083 public static String getTransitionName(@NonNull View view) { 2084 if (Build.VERSION.SDK_INT >= 21) { 2085 return view.getTransitionName(); 2086 } 2087 if (sTransitionNameMap == null) { 2088 return null; 2089 } 2090 return sTransitionNameMap.get(view); 2091 } 2092 2093 /** 2094 * Returns the current system UI visibility that is currently set for the entire window. 2095 */ 2096 public static int getWindowSystemUiVisibility(@NonNull View view) { 2097 if (Build.VERSION.SDK_INT >= 16) { 2098 return view.getWindowSystemUiVisibility(); 2099 } 2100 return 0; 2101 } 2102 2103 /** 2104 * Ask that a new dispatch of {@code View.onApplyWindowInsets(WindowInsets)} be performed. This 2105 * falls back to {@code View.requestFitSystemWindows()} where available. 2106 */ 2107 public static void requestApplyInsets(@NonNull View view) { 2108 if (Build.VERSION.SDK_INT >= 20) { 2109 view.requestApplyInsets(); 2110 } else if (Build.VERSION.SDK_INT >= 16) { 2111 view.requestFitSystemWindows(); 2112 } 2113 } 2114 2115 /** 2116 * Tells the ViewGroup whether to draw its children in the order defined by the method 2117 * {@code ViewGroup.getChildDrawingOrder(int, int)}. 2118 * 2119 * @param enabled true if the order of the children when drawing is determined by 2120 * {@link ViewGroup#getChildDrawingOrder(int, int)}, false otherwise 2121 * 2122 * <p>Prior to API 7 this will have no effect.</p> 2123 * 2124 * @deprecated Use {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)} directly. 2125 */ 2126 @Deprecated 2127 public static void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) { 2128 if (sChildrenDrawingOrderMethod == null) { 2129 try { 2130 sChildrenDrawingOrderMethod = ViewGroup.class 2131 .getDeclaredMethod("setChildrenDrawingOrderEnabled", boolean.class); 2132 } catch (NoSuchMethodException e) { 2133 Log.e(TAG, "Unable to find childrenDrawingOrderEnabled", e); 2134 } 2135 sChildrenDrawingOrderMethod.setAccessible(true); 2136 } 2137 try { 2138 sChildrenDrawingOrderMethod.invoke(viewGroup, enabled); 2139 } catch (IllegalAccessException e) { 2140 Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e); 2141 } catch (IllegalArgumentException e) { 2142 Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e); 2143 } catch (InvocationTargetException e) { 2144 Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e); 2145 } 2146 } 2147 2148 /** 2149 * Returns true if this view should adapt to fit system window insets. This method will always 2150 * return false before API 16 (Jellybean). 2151 */ 2152 public static boolean getFitsSystemWindows(@NonNull View v) { 2153 if (Build.VERSION.SDK_INT >= 16) { 2154 return v.getFitsSystemWindows(); 2155 } 2156 return false; 2157 } 2158 2159 /** 2160 * Sets whether or not this view should account for system screen decorations 2161 * such as the status bar and inset its content; that is, controlling whether 2162 * the default implementation of {@link View#fitSystemWindows(Rect)} will be 2163 * executed. See that method for more details. 2164 * 2165 * @deprecated Use {@link View#setFitsSystemWindows(boolean)} directly. 2166 */ 2167 @Deprecated 2168 public static void setFitsSystemWindows(View view, boolean fitSystemWindows) { 2169 view.setFitsSystemWindows(fitSystemWindows); 2170 } 2171 2172 /** 2173 * On API 11 devices and above, call <code>Drawable.jumpToCurrentState()</code> 2174 * on all Drawable objects associated with this view. 2175 * <p> 2176 * On API 21 and above, also calls <code>StateListAnimator#jumpToCurrentState()</code> 2177 * if there is a StateListAnimator attached to this view. 2178 * 2179 * @deprecated Use {@link View#jumpDrawablesToCurrentState()} directly. 2180 */ 2181 @Deprecated 2182 public static void jumpDrawablesToCurrentState(View v) { 2183 v.jumpDrawablesToCurrentState(); 2184 } 2185 2186 /** 2187 * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying 2188 * window insets to this view. This will only take effect on devices with API 21 or above. 2189 */ 2190 public static void setOnApplyWindowInsetsListener(@NonNull View v, 2191 final OnApplyWindowInsetsListener listener) { 2192 if (Build.VERSION.SDK_INT >= 21) { 2193 if (listener == null) { 2194 v.setOnApplyWindowInsetsListener(null); 2195 return; 2196 } 2197 2198 v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { 2199 @Override 2200 @RequiresApi(21) // TODO remove https://issuetracker.google.com/issues/76458979 2201 public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) { 2202 WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets); 2203 compatInsets = listener.onApplyWindowInsets(view, compatInsets); 2204 return (WindowInsets) WindowInsetsCompat.unwrap(compatInsets); 2205 } 2206 }); 2207 } 2208 } 2209 2210 /** 2211 * Called when the view should apply {@link WindowInsetsCompat} according to its internal policy. 2212 * 2213 * <p>Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set 2214 * it will be called during dispatch instead of this method. The listener may optionally 2215 * call this method from its own implementation if it wishes to apply the view's default 2216 * insets policy in addition to its own.</p> 2217 * 2218 * @param view The View against which to invoke the method. 2219 * @param insets Insets to apply 2220 * @return The supplied insets with any applied insets consumed 2221 */ 2222 public static WindowInsetsCompat onApplyWindowInsets(@NonNull View view, 2223 WindowInsetsCompat insets) { 2224 if (Build.VERSION.SDK_INT >= 21) { 2225 WindowInsets unwrapped = (WindowInsets) WindowInsetsCompat.unwrap(insets); 2226 WindowInsets result = view.onApplyWindowInsets(unwrapped); 2227 if (result != unwrapped) { 2228 unwrapped = new WindowInsets(result); 2229 } 2230 return WindowInsetsCompat.wrap(unwrapped); 2231 } 2232 return insets; 2233 } 2234 2235 /** 2236 * Request to apply the given window insets to this view or another view in its subtree. 2237 * 2238 * <p>This method should be called by clients wishing to apply insets corresponding to areas 2239 * obscured by window decorations or overlays. This can include the status and navigation bars, 2240 * action bars, input methods and more. New inset categories may be added in the future. 2241 * The method returns the insets provided minus any that were applied by this view or its 2242 * children.</p> 2243 * 2244 * @param insets Insets to apply 2245 * @return The provided insets minus the insets that were consumed 2246 */ 2247 public static WindowInsetsCompat dispatchApplyWindowInsets(@NonNull View view, 2248 WindowInsetsCompat insets) { 2249 if (Build.VERSION.SDK_INT >= 21) { 2250 WindowInsets unwrapped = (WindowInsets) WindowInsetsCompat.unwrap(insets); 2251 WindowInsets result = view.dispatchApplyWindowInsets(unwrapped); 2252 if (result != unwrapped) { 2253 unwrapped = new WindowInsets(result); 2254 } 2255 return WindowInsetsCompat.wrap(unwrapped); 2256 } 2257 return insets; 2258 } 2259 2260 /** 2261 * Controls whether the entire hierarchy under this view will save its 2262 * state when a state saving traversal occurs from its parent. 2263 * 2264 * @param enabled Set to false to <em>disable</em> state saving, or true 2265 * (the default) to allow it. 2266 * 2267 * @deprecated Use {@link View#setSaveFromParentEnabled(boolean)} directly. 2268 */ 2269 @Deprecated 2270 public static void setSaveFromParentEnabled(View v, boolean enabled) { 2271 v.setSaveFromParentEnabled(enabled); 2272 } 2273 2274 /** 2275 * Changes the activated state of this view. A view can be activated or not. 2276 * Note that activation is not the same as selection. Selection is 2277 * a transient property, representing the view (hierarchy) the user is 2278 * currently interacting with. Activation is a longer-term state that the 2279 * user can move views in and out of. 2280 * 2281 * @param activated true if the view must be activated, false otherwise 2282 * 2283 * @deprecated Use {@link View#setActivated(boolean)} directly. 2284 */ 2285 @Deprecated 2286 public static void setActivated(View view, boolean activated) { 2287 view.setActivated(activated); 2288 } 2289 2290 /** 2291 * Returns whether this View has content which overlaps. 2292 * 2293 * <p>This function, intended to be overridden by specific View types, is an optimization when 2294 * alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to 2295 * an offscreen buffer and then composited into place, which can be expensive. If the view has 2296 * no overlapping rendering, the view can draw each primitive with the appropriate alpha value 2297 * directly. An example of overlapping rendering is a TextView with a background image, such as 2298 * a Button. An example of non-overlapping rendering is a TextView with no background, or an 2299 * ImageView with only the foreground image. The default implementation returns true; subclasses 2300 * should override if they have cases which can be optimized.</p> 2301 * 2302 * @return true if the content in this view might overlap, false otherwise. 2303 */ 2304 public static boolean hasOverlappingRendering(@NonNull View view) { 2305 if (Build.VERSION.SDK_INT >= 16) { 2306 return view.hasOverlappingRendering(); 2307 } 2308 return true; 2309 } 2310 2311 /** 2312 * Return if the padding as been set through relative values 2313 * {@code View.setPaddingRelative(int, int, int, int)} or thru 2314 * 2315 * @return true if the padding is relative or false if it is not. 2316 */ 2317 public static boolean isPaddingRelative(@NonNull View view) { 2318 if (Build.VERSION.SDK_INT >= 17) { 2319 return view.isPaddingRelative(); 2320 } 2321 return false; 2322 } 2323 2324 /** 2325 * Set the background of the {@code view} to a given Drawable, or remove the background. If the 2326 * background has padding, {@code view}'s padding is set to the background's padding. However, 2327 * when a background is removed, this View's padding isn't touched. If setting the padding is 2328 * desired, please use{@code setPadding(int, int, int, int)}. 2329 */ 2330 public static void setBackground(@NonNull View view, @Nullable Drawable background) { 2331 if (Build.VERSION.SDK_INT >= 16) { 2332 view.setBackground(background); 2333 } else { 2334 view.setBackgroundDrawable(background); 2335 } 2336 } 2337 2338 /** 2339 * Return the tint applied to the background drawable, if specified. 2340 * <p> 2341 * Only returns meaningful info when running on API v21 or newer, or if {@code view} 2342 * implements the {@code TintableBackgroundView} interface. 2343 */ 2344 public static ColorStateList getBackgroundTintList(@NonNull View view) { 2345 if (Build.VERSION.SDK_INT >= 21) { 2346 return view.getBackgroundTintList(); 2347 } 2348 return (view instanceof TintableBackgroundView) 2349 ? ((TintableBackgroundView) view).getSupportBackgroundTintList() 2350 : null; 2351 } 2352 2353 /** 2354 * Applies a tint to the background drawable. 2355 * <p> 2356 * This will always take effect when running on API v21 or newer. When running on platforms 2357 * previous to API v21, it will only take effect if {@code view} implements the 2358 * {@code TintableBackgroundView} interface. 2359 */ 2360 public static void setBackgroundTintList(@NonNull View view, ColorStateList tintList) { 2361 if (Build.VERSION.SDK_INT >= 21) { 2362 view.setBackgroundTintList(tintList); 2363 2364 if (Build.VERSION.SDK_INT == 21) { 2365 // Work around a bug in L that did not update the state of the background 2366 // after applying the tint 2367 Drawable background = view.getBackground(); 2368 boolean hasTint = (view.getBackgroundTintList() != null) 2369 || (view.getBackgroundTintMode() != null); 2370 if ((background != null) && hasTint) { 2371 if (background.isStateful()) { 2372 background.setState(view.getDrawableState()); 2373 } 2374 view.setBackground(background); 2375 } 2376 } 2377 } else if (view instanceof TintableBackgroundView) { 2378 ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList); 2379 } 2380 } 2381 2382 /** 2383 * Return the blending mode used to apply the tint to the background 2384 * drawable, if specified. 2385 * <p> 2386 * Only returns meaningful info when running on API v21 or newer, or if {@code view} 2387 * implements the {@code TintableBackgroundView} interface. 2388 */ 2389 public static PorterDuff.Mode getBackgroundTintMode(@NonNull View view) { 2390 if (Build.VERSION.SDK_INT >= 21) { 2391 return view.getBackgroundTintMode(); 2392 } 2393 return (view instanceof TintableBackgroundView) 2394 ? ((TintableBackgroundView) view).getSupportBackgroundTintMode() 2395 : null; 2396 } 2397 2398 /** 2399 * Specifies the blending mode used to apply the tint specified by 2400 * {@link #setBackgroundTintList(android.view.View, android.content.res.ColorStateList)} to 2401 * the background drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 2402 * <p> 2403 * This will always take effect when running on API v21 or newer. When running on platforms 2404 * previous to API v21, it will only take effect if {@code view} implement the 2405 * {@code TintableBackgroundView} interface. 2406 */ 2407 public static void setBackgroundTintMode(@NonNull View view, PorterDuff.Mode mode) { 2408 if (Build.VERSION.SDK_INT >= 21) { 2409 view.setBackgroundTintMode(mode); 2410 2411 if (Build.VERSION.SDK_INT == 21) { 2412 // Work around a bug in L that did not update the state of the background 2413 // after applying the tint 2414 Drawable background = view.getBackground(); 2415 boolean hasTint = (view.getBackgroundTintList() != null) 2416 || (view.getBackgroundTintMode() != null); 2417 if ((background != null) && hasTint) { 2418 if (background.isStateful()) { 2419 background.setState(view.getDrawableState()); 2420 } 2421 view.setBackground(background); 2422 } 2423 } 2424 } else if (view instanceof TintableBackgroundView) { 2425 ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode); 2426 } 2427 } 2428 2429 // TODO: getters for various view properties (rotation, etc) 2430 2431 /** 2432 * Enable or disable nested scrolling for this view. 2433 * 2434 * <p>If this property is set to true the view will be permitted to initiate nested 2435 * scrolling operations with a compatible parent view in the current hierarchy. If this 2436 * view does not implement nested scrolling this will have no effect. Disabling nested scrolling 2437 * while a nested scroll is in progress has the effect of 2438 * {@link #stopNestedScroll(View) stopping} the nested scroll.</p> 2439 * 2440 * @param enabled true to enable nested scrolling, false to disable 2441 * 2442 * @see #isNestedScrollingEnabled(View) 2443 */ 2444 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2445 public static void setNestedScrollingEnabled(@NonNull View view, boolean enabled) { 2446 if (Build.VERSION.SDK_INT >= 21) { 2447 view.setNestedScrollingEnabled(enabled); 2448 } else { 2449 if (view instanceof NestedScrollingChild) { 2450 ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled); 2451 } 2452 } 2453 } 2454 2455 /** 2456 * Returns true if nested scrolling is enabled for this view. 2457 * 2458 * <p>If nested scrolling is enabled and this View class implementation supports it, 2459 * this view will act as a nested scrolling child view when applicable, forwarding data 2460 * about the scroll operation in progress to a compatible and cooperating nested scrolling 2461 * parent.</p> 2462 * 2463 * @return true if nested scrolling is enabled 2464 * 2465 * @see #setNestedScrollingEnabled(View, boolean) 2466 */ 2467 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2468 public static boolean isNestedScrollingEnabled(@NonNull View view) { 2469 if (Build.VERSION.SDK_INT >= 21) { 2470 return view.isNestedScrollingEnabled(); 2471 } 2472 if (view instanceof NestedScrollingChild) { 2473 return ((NestedScrollingChild) view).isNestedScrollingEnabled(); 2474 } 2475 return false; 2476 } 2477 2478 /** 2479 * Begin a nestable scroll operation along the given axes. 2480 * 2481 * <p>This version of the method just calls {@link #startNestedScroll(View, int, int)} using 2482 * the touch input type.</p> 2483 * 2484 * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL} 2485 * and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}. 2486 * @return true if a cooperative parent was found and nested scrolling has been enabled for 2487 * the current gesture. 2488 */ 2489 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2490 public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes) { 2491 if (Build.VERSION.SDK_INT >= 21) { 2492 return view.startNestedScroll(axes); 2493 } 2494 if (view instanceof NestedScrollingChild) { 2495 return ((NestedScrollingChild) view).startNestedScroll(axes); 2496 } 2497 return false; 2498 } 2499 2500 /** 2501 * Stop a nested scroll in progress. 2502 * 2503 * <p>This version of the method just calls {@link #stopNestedScroll(View, int)} using the 2504 * touch input type.</p> 2505 * 2506 * @see #startNestedScroll(View, int) 2507 */ 2508 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2509 public static void stopNestedScroll(@NonNull View view) { 2510 if (Build.VERSION.SDK_INT >= 21) { 2511 view.stopNestedScroll(); 2512 } else if (view instanceof NestedScrollingChild) { 2513 ((NestedScrollingChild) view).stopNestedScroll(); 2514 } 2515 } 2516 2517 /** 2518 * Returns true if this view has a nested scrolling parent. 2519 * 2520 * <p>This version of the method just calls {@link #hasNestedScrollingParent(View, int)} 2521 * using the touch input type.</p> 2522 * 2523 * @return whether this view has a nested scrolling parent 2524 */ 2525 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2526 public static boolean hasNestedScrollingParent(@NonNull View view) { 2527 if (Build.VERSION.SDK_INT >= 21) { 2528 return view.hasNestedScrollingParent(); 2529 } 2530 if (view instanceof NestedScrollingChild) { 2531 return ((NestedScrollingChild) view).hasNestedScrollingParent(); 2532 } 2533 return false; 2534 } 2535 2536 /** 2537 * Dispatch one step of a nested scroll in progress. 2538 * 2539 * <p>This version of the method just calls 2540 * {@link #dispatchNestedScroll(View, int, int, int, int, int[], int)} using the touch input 2541 * type.</p> 2542 * 2543 * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step 2544 * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step 2545 * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view 2546 * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view 2547 * @param offsetInWindow Optional. If not null, on return this will contain the offset 2548 * in local view coordinates of this view from before this operation 2549 * to after it completes. View implementations may use this to adjust 2550 * expected input coordinate tracking. 2551 * @return true if the event was dispatched, false if it could not be dispatched. 2552 */ 2553 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2554 public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed, 2555 int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) { 2556 if (Build.VERSION.SDK_INT >= 21) { 2557 return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 2558 offsetInWindow); 2559 } 2560 if (view instanceof NestedScrollingChild) { 2561 return ((NestedScrollingChild) view).dispatchNestedScroll(dxConsumed, dyConsumed, 2562 dxUnconsumed, dyUnconsumed, offsetInWindow); 2563 } 2564 return false; 2565 } 2566 2567 /** 2568 * Dispatch one step of a nested scroll in progress before this view consumes any portion of it. 2569 * 2570 * <p>This version of the method just calls 2571 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[], int)} using the touch input 2572 * type.</p> 2573 * 2574 * @param dx Horizontal scroll distance in pixels 2575 * @param dy Vertical scroll distance in pixels 2576 * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx 2577 * and consumed[1] the consumed dy. 2578 * @param offsetInWindow Optional. If not null, on return this will contain the offset 2579 * in local view coordinates of this view from before this operation 2580 * to after it completes. View implementations may use this to adjust 2581 * expected input coordinate tracking. 2582 * @return true if the parent consumed some or all of the scroll delta 2583 */ 2584 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2585 public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy, 2586 @Nullable int[] consumed, @Nullable int[] offsetInWindow) { 2587 if (Build.VERSION.SDK_INT >= 21) { 2588 return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); 2589 } 2590 if (view instanceof NestedScrollingChild) { 2591 return ((NestedScrollingChild) view).dispatchNestedPreScroll(dx, dy, consumed, 2592 offsetInWindow); 2593 } 2594 return false; 2595 } 2596 2597 /** 2598 * Begin a nestable scroll operation along the given axes. 2599 * 2600 * <p>A view starting a nested scroll promises to abide by the following contract:</p> 2601 * 2602 * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case 2603 * of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}. 2604 * In the case of touch scrolling the nested scroll will be terminated automatically in 2605 * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}. 2606 * In the event of programmatic scrolling the caller must explicitly call 2607 * {@link #stopNestedScroll(View)} to indicate the end of the nested scroll.</p> 2608 * 2609 * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found. 2610 * If it returns false the caller may ignore the rest of this contract until the next scroll. 2611 * Calling startNestedScroll while a nested scroll is already in progress will return true.</p> 2612 * 2613 * <p>At each incremental step of the scroll the caller should invoke 2614 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} 2615 * once it has calculated the requested scrolling delta. If it returns true the nested scrolling 2616 * parent at least partially consumed the scroll and the caller should adjust the amount it 2617 * scrolls by.</p> 2618 * 2619 * <p>After applying the remainder of the scroll delta the caller should invoke 2620 * {@link #dispatchNestedScroll(View, int, int, int, int, int[]) dispatchNestedScroll}, passing 2621 * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat 2622 * these values differently. See 2623 * {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}. 2624 * </p> 2625 * 2626 * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL} 2627 * and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}. 2628 * @param type the type of input which cause this scroll event 2629 * @return true if a cooperative parent was found and nested scrolling has been enabled for 2630 * the current gesture. 2631 * 2632 * @see #stopNestedScroll(View) 2633 * @see #dispatchNestedPreScroll(View, int, int, int[], int[]) 2634 * @see #dispatchNestedScroll(View, int, int, int, int, int[]) 2635 */ 2636 public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes, 2637 @NestedScrollType int type) { 2638 if (view instanceof NestedScrollingChild2) { 2639 return ((NestedScrollingChild2) view).startNestedScroll(axes, type); 2640 } else if (type == ViewCompat.TYPE_TOUCH) { 2641 return startNestedScroll(view, axes); 2642 } 2643 return false; 2644 } 2645 2646 /** 2647 * Stop a nested scroll in progress. 2648 * 2649 * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p> 2650 * 2651 * @param type the type of input which cause this scroll event 2652 * @see #startNestedScroll(View, int) 2653 */ 2654 public static void stopNestedScroll(@NonNull View view, @NestedScrollType int type) { 2655 if (view instanceof NestedScrollingChild2) { 2656 ((NestedScrollingChild2) view).stopNestedScroll(type); 2657 } else if (type == ViewCompat.TYPE_TOUCH) { 2658 stopNestedScroll(view); 2659 } 2660 } 2661 2662 /** 2663 * Returns true if this view has a nested scrolling parent. 2664 * 2665 * <p>The presence of a nested scrolling parent indicates that this view has initiated 2666 * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p> 2667 * 2668 * @param type the type of input which cause this scroll event 2669 * @return whether this view has a nested scrolling parent 2670 */ 2671 public static boolean hasNestedScrollingParent(@NonNull View view, @NestedScrollType int type) { 2672 if (view instanceof NestedScrollingChild2) { 2673 ((NestedScrollingChild2) view).hasNestedScrollingParent(type); 2674 } else if (type == ViewCompat.TYPE_TOUCH) { 2675 return hasNestedScrollingParent(view); 2676 } 2677 return false; 2678 } 2679 2680 /** 2681 * Dispatch one step of a nested scroll in progress. 2682 * 2683 * <p>Implementations of views that support nested scrolling should call this to report 2684 * info about a scroll in progress to the current nested scrolling parent. If a nested scroll 2685 * is not currently in progress or nested scrolling is not 2686 * {@link #isNestedScrollingEnabled(View) enabled} for this view this method does nothing.</p> 2687 * 2688 * <p>Compatible View implementations should also call 2689 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} before 2690 * consuming a component of the scroll event themselves.</p> 2691 * 2692 * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step 2693 * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step 2694 * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view 2695 * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view 2696 * @param offsetInWindow Optional. If not null, on return this will contain the offset 2697 * in local view coordinates of this view from before this operation 2698 * to after it completes. View implementations may use this to adjust 2699 * expected input coordinate tracking. 2700 * @param type the type of input which cause this scroll event 2701 * @return true if the event was dispatched, false if it could not be dispatched. 2702 * @see #dispatchNestedPreScroll(View, int, int, int[], int[]) 2703 */ 2704 public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed, 2705 int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, 2706 @NestedScrollType int type) { 2707 if (view instanceof NestedScrollingChild2) { 2708 return ((NestedScrollingChild2) view).dispatchNestedScroll(dxConsumed, dyConsumed, 2709 dxUnconsumed, dyUnconsumed, offsetInWindow, type); 2710 } else if (type == ViewCompat.TYPE_TOUCH) { 2711 return dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, 2712 dyUnconsumed, offsetInWindow); 2713 } 2714 return false; 2715 } 2716 2717 /** 2718 * Dispatch one step of a nested scroll in progress before this view consumes any portion of it. 2719 * 2720 * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch. 2721 * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested 2722 * scrolling operation to consume some or all of the scroll operation before the child view 2723 * consumes it.</p> 2724 * 2725 * @param dx Horizontal scroll distance in pixels 2726 * @param dy Vertical scroll distance in pixels 2727 * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx 2728 * and consumed[1] the consumed dy. 2729 * @param offsetInWindow Optional. If not null, on return this will contain the offset 2730 * in local view coordinates of this view from before this operation 2731 * to after it completes. View implementations may use this to adjust 2732 * expected input coordinate tracking. 2733 * @param type the type of input which cause this scroll event 2734 * @return true if the parent consumed some or all of the scroll delta 2735 * @see #dispatchNestedScroll(View, int, int, int, int, int[]) 2736 */ 2737 public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy, 2738 @Nullable int[] consumed, @Nullable int[] offsetInWindow, @NestedScrollType int type) { 2739 if (view instanceof NestedScrollingChild2) { 2740 return ((NestedScrollingChild2) view).dispatchNestedPreScroll(dx, dy, consumed, 2741 offsetInWindow, type); 2742 } else if (type == ViewCompat.TYPE_TOUCH) { 2743 return dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow); 2744 } 2745 return false; 2746 } 2747 2748 /** 2749 * Dispatch a fling to a nested scrolling parent. 2750 * 2751 * <p>This method should be used to indicate that a nested scrolling child has detected 2752 * suitable conditions for a fling. Generally this means that a touch scroll has ended with a 2753 * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds 2754 * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity} 2755 * along a scrollable axis.</p> 2756 * 2757 * <p>If a nested scrolling child view would normally fling but it is at the edge of 2758 * its own content, it can use this method to delegate the fling to its nested scrolling 2759 * parent instead. The parent may optionally consume the fling or observe a child fling.</p> 2760 * 2761 * @param velocityX Horizontal fling velocity in pixels per second 2762 * @param velocityY Vertical fling velocity in pixels per second 2763 * @param consumed true if the child consumed the fling, false otherwise 2764 * @return true if the nested scrolling parent consumed or otherwise reacted to the fling 2765 */ 2766 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2767 public static boolean dispatchNestedFling(@NonNull View view, float velocityX, float velocityY, 2768 boolean consumed) { 2769 if (Build.VERSION.SDK_INT >= 21) { 2770 return view.dispatchNestedFling(velocityX, velocityY, consumed); 2771 } 2772 if (view instanceof NestedScrollingChild) { 2773 return ((NestedScrollingChild) view).dispatchNestedFling(velocityX, velocityY, 2774 consumed); 2775 } 2776 return false; 2777 } 2778 2779 /** 2780 * Dispatch a fling to a nested scrolling parent before it is processed by this view. 2781 * 2782 * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch 2783 * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code> 2784 * offsets an opportunity for the parent view in a nested fling to fully consume the fling 2785 * before the child view consumes it. If this method returns <code>true</code>, a nested 2786 * parent view consumed the fling and this view should not scroll as a result.</p> 2787 * 2788 * <p>For a better user experience, only one view in a nested scrolling chain should consume 2789 * the fling at a time. If a parent view consumed the fling this method will return false. 2790 * Custom view implementations should account for this in two ways:</p> 2791 * 2792 * <ul> 2793 * <li>If a custom view is paged and needs to settle to a fixed page-point, do not 2794 * call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid 2795 * position regardless.</li> 2796 * <li>If a nested parent does consume the fling, this view should not scroll at all, 2797 * even to settle back to a valid idle position.</li> 2798 * </ul> 2799 * 2800 * <p>Views should also not offer fling velocities to nested parent views along an axis 2801 * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView} 2802 * should not offer a horizontal fling velocity to its parents since scrolling along that 2803 * axis is not permitted and carrying velocity along that motion does not make sense.</p> 2804 * 2805 * @param velocityX Horizontal fling velocity in pixels per second 2806 * @param velocityY Vertical fling velocity in pixels per second 2807 * @return true if a nested scrolling parent consumed the fling 2808 */ 2809 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. 2810 public static boolean dispatchNestedPreFling(@NonNull View view, float velocityX, 2811 float velocityY) { 2812 if (Build.VERSION.SDK_INT >= 21) { 2813 return view.dispatchNestedPreFling(velocityX, velocityY); 2814 } 2815 if (view instanceof NestedScrollingChild) { 2816 return ((NestedScrollingChild) view).dispatchNestedPreFling(velocityX, velocityY); 2817 } 2818 return false; 2819 } 2820 2821 /** 2822 * Returns whether the view hierarchy is currently undergoing a layout pass. This 2823 * information is useful to avoid situations such as calling {@link View#requestLayout()} 2824 * during a layout pass. 2825 * <p> 2826 * Compatibility: 2827 * <ul> 2828 * <li>API < 18: Always returns {@code false}</li> 2829 * </ul> 2830 * 2831 * @return whether the view hierarchy is currently undergoing a layout pass 2832 */ 2833 public static boolean isInLayout(@NonNull View view) { 2834 if (Build.VERSION.SDK_INT >= 18) { 2835 return view.isInLayout(); 2836 } 2837 return false; 2838 } 2839 2840 /** 2841 * Returns true if {@code view} has been through at least one layout since it 2842 * was last attached to or detached from a window. 2843 */ 2844 public static boolean isLaidOut(@NonNull View view) { 2845 if (Build.VERSION.SDK_INT >= 19) { 2846 return view.isLaidOut(); 2847 } 2848 return view.getWidth() > 0 && view.getHeight() > 0; 2849 } 2850 2851 /** 2852 * Returns whether layout direction has been resolved. 2853 * <p> 2854 * Compatibility: 2855 * <ul> 2856 * <li>API < 19: Always returns {@code false}</li> 2857 * </ul> 2858 * 2859 * @return true if layout direction has been resolved. 2860 */ 2861 public static boolean isLayoutDirectionResolved(@NonNull View view) { 2862 if (Build.VERSION.SDK_INT >= 19) { 2863 return view.isLayoutDirectionResolved(); 2864 } 2865 return false; 2866 } 2867 2868 /** 2869 * The visual z position of this view, in pixels. This is equivalent to the 2870 * {@link #setTranslationZ(View, float) translationZ} property plus the current 2871 * {@link #getElevation(View) elevation} property. 2872 * 2873 * @return The visual z position of this view, in pixels. 2874 */ 2875 public static float getZ(@NonNull View view) { 2876 if (Build.VERSION.SDK_INT >= 21) { 2877 return view.getZ(); 2878 } 2879 return 0f; 2880 } 2881 2882 /** 2883 * Sets the visual z position of this view, in pixels. This is equivalent to setting the 2884 * {@link #setTranslationZ(View, float) translationZ} property to be the difference between 2885 * the x value passed in and the current {@link #getElevation(View) elevation} property. 2886 * <p> 2887 * Compatibility: 2888 * <ul> 2889 * <li>API < 21: No-op 2890 * </ul> 2891 * 2892 * @param z The visual z position of this view, in pixels. 2893 */ 2894 public static void setZ(@NonNull View view, float z) { 2895 if (Build.VERSION.SDK_INT >= 21) { 2896 view.setZ(z); 2897 } 2898 } 2899 2900 /** 2901 * Offset this view's vertical location by the specified number of pixels. 2902 * 2903 * @param offset the number of pixels to offset the view by 2904 */ 2905 public static void offsetTopAndBottom(@NonNull View view, int offset) { 2906 if (Build.VERSION.SDK_INT >= 23) { 2907 view.offsetTopAndBottom(offset); 2908 } else if (Build.VERSION.SDK_INT >= 21) { 2909 final Rect parentRect = getEmptyTempRect(); 2910 boolean needInvalidateWorkaround = false; 2911 2912 final ViewParent parent = view.getParent(); 2913 if (parent instanceof View) { 2914 final View p = (View) parent; 2915 parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom()); 2916 // If the view currently does not currently intersect the parent (and is therefore 2917 // not displayed) we may need need to invalidate 2918 needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(), 2919 view.getRight(), view.getBottom()); 2920 } 2921 2922 // Now offset, invoking the API 14+ implementation (which contains its own workarounds) 2923 compatOffsetTopAndBottom(view, offset); 2924 2925 // The view has now been offset, so let's intersect the Rect and invalidate where 2926 // the View is now displayed 2927 if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(), 2928 view.getRight(), view.getBottom())) { 2929 ((View) parent).invalidate(parentRect); 2930 } 2931 } else { 2932 compatOffsetTopAndBottom(view, offset); 2933 } 2934 } 2935 2936 private static void compatOffsetTopAndBottom(View view, int offset) { 2937 view.offsetTopAndBottom(offset); 2938 if (view.getVisibility() == View.VISIBLE) { 2939 tickleInvalidationFlag(view); 2940 2941 ViewParent parent = view.getParent(); 2942 if (parent instanceof View) { 2943 tickleInvalidationFlag((View) parent); 2944 } 2945 } 2946 } 2947 2948 /** 2949 * Offset this view's horizontal location by the specified amount of pixels. 2950 * 2951 * @param offset the number of pixels to offset the view by 2952 */ 2953 public static void offsetLeftAndRight(@NonNull View view, int offset) { 2954 if (Build.VERSION.SDK_INT >= 23) { 2955 view.offsetLeftAndRight(offset); 2956 } else if (Build.VERSION.SDK_INT >= 21) { 2957 final Rect parentRect = getEmptyTempRect(); 2958 boolean needInvalidateWorkaround = false; 2959 2960 final ViewParent parent = view.getParent(); 2961 if (parent instanceof View) { 2962 final View p = (View) parent; 2963 parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom()); 2964 // If the view currently does not currently intersect the parent (and is therefore 2965 // not displayed) we may need need to invalidate 2966 needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(), 2967 view.getRight(), view.getBottom()); 2968 } 2969 2970 // Now offset, invoking the API 14+ implementation (which contains its own workarounds) 2971 compatOffsetLeftAndRight(view, offset); 2972 2973 // The view has now been offset, so let's intersect the Rect and invalidate where 2974 // the View is now displayed 2975 if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(), 2976 view.getRight(), view.getBottom())) { 2977 ((View) parent).invalidate(parentRect); 2978 } 2979 } else { 2980 compatOffsetLeftAndRight(view, offset); 2981 } 2982 } 2983 2984 private static void compatOffsetLeftAndRight(View view, int offset) { 2985 view.offsetLeftAndRight(offset); 2986 if (view.getVisibility() == View.VISIBLE) { 2987 tickleInvalidationFlag(view); 2988 2989 ViewParent parent = view.getParent(); 2990 if (parent instanceof View) { 2991 tickleInvalidationFlag((View) parent); 2992 } 2993 } 2994 } 2995 2996 private static void tickleInvalidationFlag(View view) { 2997 final float y = view.getTranslationY(); 2998 view.setTranslationY(y + 1); 2999 view.setTranslationY(y); 3000 } 3001 3002 /** 3003 * Sets a rectangular area on this view to which the view will be clipped 3004 * when it is drawn. Setting the value to null will remove the clip bounds 3005 * and the view will draw normally, using its full bounds. 3006 * 3007 * <p>Prior to API 18 this does nothing.</p> 3008 * 3009 * @param view The view to set clipBounds. 3010 * @param clipBounds The rectangular area, in the local coordinates of 3011 * this view, to which future drawing operations will be clipped. 3012 */ 3013 public static void setClipBounds(@NonNull View view, Rect clipBounds) { 3014 if (Build.VERSION.SDK_INT >= 18) { 3015 view.setClipBounds(clipBounds); 3016 } 3017 } 3018 3019 /** 3020 * Returns a copy of the current {@link #setClipBounds(View, Rect)}. 3021 * 3022 * <p>Prior to API 18 this will return null.</p> 3023 * 3024 * @return A copy of the current clip bounds if clip bounds are set, 3025 * otherwise null. 3026 */ 3027 @Nullable 3028 public static Rect getClipBounds(@NonNull View view) { 3029 if (Build.VERSION.SDK_INT >= 18) { 3030 return view.getClipBounds(); 3031 } 3032 return null; 3033 } 3034 3035 /** 3036 * Returns true if the provided view is currently attached to a window. 3037 */ 3038 public static boolean isAttachedToWindow(@NonNull View view) { 3039 if (Build.VERSION.SDK_INT >= 19) { 3040 return view.isAttachedToWindow(); 3041 } 3042 return view.getWindowToken() != null; 3043 } 3044 3045 /** 3046 * Returns whether the provided view has an attached {@link View.OnClickListener}. 3047 * 3048 * @return true if there is a listener, false if there is none. 3049 */ 3050 public static boolean hasOnClickListeners(@NonNull View view) { 3051 if (Build.VERSION.SDK_INT >= 15) { 3052 return view.hasOnClickListeners(); 3053 } 3054 return false; 3055 } 3056 3057 /** 3058 * Sets the state of all scroll indicators. 3059 * <p> 3060 * See {@link #setScrollIndicators(View, int, int)} for usage information. 3061 * 3062 * @param indicators a bitmask of indicators that should be enabled, or 3063 * {@code 0} to disable all indicators 3064 * 3065 * @see #setScrollIndicators(View, int, int) 3066 * @see #getScrollIndicators(View) 3067 */ 3068 public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) { 3069 if (Build.VERSION.SDK_INT >= 23) { 3070 view.setScrollIndicators(indicators); 3071 } 3072 } 3073 3074 /** 3075 * Sets the state of the scroll indicators specified by the mask. To change 3076 * all scroll indicators at once, see {@link #setScrollIndicators(View, int)}. 3077 * <p> 3078 * When a scroll indicator is enabled, it will be displayed if the view 3079 * can scroll in the direction of the indicator. 3080 * <p> 3081 * Multiple indicator types may be enabled or disabled by passing the 3082 * logical OR of the desired types. If multiple types are specified, they 3083 * will all be set to the same enabled state. 3084 * <p> 3085 * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators} 3086 * 3087 * @param indicators the indicator direction, or the logical OR of multiple 3088 * indicator directions. One or more of: 3089 * <ul> 3090 * <li>{@link #SCROLL_INDICATOR_TOP}</li> 3091 * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li> 3092 * <li>{@link #SCROLL_INDICATOR_LEFT}</li> 3093 * <li>{@link #SCROLL_INDICATOR_RIGHT}</li> 3094 * <li>{@link #SCROLL_INDICATOR_START}</li> 3095 * <li>{@link #SCROLL_INDICATOR_END}</li> 3096 * </ul> 3097 * 3098 * @see #setScrollIndicators(View, int) 3099 * @see #getScrollIndicators(View) 3100 */ 3101 public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators, 3102 @ScrollIndicators int mask) { 3103 if (Build.VERSION.SDK_INT >= 23) { 3104 view.setScrollIndicators(indicators, mask); 3105 } 3106 } 3107 3108 /** 3109 * Returns a bitmask representing the enabled scroll indicators. 3110 * <p> 3111 * For example, if the top and left scroll indicators are enabled and all 3112 * other indicators are disabled, the return value will be 3113 * {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}. 3114 * <p> 3115 * To check whether the bottom scroll indicator is enabled, use the value 3116 * of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}. 3117 * 3118 * @return a bitmask representing the enabled scroll indicators 3119 */ 3120 public static int getScrollIndicators(@NonNull View view) { 3121 if (Build.VERSION.SDK_INT >= 23) { 3122 return view.getScrollIndicators(); 3123 } 3124 return 0; 3125 } 3126 3127 /** 3128 * Set the pointer icon for the current view. 3129 * @param pointerIcon A PointerIconCompat instance which will be shown when the mouse hovers. 3130 */ 3131 public static void setPointerIcon(@NonNull View view, PointerIconCompat pointerIcon) { 3132 if (Build.VERSION.SDK_INT >= 24) { 3133 view.setPointerIcon((PointerIcon) (pointerIcon != null 3134 ? pointerIcon.getPointerIcon() : null)); 3135 } 3136 } 3137 3138 /** 3139 * Gets the logical display to which the view's window has been attached. 3140 * <p> 3141 * Compatibility: 3142 * <ul> 3143 * <li>API < 17: Returns the default display when the view is attached. Otherwise, null. 3144 * </ul> 3145 * 3146 * @return The logical display, or null if the view is not currently attached to a window. 3147 */ 3148 @Nullable 3149 public static Display getDisplay(@NonNull View view) { 3150 if (Build.VERSION.SDK_INT >= 17) { 3151 return view.getDisplay(); 3152 } 3153 if (isAttachedToWindow(view)) { 3154 final WindowManager wm = (WindowManager) view.getContext().getSystemService( 3155 Context.WINDOW_SERVICE); 3156 return wm.getDefaultDisplay(); 3157 } 3158 return null; 3159 } 3160 3161 /** 3162 * Sets the tooltip for the view. 3163 * 3164 * <p>Prior to API 26 this does nothing. Use TooltipCompat class from v7 appcompat library 3165 * for a compatible tooltip implementation.</p> 3166 * 3167 * @param tooltipText the tooltip text 3168 */ 3169 public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) { 3170 if (Build.VERSION.SDK_INT >= 26) { 3171 view.setTooltipText(tooltipText); 3172 } 3173 } 3174 3175 /** 3176 * Start the drag and drop operation. 3177 */ 3178 public static boolean startDragAndDrop(@NonNull View v, ClipData data, 3179 View.DragShadowBuilder shadowBuilder, Object localState, int flags) { 3180 if (Build.VERSION.SDK_INT >= 24) { 3181 return v.startDragAndDrop(data, shadowBuilder, localState, flags); 3182 } else { 3183 return v.startDrag(data, shadowBuilder, localState, flags); 3184 } 3185 } 3186 3187 /** 3188 * Cancel the drag and drop operation. 3189 */ 3190 public static void cancelDragAndDrop(@NonNull View v) { 3191 if (Build.VERSION.SDK_INT >= 24) { 3192 v.cancelDragAndDrop(); 3193 } 3194 } 3195 3196 /** 3197 * Update the drag shadow while drag and drop is in progress. 3198 */ 3199 public static void updateDragShadow(@NonNull View v, View.DragShadowBuilder shadowBuilder) { 3200 if (Build.VERSION.SDK_INT >= 24) { 3201 v.updateDragShadow(shadowBuilder); 3202 } 3203 } 3204 3205 /** 3206 * Gets the ID of the next keyboard navigation cluster root. 3207 * 3208 * @return the next keyboard navigation cluster ID, or {@link View#NO_ID} if the framework 3209 * should decide automatically or API < 26. 3210 */ 3211 public static int getNextClusterForwardId(@NonNull View view) { 3212 if (Build.VERSION.SDK_INT >= 26) { 3213 return view.getNextClusterForwardId(); 3214 } 3215 return View.NO_ID; 3216 } 3217 3218 /** 3219 * Sets the ID of the next keyboard navigation cluster root view. Does nothing if {@code view} 3220 * is not a keyboard navigation cluster or if API < 26. 3221 * 3222 * @param nextClusterForwardId next cluster ID, or {@link View#NO_ID} if the framework 3223 * should decide automatically. 3224 */ 3225 public static void setNextClusterForwardId(@NonNull View view, int nextClusterForwardId) { 3226 if (Build.VERSION.SDK_INT >= 26) { 3227 view.setNextClusterForwardId(nextClusterForwardId); 3228 } 3229 } 3230 3231 /** 3232 * Returns whether {@code view} is a root of a keyboard navigation cluster. Always returns 3233 * {@code false} on API < 26. 3234 * 3235 * @return {@code true} if this view is a root of a cluster, or {@code false} otherwise. 3236 */ 3237 public static boolean isKeyboardNavigationCluster(@NonNull View view) { 3238 if (Build.VERSION.SDK_INT >= 26) { 3239 return view.isKeyboardNavigationCluster(); 3240 } 3241 return false; 3242 } 3243 3244 /** 3245 * Set whether {@code view} is a root of a keyboard navigation cluster. Does nothing if 3246 * API < 26. 3247 * 3248 * @param isCluster {@code true} to mark {@code view} as the root of a cluster, {@code false} 3249 * to unmark. 3250 */ 3251 public static void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) { 3252 if (Build.VERSION.SDK_INT >= 26) { 3253 view.setKeyboardNavigationCluster(isCluster); 3254 } 3255 } 3256 3257 /** 3258 * Returns whether {@code view} should receive focus when the focus is restored for the view 3259 * hierarchy containing it. Returns {@code false} on API < 26. 3260 * <p> 3261 * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a 3262 * window or serves as a target of cluster navigation. 3263 * 3264 * @return {@code true} if {@code view} is the default-focus view, {@code false} otherwise. 3265 */ 3266 public static boolean isFocusedByDefault(@NonNull View view) { 3267 if (Build.VERSION.SDK_INT >= 26) { 3268 return view.isFocusedByDefault(); 3269 } 3270 return false; 3271 } 3272 3273 /** 3274 * Sets whether {@code view} should receive focus when the focus is restored for the view 3275 * hierarchy containing it. 3276 * <p> 3277 * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a 3278 * window or serves as a target of cluster navigation. 3279 * <p> 3280 * Does nothing on API < 26. 3281 * 3282 * @param isFocusedByDefault {@code true} to set {@code view} as the default-focus view, 3283 * {@code false} otherwise. 3284 */ 3285 public static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) { 3286 if (Build.VERSION.SDK_INT >= 26) { 3287 view.setFocusedByDefault(isFocusedByDefault); 3288 } 3289 } 3290 3291 /** 3292 * Find the nearest keyboard navigation cluster in the specified direction. 3293 * This does not actually give focus to that cluster. 3294 * 3295 * @param currentCluster The starting point of the search. {@code null} means the current 3296 * cluster is not found yet. 3297 * @param direction Direction to look. 3298 * 3299 * @return the nearest keyboard navigation cluster in the specified direction, or {@code null} 3300 * if one can't be found or if API < 26. 3301 */ 3302 public static View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster, 3303 @FocusDirection int direction) { 3304 if (Build.VERSION.SDK_INT >= 26) { 3305 return view.keyboardNavigationClusterSearch(currentCluster, direction); 3306 } 3307 return null; 3308 } 3309 3310 /** 3311 * Adds any keyboard navigation cluster roots that are descendants of {@code view} ( 3312 * including {@code view} if it is a cluster root itself) to {@code views}. Does nothing 3313 * on API < 26. 3314 * 3315 * @param views collection of keyboard navigation cluster roots found so far. 3316 * @param direction direction to look. 3317 */ 3318 public static void addKeyboardNavigationClusters(@NonNull View view, 3319 @NonNull Collection<View> views, int direction) { 3320 if (Build.VERSION.SDK_INT >= 26) { 3321 view.addKeyboardNavigationClusters(views, direction); 3322 } 3323 } 3324 3325 /** 3326 * Gives focus to the default-focus view in the view hierarchy rooted at {@code view}. 3327 * If the default-focus view cannot be found or if API < 26, this falls back to calling 3328 * {@link View#requestFocus(int)}. 3329 * 3330 * @return {@code true} if {@code view} or one of its descendants took focus, {@code false} 3331 * otherwise. 3332 */ 3333 public static boolean restoreDefaultFocus(@NonNull View view) { 3334 if (Build.VERSION.SDK_INT >= 26) { 3335 return view.restoreDefaultFocus(); 3336 } 3337 return view.requestFocus(); 3338 } 3339 3340 /** 3341 * Returns true if this view is focusable or if it contains a reachable View 3342 * for which {@link View#hasExplicitFocusable()} returns {@code true}. 3343 * A "reachable hasExplicitFocusable()" is a view whose parents do not block descendants focus. 3344 * Only {@link View#VISIBLE} views for which {@link View#getFocusable()} would return 3345 * {@link View#FOCUSABLE} are considered focusable. 3346 * 3347 * <p>This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of 3348 * {@link View#hasFocusable()} in that only views explicitly set focusable will cause 3349 * this method to return true. A view set to {@link View#FOCUSABLE_AUTO} that resolves 3350 * to focusable will not.</p> 3351 * 3352 * @return {@code true} if the view is focusable or if the view contains a focusable 3353 * view, {@code false} otherwise 3354 */ 3355 public static boolean hasExplicitFocusable(@NonNull View view) { 3356 if (Build.VERSION.SDK_INT >= 26) { 3357 return view.hasExplicitFocusable(); 3358 } 3359 return view.hasFocusable(); 3360 } 3361 3362 /** 3363 * Generate a value suitable for use in {@link View#setId(int)}. 3364 * This value will not collide with ID values generated at build time by aapt for R.id. 3365 * 3366 * @return a generated ID value 3367 */ 3368 public static int generateViewId() { 3369 if (Build.VERSION.SDK_INT >= 17) { 3370 return View.generateViewId(); 3371 } 3372 for (;;) { 3373 final int result = sNextGeneratedId.get(); 3374 // aapt-generated IDs have the high byte nonzero; clamp to the range under that. 3375 int newValue = result + 1; 3376 if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. 3377 if (sNextGeneratedId.compareAndSet(result, newValue)) { 3378 return result; 3379 } 3380 } 3381 } 3382 3383 protected ViewCompat() {} 3384 } 3385