/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.core.view; import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ClipData; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeProvider; import androidx.annotation.FloatRange; import androidx.annotation.IdRes; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; import androidx.annotation.RequiresApi; import androidx.annotation.RestrictTo; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityNodeProviderCompat; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * Helper for accessing features in {@link View}. */ public class ViewCompat { private static final String TAG = "ViewCompat"; /** @hide */ @RestrictTo(LIBRARY_GROUP) @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN, View.FOCUS_FORWARD, View.FOCUS_BACKWARD}) @Retention(RetentionPolicy.SOURCE) public @interface FocusDirection {} /** @hide */ @RestrictTo(LIBRARY_GROUP) @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN}) @Retention(RetentionPolicy.SOURCE) public @interface FocusRealDirection {} /** @hide */ @RestrictTo(LIBRARY_GROUP) @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD}) @Retention(RetentionPolicy.SOURCE) public @interface FocusRelativeDirection {} @IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER}) @Retention(RetentionPolicy.SOURCE) private @interface OverScroll {} /** * Always allow a user to over-scroll this view, provided it is a * view that can scroll. * @deprecated Use {@link View#OVER_SCROLL_ALWAYS} directly. This constant will be removed in * a future release. */ @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; /** * Allow a user to over-scroll this view only if the content is large * enough to meaningfully scroll, provided it is a view that can scroll. * @deprecated Use {@link View#OVER_SCROLL_IF_CONTENT_SCROLLS} directly. This constant will be * removed in a future release. */ @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; /** * Never allow a user to over-scroll this view. * @deprecated Use {@link View#OVER_SCROLL_NEVER} directly. This constant will be removed in * a future release. */ @Deprecated public static final int OVER_SCROLL_NEVER = 2; @TargetApi(Build.VERSION_CODES.O) @IntDef({ View.IMPORTANT_FOR_AUTOFILL_AUTO, View.IMPORTANT_FOR_AUTOFILL_YES, View.IMPORTANT_FOR_AUTOFILL_NO, View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS }) @Retention(RetentionPolicy.SOURCE) private @interface AutofillImportance {} @IntDef({ IMPORTANT_FOR_ACCESSIBILITY_AUTO, IMPORTANT_FOR_ACCESSIBILITY_YES, IMPORTANT_FOR_ACCESSIBILITY_NO, IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS }) @Retention(RetentionPolicy.SOURCE) private @interface ImportantForAccessibility {} /** * Automatically determine whether a view is important for accessibility. */ public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000; /** * The view is important for accessibility. */ public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001; /** * The view is not important for accessibility. */ public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; /** * The view is not important for accessibility, nor are any of its * descendant views. */ public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004; @IntDef({ ACCESSIBILITY_LIVE_REGION_NONE, ACCESSIBILITY_LIVE_REGION_POLITE, ACCESSIBILITY_LIVE_REGION_ASSERTIVE }) @Retention(RetentionPolicy.SOURCE) private @interface AccessibilityLiveRegion {} /** * Live region mode specifying that accessibility services should not * automatically announce changes to this view. This is the default live * region mode for most views. *
* Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. */ public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000; /** * Live region mode specifying that accessibility services should announce * changes to this view. *
* Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. */ public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001; /** * Live region mode specifying that accessibility services should interrupt * ongoing speech to immediately announce changes to this view. *
* Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. */ public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002; @IntDef({View.LAYER_TYPE_NONE, View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE}) @Retention(RetentionPolicy.SOURCE) private @interface LayerType {} /** * Indicates that the view does not have a layer. * * @deprecated Use {@link View#LAYER_TYPE_NONE} directly. */ @Deprecated public static final int LAYER_TYPE_NONE = 0; /** *
Indicates that the view has a software layer. A software layer is backed * by a bitmap and causes the view to be rendered using Android's software * rendering pipeline, even if hardware acceleration is enabled.
* *Software layers have various usages:
*When the application is not using hardware acceleration, a software layer * is useful to apply a specific color filter and/or blending mode and/or * translucency to a view and all its children.
*When the application is using hardware acceleration, a software layer * is useful to render drawing primitives not supported by the hardware * accelerated pipeline. It can also be used to cache a complex view tree * into a texture and reduce the complexity of drawing operations. For instance, * when animating a complex view tree with a translation, a software layer can * be used to render the view tree only once.
*Software layers should be avoided when the affected view tree updates * often. Every update will require to re-render the software layer, which can * potentially be slow (particularly when hardware acceleration is turned on * since the layer will have to be uploaded into a hardware texture after every * update.)
* * @deprecated Use {@link View#LAYER_TYPE_SOFTWARE} directly. */ @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; /** *Indicates that the view has a hardware layer. A hardware layer is backed * by a hardware specific texture (generally Frame Buffer Objects or FBO on * OpenGL hardware) and causes the view to be rendered using Android's hardware * rendering pipeline, but only if hardware acceleration is turned on for the * view hierarchy. When hardware acceleration is turned off, hardware layers * behave exactly as {@link View#LAYER_TYPE_SOFTWARE software layers}.
* *A hardware layer is useful to apply a specific color filter and/or * blending mode and/or translucency to a view and all its children.
*A hardware layer can be used to cache a complex view tree into a * texture and reduce the complexity of drawing operations. For instance, * when animating a complex view tree with a translation, a hardware layer can * be used to render the view tree only once.
*A hardware layer can also be used to increase the rendering quality when * rotation transformations are applied on a view. It can also be used to * prevent potential clipping issues when applying 3D transforms on a view.
* * @deprecated Use {@link View#LAYER_TYPE_HARDWARE} directly. */ @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; @IntDef({ LAYOUT_DIRECTION_LTR, LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE}) @Retention(RetentionPolicy.SOURCE) private @interface LayoutDirectionMode {} @IntDef({ LAYOUT_DIRECTION_LTR, LAYOUT_DIRECTION_RTL }) @Retention(RetentionPolicy.SOURCE) private @interface ResolvedLayoutDirectionMode {} /** * Horizontal layout direction of this view is from Left to Right. */ public static final int LAYOUT_DIRECTION_LTR = 0; /** * Horizontal layout direction of this view is from Right to Left. */ public static final int LAYOUT_DIRECTION_RTL = 1; /** * Horizontal layout direction of this view is inherited from its parent. * Use with {@link #setLayoutDirection}. */ public static final int LAYOUT_DIRECTION_INHERIT = 2; /** * Horizontal layout direction of this view is from deduced from the default language * script for the locale. Use with {@link #setLayoutDirection}. */ public static final int LAYOUT_DIRECTION_LOCALE = 3; /** * Bits of {@link #getMeasuredWidthAndState} and * {@link #getMeasuredWidthAndState} that provide the actual measured size. * * @deprecated Use {@link View#MEASURED_SIZE_MASK} directly. */ @Deprecated public static final int MEASURED_SIZE_MASK = 0x00ffffff; /** * Bits of {@link #getMeasuredWidthAndState} and * {@link #getMeasuredWidthAndState} that provide the additional state bits. * * @deprecated Use {@link View#MEASURED_STATE_MASK} directly. */ @Deprecated public static final int MEASURED_STATE_MASK = 0xff000000; /** * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits * for functions that combine both width and height into a single int, * such as {@link #getMeasuredState} and the childState argument of * {@link #resolveSizeAndState(int, int, int)}. * * @deprecated Use {@link View#MEASURED_HEIGHT_STATE_SHIFT} directly. */ @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; /** * Bit of {@link #getMeasuredWidthAndState} and * {@link #getMeasuredWidthAndState} that indicates the measured size * is smaller that the space the view would like to have. * * @deprecated Use {@link View#MEASURED_STATE_TOO_SMALL} directly. */ @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 0x01000000; /** * @hide */ @IntDef(value = {SCROLL_AXIS_NONE, SCROLL_AXIS_HORIZONTAL, SCROLL_AXIS_VERTICAL}, flag = true) @Retention(RetentionPolicy.SOURCE) @RestrictTo(LIBRARY_GROUP) public @interface ScrollAxis {} /** * Indicates no axis of view scrolling. */ public static final int SCROLL_AXIS_NONE = 0; /** * Indicates scrolling along the horizontal axis. */ public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0; /** * Indicates scrolling along the vertical axis. */ public static final int SCROLL_AXIS_VERTICAL = 1 << 1; /** * @hide */ @IntDef({TYPE_TOUCH, TYPE_NON_TOUCH}) @Retention(RetentionPolicy.SOURCE) @RestrictTo(LIBRARY_GROUP) public @interface NestedScrollType {} /** * Indicates that the input type for the gesture is from a user touching the screen. */ public static final int TYPE_TOUCH = 0; /** * Indicates that the input type for the gesture is caused by something which is not a user * touching a screen. This is usually from a fling which is settling. */ public static final int TYPE_NON_TOUCH = 1; /** @hide */ @RestrictTo(LIBRARY_GROUP) @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = { SCROLL_INDICATOR_TOP, SCROLL_INDICATOR_BOTTOM, SCROLL_INDICATOR_LEFT, SCROLL_INDICATOR_RIGHT, SCROLL_INDICATOR_START, SCROLL_INDICATOR_END, }) public @interface ScrollIndicators {} /** * Scroll indicator direction for the top edge of the view. * * @see #setScrollIndicators(View, int) * @see #setScrollIndicators(View, int, int) * @see #getScrollIndicators(View) */ public static final int SCROLL_INDICATOR_TOP = 0x1; /** * Scroll indicator direction for the bottom edge of the view. * * @see #setScrollIndicators(View, int) * @see #setScrollIndicators(View, int, int) * @see #getScrollIndicators(View) */ public static final int SCROLL_INDICATOR_BOTTOM = 0x2; /** * Scroll indicator direction for the left edge of the view. * * @see #setScrollIndicators(View, int) * @see #setScrollIndicators(View, int, int) * @see #getScrollIndicators(View) */ public static final int SCROLL_INDICATOR_LEFT = 0x4; /** * Scroll indicator direction for the right edge of the view. * * @see #setScrollIndicators(View, int) * @see #setScrollIndicators(View, int, int) * @see #getScrollIndicators(View) */ public static final int SCROLL_INDICATOR_RIGHT = 0x8; /** * Scroll indicator direction for the starting edge of the view. * * @see #setScrollIndicators(View, int) * @see #setScrollIndicators(View, int, int) * @see #getScrollIndicators(View) */ public static final int SCROLL_INDICATOR_START = 0x10; /** * Scroll indicator direction for the ending edge of the view. * * @see #setScrollIndicators(View, int) * @see #setScrollIndicators(View, int, int) * @see #getScrollIndicators(View) */ public static final int SCROLL_INDICATOR_END = 0x20; private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); private static Field sMinWidthField; private static boolean sMinWidthFieldFetched; private static Field sMinHeightField; private static boolean sMinHeightFieldFetched; private static Method sDispatchStartTemporaryDetach; private static Method sDispatchFinishTemporaryDetach; private static boolean sTempDetachBound; private static WeakHashMap* Example: Adding formatted date string to an accessibility event in addition * to the text added by the super implementation: *
public void onPopulateAccessibilityEvent(AccessibilityEvent event) { * super.onPopulateAccessibilityEvent(event); * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; * String selectedDateUtterance = DateUtils.formatDateTime(mContext, * mCurrentDate.getTimeInMillis(), flags); * event.getText().add(selectedDateUtterance); * }*
* If an {@link AccessibilityDelegateCompat} has been specified via calling * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)} * is responsible for handling this call. *
*Note: Always call the super implementation before adding * information to the event, in case the default implementation has basic information to add. *
* * @param v The View against which to invoke the method. * @param event The accessibility event which to populate. * * @see View#sendAccessibilityEvent(int) * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) * * @deprecated Call {@link View#onPopulateAccessibilityEvent(AccessibilityEvent)} directly. * This method will be removed in a future release. */ @Deprecated public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { v.onPopulateAccessibilityEvent(event); } /** * Initializes an {@link AccessibilityEvent} with information about * this View which is the event source. In other words, the source of * an accessibility event is the view whose state change triggered firing * the event. ** Example: Setting the password property of an event in addition * to properties set by the super implementation: *
public void onInitializeAccessibilityEvent(AccessibilityEvent event) { * super.onInitializeAccessibilityEvent(event); * event.setPassword(true); * }*
* If an {@link AccessibilityDelegateCompat} has been specified via calling * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)} * is responsible for handling this call. * * @param v The View against which to invoke the method. * @param event The event to initialize. * * @see View#sendAccessibilityEvent(int) * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) * * @deprecated Call {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)} directly. * This method will be removed in a future release. */ @Deprecated public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { v.onInitializeAccessibilityEvent(event); } /** * Initializes an {@link AccessibilityNodeInfoCompat} with information * about this view. The base implementation sets: *
* If an {@link AccessibilityDelegateCompat} has been specified via calling * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} * method is responsible for handling this call. * * @param v The View against which to invoke the method. * @param info The instance to initialize. */ public static void onInitializeAccessibilityNodeInfo(@NonNull View v, AccessibilityNodeInfoCompat info) { v.onInitializeAccessibilityNodeInfo(info.unwrap()); } /** * Sets a delegate for implementing accessibility support via composition * (as opposed to inheritance). For more details, see * {@link AccessibilityDelegateCompat}. *
* Note: On platform versions prior to * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on * views in the {@code android.widget.*} package are called before * host methods. This prevents certain properties such as class name from * being modified by overriding * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}, * as any changes will be overwritten by the host class. *
* Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate * methods are called after host methods, which all properties to be * modified without being overwritten by the host class. * * @param delegate the object to which accessibility method calls should be * delegated * @see AccessibilityDelegateCompat */ public static void setAccessibilityDelegate(@NonNull View v, AccessibilityDelegateCompat delegate) { v.setAccessibilityDelegate(delegate == null ? null : delegate.getBridge()); } /** * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how * to autofill the view with the user's data. * *
Typically, there is only one way to autofill a view, but there could be more than one. * For example, if the application accepts either an username or email address to identify * an user. * *
These hints are not validated by the Android System, but passed "as is" to the service. * Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_} * constants such as: * {@link View#AUTOFILL_HINT_USERNAME}, {@link View#AUTOFILL_HINT_PASSWORD}, * {@link View#AUTOFILL_HINT_EMAIL_ADDRESS}, * {@link View#AUTOFILL_HINT_NAME}, * {@link View#AUTOFILL_HINT_PHONE}, * {@link View#AUTOFILL_HINT_POSTAL_ADDRESS}, {@link View#AUTOFILL_HINT_POSTAL_CODE}, * {@link View#AUTOFILL_HINT_CREDIT_CARD_NUMBER}, * {@link View#AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}, * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}, * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}, * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}. * *
This method is only supported on API >= 26. * On API 25 and below, it is a no-op
* * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set. * @attr ref android.R.styleable#View_autofillHints */ public static void setAutofillHints(@NonNull View v, @Nullable String... autofillHints) { if (Build.VERSION.SDK_INT >= 26) { v.setAutofillHints(autofillHints); } } /** * Gets the mode for determining whether this view is important for autofill. * *See {@link #setImportantForAutofill(View, int)} and {@link #isImportantForAutofill(View)} * for more info about this mode. * *
This method is only supported on API >= 26. * On API 25 and below, it will always return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}.
* * @return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to * {@link #setImportantForAutofill(View, int)}. * * @attr ref android.R.styleable#View_importantForAutofill */ @SuppressLint("InlinedApi") public static @AutofillImportance int getImportantForAutofill(@NonNull View v) { if (Build.VERSION.SDK_INT >= 26) { return v.getImportantForAutofill(); } return View.IMPORTANT_FOR_AUTOFILL_AUTO; } /** * Sets the mode for determining whether this view is considered important for autofill. * *The platform determines the importance for autofill automatically but you * can use this method to customize the behavior. For example: * *
NOTE: setting the mode as does {@link View#IMPORTANT_FOR_AUTOFILL_NO} or * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and * its children) will be always be considered not important; for example, when the user * explicitly makes an autofill request, all views are considered important. See * {@link #isImportantForAutofill(View)} for more details about how the View's importance for * autofill is used. * *
This method is only supported on API >= 26. * On API 25 and below, it is a no-op
* * * @param mode {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}, * {@link View#IMPORTANT_FOR_AUTOFILL_YES}, * {@link View#IMPORTANT_FOR_AUTOFILL_NO}, * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, * or {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}. * * @attr ref android.R.styleable#View_importantForAutofill */ public static void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) { if (Build.VERSION.SDK_INT >= 26) { v.setImportantForAutofill(mode); } } /** * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode} * associated with this view is considered important for autofill purposes. * *Generally speaking, a view is important for autofill if: *
For example, view containers should typically return {@code false} for performance reasons * (since the important info is provided by their children), but if its properties have relevant * information (for example, a resource id called {@code credentials}, it should return * {@code true}. On the other hand, views representing labels or editable fields should * typically return {@code true}, but in some cases they could return {@code false} * (for example, if they're part of a "Captcha" mechanism). * *
The value returned by this method depends on the value returned by * {@link #getImportantForAutofill(View)}: * *
When a view is considered important for autofill: *
On the other hand, when a view is considered not important for autofill: *
This method is only supported on API >= 26. * On API 25 and below, it will always return {@code true}.
* * @return whether the view is considered important for autofill. * * @see #setImportantForAutofill(View, int) * @see View#IMPORTANT_FOR_AUTOFILL_AUTO * @see View#IMPORTANT_FOR_AUTOFILL_YES * @see View#IMPORTANT_FOR_AUTOFILL_NO * @see View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS * @see View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS * @see android.view.autofill.AutofillManager#requestAutofill(View) */ public static boolean isImportantForAutofill(@NonNull View v) { if (Build.VERSION.SDK_INT >= 26) { return v.isImportantForAutofill(); } return true; } /** * Checks whether provided View has an accessibility delegate attached to it. * * @param v The View instance to check * @return True if the View has an accessibility delegate */ public static boolean hasAccessibilityDelegate(@NonNull View v) { if (sAccessibilityDelegateCheckFailed) { return false; // View implementation might have changed. } if (sAccessibilityDelegateField == null) { try { sAccessibilityDelegateField = View.class .getDeclaredField("mAccessibilityDelegate"); sAccessibilityDelegateField.setAccessible(true); } catch (Throwable t) { sAccessibilityDelegateCheckFailed = true; return false; } } try { return sAccessibilityDelegateField.get(v) != null; } catch (Throwable t) { sAccessibilityDelegateCheckFailed = true; return false; } } /** * Indicates whether the view is currently tracking transient state that the * app should not need to concern itself with saving and restoring, but that * the framework should take special note to preserve when possible. * * @param view View to check for transient state * @return true if the view has transient state */ public static boolean hasTransientState(@NonNull View view) { if (Build.VERSION.SDK_INT >= 16) { return view.hasTransientState(); } return false; } /** * Set whether this view is currently tracking transient state that the * framework should attempt to preserve when possible. * * @param view View tracking transient state * @param hasTransientState true if this view has transient state */ public static void setHasTransientState(@NonNull View view, boolean hasTransientState) { if (Build.VERSION.SDK_INT >= 16) { view.setHasTransientState(hasTransientState); } } /** *Cause an invalidate to happen on the next animation time step, typically the * next display frame.
* *This method can be invoked from outside of the UI thread * only when this View is attached to a window.
* * @param view View to invalidate */ public static void postInvalidateOnAnimation(@NonNull View view) { if (Build.VERSION.SDK_INT >= 16) { view.postInvalidateOnAnimation(); } else { view.postInvalidate(); } } /** *Cause an invalidate of the specified area to happen on the next animation * time step, typically the next display frame.
* *This method can be invoked from outside of the UI thread * only when this View is attached to a window.
* * @param view View to invalidate * @param left The left coordinate of the rectangle to invalidate. * @param top The top coordinate of the rectangle to invalidate. * @param right The right coordinate of the rectangle to invalidate. * @param bottom The bottom coordinate of the rectangle to invalidate. */ public static void postInvalidateOnAnimation(@NonNull View view, int left, int top, int right, int bottom) { if (Build.VERSION.SDK_INT >= 16) { view.postInvalidateOnAnimation(left, top, right, bottom); } else { view.postInvalidate(left, top, right, bottom); } } /** *Causes the Runnable to execute on the next animation time step. * The runnable will be run on the user interface thread.
* *This method can be invoked from outside of the UI thread * only when this View is attached to a window.
* * @param view View to post this Runnable to * @param action The Runnable that will be executed. */ public static void postOnAnimation(@NonNull View view, Runnable action) { if (Build.VERSION.SDK_INT >= 16) { view.postOnAnimation(action); } else { view.postDelayed(action, ValueAnimator.getFrameDelay()); } } /** *Causes the Runnable to execute on the next animation time step, * after the specified amount of time elapses. * The runnable will be run on the user interface thread.
* *This method can be invoked from outside of the UI thread * only when this View is attached to a window.
* * @param view The view to post this Runnable to * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. */ public static void postOnAnimationDelayed(@NonNull View view, Runnable action, long delayMillis) { if (Build.VERSION.SDK_INT >= 16) { view.postOnAnimationDelayed(action, delayMillis); } else { view.postDelayed(action, ValueAnimator.getFrameDelay() + delayMillis); } } /** * Gets the mode for determining whether this View is important for accessibility * which is if it fires accessibility events and if it is reported to * accessibility services that query the screen. * * @param view The view whose property to get. * @return The mode for determining whether a View is important for accessibility. * * @see #IMPORTANT_FOR_ACCESSIBILITY_YES * @see #IMPORTANT_FOR_ACCESSIBILITY_NO * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO */ @ImportantForAccessibility public static int getImportantForAccessibility(@NonNull View view) { if (Build.VERSION.SDK_INT >= 16) { return view.getImportantForAccessibility(); } return IMPORTANT_FOR_ACCESSIBILITY_AUTO; } /** * Sets how to determine whether this view is important for accessibility * which is if it fires accessibility events and if it is reported to * accessibility services that query the screen. ** Note: If the current platform version does not support the * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} mode, then * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO} will be used as it is the * closest terms of semantics. *
* * @param view The view whose property to set. * @param mode How to determine whether this view is important for accessibility. * * @see #IMPORTANT_FOR_ACCESSIBILITY_YES * @see #IMPORTANT_FOR_ACCESSIBILITY_NO * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO */ public static void setImportantForAccessibility(@NonNull View view, @ImportantForAccessibility int mode) { if (Build.VERSION.SDK_INT >= 19) { view.setImportantForAccessibility(mode); } else if (Build.VERSION.SDK_INT >= 16) { // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS is not available // on this platform so replace with IMPORTANT_FOR_ACCESSIBILITY_NO // which is closer semantically. if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) { mode = IMPORTANT_FOR_ACCESSIBILITY_NO; } //noinspection WrongConstant view.setImportantForAccessibility(mode); } } /** * Computes whether this view should be exposed for accessibility. In * general, views that are interactive or provide information are exposed * while views that serve only as containers are hidden. *
* If an ancestor of this view has importance
* {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, this method
* returns false
.
*
* Otherwise, the value is computed according to the view's * {@link #getImportantForAccessibility(View)} value: *
false
*
* true
* true
if
* view satisfies any of the following:
* * Note: Prior to API 21, this method will always return {@code true}. * * @return Whether the view is exposed for accessibility. * @see #setImportantForAccessibility(View, int) * @see #getImportantForAccessibility(View) */ public static boolean isImportantForAccessibility(@NonNull View view) { if (Build.VERSION.SDK_INT >= 21) { return view.isImportantForAccessibility(); } return true; } /** * Performs the specified accessibility action on the view. For * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}. *
* If an {@link AccessibilityDelegateCompat} has been specified via calling * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)} * is responsible for handling this call. *
* * @param action The action to perform. * @param arguments Optional action arguments. * @return Whether the action was performed. */ public static boolean performAccessibilityAction(@NonNull View view, int action, Bundle arguments) { if (Build.VERSION.SDK_INT >= 16) { return view.performAccessibilityAction(action, arguments); } return false; } /** * Gets the provider for managing a virtual view hierarchy rooted at this View * and reported to {@link android.accessibilityservice.AccessibilityService}s * that explore the window content. ** If this method returns an instance, this instance is responsible for managing * {@link AccessibilityNodeInfoCompat}s describing the virtual sub-tree rooted at * this View including the one representing the View itself. Similarly the returned * instance is responsible for performing accessibility actions on any virtual * view or the root view itself. *
** If an {@link AccessibilityDelegateCompat} has been specified via calling * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)} * is responsible for handling this call. *
* * @param view The view whose property to get. * @return The provider. * * @see AccessibilityNodeProviderCompat */ public static AccessibilityNodeProviderCompat getAccessibilityNodeProvider(@NonNull View view) { if (Build.VERSION.SDK_INT >= 16) { AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); if (provider != null) { return new AccessibilityNodeProviderCompat(provider); } } return null; } /** * The opacity of the view. This is a value from 0 to 1, where 0 means the view is * completely transparent and 1 means the view is completely opaque. * *By default this is 1.0f. * @return The opacity of the view. * * @deprecated Use {@link View#getAlpha()} directly. */ @Deprecated public static float getAlpha(View view) { return view.getAlpha(); } /** *
Specifies the type of layer backing this view. The layer can be * {@link View#LAYER_TYPE_NONE disabled}, {@link View#LAYER_TYPE_SOFTWARE software} or * {@link View#LAYER_TYPE_HARDWARE hardware}.
* *A layer is associated with an optional {@link android.graphics.Paint} * instance that controls how the layer is composed on screen. The following * properties of the paint are taken into account when composing the layer:
*If this view has an alpha value set to < 1.0 by calling * setAlpha(float), the alpha value of the layer's paint is replaced by * this view's alpha value. Calling setAlpha(float) is therefore * equivalent to setting a hardware layer on this view and providing a paint with * the desired alpha value.
* *
Refer to the documentation of {@link View#LAYER_TYPE_NONE disabled}, * {@link View#LAYER_TYPE_SOFTWARE software} and {@link View#LAYER_TYPE_HARDWARE hardware} * for more information on when and how to use layers.
* * @param view View to set the layer type for * @param layerType The type of layer to use with this view, must be one of * {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or * {@link View#LAYER_TYPE_HARDWARE} * @param paint The paint used to compose the layer. This argument is optional * and can be null. It is ignored when the layer type is * {@link View#LAYER_TYPE_NONE} * * @deprecated Use {@link View#setLayerType(int, Paint)} directly. */ @Deprecated public static void setLayerType(View view, @LayerType int layerType, Paint paint) { view.setLayerType(layerType, paint); } /** * Indicates what type of layer is currently associated with this view. By default * a view does not have a layer, and the layer type is {@link View#LAYER_TYPE_NONE}. * Refer to the documentation of * {@link #setLayerType(android.view.View, int, android.graphics.Paint)} * for more information on the different types of layers. * * @param view The view to fetch the layer type from * @return {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or * {@link View#LAYER_TYPE_HARDWARE} * * @see #setLayerType(android.view.View, int, android.graphics.Paint) * @see View#LAYER_TYPE_NONE * @see View#LAYER_TYPE_SOFTWARE * @see View#LAYER_TYPE_HARDWARE * * @deprecated Use {@link View#getLayerType()} directly. */ @Deprecated @LayerType public static int getLayerType(View view) { //noinspection ResourceType return view.getLayerType(); } /** * Gets the id of a view for which a given view serves as a label for * accessibility purposes. * * @param view The view on which to invoke the corresponding method. * @return The labeled view id. */ public static int getLabelFor(@NonNull View view) { if (Build.VERSION.SDK_INT >= 17) { return view.getLabelFor(); } return 0; } /** * Sets the id of a view for which a given view serves as a label for * accessibility purposes. * * @param view The view on which to invoke the corresponding method. * @param labeledId The labeled view id. */ public static void setLabelFor(@NonNull View view, @IdRes int labeledId) { if (Build.VERSION.SDK_INT >= 17) { view.setLabelFor(labeledId); } } /** * Updates the {@link Paint} object used with the current layer (used only if the current * layer type is not set to {@link View#LAYER_TYPE_NONE}). Changed properties of the Paint * provided to {@link #setLayerType(android.view.View, int, android.graphics.Paint)} * will be used the next time the View is redrawn, but * {@link #setLayerPaint(android.view.View, android.graphics.Paint)} * must be called to ensure that the view gets redrawn immediately. * *A layer is associated with an optional {@link android.graphics.Paint} * instance that controls how the layer is composed on screen. The following * properties of the paint are taken into account when composing the layer:
*If this view has an alpha value set to < 1.0 by calling * View#setAlpha(float), the alpha value of the layer's paint is replaced by * this view's alpha value. Calling View#setAlpha(float) is therefore * equivalent to setting a hardware layer on this view and providing a paint with * the desired alpha value.
* * @param view View to set a layer paint for * @param paint The paint used to compose the layer. This argument is optional * and can be null. It is ignored when the layer type is * {@link View#LAYER_TYPE_NONE} * * @see #setLayerType(View, int, android.graphics.Paint) */ public static void setLayerPaint(@NonNull View view, Paint paint) { if (Build.VERSION.SDK_INT >= 17) { view.setLayerPaint(paint); } else { // Make sure the paint is correct; this will be cheap if it's the same // instance as was used to call setLayerType earlier. view.setLayerType(view.getLayerType(), paint); // This is expensive, but the only way to accomplish this before JB-MR1. view.invalidate(); } } /** * Returns the resolved layout direction for this view. * * @param view View to get layout direction for * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. * * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version * is lower than Jellybean MR1 (API 17) */ @ResolvedLayoutDirectionMode public static int getLayoutDirection(@NonNull View view) { if (Build.VERSION.SDK_INT >= 17) { return view.getLayoutDirection(); } return LAYOUT_DIRECTION_LTR; } /** * Set the layout direction for this view. This will propagate a reset of layout direction * resolution to the view's children and resolve layout direction for this view. * * @param view View to set layout direction for * @param layoutDirection the layout direction to set. Should be one of: * * {@link #LAYOUT_DIRECTION_LTR}, * {@link #LAYOUT_DIRECTION_RTL}, * {@link #LAYOUT_DIRECTION_INHERIT}, * {@link #LAYOUT_DIRECTION_LOCALE}. * * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution * proceeds up the parent chain of the view to get the value. If there is no parent, then it * will return the default {@link #LAYOUT_DIRECTION_LTR}. */ public static void setLayoutDirection(@NonNull View view, @LayoutDirectionMode int layoutDirection) { if (Build.VERSION.SDK_INT >= 17) { view.setLayoutDirection(layoutDirection); } } /** * Gets the parent for accessibility purposes. Note that the parent for * accessibility is not necessary the immediate parent. It is the first * predecessor that is important for accessibility. * * @param view View to retrieve parent for * @return The parent for use in accessibility inspection */ public static ViewParent getParentForAccessibility(@NonNull View view) { if (Build.VERSION.SDK_INT >= 16) { return view.getParentForAccessibility(); } return view.getParent(); } /** * Finds the first descendant view with the given ID, the view itself if the ID matches * {@link View#getId()}, or throws an IllegalArgumentException if the ID is invalid or there * is no matching view in the hierarchy. *
* Note: In most cases -- depending on compiler support --
* the resulting view is automatically cast to the target class type. If
* the target class type is unconstrained, an explicit cast may be
* necessary.
*
* @param id the ID to search for
* @return a view with given ID
* @see View#findViewById(int)
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public static
* For example, in a login screen with a TextView that displays an "incorrect
* password" notification, that view should be marked as a live region with
* mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
*
* To disable change notifications for this view, use
* {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
* mode for most views.
*
* To indicate that the user should be notified of changes, use
* {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
*
* If the view's changes should interrupt ongoing speech and notify the user
* immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
*
* @param view The view on which to set the live region mode
* @param mode The live region mode for this view, one of:
*
*
* @param view The view whose Matrix will be returned
* @return The current transform matrix for the view
*
* @see #getRotation(View)
* @see #getScaleX(View)
* @see #getScaleY(View)
* @see #getPivotX(View)
* @see #getPivotY(View)
*
* @deprecated Use {@link View#getMatrix()} directly.
*/
@Deprecated
@Nullable
public static Matrix getMatrix(View view) {
return view.getMatrix();
}
/**
* Returns the minimum width of the view.
*
* Prior to API 16, this method may return 0 on some platforms. Prior to API 16, this method may return 0 on some platforms. Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
* completely transparent and 1 means the view is completely opaque. Note that setting alpha to a translucent value (0 < alpha < 1) can have significant
* performance implications, especially for large views. It is best to use the alpha property
* sparingly and transiently, as in the case of fading animations. This returns null if the View has not been given a name. Prior to API 7 this will have no effect.
* On API 21 and above, also calls Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set
* it will be called during dispatch instead of this method. The listener may optionally
* call this method from its own implementation if it wishes to apply the view's default
* insets policy in addition to its own. This method should be called by clients wishing to apply insets corresponding to areas
* obscured by window decorations or overlays. This can include the status and navigation bars,
* action bars, input methods and more. New inset categories may be added in the future.
* The method returns the insets provided minus any that were applied by this view or its
* children. This function, intended to be overridden by specific View types, is an optimization when
* alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to
* an offscreen buffer and then composited into place, which can be expensive. If the view has
* no overlapping rendering, the view can draw each primitive with the appropriate alpha value
* directly. An example of overlapping rendering is a TextView with a background image, such as
* a Button. An example of non-overlapping rendering is a TextView with no background, or an
* ImageView with only the foreground image. The default implementation returns true; subclasses
* should override if they have cases which can be optimized.
* Only returns meaningful info when running on API v21 or newer, or if {@code view}
* implements the {@code TintableBackgroundView} interface.
*/
public static ColorStateList getBackgroundTintList(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 21) {
return view.getBackgroundTintList();
}
return (view instanceof TintableBackgroundView)
? ((TintableBackgroundView) view).getSupportBackgroundTintList()
: null;
}
/**
* Applies a tint to the background drawable.
*
* This will always take effect when running on API v21 or newer. When running on platforms
* previous to API v21, it will only take effect if {@code view} implements the
* {@code TintableBackgroundView} interface.
*/
public static void setBackgroundTintList(@NonNull View view, ColorStateList tintList) {
if (Build.VERSION.SDK_INT >= 21) {
view.setBackgroundTintList(tintList);
if (Build.VERSION.SDK_INT == 21) {
// Work around a bug in L that did not update the state of the background
// after applying the tint
Drawable background = view.getBackground();
boolean hasTint = (view.getBackgroundTintList() != null)
|| (view.getBackgroundTintMode() != null);
if ((background != null) && hasTint) {
if (background.isStateful()) {
background.setState(view.getDrawableState());
}
view.setBackground(background);
}
}
} else if (view instanceof TintableBackgroundView) {
((TintableBackgroundView) view).setSupportBackgroundTintList(tintList);
}
}
/**
* Return the blending mode used to apply the tint to the background
* drawable, if specified.
*
* Only returns meaningful info when running on API v21 or newer, or if {@code view}
* implements the {@code TintableBackgroundView} interface.
*/
public static PorterDuff.Mode getBackgroundTintMode(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 21) {
return view.getBackgroundTintMode();
}
return (view instanceof TintableBackgroundView)
? ((TintableBackgroundView) view).getSupportBackgroundTintMode()
: null;
}
/**
* Specifies the blending mode used to apply the tint specified by
* {@link #setBackgroundTintList(android.view.View, android.content.res.ColorStateList)} to
* the background drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
*
* This will always take effect when running on API v21 or newer. When running on platforms
* previous to API v21, it will only take effect if {@code view} implement the
* {@code TintableBackgroundView} interface.
*/
public static void setBackgroundTintMode(@NonNull View view, PorterDuff.Mode mode) {
if (Build.VERSION.SDK_INT >= 21) {
view.setBackgroundTintMode(mode);
if (Build.VERSION.SDK_INT == 21) {
// Work around a bug in L that did not update the state of the background
// after applying the tint
Drawable background = view.getBackground();
boolean hasTint = (view.getBackgroundTintList() != null)
|| (view.getBackgroundTintMode() != null);
if ((background != null) && hasTint) {
if (background.isStateful()) {
background.setState(view.getDrawableState());
}
view.setBackground(background);
}
}
} else if (view instanceof TintableBackgroundView) {
((TintableBackgroundView) view).setSupportBackgroundTintMode(mode);
}
}
// TODO: getters for various view properties (rotation, etc)
/**
* Enable or disable nested scrolling for this view.
*
* If this property is set to true the view will be permitted to initiate nested
* scrolling operations with a compatible parent view in the current hierarchy. If this
* view does not implement nested scrolling this will have no effect. Disabling nested scrolling
* while a nested scroll is in progress has the effect of
* {@link #stopNestedScroll(View) stopping} the nested scroll. If nested scrolling is enabled and this View class implementation supports it,
* this view will act as a nested scrolling child view when applicable, forwarding data
* about the scroll operation in progress to a compatible and cooperating nested scrolling
* parent. This version of the method just calls {@link #startNestedScroll(View, int, int)} using
* the touch input type. This version of the method just calls {@link #stopNestedScroll(View, int)} using the
* touch input type. This version of the method just calls {@link #hasNestedScrollingParent(View, int)}
* using the touch input type. This version of the method just calls
* {@link #dispatchNestedScroll(View, int, int, int, int, int[], int)} using the touch input
* type. This version of the method just calls
* {@link #dispatchNestedPreScroll(View, int, int, int[], int[], int)} using the touch input
* type. A view starting a nested scroll promises to abide by the following contract: The view will call startNestedScroll upon initiating a scroll operation. In the case
* of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
* In the case of touch scrolling the nested scroll will be terminated automatically in
* the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
* In the event of programmatic scrolling the caller must explicitly call
* {@link #stopNestedScroll(View)} to indicate the end of the nested scroll. If At each incremental step of the scroll the caller should invoke
* {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll}
* once it has calculated the requested scrolling delta. If it returns true the nested scrolling
* parent at least partially consumed the scroll and the caller should adjust the amount it
* scrolls by. After applying the remainder of the scroll delta the caller should invoke
* {@link #dispatchNestedScroll(View, int, int, int, int, int[]) dispatchNestedScroll}, passing
* both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
* these values differently. See
* {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}.
* Calling this method when a nested scroll is not currently in progress is harmless. The presence of a nested scrolling parent indicates that this view has initiated
* a nested scroll and it was accepted by an ancestor view further up the view hierarchy. Implementations of views that support nested scrolling should call this to report
* info about a scroll in progress to the current nested scrolling parent. If a nested scroll
* is not currently in progress or nested scrolling is not
* {@link #isNestedScrollingEnabled(View) enabled} for this view this method does nothing. Compatible View implementations should also call
* {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} before
* consuming a component of the scroll event themselves. Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
* This method should be used to indicate that a nested scrolling child has detected
* suitable conditions for a fling. Generally this means that a touch scroll has ended with a
* {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
* the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
* along a scrollable axis. If a nested scrolling child view would normally fling but it is at the edge of
* its own content, it can use this method to delegate the fling to its nested scrolling
* parent instead. The parent may optionally consume the fling or observe a child fling. Nested pre-fling events are to nested fling events what touch intercept is to touch
* and what nested pre-scroll is to nested scroll. For a better user experience, only one view in a nested scrolling chain should consume
* the fling at a time. If a parent view consumed the fling this method will return false.
* Custom view implementations should account for this in two ways: Views should also not offer fling velocities to nested parent views along an axis
* where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
* should not offer a horizontal fling velocity to its parents since scrolling along that
* axis is not permitted and carrying velocity along that motion does not make sense.
* Compatibility:
*
* Compatibility:
*
* Compatibility:
* Prior to API 18 this does nothing. Prior to API 18 this will return null.
* See {@link #setScrollIndicators(View, int, int)} for usage information.
*
* @param indicators a bitmask of indicators that should be enabled, or
* {@code 0} to disable all indicators
*
* @see #setScrollIndicators(View, int, int)
* @see #getScrollIndicators(View)
*/
public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) {
if (Build.VERSION.SDK_INT >= 23) {
view.setScrollIndicators(indicators);
}
}
/**
* Sets the state of the scroll indicators specified by the mask. To change
* all scroll indicators at once, see {@link #setScrollIndicators(View, int)}.
*
* When a scroll indicator is enabled, it will be displayed if the view
* can scroll in the direction of the indicator.
*
* Multiple indicator types may be enabled or disabled by passing the
* logical OR of the desired types. If multiple types are specified, they
* will all be set to the same enabled state.
*
* For example, to enable the top scroll indicatorExample: {@code setScrollIndicators}
*
* @param indicators the indicator direction, or the logical OR of multiple
* indicator directions. One or more of:
*
* For example, if the top and left scroll indicators are enabled and all
* other indicators are disabled, the return value will be
* {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}.
*
* To check whether the bottom scroll indicator is enabled, use the value
* of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}.
*
* @return a bitmask representing the enabled scroll indicators
*/
public static int getScrollIndicators(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 23) {
return view.getScrollIndicators();
}
return 0;
}
/**
* Set the pointer icon for the current view.
* @param pointerIcon A PointerIconCompat instance which will be shown when the mouse hovers.
*/
public static void setPointerIcon(@NonNull View view, PointerIconCompat pointerIcon) {
if (Build.VERSION.SDK_INT >= 24) {
view.setPointerIcon((PointerIcon) (pointerIcon != null
? pointerIcon.getPointerIcon() : null));
}
}
/**
* Gets the logical display to which the view's window has been attached.
*
* Compatibility:
* Prior to API 26 this does nothing. Use TooltipCompat class from v7 appcompat library
* for a compatible tooltip implementation.
* Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
* window or serves as a target of cluster navigation.
*
* @return {@code true} if {@code view} is the default-focus view, {@code false} otherwise.
*/
public static boolean isFocusedByDefault(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 26) {
return view.isFocusedByDefault();
}
return false;
}
/**
* Sets whether {@code view} should receive focus when the focus is restored for the view
* hierarchy containing it.
*
* Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
* window or serves as a target of cluster navigation.
*
* Does nothing on API < 26.
*
* @param isFocusedByDefault {@code true} to set {@code view} as the default-focus view,
* {@code false} otherwise.
*/
public static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) {
if (Build.VERSION.SDK_INT >= 26) {
view.setFocusedByDefault(isFocusedByDefault);
}
}
/**
* Find the nearest keyboard navigation cluster in the specified direction.
* This does not actually give focus to that cluster.
*
* @param currentCluster The starting point of the search. {@code null} means the current
* cluster is not found yet.
* @param direction Direction to look.
*
* @return the nearest keyboard navigation cluster in the specified direction, or {@code null}
* if one can't be found or if API < 26.
*/
public static View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster,
@FocusDirection int direction) {
if (Build.VERSION.SDK_INT >= 26) {
return view.keyboardNavigationClusterSearch(currentCluster, direction);
}
return null;
}
/**
* Adds any keyboard navigation cluster roots that are descendants of {@code view} (
* including {@code view} if it is a cluster root itself) to {@code views}. Does nothing
* on API < 26.
*
* @param views collection of keyboard navigation cluster roots found so far.
* @param direction direction to look.
*/
public static void addKeyboardNavigationClusters(@NonNull View view,
@NonNull Collection This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of
* {@link View#hasFocusable()} in that only views explicitly set focusable will cause
* this method to return true. A view set to {@link View#FOCUSABLE_AUTO} that resolves
* to focusable will not.
*
*/
public static void setAccessibilityLiveRegion(@NonNull View view,
@AccessibilityLiveRegion int mode) {
if (Build.VERSION.SDK_INT >= 19) {
view.setAccessibilityLiveRegion(mode);
}
}
/**
* Returns the start padding of the specified view depending on its resolved layout direction.
* If there are inset and enabled scrollbars, this value may include the space
* required to display the scrollbars as well.
*
* @param view The view to get padding for
* @return the start padding in pixels
*/
@Px
public static int getPaddingStart(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 17) {
return view.getPaddingStart();
}
return view.getPaddingLeft();
}
/**
* Returns the end padding of the specified view depending on its resolved layout direction.
* If there are inset and enabled scrollbars, this value may include the space
* required to display the scrollbars as well.
*
* @param view The view to get padding for
* @return the end padding in pixels
*/
@Px
public static int getPaddingEnd(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 17) {
return view.getPaddingEnd();
}
return view.getPaddingRight();
}
/**
* Sets the relative padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingStart}, {@link View#getPaddingTop},
* {@link #getPaddingEnd} and {@link View#getPaddingBottom} may be different
* from the values set in this call.
*
* @param view The view on which to set relative padding
* @param start the start padding in pixels
* @param top the top padding in pixels
* @param end the end padding in pixels
* @param bottom the bottom padding in pixels
*/
public static void setPaddingRelative(@NonNull View view, @Px int start, @Px int top,
@Px int end, @Px int bottom) {
if (Build.VERSION.SDK_INT >= 17) {
view.setPaddingRelative(start, top, end, bottom);
} else {
view.setPadding(start, top, end, bottom);
}
}
private static void bindTempDetach() {
try {
sDispatchStartTemporaryDetach = View.class.getDeclaredMethod(
"dispatchStartTemporaryDetach");
sDispatchFinishTemporaryDetach = View.class.getDeclaredMethod(
"dispatchFinishTemporaryDetach");
} catch (NoSuchMethodException e) {
Log.e(TAG, "Couldn't find method", e);
}
sTempDetachBound = true;
}
/**
* Notify a view that it is being temporarily detached.
*/
public static void dispatchStartTemporaryDetach(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 24) {
view.dispatchStartTemporaryDetach();
} else {
if (!sTempDetachBound) {
bindTempDetach();
}
if (sDispatchStartTemporaryDetach != null) {
try {
sDispatchStartTemporaryDetach.invoke(view);
} catch (Exception e) {
Log.d(TAG, "Error calling dispatchStartTemporaryDetach", e);
}
} else {
// Try this instead
view.onStartTemporaryDetach();
}
}
}
/**
* Notify a view that its temporary detach has ended; the view is now reattached.
*/
public static void dispatchFinishTemporaryDetach(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 24) {
view.dispatchFinishTemporaryDetach();
} else {
if (!sTempDetachBound) {
bindTempDetach();
}
if (sDispatchFinishTemporaryDetach != null) {
try {
sDispatchFinishTemporaryDetach.invoke(view);
} catch (Exception e) {
Log.d(TAG, "Error calling dispatchFinishTemporaryDetach", e);
}
} else {
// Try this instead
view.onFinishTemporaryDetach();
}
}
}
/**
* The horizontal location of this view relative to its {@link View#getLeft() left} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The horizontal position of this view relative to its left position, in pixels.
*
* @deprecated Use {@link View#getTranslationX()} directly.
*/
@Deprecated
public static float getTranslationX(View view) {
return view.getTranslationX();
}
/**
* The vertical location of this view relative to its {@link View#getTop() top} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The vertical position of this view relative to its top position, in pixels.
*
* @deprecated Use {@link View#getTranslationY()} directly.
*/
@Deprecated
public static float getTranslationY(View view) {
return view.getTranslationY();
}
/**
* The transform matrix of this view, which is calculated based on the current
* rotation, scale, and pivot properties.
* Drawable.jumpToCurrentState()
* on all Drawable objects associated with this view.
* StateListAnimator#jumpToCurrentState()
* if there is a StateListAnimator attached to this view.
*
* @deprecated Use {@link View#jumpDrawablesToCurrentState()} directly.
*/
@Deprecated
public static void jumpDrawablesToCurrentState(View v) {
v.jumpDrawablesToCurrentState();
}
/**
* Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying
* window insets to this view. This will only take effect on devices with API 21 or above.
*/
public static void setOnApplyWindowInsetsListener(@NonNull View v,
final OnApplyWindowInsetsListener listener) {
if (Build.VERSION.SDK_INT >= 21) {
if (listener == null) {
v.setOnApplyWindowInsetsListener(null);
return;
}
v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
@RequiresApi(21) // TODO remove https://issuetracker.google.com/issues/76458979
public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets);
compatInsets = listener.onApplyWindowInsets(view, compatInsets);
return (WindowInsets) WindowInsetsCompat.unwrap(compatInsets);
}
});
}
}
/**
* Called when the view should apply {@link WindowInsetsCompat} according to its internal policy.
*
* startNestedScroll
returns true, a cooperative parent was found.
* If it returns false the caller may ignore the rest of this contract until the next scroll.
* Calling startNestedScroll while a nested scroll is already in progress will return true.dispatchNestedPreScroll
offers an opportunity for the parent view in a nested
* scrolling operation to consume some or all of the scroll operation before the child view
* consumes it.dispatchNestedPreFling
* offsets an opportunity for the parent view in a nested fling to fully consume the fling
* before the child view consumes it. If this method returns true
, a nested
* parent view consumed the fling and this view should not scroll as a result.
*
*
* dispatchNestedPreFling
; consume the fling and settle to a valid
* position regardless.
*
*
* @return whether the view hierarchy is currently undergoing a layout pass
*/
public static boolean isInLayout(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 18) {
return view.isInLayout();
}
return false;
}
/**
* Returns true if {@code view} has been through at least one layout since it
* was last attached to or detached from a window.
*/
public static boolean isLaidOut(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 19) {
return view.isLaidOut();
}
return view.getWidth() > 0 && view.getHeight() > 0;
}
/**
* Returns whether layout direction has been resolved.
*
*
*
* @return true if layout direction has been resolved.
*/
public static boolean isLayoutDirectionResolved(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 19) {
return view.isLayoutDirectionResolved();
}
return false;
}
/**
* The visual z position of this view, in pixels. This is equivalent to the
* {@link #setTranslationZ(View, float) translationZ} property plus the current
* {@link #getElevation(View) elevation} property.
*
* @return The visual z position of this view, in pixels.
*/
public static float getZ(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 21) {
return view.getZ();
}
return 0f;
}
/**
* Sets the visual z position of this view, in pixels. This is equivalent to setting the
* {@link #setTranslationZ(View, float) translationZ} property to be the difference between
* the x value passed in and the current {@link #getElevation(View) elevation} property.
*
*
*
* @param z The visual z position of this view, in pixels.
*/
public static void setZ(@NonNull View view, float z) {
if (Build.VERSION.SDK_INT >= 21) {
view.setZ(z);
}
}
/**
* Offset this view's vertical location by the specified number of pixels.
*
* @param offset the number of pixels to offset the view by
*/
public static void offsetTopAndBottom(@NonNull View view, int offset) {
if (Build.VERSION.SDK_INT >= 23) {
view.offsetTopAndBottom(offset);
} else if (Build.VERSION.SDK_INT >= 21) {
final Rect parentRect = getEmptyTempRect();
boolean needInvalidateWorkaround = false;
final ViewParent parent = view.getParent();
if (parent instanceof View) {
final View p = (View) parent;
parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
// If the view currently does not currently intersect the parent (and is therefore
// not displayed) we may need need to invalidate
needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom());
}
// Now offset, invoking the API 14+ implementation (which contains its own workarounds)
compatOffsetTopAndBottom(view, offset);
// The view has now been offset, so let's intersect the Rect and invalidate where
// the View is now displayed
if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom())) {
((View) parent).invalidate(parentRect);
}
} else {
compatOffsetTopAndBottom(view, offset);
}
}
private static void compatOffsetTopAndBottom(View view, int offset) {
view.offsetTopAndBottom(offset);
if (view.getVisibility() == View.VISIBLE) {
tickleInvalidationFlag(view);
ViewParent parent = view.getParent();
if (parent instanceof View) {
tickleInvalidationFlag((View) parent);
}
}
}
/**
* Offset this view's horizontal location by the specified amount of pixels.
*
* @param offset the number of pixels to offset the view by
*/
public static void offsetLeftAndRight(@NonNull View view, int offset) {
if (Build.VERSION.SDK_INT >= 23) {
view.offsetLeftAndRight(offset);
} else if (Build.VERSION.SDK_INT >= 21) {
final Rect parentRect = getEmptyTempRect();
boolean needInvalidateWorkaround = false;
final ViewParent parent = view.getParent();
if (parent instanceof View) {
final View p = (View) parent;
parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
// If the view currently does not currently intersect the parent (and is therefore
// not displayed) we may need need to invalidate
needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom());
}
// Now offset, invoking the API 14+ implementation (which contains its own workarounds)
compatOffsetLeftAndRight(view, offset);
// The view has now been offset, so let's intersect the Rect and invalidate where
// the View is now displayed
if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom())) {
((View) parent).invalidate(parentRect);
}
} else {
compatOffsetLeftAndRight(view, offset);
}
}
private static void compatOffsetLeftAndRight(View view, int offset) {
view.offsetLeftAndRight(offset);
if (view.getVisibility() == View.VISIBLE) {
tickleInvalidationFlag(view);
ViewParent parent = view.getParent();
if (parent instanceof View) {
tickleInvalidationFlag((View) parent);
}
}
}
private static void tickleInvalidationFlag(View view) {
final float y = view.getTranslationY();
view.setTranslationY(y + 1);
view.setTranslationY(y);
}
/**
* Sets a rectangular area on this view to which the view will be clipped
* when it is drawn. Setting the value to null will remove the clip bounds
* and the view will draw normally, using its full bounds.
*
*
*
*
* @see #setScrollIndicators(View, int)
* @see #getScrollIndicators(View)
*/
public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators,
@ScrollIndicators int mask) {
if (Build.VERSION.SDK_INT >= 23) {
view.setScrollIndicators(indicators, mask);
}
}
/**
* Returns a bitmask representing the enabled scroll indicators.
*
*
*
* @return The logical display, or null if the view is not currently attached to a window.
*/
@Nullable
public static Display getDisplay(@NonNull View view) {
if (Build.VERSION.SDK_INT >= 17) {
return view.getDisplay();
}
if (isAttachedToWindow(view)) {
final WindowManager wm = (WindowManager) view.getContext().getSystemService(
Context.WINDOW_SERVICE);
return wm.getDefaultDisplay();
}
return null;
}
/**
* Sets the tooltip for the view.
*
*