안드로이드 View class - andeuloideu View class

/* * Copyright (C) 2006 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 android.view; importstatic android.content.res.Resources.ID_NULL; importstatic android.view.ContentInfo.SOURCE_DRAG_AND_DROP; importstatic android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; importstatic android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS; importstatic android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; importstatic android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; importstatic android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN; importstatic android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH; importstatic android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE; importstatic com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; importstatic com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; importstatic com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; importstatic com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; importstatic java.lang.Math.max; import android.animation.AnimatorInflater; import android.animation.StateListAnimator; import android.annotation.AttrRes; import android.annotation.CallSuper; import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.FloatRange; import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.Size; import android.annotation.StyleRes; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UiContext; import android.annotation.UiThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.AutofillOptions; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.RenderEffect; import android.graphics.RenderNode; import android.graphics.Shader; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManagerGlobal; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.sysprop.DisplayProperties; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.LayoutDirection; import android.util.Log; import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.Pair; import android.util.Pools.SynchronizedPool; import android.util.Property; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StateSet; import android.util.SuperNotCalledException; import android.util.TypedValue; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; import android.view.AccessibilityIterators.ParagraphTextSegmentIterator; import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.ContextMenu.ContextMenuInfo; import android.view.InputDevice.InputSourceClass; import android.view.Window.OnContentApplyWindowInsetsListener; import android.view.WindowInsets.Type; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeIdManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Transformation; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; import android.view.displayhash.DisplayHash; import android.view.displayhash.DisplayHashManager; import android.view.displayhash.DisplayHashResultCallback; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; import android.view.inspector.InspectableProperty.FlagEntry; import android.view.translation.TranslationCapability; import android.view.translation.TranslationSpec.DataFormat; import android.view.translation.ViewTranslationCallback; import android.view.translation.ViewTranslationRequest; import android.view.translation.ViewTranslationResponse; import android.widget.Checkable; import android.widget.FrameLayout; import android.widget.ScrollBarDrawable; import android.window.OnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.Preconditions; import com.android.internal.view.ScrollCaptureInternal; import com.android.internal.view.TooltipPopup; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.ScrollBarUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Predicate; /** * <p> * This class represents the basic building block for user interface components. A View * occupies a rectangular area on the screen and is responsible for drawing and * event handling. View is the base class for <em>widgets</em>, which are * used to create interactive UI components (buttons, text fields, etc.). The * {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which * are invisible containers that hold other Views (or other ViewGroups) and define * their layout properties. * </p> * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For information about using this class to develop your application's user interface, * read the <a href="{@docRoot}guide/topics/ui/index.html">User Interface</a> developer guide. * </div> * * <a name="Using"></a> * <h3>Using Views</h3> * <p> * All of the views in a window are arranged in a single tree. You can add views * either from code or by specifying a tree of views in one or more XML layout * files. There are many specialized subclasses of views that act as controls or * are capable of displaying text, images, or other content. * </p> * <p> * Once you have created a tree of views, there are typically a few types of * common operations you may wish to perform: * <ul> * <li><strong>Set properties:</strong> for example setting the text of a * {@link android.widget.TextView}. The available properties and the methods * that set them will vary among the different subclasses of views. Note that * properties that are known at build time can be set in the XML layout * files.</li> * <li><strong>Set focus:</strong> The framework will handle moving focus in * response to user input. To force focus to a specific view, call * {@link #requestFocus}.</li> * <li><strong>Set up listeners:</strong> Views allow clients to set listeners * that will be notified when something interesting happens to the view. For * example, all views will let you set a listener to be notified when the view * gains or loses focus. You can register such a listener using * {@link #setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}. * Other view subclasses offer more specialized listeners. For example, a Button * exposes a listener to notify clients when the button is clicked.</li> * <li><strong>Set visibility:</strong> You can hide or show views using * {@link #setVisibility(int)}.</li> * </ul> * </p> * <p><em> * Note: The Android framework is responsible for measuring, laying out and * drawing views. You should not call methods that perform these actions on * views yourself unless you are actually implementing a * {@link android.view.ViewGroup}. * </em></p> * * <a name="Lifecycle"></a> * <h3>Implementing a Custom View</h3> * * <p> * To implement a custom view, you will usually begin by providing overrides for * some of the standard methods that the framework calls on all views. You do * not need to override all of these methods. In fact, you can start by just * overriding {@link #onDraw(android.graphics.Canvas)}. * <table border="2" width="85%" align="center" cellpadding="5"> * <thead> * <tr><th>Category</th> <th>Methods</th> <th>Description</th></tr> * </thead> * * <tbody> * <tr> * <td rowspan="2">Creation</td> * <td>Constructors</td> * <td>There is a form of the constructor that are called when the view * is created from code and a form that is called when the view is * inflated from a layout file. The second form should parse and apply * any attributes defined in the layout file. * </td> * </tr> * <tr> * <td><code>{@link #onFinishInflate()}</code></td> * <td>Called after a view and all of its children has been inflated * from XML.</td> * </tr> * * <tr> * <td rowspan="3">Layout</td> * <td><code>{@link #onMeasure(int, int)}</code></td> * <td>Called to determine the size requirements for this view and all * of its children. * </td> * </tr> * <tr> * <td><code>{@link #onLayout(boolean, int, int, int, int)}</code></td> * <td>Called when this view should assign a size and position to all * of its children. * </td> * </tr> * <tr> * <td><code>{@link #onSizeChanged(int, int, int, int)}</code></td> * <td>Called when the size of this view has changed. * </td> * </tr> * * <tr> * <td>Drawing</td> * <td><code>{@link #onDraw(android.graphics.Canvas)}</code></td> * <td>Called when the view should render its content. * </td> * </tr> * * <tr> * <td rowspan="4">Event processing</td> * <td><code>{@link #onKeyDown(int, KeyEvent)}</code></td> * <td>Called when a new hardware key event occurs. * </td> * </tr> * <tr> * <td><code>{@link #onKeyUp(int, KeyEvent)}</code></td> * <td>Called when a hardware key up event occurs. * </td> * </tr> * <tr> * <td><code>{@link #onTrackballEvent(MotionEvent)}</code></td> * <td>Called when a trackball motion event occurs. * </td> * </tr> * <tr> * <td><code>{@link #onTouchEvent(MotionEvent)}</code></td> * <td>Called when a touch screen motion event occurs. * </td> * </tr> * * <tr> * <td rowspan="2">Focus</td> * <td><code>{@link #onFocusChanged(boolean, int, android.graphics.Rect)}</code></td> * <td>Called when the view gains or loses focus. * </td> * </tr> * * <tr> * <td><code>{@link #onWindowFocusChanged(boolean)}</code></td> * <td>Called when the window containing the view gains or loses focus. * </td> * </tr> * * <tr> * <td rowspan="3">Attaching</td> * <td><code>{@link #onAttachedToWindow()}</code></td> * <td>Called when the view is attached to a window. * </td> * </tr> * * <tr> * <td><code>{@link #onDetachedFromWindow}</code></td> * <td>Called when the view is detached from its window. * </td> * </tr> * * <tr> * <td><code>{@link #onWindowVisibilityChanged(int)}</code></td> * <td>Called when the visibility of the window containing the view * has changed. * </td> * </tr> * </tbody> * * </table> * </p> * * <a name="IDs"></a> * <h3>IDs</h3> * Views may have an integer id associated with them. These ids are typically * assigned in the layout XML files, and are used to find specific views within * the view tree. A common pattern is to: * <ul> * <li>Define a Button in the layout file and assign it a unique ID. * <pre> * &lt;Button * android:id="@+id/my_button" * android:layout_width="wrap_content" * android:layout_height="wrap_content" * android:text="@string/my_button_text"/&gt; * </pre></li> * <li>From the onCreate method of an Activity, find the Button * <pre class="prettyprint"> * Button myButton = findViewById(R.id.my_button); * </pre></li> * </ul> * <p> * View IDs need not be unique throughout the tree, but it is good practice to * ensure that they are at least unique within the part of the tree you are * searching. * </p> * * <a name="Position"></a> * <h3>Position</h3> * <p> * The geometry of a view is that of a rectangle. A view has a location, * expressed as a pair of <em>left</em> and <em>top</em> coordinates, and * two dimensions, expressed as a width and a height. The unit for location * and dimensions is the pixel. * </p> * * <p> * It is possible to retrieve the location of a view by invoking the methods * {@link #getLeft()} and {@link #getTop()}. The former returns the left, or X, * coordinate of the rectangle representing the view. The latter returns the * top, or Y, coordinate of the rectangle representing the view. These methods * both return the location of the view relative to its parent. For instance, * when getLeft() returns 20, that means the view is located 20 pixels to the * right of the left edge of its direct parent. * </p> * * <p> * In addition, several convenience methods are offered to avoid unnecessary * computations, namely {@link #getRight()} and {@link #getBottom()}. * These methods return the coordinates of the right and bottom edges of the * rectangle representing the view. For instance, calling {@link #getRight()} * is similar to the following computation: <code>getLeft() + getWidth()</code> * (see <a href="#SizePaddingMargins">Size</a> for more information about the width.) * </p> * * <a name="SizePaddingMargins"></a> * <h3>Size, padding and margins</h3> * <p> * The size of a view is expressed with a width and a height. A view actually * possess two pairs of width and height values. * </p> * * <p> * The first pair is known as <em>measured width</em> and * <em>measured height</em>. These dimensions define how big a view wants to be * within its parent (see <a href="#Layout">Layout</a> for more details.) The * measured dimensions can be obtained by calling {@link #getMeasuredWidth()} * and {@link #getMeasuredHeight()}. * </p> * * <p> * The second pair is simply known as <em>width</em> and <em>height</em>, or * sometimes <em>drawing width</em> and <em>drawing height</em>. These * dimensions define the actual size of the view on screen, at drawing time and * after layout. These values may, but do not have to, be different from the * measured width and height. The width and height can be obtained by calling * {@link #getWidth()} and {@link #getHeight()}. * </p> * * <p> * To measure its dimensions, a view takes into account its padding. The padding * is expressed in pixels for the left, top, right and bottom parts of the view. * Padding can be used to offset the content of the view by a specific amount of * pixels. For instance, a left padding of 2 will push the view's content by * 2 pixels to the right of the left edge. Padding can be set using the * {@link #setPadding(int, int, int, int)} or {@link #setPaddingRelative(int, int, int, int)} * method and queried by calling {@link #getPaddingLeft()}, {@link #getPaddingTop()}, * {@link #getPaddingRight()}, {@link #getPaddingBottom()}, {@link #getPaddingStart()}, * {@link #getPaddingEnd()}. * </p> * * <p> * Even though a view can define a padding, it does not provide any support for * margins. However, view groups provide such a support. Refer to * {@link android.view.ViewGroup} and * {@link android.view.ViewGroup.MarginLayoutParams} for further information. * </p> * * <a name="Layout"></a> * <h3>Layout</h3> * <p> * Layout is a two pass process: a measure pass and a layout pass. The measuring * pass is implemented in {@link #measure(int, int)} and is a top-down traversal * of the view tree. Each view pushes dimension specifications down the tree * during the recursion. At the end of the measure pass, every view has stored * its measurements. The second pass happens in * {@link #layout(int,int,int,int)} and is also top-down. During * this pass each parent is responsible for positioning all of its children * using the sizes computed in the measure pass. * </p> * * <p> * When a view's measure() method returns, its {@link #getMeasuredWidth()} and * {@link #getMeasuredHeight()} values must be set, along with those for all of * that view's descendants. A view's measured width and measured height values * must respect the constraints imposed by the view's parents. This guarantees * that at the end of the measure pass, all parents accept all of their * children's measurements. A parent view may call measure() more than once on * its children. For example, the parent may measure each child once with * unspecified dimensions to find out how big they want to be, then call * measure() on them again with actual numbers if the sum of all the children's * unconstrained sizes is too big or too small. * </p> * * <p> * The measure pass uses two classes to communicate dimensions. The * {@link MeasureSpec} class is used by views to tell their parents how they * want to be measured and positioned. The base LayoutParams class just * describes how big the view wants to be for both width and height. For each * dimension, it can specify one of: * <ul> * <li> an exact number * <li>MATCH_PARENT, which means the view wants to be as big as its parent * (minus padding) * <li> WRAP_CONTENT, which means that the view wants to be just big enough to * enclose its content (plus padding). * </ul> * There are subclasses of LayoutParams for different subclasses of ViewGroup. * For example, AbsoluteLayout has its own subclass of LayoutParams which adds * an X and Y value. * </p> * * <p> * MeasureSpecs are used to push requirements down the tree from parent to * child. A MeasureSpec can be in one of three modes: * <ul> * <li>UNSPECIFIED: This is used by a parent to determine the desired dimension * of a child view. For example, a LinearLayout may call measure() on its child * with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how * tall the child view wants to be given a width of 240 pixels. * <li>EXACTLY: This is used by the parent to impose an exact size on the * child. The child must use this size, and guarantee that all of its * descendants will fit within this size. * <li>AT_MOST: This is used by the parent to impose a maximum size on the * child. The child must guarantee that it and all of its descendants will fit * within this size. * </ul> * </p> * * <p> * To initiate a layout, call {@link #requestLayout}. This method is typically * called by a view on itself when it believes that it can no longer fit within * its current bounds. * </p> * * <a name="Drawing"></a> * <h3>Drawing</h3> * <p> * Drawing is handled by walking the tree and recording the drawing commands of * any View that needs to update. After this, the drawing commands of the * entire tree are issued to screen, clipped to the newly damaged area. * </p> * * <p> * The tree is largely recorded and drawn in order, with parents drawn before * (i.e., behind) their children, with siblings drawn in the order they appear * in the tree. If you set a background drawable for a View, then the View will * draw it before calling back to its <code>onDraw()</code> method. The child * drawing order can be overridden with * {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean) custom child drawing order} * in a ViewGroup, and with {@link #setZ(float)} custom Z values} set on Views. * </p> * * <p> * To force a view to draw, call {@link #invalidate()}. * </p> * * <a name="EventHandlingThreading"></a> * <h3>Event Handling and Threading</h3> * <p> * The basic cycle of a view is as follows: * <ol> * <li>An event comes in and is dispatched to the appropriate view. The view * handles the event and notifies any listeners.</li> * <li>If in the course of processing the event, the view's bounds may need * to be changed, the view will call {@link #requestLayout()}.</li> * <li>Similarly, if in the course of processing the event the view's appearance * may need to be changed, the view will call {@link #invalidate()}.</li> * <li>If either {@link #requestLayout()} or {@link #invalidate()} were called, * the framework will take care of measuring, laying out, and drawing the tree * as appropriate.</li> * </ol> * </p> * * <p><em>Note: The entire view tree is single threaded. You must always be on * the UI thread when calling any method on any view.</em> * If you are doing work on other threads and want to update the state of a view * from that thread, you should use a {@link Handler}. * </p> * * <a name="FocusHandling"></a> * <h3>Focus Handling</h3> * <p> * The framework will handle routine focus movement in response to user input. * This includes changing the focus as views are removed or hidden, or as new * views become available. Views indicate their willingness to take focus * through the {@link #isFocusable} method. To change whether a view can take * focus, call {@link #setFocusable(boolean)}. When in touch mode (see notes below) * views indicate whether they still would like focus via {@link #isFocusableInTouchMode} * and can change this via {@link #setFocusableInTouchMode(boolean)}. * </p> * <p> * Focus movement is based on an algorithm which finds the nearest neighbor in a * given direction. In rare cases, the default algorithm may not match the * intended behavior of the developer. In these situations, you can provide * explicit overrides by using these XML attributes in the layout file: * <pre> * nextFocusDown * nextFocusLeft * nextFocusRight * nextFocusUp * </pre> * </p> * * * <p> * To get a particular view to take focus, call {@link #requestFocus()}. * </p> * * <a name="TouchMode"></a> * <h3>Touch Mode</h3> * <p> * When a user is navigating a user interface via directional keys such as a D-pad, it is * necessary to give focus to actionable items such as buttons so the user can see * what will take input. If the device has touch capabilities, however, and the user * begins interacting with the interface by touching it, it is no longer necessary to * always highlight, or give focus to, a particular view. This motivates a mode * for interaction named 'touch mode'. * </p> * <p> * For a touch capable device, once the user touches the screen, the device * will enter touch mode. From this point onward, only views for which * {@link #isFocusableInTouchMode} is true will be focusable, such as text editing widgets. * Other views that are touchable, like buttons, will not take focus when touched; they will * only fire the on click listeners. * </p> * <p> * Any time a user hits a directional key, such as a D-pad direction, the view device will * exit touch mode, and find a view to take focus, so that the user may resume interacting * with the user interface without touching the screen again. * </p> * <p> * The touch mode state is maintained across {@link android.app.Activity}s. Call * {@link #isInTouchMode} to see whether the device is currently in touch mode. * </p> * * <a name="Scrolling"></a> * <h3>Scrolling</h3> * <p> * The framework provides basic support for views that wish to internally * scroll their content. This includes keeping track of the X and Y scroll * offset as well as mechanisms for drawing scrollbars. See * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)}, and * {@link #awakenScrollBars()} for more details. * </p> * * <a name="Tags"></a> * <h3>Tags</h3> * <p> * Unlike IDs, tags are not used to identify views. Tags are essentially an * extra piece of information that can be associated with a view. They are most * often used as a convenience to store data related to views in the views * themselves rather than by putting them in a separate structure. * </p> * <p> * Tags may be specified with character sequence values in layout XML as either * a single tag using the {@link android.R.styleable#View_tag android:tag} * attribute or multiple tags using the {@code <tag>} child element: * <pre> * &lt;View ... * android:tag="@string/mytag_value" /&gt; * &lt;View ...&gt; * &lt;tag android:id="@+id/mytag" * android:value="@string/mytag_value" /&gt; * &lt;/View> * </pre> * </p> * <p> * Tags may also be specified with arbitrary objects from code using * {@link #setTag(Object)} or {@link #setTag(int, Object)}. * </p> * * <a name="Themes"></a> * <h3>Themes</h3> * <p> * By default, Views are created using the theme of the Context object supplied * to their constructor; however, a different theme may be specified by using * the {@link android.R.styleable#View_theme android:theme} attribute in layout * XML or by passing a {@link ContextThemeWrapper} to the constructor from * code. * </p> * <p> * When the {@link android.R.styleable#View_theme android:theme} attribute is * used in XML, the specified theme is applied on top of the inflation * context's theme (see {@link LayoutInflater}) and used for the view itself as * well as any child elements. * </p> * <p> * In the following example, both views will be created using the Material dark * color scheme; however, because an overlay theme is used which only defines a * subset of attributes, the value of * {@link android.R.styleable#Theme_colorAccent android:colorAccent} defined on * the inflation context's theme (e.g. the Activity theme) will be preserved. * <pre> * &lt;LinearLayout * ... * android:theme="@android:theme/ThemeOverlay.Material.Dark"&gt; * &lt;View ...&gt; * &lt;/LinearLayout&gt; * </pre> * </p> * * <a name="Properties"></a> * <h3>Properties</h3> * <p> * The View class exposes an {@link #ALPHA} property, as well as several transform-related * properties, such as {@link #TRANSLATION_X} and {@link #TRANSLATION_Y}. These properties are * available both in the {@link Property} form as well as in similarly-named setter/getter * methods (such as {@link #setAlpha(float)} for {@link #ALPHA}). These properties can * be used to set persistent state associated with these rendering-related properties on the view. * The properties and methods can also be used in conjunction with * {@link android.animation.Animator Animator}-based animations, described more in the * <a href="#Animation">Animation</a> section. * </p> * * <a name="Animation"></a> * <h3>Animation</h3> * <p> * Starting with Android 3.0, the preferred way of animating views is to use the * {@link android.animation} package APIs. These {@link android.animation.Animator Animator}-based * classes change actual properties of the View object, such as {@link #setAlpha(float) alpha} and * {@link #setTranslationX(float) translationX}. This behavior is contrasted to that of the pre-3.0 * {@link android.view.animation.Animation Animation}-based classes, which instead animate only * how the view is drawn on the display. In particular, the {@link ViewPropertyAnimator} class * makes animating these View properties particularly easy and efficient. * </p> * <p> * Alternatively, you can use the pre-3.0 animation classes to animate how Views are rendered. * You can attach an {@link Animation} object to a view using * {@link #setAnimation(Animation)} or * {@link #startAnimation(Animation)}. The animation can alter the scale, * rotation, translation and alpha of a view over time. If the animation is * attached to a view that has children, the animation will affect the entire * subtree rooted by that node. When an animation is started, the framework will * take care of redrawing the appropriate views until the animation completes. * </p> * * <a name="Security"></a> * <h3>Security</h3> * <p> * Sometimes it is essential that an application be able to verify that an action * is being performed with the full knowledge and consent of the user, such as * granting a permission request, making a purchase or clicking on an advertisement. * Unfortunately, a malicious application could try to spoof the user into * performing these actions, unaware, by concealing the intended purpose of the view. * As a remedy, the framework offers a touch filtering mechanism that can be used to * improve the security of views that provide access to sensitive functionality. * </p><p> * To enable touch filtering, call {@link #setFilterTouchesWhenObscured(boolean)} or set the * android:filterTouchesWhenObscured layout attribute to true. When enabled, the framework * will discard touches that are received whenever the view's window is obscured by * another visible window at the touched location. As a result, the view will not receive touches * whenever the touch passed through a toast, dialog or other window that appears above the view's * window. * </p><p> * For more fine-grained control over security, consider overriding the * {@link #onFilterTouchEventForSecurity(MotionEvent)} method to implement your own * security policy. See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}. * </p> * * @attr ref android.R.styleable#View_accessibilityHeading * @attr ref android.R.styleable#View_allowClickWhenDisabled * @attr ref android.R.styleable#View_alpha * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable * @attr ref android.R.styleable#View_clipToOutline * @attr ref android.R.styleable#View_contentDescription * @attr ref android.R.styleable#View_drawingCacheQuality * @attr ref android.R.styleable#View_duplicateParentState * @attr ref android.R.styleable#View_id * @attr ref android.R.styleable#View_requiresFadingEdge * @attr ref android.R.styleable#View_fadeScrollbars * @attr ref android.R.styleable#View_fadingEdgeLength * @attr ref android.R.styleable#View_filterTouchesWhenObscured * @attr ref android.R.styleable#View_fitsSystemWindows * @attr ref android.R.styleable#View_isScrollContainer * @attr ref android.R.styleable#View_focusable * @attr ref android.R.styleable#View_focusableInTouchMode * @attr ref android.R.styleable#View_focusedByDefault * @attr ref android.R.styleable#View_hapticFeedbackEnabled * @attr ref android.R.styleable#View_keepScreenOn * @attr ref android.R.styleable#View_keyboardNavigationCluster * @attr ref android.R.styleable#View_layerType * @attr ref android.R.styleable#View_layoutDirection * @attr ref android.R.styleable#View_longClickable * @attr ref android.R.styleable#View_minHeight * @attr ref android.R.styleable#View_minWidth * @attr ref android.R.styleable#View_nextClusterForward * @attr ref android.R.styleable#View_nextFocusDown * @attr ref android.R.styleable#View_nextFocusLeft * @attr ref android.R.styleable#View_nextFocusRight * @attr ref android.R.styleable#View_nextFocusUp * @attr ref android.R.styleable#View_onClick * @attr ref android.R.styleable#View_outlineSpotShadowColor * @attr ref android.R.styleable#View_outlineAmbientShadowColor * @attr ref android.R.styleable#View_padding * @attr ref android.R.styleable#View_paddingHorizontal * @attr ref android.R.styleable#View_paddingVertical * @attr ref android.R.styleable#View_paddingBottom * @attr ref android.R.styleable#View_paddingLeft * @attr ref android.R.styleable#View_paddingRight * @attr ref android.R.styleable#View_paddingTop * @attr ref android.R.styleable#View_paddingStart * @attr ref android.R.styleable#View_paddingEnd * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_rotation * @attr ref android.R.styleable#View_rotationX * @attr ref android.R.styleable#View_rotationY * @attr ref android.R.styleable#View_scaleX * @attr ref android.R.styleable#View_scaleY * @attr ref android.R.styleable#View_scrollX * @attr ref android.R.styleable#View_scrollY * @attr ref android.R.styleable#View_scrollbarSize * @attr ref android.R.styleable#View_scrollbarStyle * @attr ref android.R.styleable#View_scrollbars * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade * @attr ref android.R.styleable#View_scrollbarFadeDuration * @attr ref android.R.styleable#View_scrollbarTrackHorizontal * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarThumbVertical * @attr ref android.R.styleable#View_scrollbarTrackVertical * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack * @attr ref android.R.styleable#View_stateListAnimator * @attr ref android.R.styleable#View_transitionName * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag * @attr ref android.R.styleable#View_textAlignment * @attr ref android.R.styleable#View_textDirection * @attr ref android.R.styleable#View_transformPivotX * @attr ref android.R.styleable#View_transformPivotY * @attr ref android.R.styleable#View_translationX * @attr ref android.R.styleable#View_translationY * @attr ref android.R.styleable#View_translationZ * @attr ref android.R.styleable#View_visibility * @attr ref android.R.styleable#View_theme * * @see android.view.ViewGroup */ @UiThread publicclassViewimplementsDrawable.Callback,KeyEvent.Callback, AccessibilityEventSource{ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privatestaticfinalboolean DBG =false; /** @hide */ publicstaticboolean DEBUG_DRAW =false; /** * The logging tag used by this class with android.util.Log. */ protectedstaticfinalString VIEW_LOG_TAG ="View"; /** * The logging tag used by this class when logging verbose, autofill-related messages. */ // NOTE: We cannot use android.view.autofill.Helper.sVerbose because that variable is not // set if a session is not started. privatestaticfinalString AUTOFILL_LOG_TAG ="View.Autofill"; /** * The logging tag used by this class when logging content capture-related messages. */ privatestaticfinalString CONTENT_CAPTURE_LOG_TAG ="View.ContentCapture"; privatestaticfinalboolean DEBUG_CONTENT_CAPTURE =false; /** * When set to true, this view will save its attribute data. * * @hide */ publicstaticboolean sDebugViewAttributes =false; /** * When set to this application package view will save its attribute data. * * @hide */ publicstaticString sDebugViewAttributesApplicationPackage; /** * Used to mark a View that has no ID. */ publicstaticfinalint NO_ID =-1; /** * Last ID that is given to Views that are no part of activities. * * {@hide} */ publicstaticfinalint LAST_APP_AUTOFILL_ID =Integer.MAX_VALUE /2; /** * Attribute to find the autofilled highlight * * @see #getAutofilledDrawable() */ privatestaticfinalint[] AUTOFILL_HIGHLIGHT_ATTR = newint[]{android.R.attr.autofilledHighlight}; /** * Signals that compatibility booleans have been initialized according to * target SDK versions. */ privatestaticboolean sCompatibilityDone =false; /** * Use the old (broken) way of building MeasureSpecs. */ privatestaticboolean sUseBrokenMakeMeasureSpec =false; /** * Always return a size of 0 for MeasureSpec values with a mode of UNSPECIFIED */ staticboolean sUseZeroUnspecifiedMeasureSpec =false; /** * Ignore any optimizations using the measure cache. */ privatestaticboolean sIgnoreMeasureCache =false; /** * Ignore an optimization that skips unnecessary EXACTLY layout passes. */ privatestaticboolean sAlwaysRemeasureExactly =false; /** * Allow setForeground/setBackground to be called (and ignored) on a textureview, * without throwing */ staticboolean sTextureViewIgnoresDrawableSetters =false; /** * Prior to N, some ViewGroups would not convert LayoutParams properly even though both extend * MarginLayoutParams. For instance, converting LinearLayout.LayoutParams to * RelativeLayout.LayoutParams would lose margin information. This is fixed on N but target API * check is implemented for backwards compatibility. * * {@hide} */ protectedstaticboolean sPreserveMarginParamsInLayoutParamConversion; /** * Prior to N, when drag enters into child of a view that has already received an * ACTION_DRAG_ENTERED event, the parent doesn't get a ACTION_DRAG_EXITED event. * ACTION_DRAG_LOCATION and ACTION_DROP were delivered to the parent of a view that returned * false from its event handler for these events. * Starting from N, the parent will get ACTION_DRAG_EXITED event before the child gets its * ACTION_DRAG_ENTERED. ACTION_DRAG_LOCATION and ACTION_DROP are never propagated to the parent. * sCascadedDragDrop is true for pre-N apps for backwards compatibility implementation. */ staticboolean sCascadedDragDrop; /** * Prior to O, auto-focusable didn't exist and widgets such as ListView use hasFocusable * to determine things like whether or not to permit item click events. We can't break * apps that do this just because more things (clickable things) are now auto-focusable * and they would get different results, so give old behavior to old apps. */ staticboolean sHasFocusableExcludeAutoFocusable; /** * Prior to O, auto-focusable didn't exist and views marked as clickable weren't implicitly * made focusable by default. As a result, apps could (incorrectly) change the clickable * setting of views off the UI thread. Now that clickable can effect the focusable state, * changing the clickable attribute off the UI thread will cause an exception (since changing * the focusable state checks). In order to prevent apps from crashing, we will handle this * specific case and just not notify parents on new focusables resulting from marking views * clickable from outside the UI thread. */ privatestaticboolean sAutoFocusableOffUIThreadWontNotifyParents; /** * Prior to P things like setScaleX() allowed passing float values that were bogus such as * Float.NaN. If the app is targetting P or later then passing these values will result in an * exception being thrown. If the app is targetting an earlier SDK version, then we will * silently clamp these values to avoid crashes elsewhere when the rendering code hits * these bogus values. */ privatestaticboolean sThrowOnInvalidFloatProperties; /** * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow. * Currently zero size SurfaceControl cannot be created thus we create a 1x1 surface instead. */ privatestaticboolean sAcceptZeroSizeDragShadow; /** * Prior to R, {@link #dispatchApplyWindowInsets} had an issue: * <p>The modified insets changed by {@link #onApplyWindowInsets} were passed to the * entire view hierarchy in prefix order, including siblings as well as siblings of parents * further down the hierarchy. This violates the basic concepts of the view hierarchy, and * thus, the hierarchical dispatching mechanism was hard to use for apps. * <p> * In order to make window inset dispatching work properly, we dispatch window insets * in the view hierarchy in a proper hierarchical manner if this flag is set to {@code false}. */ staticboolean sBrokenInsetsDispatch; /** * Prior to Q, calling * {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} * did not call update the window format so the opacity of the background was not correctly * applied to the window. Some applications rely on this misbehavior to work properly. * <p> * From Q, {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} is * the same as {@link com.android.internal.policy.DecorView#setWindowBackground(Drawable)} * which updates the window format. * @hide */ protectedstaticboolean sBrokenWindowBackground; /** * Prior to R, we were always forcing a layout of the entire hierarchy when insets changed from * the server. This is inefficient and not all apps use it. Instead, we want to rely on apps * calling {@link #requestLayout} when they need to relayout based on an insets change. */ staticboolean sForceLayoutWhenInsetsChanged; /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) public@interfaceFocusable{} /** * This view does not want keystrokes. * <p> * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code * android:focusable}. */ publicstaticfinalint NOT_FOCUSABLE =0x00000000; /** * This view wants keystrokes. * <p> * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code * android:focusable}. */ publicstaticfinalint FOCUSABLE =0x00000001; /** * This view determines focusability automatically. This is the default. * <p> * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code * android:focusable}. */ publicstaticfinalint FOCUSABLE_AUTO =0x00000010; /** * Mask for use with setFlags indicating bits used for focus. */ privatestaticfinalint FOCUSABLE_MASK =0x00000011; /** * This view will adjust its padding to fit sytem windows (e.g. status bar) */ privatestaticfinalint FITS_SYSTEM_WINDOWS =0x00000002; /** @hide */ @IntDef({VISIBLE, INVISIBLE, GONE}) @Retention(RetentionPolicy.SOURCE) public@interfaceVisibility{} /** * This view is visible. * Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code * android:visibility}. */ publicstaticfinalint VISIBLE =0x00000000; /** * This view is invisible, but it still takes up space for layout purposes. * Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code * android:visibility}. */ publicstaticfinalint INVISIBLE =0x00000004; /** * This view is invisible, and it doesn't take any space for layout * purposes. Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code * android:visibility}. */ publicstaticfinalint GONE =0x00000008; /** * Mask for use with setFlags indicating bits used for visibility. * {@hide} */ staticfinalint VISIBILITY_MASK =0x0000000C; privatestaticfinalint[] VISIBILITY_FLAGS ={VISIBLE, INVISIBLE, GONE}; /** * Hint indicating that this view can be autofilled with an email address. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_EMAIL_ADDRESS}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_EMAIL_ADDRESS ="emailAddress"; /** * Hint indicating that this view can be autofilled with a user's real name. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_NAME}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_NAME ="name"; /** * Hint indicating that this view can be autofilled with a username. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_USERNAME}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_USERNAME ="username"; /** * Hint indicating that this view can be autofilled with a password. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_PASSWORD}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_PASSWORD ="password"; /** * Hint indicating that this view can be autofilled with a phone number. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_PHONE}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_PHONE ="phone"; /** * Hint indicating that this view can be autofilled with a postal address. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_POSTAL_ADDRESS}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_POSTAL_ADDRESS ="postalAddress"; /** * Hint indicating that this view can be autofilled with a postal code. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_POSTAL_CODE}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_POSTAL_CODE ="postalCode"; /** * Hint indicating that this view can be autofilled with a credit card number. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_NUMBER}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_CREDIT_CARD_NUMBER ="creditCardNumber"; /** * Hint indicating that this view can be autofilled with a credit card security code. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE ="creditCardSecurityCode"; /** * Hint indicating that this view can be autofilled with a credit card expiration date. * * <p>It should be used when the credit card expiration date is represented by just one view; * if it is represented by more than one (for example, one view for the month and another view * for the year), then each of these views should use the hint specific for the unit * ({@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}, * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}, * or {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}). * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}</code>). * * <p>When annotating a view with this hint, it's recommended to use a date autofill value to * avoid ambiguity when the autofill service provides a value for it. To understand why a * value can be ambiguous, consider "April of 2020", which could be represented as either of * the following options: * * <ul> * <li>{@code "04/2020"} * <li>{@code "4/2020"} * <li>{@code "2020/04"} * <li>{@code "2020/4"} * <li>{@code "April/2020"} * <li>{@code "Apr/2020"} * </ul> * * <p>You define a date autofill value for the view by overriding the following methods: * * <ol> * <li>{@link #getAutofillType()} to return {@link #AUTOFILL_TYPE_DATE}. * <li>{@link #getAutofillValue()} to return a * {@link AutofillValue#forDate(long) date autofillvalue}. * <li>{@link #autofill(AutofillValue)} to expect a data autofillvalue. * </ol> * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate"; /** * Hint indicating that this view can be autofilled with a credit card expiration month. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}</code>). * * <p>When annotating a view with this hint, it's recommended to use a text autofill value * whose value is the numerical representation of the month, starting on {@code 1} to avoid * ambiguity when the autofill service provides a value for it. To understand why a * value can be ambiguous, consider "January", which could be represented as either of * * <ul> * <li>{@code "1"}: recommended way. * <li>{@code "0"}: if following the {@link Calendar#MONTH} convention. * <li>{@code "January"}: full name, in English. * <li>{@code "jan"}: abbreviated name, in English. * <li>{@code "Janeiro"}: full name, in another language. * </ul> * * <p>Another recommended approach is to use a date autofill value - see * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} for more details. * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth"; /** * Hint indicating that this view can be autofilled with a credit card expiration year. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear"; /** * Hint indicating that this view can be autofilled with a credit card expiration day. * * <p>Can be used with either {@link #setAutofillHints(String[])} or * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}</code>). * * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints. */ publicstaticfinalString AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY ="creditCardExpirationDay"; /** * A hint indicating that this view can be autofilled with a password. * * This is a heuristic-based hint that is meant to be used by UI Toolkit developers when a * view is a password field but doesn't specify a * <code>{@value View#AUTOFILL_HINT_PASSWORD}</code>. * @hide */ // TODO(229765029): unhide this for UI toolkit publicstaticfinalString AUTOFILL_HINT_PASSWORD_AUTO ="passwordAuto"; /** * Hints for the autofill services that describes the content of the view. */ private@NullableString[] mAutofillHints; /** * Autofill id, lazily created on calls to {@link #getAutofillId()}. */ privateAutofillId mAutofillId; /** @hide */ @IntDef(prefix ={"AUTOFILL_TYPE_"}, value ={ AUTOFILL_TYPE_NONE, AUTOFILL_TYPE_TEXT, AUTOFILL_TYPE_TOGGLE, AUTOFILL_TYPE_LIST, AUTOFILL_TYPE_DATE, }) @Retention(RetentionPolicy.SOURCE) public@interfaceAutofillType{} /** * Autofill type for views that cannot be autofilled. * * <p>Typically used when the view is read-only; for example, a text label. * * @see #getAutofillType() */ publicstaticfinalint AUTOFILL_TYPE_NONE =0; /** * Autofill type for a text field, which is filled by a {@link CharSequence}. * * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through * {@link AutofillValue#forText(CharSequence)}, and the value passed to autofill a * {@link View} can be fetched through {@link AutofillValue#getTextValue()}. * * @see #getAutofillType() */ publicstaticfinalint AUTOFILL_TYPE_TEXT =1; /** * Autofill type for a togglable field, which is filled by a {@code boolean}. * * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through * {@link AutofillValue#forToggle(boolean)}, and the value passed to autofill a * {@link View} can be fetched through {@link AutofillValue#getToggleValue()}. * * @see #getAutofillType() */ publicstaticfinalint AUTOFILL_TYPE_TOGGLE =2; /** * Autofill type for a selection list field, which is filled by an {@code int} * representing the element index inside the list (starting at {@code 0}). * * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through * {@link AutofillValue#forList(int)}, and the value passed to autofill a * {@link View} can be fetched through {@link AutofillValue#getListValue()}. * * <p>The available options in the selection list are typically provided by * {@link android.app.assist.AssistStructure.ViewNode#getAutofillOptions()}. * * @see #getAutofillType() */ publicstaticfinalint AUTOFILL_TYPE_LIST =3; /** * Autofill type for a field that contains a date, which is represented by a long representing * the number of milliseconds since the standard base time known as "the epoch", namely * January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}. * * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through * {@link AutofillValue#forDate(long)}, and the values passed to * autofill a {@link View} can be fetched through {@link AutofillValue#getDateValue()}. * * @see #getAutofillType() */ publicstaticfinalint AUTOFILL_TYPE_DATE =4; /** @hide */ @IntDef(prefix ={"IMPORTANT_FOR_AUTOFILL_"}, value ={ IMPORTANT_FOR_AUTOFILL_AUTO, IMPORTANT_FOR_AUTOFILL_YES, IMPORTANT_FOR_AUTOFILL_NO, IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS }) @Retention(RetentionPolicy.SOURCE) public@interfaceAutofillImportance{} /** * Automatically determine whether a view is important for autofill. * * @see #isImportantForAutofill() * @see #setImportantForAutofill(int) */ publicstaticfinalint IMPORTANT_FOR_AUTOFILL_AUTO =0x0; /** * The view is important for autofill, and its children (if any) will be traversed. * * @see #isImportantForAutofill() * @see #setImportantForAutofill(int) */ publicstaticfinalint IMPORTANT_FOR_AUTOFILL_YES =0x1; /** * The view is not important for autofill, but its children (if any) will be traversed. * * @see #isImportantForAutofill() * @see #setImportantForAutofill(int) */ publicstaticfinalint IMPORTANT_FOR_AUTOFILL_NO =0x2; /** * The view is important for autofill, but its children (if any) will not be traversed. * * @see #isImportantForAutofill() * @see #setImportantForAutofill(int) */ publicstaticfinalint IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS =0x4; /** * The view is not important for autofill, and its children (if any) will not be traversed. * * @see #isImportantForAutofill() * @see #setImportantForAutofill(int) */ publicstaticfinalint IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS =0x8; /** @hide */ @IntDef(flag =true, prefix ={"AUTOFILL_FLAG_"}, value ={ AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS }) @Retention(RetentionPolicy.SOURCE) public@interfaceAutofillFlags{} /** * Flag requesting you to add views that are marked as not important for autofill * (see {@link #setImportantForAutofill(int)}) to a {@link ViewStructure}. */ publicstaticfinalint AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS =0x1; /** @hide */ @IntDef(prefix ={"IMPORTANT_FOR_CONTENT_CAPTURE_"}, value ={ IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, IMPORTANT_FOR_CONTENT_CAPTURE_YES, IMPORTANT_FOR_CONTENT_CAPTURE_NO, IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS }) @Retention(RetentionPolicy.SOURCE) public@interfaceContentCaptureImportance{} /** * Automatically determine whether a view is important for content capture. * * @see #isImportantForContentCapture() * @see #setImportantForContentCapture(int) */ publicstaticfinalint IMPORTANT_FOR_CONTENT_CAPTURE_AUTO =0x0; /** * The view is important for content capture, and its children (if any) will be traversed. * * @see #isImportantForContentCapture() * @see #setImportantForContentCapture(int) */ publicstaticfinalint IMPORTANT_FOR_CONTENT_CAPTURE_YES =0x1; /** * The view is not important for content capture, but its children (if any) will be traversed. * * @see #isImportantForContentCapture() * @see #setImportantForContentCapture(int) */ publicstaticfinalint IMPORTANT_FOR_CONTENT_CAPTURE_NO =0x2; /** * The view is important for content capture, but its children (if any) will not be traversed. * * @see #isImportantForContentCapture() * @see #setImportantForContentCapture(int) */ publicstaticfinalint IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS =0x4; /** * The view is not important for content capture, and its children (if any) will not be * traversed. * * @see #isImportantForContentCapture() * @see #setImportantForContentCapture(int) */ publicstaticfinalint IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS =0x8; /** {@hide} */ @IntDef(flag =true, prefix ={"SCROLL_CAPTURE_HINT_"}, value ={ SCROLL_CAPTURE_HINT_AUTO, SCROLL_CAPTURE_HINT_EXCLUDE, SCROLL_CAPTURE_HINT_INCLUDE, SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS }) @Retention(RetentionPolicy.SOURCE) public@interfaceScrollCaptureHint{} /** * The content of this view will be considered for scroll capture if scrolling is possible. * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) */ publicstaticfinalint SCROLL_CAPTURE_HINT_AUTO =0; /** * Explicitly exclude this view as a potential scroll capture target. The system will not * consider it. Mutually exclusive with {@link #SCROLL_CAPTURE_HINT_INCLUDE}, which this flag * takes precedence over. * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) */ publicstaticfinalint SCROLL_CAPTURE_HINT_EXCLUDE =0x1; /** * Explicitly include this view as a potential scroll capture target. When locating a scroll * capture target, this view will be prioritized before others without this flag. Mutually * exclusive with {@link #SCROLL_CAPTURE_HINT_EXCLUDE}, which takes precedence. * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) */ publicstaticfinalint SCROLL_CAPTURE_HINT_INCLUDE =0x2; /** * Explicitly exclude all children of this view as potential scroll capture targets. This view * is unaffected. Note: Excluded children are not considered, regardless of {@link * #SCROLL_CAPTURE_HINT_INCLUDE}. * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) */ publicstaticfinalint SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS =0x4; /** * This view is enabled. Interpretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. * {@hide} */ staticfinalint ENABLED =0x00000000; /** * This view is disabled. Interpretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. * {@hide} */ staticfinalint DISABLED =0x00000020; /** * Mask for use with setFlags indicating bits used for indicating whether * this view is enabled * {@hide} */ staticfinalint ENABLED_MASK =0x00000020; /** * This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be * called and further optimizations will be performed. It is okay to have * this flag set and a background. Use with DRAW_MASK when calling setFlags. * {@hide} */ staticfinalint WILL_NOT_DRAW =0x00000080; /** * Mask for use with setFlags indicating bits used for indicating whether * this view is will draw * {@hide} */ staticfinalint DRAW_MASK =0x00000080; /** * <p>This view doesn't show scrollbars.</p> * {@hide} */ staticfinalint SCROLLBARS_NONE =0x00000000; /** * <p>This view shows horizontal scrollbars.</p> * {@hide} */ staticfinalint SCROLLBARS_HORIZONTAL =0x00000100; /** * <p>This view shows vertical scrollbars.</p> * {@hide} */ staticfinalint SCROLLBARS_VERTICAL =0x00000200; /** * <p>Mask for use with setFlags indicating bits used for indicating which * scrollbars are enabled.</p> * {@hide} */ staticfinalint SCROLLBARS_MASK =0x00000300; /** * Indicates that the view should filter touches when its window is obscured. * Refer to the class comments for more information about this security feature. * {@hide} */ staticfinalint FILTER_TOUCHES_WHEN_OBSCURED =0x00000400; /** * Set for framework elements that use FITS_SYSTEM_WINDOWS, to indicate * that they are optional and should be skipped if the window has * requested system UI flags that ignore those insets for layout. * <p> * This is only used for support library as of Android R. The framework now uses * {@link #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS} such that it can skip the legacy * insets path that loses insets information. */ staticfinalint OPTIONAL_FITS_SYSTEM_WINDOWS =0x00000800; /** * <p>This view doesn't show fading edges.</p> * {@hide} */ staticfinalint FADING_EDGE_NONE =0x00000000; /** * <p>This view shows horizontal fading edges.</p> * {@hide} */ staticfinalint FADING_EDGE_HORIZONTAL =0x00001000; /** * <p>This view shows vertical fading edges.</p> * {@hide} */ staticfinalint FADING_EDGE_VERTICAL =0x00002000; /** * <p>Mask for use with setFlags indicating bits used for indicating which * fading edges are enabled.</p> * {@hide} */ staticfinalint FADING_EDGE_MASK =0x00003000; /** * <p>Indicates this view can be clicked. When clickable, a View reacts * to clicks by notifying the OnClickListener.<p> * {@hide} */ staticfinalint CLICKABLE =0x00004000; /** * <p>Indicates this view is caching its drawing into a bitmap.</p> * {@hide} */ staticfinalint DRAWING_CACHE_ENABLED =0x00008000; /** * <p>Indicates that no icicle should be saved for this view.<p> * {@hide} */ staticfinalint SAVE_DISABLED =0x000010000; /** * <p>Mask for use with setFlags indicating bits used for the saveEnabled * property.</p> * {@hide} */ staticfinalint SAVE_DISABLED_MASK =0x000010000; /** * <p>Indicates that no drawing cache should ever be created for this view.<p> * {@hide} */ staticfinalint WILL_NOT_CACHE_DRAWING =0x000020000; /** * <p>Indicates this view can take / keep focus when int touch mode.</p> * {@hide} */ staticfinalint FOCUSABLE_IN_TOUCH_MODE =0x00040000; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix ={"DRAWING_CACHE_QUALITY_"}, value ={ DRAWING_CACHE_QUALITY_LOW, DRAWING_CACHE_QUALITY_HIGH, DRAWING_CACHE_QUALITY_AUTO }) public@interfaceDrawingCacheQuality{} /** * <p>Enables low quality mode for the drawing cache.</p> * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicstaticfinalint DRAWING_CACHE_QUALITY_LOW =0x00080000; /** * <p>Enables high quality mode for the drawing cache.</p> * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicstaticfinalint DRAWING_CACHE_QUALITY_HIGH =0x00100000; /** * <p>Enables automatic quality mode for the drawing cache.</p> * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicstaticfinalint DRAWING_CACHE_QUALITY_AUTO =0x00000000; privatestaticfinalint[] DRAWING_CACHE_QUALITY_FLAGS ={ DRAWING_CACHE_QUALITY_AUTO, DRAWING_CACHE_QUALITY_LOW, DRAWING_CACHE_QUALITY_HIGH }; /** * <p>Mask for use with setFlags indicating bits used for the cache * quality property.</p> * {@hide} */ staticfinalint DRAWING_CACHE_QUALITY_MASK =0x00180000; /** * <p> * Indicates this view can be long clicked. When long clickable, a View * reacts to long clicks by notifying the OnLongClickListener or showing a * context menu. * </p> * {@hide} */ staticfinalint LONG_CLICKABLE =0x00200000; /** * <p>Indicates that this view gets its drawable states from its direct parent * and ignores its original internal states.</p> * * @hide */ staticfinalint DUPLICATE_PARENT_STATE =0x00400000; /** * <p> * Indicates this view can be context clicked. When context clickable, a View reacts to a * context click (e.g. a primary stylus button press or right mouse click) by notifying the * OnContextClickListener. * </p> * {@hide} */ staticfinalint CONTEXT_CLICKABLE =0x00800000; /** @hide */ @IntDef(prefix ={"SCROLLBARS_"}, value ={ SCROLLBARS_INSIDE_OVERLAY, SCROLLBARS_INSIDE_INSET, SCROLLBARS_OUTSIDE_OVERLAY, SCROLLBARS_OUTSIDE_INSET }) @Retention(RetentionPolicy.SOURCE) public@interfaceScrollBarStyle{} /** * The scrollbar style to display the scrollbars inside the content area, * without increasing the padding. The scrollbars will be overlaid with * translucency on the view's content. */ publicstaticfinalint SCROLLBARS_INSIDE_OVERLAY =0; /** * The scrollbar style to display the scrollbars inside the padded area, * increasing the padding of the view. The scrollbars will not overlap the * content area of the view. */ publicstaticfinalint SCROLLBARS_INSIDE_INSET =0x01000000; /** * The scrollbar style to display the scrollbars at the edge of the view, * without increasing the padding. The scrollbars will be overlaid with * translucency. */ publicstaticfinalint SCROLLBARS_OUTSIDE_OVERLAY =0x02000000; /** * The scrollbar style to display the scrollbars at the edge of the view, * increasing the padding of the view. The scrollbars will only overlap the * background, if any. */ publicstaticfinalint SCROLLBARS_OUTSIDE_INSET =0x03000000; /** * Mask to check if the scrollbar style is overlay or inset. * {@hide} */ staticfinalint SCROLLBARS_INSET_MASK =0x01000000; /** * Mask to check if the scrollbar style is inside or outside. * {@hide} */ staticfinalint SCROLLBARS_OUTSIDE_MASK =0x02000000; /** * Mask for scrollbar style. * {@hide} */ staticfinalint SCROLLBARS_STYLE_MASK =0x03000000; /** * View flag indicating that the screen should remain on while the * window containing this view is visible to the user. This effectively * takes care of automatically setting the WindowManager's * {@link WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON}. */ publicstaticfinalint KEEP_SCREEN_ON =0x04000000; /** * View flag indicating whether this view should have sound effects enabled * for events such as clicking and touching. */ publicstaticfinalint SOUND_EFFECTS_ENABLED =0x08000000; /** * View flag indicating whether this view should have haptic feedback * enabled for events such as long presses. */ publicstaticfinalint HAPTIC_FEEDBACK_ENABLED =0x10000000; /** * <p>Indicates that the view hierarchy should stop saving state when * it reaches this view. If state saving is initiated immediately at * the view, it will be allowed. * {@hide} */ staticfinalint PARENT_SAVE_DISABLED =0x20000000; /** * <p>Mask for use with setFlags indicating bits used for PARENT_SAVE_DISABLED.</p> * {@hide} */ staticfinalint PARENT_SAVE_DISABLED_MASK =0x20000000; privatestaticPaint sDebugPaint; /** * <p>Indicates this view can display a tooltip on hover or long press.</p> * {@hide} */ staticfinalint TOOLTIP =0x40000000; /** @hide */ @IntDef(flag =true, prefix ={"FOCUSABLES_"}, value ={ FOCUSABLES_ALL, FOCUSABLES_TOUCH_MODE }) @Retention(RetentionPolicy.SOURCE) public@interfaceFocusableMode{} /** * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} * should add all focusable Views regardless if they are focusable in touch mode. */ publicstaticfinalint FOCUSABLES_ALL =0x00000000; /** * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} * should add only Views focusable in touch mode. */ publicstaticfinalint FOCUSABLES_TOUCH_MODE =0x00000001; /** @hide */ @IntDef(prefix ={"FOCUS_"}, value ={ FOCUS_BACKWARD, FOCUS_FORWARD, FOCUS_LEFT, FOCUS_UP, FOCUS_RIGHT, FOCUS_DOWN }) @Retention(RetentionPolicy.SOURCE) public@interfaceFocusDirection{} /** @hide */ @IntDef(prefix ={"FOCUS_"}, value ={ FOCUS_LEFT, FOCUS_UP, FOCUS_RIGHT, FOCUS_DOWN }) @Retention(RetentionPolicy.SOURCE) public@interfaceFocusRealDirection{}// Like @FocusDirection, but without forward/backward /** * Use with {@link #focusSearch(int)}. Move focus to the previous selectable * item. */ publicstaticfinalint FOCUS_BACKWARD =0x00000001; /** * Use with {@link #focusSearch(int)}. Move focus to the next selectable * item. */ publicstaticfinalint FOCUS_FORWARD =0x00000002; /** * Use with {@link #focusSearch(int)}. Move focus to the left. */ publicstaticfinalint FOCUS_LEFT =0x00000011; /** * Use with {@link #focusSearch(int)}. Move focus up. */ publicstaticfinalint FOCUS_UP =0x00000021; /** * Use with {@link #focusSearch(int)}. Move focus to the right. */ publicstaticfinalint FOCUS_RIGHT =0x00000042; /** * Use with {@link #focusSearch(int)}. Move focus down. */ publicstaticfinalint FOCUS_DOWN =0x00000082; /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the actual measured size. */ publicstaticfinalint MEASURED_SIZE_MASK =0x00ffffff; /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the additional state bits. */ publicstaticfinalint 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)}. */ publicstaticfinalint 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. */ publicstaticfinalint MEASURED_STATE_TOO_SMALL =0x01000000; /** * Base View state sets */ // Singles /** * Indicates the view has no states set. States are used with * {@link android.graphics.drawable.Drawable} to change the drawing of the * view depending on its state. * * @see android.graphics.drawable.Drawable * @see #getDrawableState() */ protectedstaticfinalint[] EMPTY_STATE_SET; /** * Indicates the view is enabled. States are used with * {@link android.graphics.drawable.Drawable} to change the drawing of the * view depending on its state. * * @see android.graphics.drawable.Drawable * @see #getDrawableState() */ protectedstaticfinalint[] ENABLED_STATE_SET; /** * Indicates the view is focused. States are used with * {@link android.graphics.drawable.Drawable} to change the drawing of the * view depending on its state. * * @see android.graphics.drawable.Drawable * @see #getDrawableState() */ protectedstaticfinalint[] FOCUSED_STATE_SET; /** * Indicates the view is selected. States are used with * {@link android.graphics.drawable.Drawable} to change the drawing of the * view depending on its state. * * @see android.graphics.drawable.Drawable * @see #getDrawableState() */ protectedstaticfinalint[] SELECTED_STATE_SET; /** * Indicates the view is pressed. States are used with * {@link android.graphics.drawable.Drawable} to change the drawing of the * view depending on its state. * * @see android.graphics.drawable.Drawable * @see #getDrawableState() */ protectedstaticfinalint[] PRESSED_STATE_SET; /** * Indicates the view's window has focus. States are used with * {@link android.graphics.drawable.Drawable} to change the drawing of the * view depending on its state. * * @see android.graphics.drawable.Drawable * @see #getDrawableState() */ protectedstaticfinalint[] WINDOW_FOCUSED_STATE_SET; // Doubles /** * Indicates the view is enabled and has the focus. * * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET */ protectedstaticfinalint[] ENABLED_FOCUSED_STATE_SET; /** * Indicates the view is enabled and selected. * * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET */ protectedstaticfinalint[] ENABLED_SELECTED_STATE_SET; /** * Indicates the view is enabled and that its window has focus. * * @see #ENABLED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] ENABLED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is focused and selected. * * @see #FOCUSED_STATE_SET * @see #SELECTED_STATE_SET */ protectedstaticfinalint[] FOCUSED_SELECTED_STATE_SET; /** * Indicates the view has the focus and that its window has the focus. * * @see #FOCUSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] FOCUSED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is selected and that its window has the focus. * * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] SELECTED_WINDOW_FOCUSED_STATE_SET; // Triples /** * Indicates the view is enabled, focused and selected. * * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET * @see #SELECTED_STATE_SET */ protectedstaticfinalint[] ENABLED_FOCUSED_SELECTED_STATE_SET; /** * Indicates the view is enabled, focused and its window has the focus. * * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is enabled, selected and its window has the focus. * * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is focused, selected and its window has the focus. * * @see #FOCUSED_STATE_SET * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is enabled, focused, selected and its window * has the focus. * * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed and its window has the focus. * * @see #PRESSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed and selected. * * @see #PRESSED_STATE_SET * @see #SELECTED_STATE_SET */ protectedstaticfinalint[] PRESSED_SELECTED_STATE_SET; /** * Indicates the view is pressed, selected and its window has the focus. * * @see #PRESSED_STATE_SET * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed and focused. * * @see #PRESSED_STATE_SET * @see #FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_FOCUSED_STATE_SET; /** * Indicates the view is pressed, focused and its window has the focus. * * @see #PRESSED_STATE_SET * @see #FOCUSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed, focused and selected. * * @see #PRESSED_STATE_SET * @see #SELECTED_STATE_SET * @see #FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_FOCUSED_SELECTED_STATE_SET; /** * Indicates the view is pressed, focused, selected and its window has the focus. * * @see #PRESSED_STATE_SET * @see #FOCUSED_STATE_SET * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed and enabled. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_STATE_SET; /** * Indicates the view is pressed, enabled and its window has the focus. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed, enabled and selected. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_SELECTED_STATE_SET; /** * Indicates the view is pressed, enabled, selected and its window has the * focus. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed, enabled and focused. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_FOCUSED_STATE_SET; /** * Indicates the view is pressed, enabled, focused and its window has the * focus. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET; /** * Indicates the view is pressed, enabled, focused and selected. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET * @see #FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET; /** * Indicates the view is pressed, enabled, focused, selected and its window * has the focus. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET * @see #FOCUSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protectedstaticfinalint[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; static{ EMPTY_STATE_SET =StateSet.get(0); WINDOW_FOCUSED_STATE_SET =StateSet.get(StateSet.VIEW_STATE_WINDOW_FOCUSED); SELECTED_STATE_SET =StateSet.get(StateSet.VIEW_STATE_SELECTED); SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED); FOCUSED_STATE_SET =StateSet.get(StateSet.VIEW_STATE_FOCUSED); FOCUSED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_FOCUSED); FOCUSED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED); FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED); ENABLED_STATE_SET =StateSet.get(StateSet.VIEW_STATE_ENABLED); ENABLED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_ENABLED); ENABLED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_ENABLED); ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_ENABLED); ENABLED_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_ENABLED); ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_ENABLED); ENABLED_FOCUSED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_ENABLED); ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED|StateSet.VIEW_STATE_ENABLED); PRESSED_STATE_SET =StateSet.get(StateSet.VIEW_STATE_PRESSED); PRESSED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_PRESSED); PRESSED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_PRESSED); PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_PRESSED); PRESSED_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_PRESSED); PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_PRESSED); PRESSED_FOCUSED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_PRESSED); PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED |StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =StateSet.get( StateSet.VIEW_STATE_WINDOW_FOCUSED |StateSet.VIEW_STATE_SELECTED |StateSet.VIEW_STATE_FOCUSED|StateSet.VIEW_STATE_ENABLED |StateSet.VIEW_STATE_PRESSED); } /** * Accessibility event types that are dispatched for text population. */ privatestaticfinalint POPULATING_ACCESSIBILITY_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED |AccessibilityEvent.TYPE_VIEW_LONG_CLICKED |AccessibilityEvent.TYPE_VIEW_SELECTED |AccessibilityEvent.TYPE_VIEW_FOCUSED |AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |AccessibilityEvent.TYPE_VIEW_HOVER_ENTER |AccessibilityEvent.TYPE_VIEW_HOVER_EXIT |AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED |AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED |AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED |AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; staticfinalint DEBUG_CORNERS_COLOR =Color.rgb(63,127,255); staticfinalint DEBUG_CORNERS_SIZE_DIP =8; /** * Temporary Rect currently for use in setBackground(). This will probably * be extended in the future to hold our own class with more than just * a Rect. :) */ staticfinalThreadLocal<Rect> sThreadLocal =ThreadLocal.withInitial(Rect::new); /** * Map used to store views' tags. */ @UnsupportedAppUsage privateSparseArray<Object> mKeyedTags; /** * The next available accessibility id. */ privatestaticint sNextAccessibilityViewId; /** * The animation currently associated with this view. * @hide */ protectedAnimation mCurrentAnimation =null; /** * Width as measured during measure pass. * {@hide} */ @ViewDebug.ExportedProperty(category ="measurement") @UnsupportedAppUsage int mMeasuredWidth; /** * Height as measured during measure pass. * {@hide} */ @ViewDebug.ExportedProperty(category ="measurement") @UnsupportedAppUsage int mMeasuredHeight; /** * Flag to indicate that this view was marked INVALIDATED, or had its display list * invalidated, prior to the current drawing iteration. If true, the view must re-draw * its display list. This flag, used only when hw accelerated, allows us to clear the * flag while retaining this information until it's needed (at getDisplayList() time and * in drawChild(), when we decide to draw a view's children's display lists into our own). * * {@hide} */ @UnsupportedAppUsage boolean mRecreateDisplayList =false; /** * The view's identifier. * {@hide} * * @see #setId(int) * @see #getId() */ @IdRes @ViewDebug.ExportedProperty(resolveId =true) int mID = NO_ID; /** The ID of this view for autofill purposes. * <ul> * <li>== {@link #NO_ID}: ID has not been assigned yet * <li>&le; {@link #LAST_APP_AUTOFILL_ID}: View is not part of a activity. The ID is * unique in the process. This might change * over activity lifecycle events. * <li>&gt; {@link #LAST_APP_AUTOFILL_ID}: View is part of a activity. The ID is * unique in the activity. This stays the same * over activity lifecycle events. */ privateint mAutofillViewId = NO_ID; // ID for accessibility purposes. This ID must be unique for every window @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateint mAccessibilityViewId = NO_ID; privateint mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; /** * The view's tag. * {@hide} * * @see #setTag(Object) * @see #getTag() */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedObject mTag =null; /* * Masks for mPrivateFlags, as generated by dumpFlags(): * * |-------|-------|-------|-------| * 1 PFLAG_WANTS_FOCUS * 1 PFLAG_FOCUSED * 1 PFLAG_SELECTED * 1 PFLAG_IS_ROOT_NAMESPACE * 1 PFLAG_HAS_BOUNDS * 1 PFLAG_DRAWN * 1 PFLAG_DRAW_ANIMATION * 1 PFLAG_SKIP_DRAW * 1 PFLAG_REQUEST_TRANSPARENT_REGIONS * 1 PFLAG_DRAWABLE_STATE_DIRTY * 1 PFLAG_MEASURED_DIMENSION_SET * 1 PFLAG_FORCE_LAYOUT * 1 PFLAG_LAYOUT_REQUIRED * 1 PFLAG_PRESSED * 1 PFLAG_DRAWING_CACHE_VALID * 1 PFLAG_ANIMATION_STARTED * 1 PFLAG_SAVE_STATE_CALLED * 1 PFLAG_ALPHA_SET * 1 PFLAG_SCROLL_CONTAINER * 1 PFLAG_SCROLL_CONTAINER_ADDED * 1 PFLAG_DIRTY * 1 PFLAG_DIRTY_MASK * 1 PFLAG_OPAQUE_BACKGROUND * 1 PFLAG_OPAQUE_SCROLLBARS * 11 PFLAG_OPAQUE_MASK * 1 PFLAG_PREPRESSED * 1 PFLAG_CANCEL_NEXT_UP_EVENT * 1 PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH * 1 PFLAG_HOVERED * 1 PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK * 1 PFLAG_ACTIVATED * 1 PFLAG_INVALIDATED * |-------|-------|-------|-------| */ /** {@hide} */ staticfinalint PFLAG_WANTS_FOCUS =0x00000001; /** {@hide} */ staticfinalint PFLAG_FOCUSED =0x00000002; /** {@hide} */ staticfinalint PFLAG_SELECTED =0x00000004; /** {@hide} */ staticfinalint PFLAG_IS_ROOT_NAMESPACE =0x00000008; /** {@hide} */ staticfinalint PFLAG_HAS_BOUNDS =0x00000010; /** {@hide} */ staticfinalint PFLAG_DRAWN =0x00000020; /** * When this flag is set, this view is running an animation on behalf of its * children and should therefore not cancel invalidate requests, even if they * lie outside of this view's bounds. * * {@hide} */ staticfinalint PFLAG_DRAW_ANIMATION =0x00000040; /** {@hide} */ staticfinalint PFLAG_SKIP_DRAW =0x00000080; /** {@hide} */ staticfinalint PFLAG_REQUEST_TRANSPARENT_REGIONS =0x00000200; /** {@hide} */ staticfinalint PFLAG_DRAWABLE_STATE_DIRTY =0x00000400; /** {@hide} */ staticfinalint PFLAG_MEASURED_DIMENSION_SET =0x00000800; /** {@hide} */ staticfinalint PFLAG_FORCE_LAYOUT =0x00001000; /** {@hide} */ staticfinalint PFLAG_LAYOUT_REQUIRED =0x00002000; privatestaticfinalint PFLAG_PRESSED =0x00004000; /** {@hide} */ staticfinalint PFLAG_DRAWING_CACHE_VALID =0x00008000; /** * Flag used to indicate that this view should be drawn once more (and only once * more) after its animation has completed. * {@hide} */ staticfinalint PFLAG_ANIMATION_STARTED =0x00010000; privatestaticfinalint PFLAG_SAVE_STATE_CALLED =0x00020000; /** * Indicates that the View returned true when onSetAlpha() was called and that * the alpha must be restored. * {@hide} */ staticfinalint PFLAG_ALPHA_SET =0x00040000; /** * Set by {@link #setScrollContainer(boolean)}. */ staticfinalint PFLAG_SCROLL_CONTAINER =0x00080000; /** * Set by {@link #setScrollContainer(boolean)}. */ staticfinalint PFLAG_SCROLL_CONTAINER_ADDED =0x00100000; /** * View flag indicating whether this view was invalidated (fully or partially.) * * @hide */ staticfinalint PFLAG_DIRTY =0x00200000; /** * Mask for {@link #PFLAG_DIRTY}. * * @hide */ staticfinalint PFLAG_DIRTY_MASK =0x00200000; /** * Indicates whether the background is opaque. * * @hide */ staticfinalint PFLAG_OPAQUE_BACKGROUND =0x00800000; /** * Indicates whether the scrollbars are opaque. * * @hide */ staticfinalint PFLAG_OPAQUE_SCROLLBARS =0x01000000; /** * Indicates whether the view is opaque. * * @hide */ staticfinalint PFLAG_OPAQUE_MASK =0x01800000; /** * Indicates a prepressed state; * the short time between ACTION_DOWN and recognizing * a 'real' press. Prepressed is used to recognize quick taps * even when they are shorter than ViewConfiguration.getTapTimeout(). * * @hide */ privatestaticfinalint PFLAG_PREPRESSED =0x02000000; /** * Indicates whether the view is temporarily detached. * * @hide */ staticfinalint PFLAG_CANCEL_NEXT_UP_EVENT =0x04000000; /** * Indicates that we should awaken scroll bars once attached * * PLEASE NOTE: This flag is now unused as we now send onVisibilityChanged * during window attachment and it is no longer needed. Feel free to repurpose it. * * @hide */ privatestaticfinalint PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH =0x08000000; /** * Indicates that the view has received HOVER_ENTER. Cleared on HOVER_EXIT. * @hide */ privatestaticfinalint PFLAG_HOVERED =0x10000000; /** * Flag set by {@link AutofillManager} if it needs to be notified when this view is clicked. */ privatestaticfinalint PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK =0x20000000; /** {@hide} */ staticfinalint PFLAG_ACTIVATED =0x40000000; /** * Indicates that this view was specifically invalidated, not just dirtied because some * child view was invalidated. The flag is used to determine when we need to recreate * a view's display list (as opposed to just returning a reference to its existing * display list). * * @hide */ staticfinalint PFLAG_INVALIDATED =0x80000000; /* End of masks for mPrivateFlags */ /* * Masks for mPrivateFlags2, as generated by dumpFlags(): * * |-------|-------|-------|-------| * 1 PFLAG2_DRAG_CAN_ACCEPT * 1 PFLAG2_DRAG_HOVERED * 11 PFLAG2_LAYOUT_DIRECTION_MASK * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED * 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK * 1 PFLAG2_TEXT_DIRECTION_FLAGS[1] * 1 PFLAG2_TEXT_DIRECTION_FLAGS[2] * 11 PFLAG2_TEXT_DIRECTION_FLAGS[3] * 1 PFLAG2_TEXT_DIRECTION_FLAGS[4] * 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5] * 11 PFLAG2_TEXT_DIRECTION_FLAGS[6] * 111 PFLAG2_TEXT_DIRECTION_FLAGS[7] * 111 PFLAG2_TEXT_DIRECTION_MASK * 1 PFLAG2_TEXT_DIRECTION_RESOLVED * 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT * 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1] * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2] * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3] * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4] * 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5] * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6] * 111 PFLAG2_TEXT_ALIGNMENT_MASK * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT * 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK * 111 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK * 11 PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK * 1 PFLAG2_ACCESSIBILITY_FOCUSED * 1 PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED * 1 PFLAG2_VIEW_QUICK_REJECTED * 1 PFLAG2_PADDING_RESOLVED * 1 PFLAG2_DRAWABLE_RESOLVED * 1 PFLAG2_HAS_TRANSIENT_STATE * |-------|-------|-------|-------| */ /** * Indicates that this view has reported that it can accept the current drag's content. * Cleared when the drag operation concludes. * @hide */ staticfinalint PFLAG2_DRAG_CAN_ACCEPT =0x00000001; /** * Indicates that this view is currently directly under the drag location in a * drag-and-drop operation involving content that it can accept. Cleared when * the drag exits the view, or when the drag operation concludes. * @hide */ staticfinalint PFLAG2_DRAG_HOVERED =0x00000002; /** @hide */ @IntDef(prefix ={"LAYOUT_DIRECTION_"}, value ={ LAYOUT_DIRECTION_LTR, LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE }) @Retention(RetentionPolicy.SOURCE) // Not called LayoutDirection to avoid conflict with android.util.LayoutDirection public@interfaceLayoutDir{} /** @hide */ @IntDef(prefix ={"LAYOUT_DIRECTION_"}, value ={ LAYOUT_DIRECTION_LTR, LAYOUT_DIRECTION_RTL }) @Retention(RetentionPolicy.SOURCE) public@interfaceResolvedLayoutDir{} /** * A flag to indicate that the layout direction of this view has not been defined yet. * @hide */ publicstaticfinalint LAYOUT_DIRECTION_UNDEFINED =LayoutDirection.UNDEFINED; /** * Horizontal layout direction of this view is from Left to Right. * Use with {@link #setLayoutDirection}. */ publicstaticfinalint LAYOUT_DIRECTION_LTR =LayoutDirection.LTR; /** * Horizontal layout direction of this view is from Right to Left. * Use with {@link #setLayoutDirection}. */ publicstaticfinalint LAYOUT_DIRECTION_RTL =LayoutDirection.RTL; /** * Horizontal layout direction of this view is inherited from its parent. * Use with {@link #setLayoutDirection}. */ publicstaticfinalint LAYOUT_DIRECTION_INHERIT =LayoutDirection.INHERIT; /** * Horizontal layout direction of this view is from deduced from the default language * script for the locale. Use with {@link #setLayoutDirection}. */ publicstaticfinalint LAYOUT_DIRECTION_LOCALE =LayoutDirection.LOCALE; /** * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED) * @hide */ staticfinalint PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT =2; /** * Mask for use with private flags indicating bits used for horizontal layout direction. * @hide */ staticfinalint PFLAG2_LAYOUT_DIRECTION_MASK =0x00000003<< PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /** * Indicates whether the view horizontal layout direction has been resolved and drawn to the * right-to-left direction. * @hide */ staticfinalint PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL =4<< PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /** * Indicates whether the view horizontal layout direction has been resolved. * @hide */ staticfinalint PFLAG2_LAYOUT_DIRECTION_RESOLVED =8<< PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /** * Mask for use with private flags indicating bits used for resolved horizontal layout direction. * @hide */ staticfinalint PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK =0x0000000C << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /* * Array of horizontal layout direction flags for mapping attribute "layoutDirection" to correct * flag value. * @hide */ privatestaticfinalint[] LAYOUT_DIRECTION_FLAGS ={ LAYOUT_DIRECTION_LTR, LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE }; /** * Default horizontal layout direction. */ privatestaticfinalint LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT; /** * Default horizontal layout direction. * @hide */ staticfinalint LAYOUT_DIRECTION_RESOLVED_DEFAULT = LAYOUT_DIRECTION_LTR; /** * Text direction is inherited through {@link ViewGroup} */ publicstaticfinalint TEXT_DIRECTION_INHERIT =0; /** * Text direction is using "first strong algorithm". The first strong directional character * determines the paragraph direction. If there is no strong directional character, the * paragraph direction is the view's resolved layout direction. */ publicstaticfinalint TEXT_DIRECTION_FIRST_STRONG =1; /** * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters. * If there are neither, the paragraph direction is the view's resolved layout direction. */ publicstaticfinalint TEXT_DIRECTION_ANY_RTL =2; /** * Text direction is forced to LTR. */ publicstaticfinalint TEXT_DIRECTION_LTR =3; /** * Text direction is forced to RTL. */ publicstaticfinalint TEXT_DIRECTION_RTL =4; /** * Text direction is coming from the system Locale. */ publicstaticfinalint TEXT_DIRECTION_LOCALE =5; /** * Text direction is using "first strong algorithm". The first strong directional character * determines the paragraph direction. If there is no strong directional character, the * paragraph direction is LTR. */ publicstaticfinalint TEXT_DIRECTION_FIRST_STRONG_LTR =6; /** * Text direction is using "first strong algorithm". The first strong directional character * determines the paragraph direction. If there is no strong directional character, the * paragraph direction is RTL. */ publicstaticfinalint TEXT_DIRECTION_FIRST_STRONG_RTL =7; /** * Default text direction is inherited */ privatestaticfinalint TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT; /** * Default resolved text direction * @hide */ staticfinalint TEXT_DIRECTION_RESOLVED_DEFAULT = TEXT_DIRECTION_FIRST_STRONG; /** * Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED) * @hide */ staticfinalint PFLAG2_TEXT_DIRECTION_MASK_SHIFT =6; /** * Mask for use with private flags indicating bits used for text direction. * @hide */ staticfinalint PFLAG2_TEXT_DIRECTION_MASK =0x00000007 << PFLAG2_TEXT_DIRECTION_MASK_SHIFT; /** * Array of text direction flags for mapping attribute "textDirection" to correct * flag value. * @hide */ privatestaticfinalint[] PFLAG2_TEXT_DIRECTION_FLAGS ={ TEXT_DIRECTION_INHERIT << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_FIRST_STRONG << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_ANY_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_LTR << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_LOCALE << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_FIRST_STRONG_LTR << PFLAG2_TEXT_DIRECTION_MASK_SHIFT, TEXT_DIRECTION_FIRST_STRONG_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT }; /** * Indicates whether the view text direction has been resolved. * @hide */ staticfinalint PFLAG2_TEXT_DIRECTION_RESOLVED =0x00000008 << PFLAG2_TEXT_DIRECTION_MASK_SHIFT; /** * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED) * @hide */ staticfinalint PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT =10; /** * Mask for use with private flags indicating bits used for resolved text direction. * @hide */ staticfinalint PFLAG2_TEXT_DIRECTION_RESOLVED_MASK =0x00000007 << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; /** * Indicates whether the view text direction has been resolved to the "first strong" heuristic. * @hide */ staticfinalint PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT = TEXT_DIRECTION_RESOLVED_DEFAULT << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; /** @hide */ @IntDef(prefix ={"TEXT_ALIGNMENT_"}, value ={ TEXT_ALIGNMENT_INHERIT, TEXT_ALIGNMENT_GRAVITY, TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_TEXT_START, TEXT_ALIGNMENT_TEXT_END, TEXT_ALIGNMENT_VIEW_START, TEXT_ALIGNMENT_VIEW_END }) @Retention(RetentionPolicy.SOURCE) public@interfaceTextAlignment{} /** * Default text alignment. The text alignment of this View is inherited from its parent. * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_INHERIT =0; /** * Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL, * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph's text direction. * * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_GRAVITY =1; /** * Align to the start of the paragraph, e.g. ALIGN_NORMAL. * * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_TEXT_START =2; /** * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE. * * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_TEXT_END =3; /** * Center the paragraph, e.g. ALIGN_CENTER. * * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_CENTER =4; /** * Align to the start of the view, which is ALIGN_LEFT if the view's resolved * layoutDirection is LTR, and ALIGN_RIGHT otherwise. * * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_VIEW_START =5; /** * Align to the end of the view, which is ALIGN_RIGHT if the view's resolved * layoutDirection is LTR, and ALIGN_LEFT otherwise. * * Use with {@link #setTextAlignment(int)} */ publicstaticfinalint TEXT_ALIGNMENT_VIEW_END =6; /** * Default text alignment is inherited */ privatestaticfinalint TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY; /** * Default resolved text alignment * @hide */ staticfinalint TEXT_ALIGNMENT_RESOLVED_DEFAULT = TEXT_ALIGNMENT_GRAVITY; /** * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED) * @hide */ staticfinalint PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT =13; /** * Mask for use with private flags indicating bits used for text alignment. * @hide */ staticfinalint PFLAG2_TEXT_ALIGNMENT_MASK =0x00000007<< PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT; /** * Array of text direction flags for mapping attribute "textAlignment" to correct * flag value. * @hide */ privatestaticfinalint[] PFLAG2_TEXT_ALIGNMENT_FLAGS ={ TEXT_ALIGNMENT_INHERIT << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT, TEXT_ALIGNMENT_GRAVITY << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT, TEXT_ALIGNMENT_TEXT_START << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT, TEXT_ALIGNMENT_TEXT_END << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT, TEXT_ALIGNMENT_CENTER << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT, TEXT_ALIGNMENT_VIEW_START << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT, TEXT_ALIGNMENT_VIEW_END << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT }; /** * Indicates whether the view text alignment has been resolved. * @hide */ staticfinalint PFLAG2_TEXT_ALIGNMENT_RESOLVED =0x00000008<< PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT; /** * Bit shift to get the resolved text alignment. * @hide */ staticfinalint PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT =17; /** * Mask for use with private flags indicating bits used for text alignment. * @hide */ staticfinalint PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK =0x00000007 << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; /** * Indicates whether if the view text alignment has been resolved to gravity */ privatestaticfinalint PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT = TEXT_ALIGNMENT_RESOLVED_DEFAULT << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; // Accessiblity constants for mPrivateFlags2 /** * Shift for the bits in {@link #mPrivateFlags2} related to the * "importantForAccessibility" attribute. */ staticfinalint PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT =20; /** * Automatically determine whether a view is important for accessibility. */ publicstaticfinalint IMPORTANT_FOR_ACCESSIBILITY_AUTO =0x00000000; /** * The view is important for accessibility. */ publicstaticfinalint IMPORTANT_FOR_ACCESSIBILITY_YES =0x00000001; /** * The view is not important for accessibility. */ publicstaticfinalint IMPORTANT_FOR_ACCESSIBILITY_NO =0x00000002; /** * The view is not important for accessibility, nor are any of its * descendant views. */ publicstaticfinalint IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS =0x00000004; /** * The default whether the view is important for accessibility. */ staticfinalint IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO; /** * Mask for obtaining the bits which specify how to determine * whether a view is important for accessibility. */ staticfinalint PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK =(IMPORTANT_FOR_ACCESSIBILITY_AUTO | IMPORTANT_FOR_ACCESSIBILITY_YES | IMPORTANT_FOR_ACCESSIBILITY_NO | IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT; /** * Shift for the bits in {@link #mPrivateFlags2} related to the * "accessibilityLiveRegion" attribute. */ staticfinalint PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT =23; /** * 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. * <p> * Use with {@link #setAccessibilityLiveRegion(int)}. */ publicstaticfinalint ACCESSIBILITY_LIVE_REGION_NONE =0x00000000; /** * Live region mode specifying that accessibility services should announce * changes to this view. * <p> * Use with {@link #setAccessibilityLiveRegion(int)}. */ publicstaticfinalint ACCESSIBILITY_LIVE_REGION_POLITE =0x00000001; /** * Live region mode specifying that accessibility services should interrupt * ongoing speech to immediately announce changes to this view. * <p> * Use with {@link #setAccessibilityLiveRegion(int)}. */ publicstaticfinalint ACCESSIBILITY_LIVE_REGION_ASSERTIVE =0x00000002; /** * The default whether the view is important for accessibility. */ staticfinalint ACCESSIBILITY_LIVE_REGION_DEFAULT = ACCESSIBILITY_LIVE_REGION_NONE; /** * Mask for obtaining the bits which specify a view's accessibility live * region mode. */ staticfinalint PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK =(ACCESSIBILITY_LIVE_REGION_NONE | ACCESSIBILITY_LIVE_REGION_POLITE | ACCESSIBILITY_LIVE_REGION_ASSERTIVE) << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT; /** * Flag indicating whether a view has accessibility focus. */ staticfinalint PFLAG2_ACCESSIBILITY_FOCUSED =0x04000000; /** * Flag whether the accessibility state of the subtree rooted at this view changed. */ staticfinalint PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED =0x08000000; /** * Flag indicating whether a view failed the quickReject() check in draw(). This condition * is used to check whether later changes to the view's transform should invalidate the * view to force the quickReject test to run again. */ staticfinalint PFLAG2_VIEW_QUICK_REJECTED =0x10000000; /** * Flag indicating that start/end padding has been resolved into left/right padding * for use in measurement, layout, drawing, etc. This is set by {@link #resolvePadding()} * and checked by {@link #measure(int, int)} to determine if padding needs to be resolved * during measurement. In some special cases this is required such as when an adapter-based * view measures prospective children without attaching them to a window. */ staticfinalint PFLAG2_PADDING_RESOLVED =0x20000000; /** * Flag indicating that the start/end drawables has been resolved into left/right ones. */ staticfinalint PFLAG2_DRAWABLE_RESOLVED =0x40000000; /** * Indicates that the view is tracking some sort of transient state * that the app should not need to be aware of, but that the framework * should take special care to preserve. */ staticfinalint PFLAG2_HAS_TRANSIENT_STATE =0x80000000; /** * Group of bits indicating that RTL properties resolution is done. */ staticfinalint ALL_RTL_PROPERTIES_RESOLVED = PFLAG2_LAYOUT_DIRECTION_RESOLVED | PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED | PFLAG2_PADDING_RESOLVED | PFLAG2_DRAWABLE_RESOLVED; // There are a couple of flags left in mPrivateFlags2 /* End of masks for mPrivateFlags2 */ /* * Masks for mPrivateFlags3, as generated by dumpFlags(): * * |-------|-------|-------|-------| * 1 PFLAG3_VIEW_IS_ANIMATING_TRANSFORM * 1 PFLAG3_VIEW_IS_ANIMATING_ALPHA * 1 PFLAG3_IS_LAID_OUT * 1 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT * 1 PFLAG3_CALLED_SUPER * 1 PFLAG3_APPLYING_INSETS * 1 PFLAG3_FITTING_SYSTEM_WINDOWS * 1 PFLAG3_NESTED_SCROLLING_ENABLED * 1 PFLAG3_SCROLL_INDICATOR_TOP * 1 PFLAG3_SCROLL_INDICATOR_BOTTOM * 1 PFLAG3_SCROLL_INDICATOR_LEFT * 1 PFLAG3_SCROLL_INDICATOR_RIGHT * 1 PFLAG3_SCROLL_INDICATOR_START * 1 PFLAG3_SCROLL_INDICATOR_END * 1 PFLAG3_ASSIST_BLOCKED * 1 PFLAG3_CLUSTER * 1 PFLAG3_IS_AUTOFILLED * 1 PFLAG3_FINGER_DOWN * 1 PFLAG3_FOCUSED_BY_DEFAULT * 1111 PFLAG3_IMPORTANT_FOR_AUTOFILL * 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE * 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED * 1 PFLAG3_TEMPORARY_DETACH * 1 PFLAG3_NO_REVEAL_ON_FOCUS * 1 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT * 1 PFLAG3_SCREEN_READER_FOCUSABLE * 1 PFLAG3_AGGREGATED_VISIBLE * 1 PFLAG3_AUTOFILLID_EXPLICITLY_SET * 1 PFLAG3_ACCESSIBILITY_HEADING * |-------|-------|-------|-------| */ /** * Flag indicating that view has a transform animation set on it. This is used to track whether * an animation is cleared between successive frames, in order to tell the associated * DisplayList to clear its animation matrix. */ staticfinalint PFLAG3_VIEW_IS_ANIMATING_TRANSFORM =0x1; /** * Flag indicating that view has an alpha animation set on it. This is used to track whether an * animation is cleared between successive frames, in order to tell the associated * DisplayList to restore its alpha value. */ staticfinalint PFLAG3_VIEW_IS_ANIMATING_ALPHA =0x2; /** * Flag indicating that the view has been through at least one layout since it * was last attached to a window. */ staticfinalint PFLAG3_IS_LAID_OUT =0x4; /** * Flag indicating that a call to measure() was skipped and should be done * instead when layout() is invoked. */ staticfinalint PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT =0x8; /** * Flag indicating that an overridden method correctly called down to * the superclass implementation as required by the API spec. */ staticfinalint PFLAG3_CALLED_SUPER =0x10; /** * Flag indicating that we're in the process of applying window insets. */ staticfinalint PFLAG3_APPLYING_INSETS =0x20; /** * Flag indicating that we're in the process of fitting system windows using the old method. */ staticfinalint PFLAG3_FITTING_SYSTEM_WINDOWS =0x40; /** * Flag indicating that nested scrolling is enabled for this view. * The view will optionally cooperate with views up its parent chain to allow for * integrated nested scrolling along the same axis. */ staticfinalint PFLAG3_NESTED_SCROLLING_ENABLED =0x80; /** * Flag indicating that the bottom scroll indicator should be displayed * when this view can scroll up. */ staticfinalint PFLAG3_SCROLL_INDICATOR_TOP =0x0100; /** * Flag indicating that the bottom scroll indicator should be displayed * when this view can scroll down. */ staticfinalint PFLAG3_SCROLL_INDICATOR_BOTTOM =0x0200; /** * Flag indicating that the left scroll indicator should be displayed * when this view can scroll left. */ staticfinalint PFLAG3_SCROLL_INDICATOR_LEFT =0x0400; /** * Flag indicating that the right scroll indicator should be displayed * when this view can scroll right. */ staticfinalint PFLAG3_SCROLL_INDICATOR_RIGHT =0x0800; /** * Flag indicating that the start scroll indicator should be displayed * when this view can scroll in the start direction. */ staticfinalint PFLAG3_SCROLL_INDICATOR_START =0x1000; /** * Flag indicating that the end scroll indicator should be displayed * when this view can scroll in the end direction. */ staticfinalint PFLAG3_SCROLL_INDICATOR_END =0x2000; staticfinalint DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED; staticfinalint SCROLL_INDICATORS_NONE =0x0000; /** * Mask for use with setFlags indicating bits used for indicating which * scroll indicators are enabled. */ staticfinalint SCROLL_INDICATORS_PFLAG3_MASK = PFLAG3_SCROLL_INDICATOR_TOP | PFLAG3_SCROLL_INDICATOR_BOTTOM | PFLAG3_SCROLL_INDICATOR_LEFT | PFLAG3_SCROLL_INDICATOR_RIGHT | PFLAG3_SCROLL_INDICATOR_START | PFLAG3_SCROLL_INDICATOR_END; /** * Left-shift required to translate between public scroll indicator flags * and internal PFLAGS3 flags. When used as a right-shift, translates * PFLAGS3 flags to public flags. */ staticfinalint SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT =8; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag =true, prefix ={"SCROLL_INDICATOR_"}, value ={ SCROLL_INDICATOR_TOP, SCROLL_INDICATOR_BOTTOM, SCROLL_INDICATOR_LEFT, SCROLL_INDICATOR_RIGHT, SCROLL_INDICATOR_START, SCROLL_INDICATOR_END, }) public@interfaceScrollIndicators{} /** * Scroll indicator direction for the top edge of the view. * * @see #setScrollIndicators(int) * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() */ publicstaticfinalint SCROLL_INDICATOR_TOP = PFLAG3_SCROLL_INDICATOR_TOP >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** * Scroll indicator direction for the bottom edge of the view. * * @see #setScrollIndicators(int) * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() */ publicstaticfinalint SCROLL_INDICATOR_BOTTOM = PFLAG3_SCROLL_INDICATOR_BOTTOM >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** * Scroll indicator direction for the left edge of the view. * * @see #setScrollIndicators(int) * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() */ publicstaticfinalint SCROLL_INDICATOR_LEFT = PFLAG3_SCROLL_INDICATOR_LEFT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** * Scroll indicator direction for the right edge of the view. * * @see #setScrollIndicators(int) * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() */ publicstaticfinalint SCROLL_INDICATOR_RIGHT = PFLAG3_SCROLL_INDICATOR_RIGHT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** * Scroll indicator direction for the starting edge of the view. * <p> * Resolved according to the view's layout direction, see * {@link #getLayoutDirection()} for more information. * * @see #setScrollIndicators(int) * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() */ publicstaticfinalint SCROLL_INDICATOR_START = PFLAG3_SCROLL_INDICATOR_START >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** * Scroll indicator direction for the ending edge of the view. * <p> * Resolved according to the view's layout direction, see * {@link #getLayoutDirection()} for more information. * * @see #setScrollIndicators(int) * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() */ publicstaticfinalint SCROLL_INDICATOR_END = PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** * <p>Indicates that we are allowing {@link ViewStructure} to traverse * into this view.<p> */ staticfinalint PFLAG3_ASSIST_BLOCKED =0x4000; /** * Flag indicating that the view is a root of a keyboard navigation cluster. * * @see #isKeyboardNavigationCluster() * @see #setKeyboardNavigationCluster(boolean) */ privatestaticfinalint PFLAG3_CLUSTER =0x8000; /** * Flag indicating that the view is autofilled * * @see #isAutofilled() * @see #setAutofilled(boolean, boolean) */ privatestaticfinalint PFLAG3_IS_AUTOFILLED =0x10000; /** * Indicates that the user is currently touching the screen. * Currently used for the tooltip positioning only. */ privatestaticfinalint PFLAG3_FINGER_DOWN =0x20000; /** * Flag indicating that this view is the default-focus view. * * @see #isFocusedByDefault() * @see #setFocusedByDefault(boolean) */ privatestaticfinalint PFLAG3_FOCUSED_BY_DEFAULT =0x40000; /** * Shift for the bits in {@link #mPrivateFlags3} related to the * "importantForAutofill" attribute. */ staticfinalint PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT =19; /** * Mask for obtaining the bits which specify how to determine * whether a view is important for autofill. */ staticfinalint PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK =(IMPORTANT_FOR_AUTOFILL_AUTO | IMPORTANT_FOR_AUTOFILL_YES | IMPORTANT_FOR_AUTOFILL_NO | IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS | IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS) << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT; /** * Whether this view has rendered elements that overlap (see {@link * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and * {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when * PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED has been set. Otherwise, the value is * determined by whatever {@link #hasOverlappingRendering()} returns. */ privatestaticfinalint PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE =0x800000; /** * Whether {@link #forceHasOverlappingRendering(boolean)} has been called. When true, value * in PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE is valid. */ privatestaticfinalint PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED =0x1000000; /** * Flag indicating that the view is temporarily detached from the parent view. * * @see #onStartTemporaryDetach() * @see #onFinishTemporaryDetach() */ staticfinalint PFLAG3_TEMPORARY_DETACH =0x2000000; /** * Flag indicating that the view does not wish to be revealed within its parent * hierarchy when it gains focus. Expressed in the negative since the historical * default behavior is to reveal on focus; this flag suppresses that behavior. * * @see #setRevealOnFocusHint(boolean) * @see #getRevealOnFocusHint() */ privatestaticfinalint PFLAG3_NO_REVEAL_ON_FOCUS =0x4000000; /** * Flag indicating that when layout is completed we should notify * that the view was entered for autofill purposes. To minimize * showing autofill for views not visible to the user we evaluate * user visibility which cannot be done until the view is laid out. */ staticfinalint PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT =0x8000000; /** * Works like focusable for screen readers, but without the side effects on input focus. * @see #setScreenReaderFocusable(boolean) */ privatestaticfinalint PFLAG3_SCREEN_READER_FOCUSABLE =0x10000000; /** * The last aggregated visibility. Used to detect when it truly changes. */ privatestaticfinalint PFLAG3_AGGREGATED_VISIBLE =0x20000000; /** * Used to indicate that {@link #mAutofillId} was explicitly set through * {@link #setAutofillId(AutofillId)}. */ privatestaticfinalint PFLAG3_AUTOFILLID_EXPLICITLY_SET =0x40000000; /** * Indicates if the View is a heading for accessibility purposes */ privatestaticfinalint PFLAG3_ACCESSIBILITY_HEADING =0x80000000; /* End of masks for mPrivateFlags3 */ /* * Masks for mPrivateFlags4, as generated by dumpFlags(): * * |-------|-------|-------|-------| * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE * 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK * 1 PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS * 1 PFLAG4_AUTOFILL_HIDE_HIGHLIGHT * 11 PFLAG4_SCROLL_CAPTURE_HINT_MASK * 1 PFLAG4_ALLOW_CLICK_WHEN_DISABLED * 1 PFLAG4_DETACHED * 1 PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE * 1 PFLAG4_DRAG_A11Y_STARTED * 1 PFLAG4_AUTO_HANDWRITING_INITIATION_ENABLED * |-------|-------|-------|-------| */ /** * Mask for obtaining the bits which specify how to determine * whether a view is important for autofill. * * <p>NOTE: the important for content capture values were the first flags added and are set in * the rightmost position, so we don't need to shift them */ privatestaticfinalint PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES | IMPORTANT_FOR_CONTENT_CAPTURE_NO | IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS | IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS; /* * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods * should be called. * * The idea is to call notifyAppeared() after the view is layout and visible, then call * notifyDisappeared() when it's gone (without known when it was removed from the parent). */ privatestaticfinalint PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED =0x10; privatestaticfinalint PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED =0x20; /* * Flags used to cache the value returned by isImportantForContentCapture while the view * hierarchy is being traversed. */ privatestaticfinalint PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED =0x40; privatestaticfinalint PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE =0x80; privatestaticfinalint PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK = PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; /** * @see #OPTIONAL_FITS_SYSTEM_WINDOWS */ staticfinalint PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS =0x000000100; /** * Flag indicating the field should not have yellow highlight when autofilled. */ privatestaticfinalint PFLAG4_AUTOFILL_HIDE_HIGHLIGHT =0x200; /** * Shift for the bits in {@link #mPrivateFlags4} related to scroll capture. */ staticfinalint PFLAG4_SCROLL_CAPTURE_HINT_SHIFT =10; staticfinalint PFLAG4_SCROLL_CAPTURE_HINT_MASK =(SCROLL_CAPTURE_HINT_INCLUDE | SCROLL_CAPTURE_HINT_EXCLUDE | SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) << PFLAG4_SCROLL_CAPTURE_HINT_SHIFT; /** * Indicates if the view can receive click events when disabled. */ privatestaticfinalint PFLAG4_ALLOW_CLICK_WHEN_DISABLED =0x000001000; /** * Indicates if the view is just detached. */ privatestaticfinalint PFLAG4_DETACHED =0x000002000; /** * Indicates that the view has transient state because the system is translating it. */ privatestaticfinalint PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE =0x000004000; /** * Indicates that the view has started a drag with {@link AccessibilityAction#ACTION_DRAG_START} */ privatestaticfinalint PFLAG4_DRAG_A11Y_STARTED =0x000008000; /** * Indicates that the view enables auto handwriting initiation. */ privatestaticfinalint PFLAG4_AUTO_HANDWRITING_ENABLED =0x000010000; /* End of masks for mPrivateFlags4 */ /** @hide */ protectedstaticfinalint VIEW_STRUCTURE_FOR_ASSIST =0; /** @hide */ protectedstaticfinalint VIEW_STRUCTURE_FOR_AUTOFILL =1; /** @hide */ protectedstaticfinalint VIEW_STRUCTURE_FOR_CONTENT_CAPTURE =2; /** @hide */ @IntDef(flag =true, prefix ={"VIEW_STRUCTURE_FOR"}, value ={ VIEW_STRUCTURE_FOR_ASSIST, VIEW_STRUCTURE_FOR_AUTOFILL, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE }) @Retention(RetentionPolicy.SOURCE) public@interfaceViewStructureType{} /** * Always allow a user to over-scroll this view, provided it is a * view that can scroll. * * @see #getOverScrollMode() * @see #setOverScrollMode(int) */ publicstaticfinalint 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. * * @see #getOverScrollMode() * @see #setOverScrollMode(int) */ publicstaticfinalint OVER_SCROLL_IF_CONTENT_SCROLLS =1; /** * Never allow a user to over-scroll this view. * * @see #getOverScrollMode() * @see #setOverScrollMode(int) */ publicstaticfinalint OVER_SCROLL_NEVER =2; /** * Special constant for {@link #setSystemUiVisibility(int)}: View has * requested the system UI (status bar) to be visible (the default). * * @see #setSystemUiVisibility(int) * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController} * instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_VISIBLE =0; /** * Flag for {@link #setSystemUiVisibility(int)}: View has requested the * system UI to enter an unobtrusive "low profile" mode. * * <p>This is for use in games, book readers, video players, or any other * "immersive" application where the usual system chrome is deemed too distracting. * * <p>In low profile mode, the status bar and/or navigation icons may dim. * * @see #setSystemUiVisibility(int) * @deprecated Low profile mode is deprecated. Hide the system bars instead if the application * needs to be in a unobtrusive mode. Use {@link WindowInsetsController#hide(int)} with * {@link Type#systemBars()}. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_LOW_PROFILE =0x00000001; /** * Flag for {@link #setSystemUiVisibility(int)}: View has requested that the * system navigation be temporarily hidden. * * <p>This is an even less obtrusive state than that called for by * {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls * (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause * those to disappear. This is useful (in conjunction with the * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN} * window flags) for displaying content using every last pixel on the display. * * <p>There is a limitation: because navigation controls are so important, the least user * interaction will cause them to reappear immediately. When this happens, both * this flag and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be cleared automatically, * so that both elements reappear at the same time. * * @see #setSystemUiVisibility(int) * @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#navigationBars()} * instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_HIDE_NAVIGATION =0x00000002; /** * Flag for {@link #setSystemUiVisibility(int)}: View has requested to go * into the normal fullscreen mode so that its content can take over the screen * while still allowing the user to interact with the application. * * <p>This has the same visual effect as * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN * WindowManager.LayoutParams.FLAG_FULLSCREEN}, * meaning that non-critical screen decorations (such as the status bar) will be * hidden while the user is in the View's window, focusing the experience on * that content. Unlike the window flag, if you are using ActionBar in * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY * Window.FEATURE_ACTION_BAR_OVERLAY}, then enabling this flag will also * hide the action bar. * * <p>This approach to going fullscreen is best used over the window flag when * it is a transient state -- that is, the application does this at certain * points in its user interaction where it wants to allow the user to focus * on content, but not as a continuous state. For situations where the application * would like to simply stay full screen the entire time (such as a game that * wants to take over the screen), the * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN window flag} * is usually a better approach. The state set here will be removed by the system * in various situations (such as the user moving to another application) like * the other system UI states. * * <p>When using this flag, the application should provide some easy facility * for the user to go out of it. A common example would be in an e-book * reader, where tapping on the screen brings back whatever screen and UI * decorations that had been hidden while the user was immersed in reading * the book. * * @see #setSystemUiVisibility(int) * @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#statusBars()} * instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_FULLSCREEN =0x00000004; /** * Flag for {@link #setSystemUiVisibility(int)}: When using other layout * flags, we would like a stable view of the content insets given to * {@link #fitSystemWindows(Rect)}. This means that the insets seen there * will always represent the worst case that the application can expect * as a continuous state. In the stock Android UI this is the space for * the system bar, nav bar, and status bar, but not more transient elements * such as an input method. * * The stable layout your UI sees is based on the system UI modes you can * switch to. That is, if you specify {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} * then you will get a stable layout for changes of the * {@link #SYSTEM_UI_FLAG_FULLSCREEN} mode; if you specify * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} and * {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}, then you can transition * to {@link #SYSTEM_UI_FLAG_FULLSCREEN} and {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} * with a stable layout. (Note that you should avoid using * {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} by itself.) * * If you have set the window flag {@link WindowManager.LayoutParams#FLAG_FULLSCREEN} * to hide the status bar (instead of using {@link #SYSTEM_UI_FLAG_FULLSCREEN}), * then a hidden status bar will be considered a "stable" state for purposes * here. This allows your UI to continually hide the status bar, while still * using the system UI flags to hide the action bar while still retaining * a stable layout. Note that changing the window fullscreen flag will never * provide a stable layout for a clean transition. * * <p>If you are using ActionBar in * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY * Window.FEATURE_ACTION_BAR_OVERLAY}, this flag will also impact the * insets it adds to those given to the application. * * @deprecated Use {@link WindowInsets#getInsetsIgnoringVisibility(int)} instead to retrieve * insets that don't change when system bars change visibility state. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_LAYOUT_STABLE =0x00000100; /** * Flag for {@link #setSystemUiVisibility(int)}: View would like its window * to be laid out as if it has requested * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, even if it currently hasn't. This * allows it to avoid artifacts when switching in and out of that mode, at * the expense that some of its user interface may be covered by screen * decorations when they are shown. You can perform layout of your inner * UI elements to account for the navigation system UI through the * {@link #fitSystemWindows(Rect)} method. * * @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with * {@link Type#navigationBars()}. For non-floating windows that fill the screen, call * {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}. */ publicstaticfinalint SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION =0x00000200; /** * Flag for {@link #setSystemUiVisibility(int)}: View would like its window * to be laid out as if it has requested * {@link #SYSTEM_UI_FLAG_FULLSCREEN}, even if it currently hasn't. This * allows it to avoid artifacts when switching in and out of that mode, at * the expense that some of its user interface may be covered by screen * decorations when they are shown. You can perform layout of your inner * UI elements to account for non-fullscreen system UI through the * {@link #fitSystemWindows(Rect)} method. * * <p>Note: on displays that have a {@link DisplayCutout}, the window may still be placed * differently than if {@link #SYSTEM_UI_FLAG_FULLSCREEN} was set, if the * window's {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode * layoutInDisplayCutoutMode} is * {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT * LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}. To avoid this, use either of the other modes. * * @see WindowManager.LayoutParams#layoutInDisplayCutoutMode * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER * * @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with * {@link Type#statusBars()} ()}. For non-floating windows that fill the screen, call * {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN =0x00000400; /** * Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when * hiding the navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. If this flag is * not set, {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} will be force cleared by the system on any * user interaction. * <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only * has an effect when used in combination with that flag.</p> * * @deprecated Use {@link WindowInsetsController#BEHAVIOR_DEFAULT} instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_IMMERSIVE =0x00000800; /** * Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when * hiding the status bar with {@link #SYSTEM_UI_FLAG_FULLSCREEN} and/or hiding the navigation * bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. Use this flag to create an immersive * experience while also hiding the system bars. If this flag is not set, * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} will be force cleared by the system on any user * interaction, and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be force-cleared by the system * if the user swipes from the top of the screen. * <p>When system bars are hidden in immersive mode, they can be revealed temporarily with * system gestures, such as swiping from the top of the screen. These transient system bars * will overlay app's content, may have some degree of transparency, and will automatically * hide after a short timeout. * </p><p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_FULLSCREEN} and * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination * with one or both of those flags.</p> * * @deprecated Use {@link WindowInsetsController#BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE} instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_IMMERSIVE_STICKY =0x00001000; /** * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that * is compatible with light status bar backgrounds. * * <p>For this to take effect, the window must request * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS * FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS * FLAG_TRANSLUCENT_STATUS}. * * @see android.R.attr#windowLightStatusBar * @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_STATUS_BARS} instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_LIGHT_STATUS_BAR =0x00002000; /** * This flag was previously used for a private API. DO NOT reuse it for a public API as it might * trigger undefined behavior on older platforms with apps compiled against a new SDK. */ privatestaticfinalint SYSTEM_UI_RESERVED_LEGACY1 =0x00004000; /** * This flag was previously used for a private API. DO NOT reuse it for a public API as it might * trigger undefined behavior on older platforms with apps compiled against a new SDK. */ privatestaticfinalint SYSTEM_UI_RESERVED_LEGACY2 =0x00010000; /** * Flag for {@link #setSystemUiVisibility(int)}: Requests the navigation bar to draw in a mode * that is compatible with light navigation bar backgrounds. * * <p>For this to take effect, the window must request * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS * FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION * FLAG_TRANSLUCENT_NAVIGATION}. * * @see android.R.attr#windowLightNavigationBar * @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_NAVIGATION_BARS} instead. */ @Deprecated publicstaticfinalint SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR =0x00000010; /** * @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead. */ @Deprecated publicstaticfinalint STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE; /** * @deprecated Use {@link #SYSTEM_UI_FLAG_VISIBLE} instead. */ @Deprecated publicstaticfinalint STATUS_BAR_VISIBLE = SYSTEM_UI_FLAG_VISIBLE; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to make the status bar not expandable. Unless you also * set {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show. */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicstaticfinalint STATUS_BAR_DISABLE_EXPAND =0x00010000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide notification icons and scrolling ticker text. */ publicstaticfinalint STATUS_BAR_DISABLE_NOTIFICATION_ICONS =0x00020000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to disable incoming notification alerts. This will not block * icons, but it will block sound, vibrating and other visual or aural notifications. */ publicstaticfinalint STATUS_BAR_DISABLE_NOTIFICATION_ALERTS =0x00040000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide only the scrolling ticker. Note that * {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS} implies * {@link #STATUS_BAR_DISABLE_NOTIFICATION_TICKER}. */ publicstaticfinalint STATUS_BAR_DISABLE_NOTIFICATION_TICKER =0x00080000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide the center system info area. */ publicstaticfinalint STATUS_BAR_DISABLE_SYSTEM_INFO =0x00100000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide only the home button. Don't use this * unless you're a special part of the system UI (i.e., setup wizard, keyguard). */ @UnsupportedAppUsage publicstaticfinalint STATUS_BAR_DISABLE_HOME =0x00200000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide only the back button. Don't use this * unless you're a special part of the system UI (i.e., setup wizard, keyguard). */ @UnsupportedAppUsage publicstaticfinalint STATUS_BAR_DISABLE_BACK =0x00400000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide only the clock. You might use this if your activity has * its own clock making the status bar's clock redundant. */ publicstaticfinalint STATUS_BAR_DISABLE_CLOCK =0x00800000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to hide only the recent apps button. Don't use this * unless you're a special part of the system UI (i.e., setup wizard, keyguard). */ @UnsupportedAppUsage publicstaticfinalint STATUS_BAR_DISABLE_RECENT =0x01000000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to disable the global search gesture. Don't use this * unless you're a special part of the system UI (i.e., setup wizard, keyguard). */ publicstaticfinalint STATUS_BAR_DISABLE_SEARCH =0x02000000; /** * @hide * * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked * out of the public fields to keep the undefined bits out of the developer's way. * * Flag to disable the ongoing call chip. */ publicstaticfinalint STATUS_BAR_DISABLE_ONGOING_CALL_CHIP =0x04000000; /** * @hide */ publicstaticfinalint PUBLIC_STATUS_BAR_VISIBILITY_MASK =0x00003FF7; /** * These are the system UI flags that can be cleared by events outside * of an application. Currently this is just the ability to tap on the * screen while hiding the navigation bar to have it return. * @hide */ publicstaticfinalint SYSTEM_UI_CLEARABLE_FLAGS = SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_FULLSCREEN; /** * Flags that can impact the layout in relation to system UI. * * @deprecated System UI layout flags are deprecated. */ @Deprecated publicstaticfinalint SYSTEM_UI_LAYOUT_FLAGS = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; /** @hide */ @IntDef(flag =true, prefix ={"FIND_VIEWS_"}, value ={ FIND_VIEWS_WITH_TEXT, FIND_VIEWS_WITH_CONTENT_DESCRIPTION }) @Retention(RetentionPolicy.SOURCE) public@interfaceFindViewFlags{} /** * Find views that render the specified text. * * @see #findViewsWithText(ArrayList, CharSequence, int) */ publicstaticfinalint FIND_VIEWS_WITH_TEXT =0x00000001; /** * Find find views that contain the specified content description. * * @see #findViewsWithText(ArrayList, CharSequence, int) */ publicstaticfinalint FIND_VIEWS_WITH_CONTENT_DESCRIPTION =0x00000002; /** * Find views that contain {@link AccessibilityNodeProvider}. Such * a View is a root of virtual view hierarchy and may contain the searched * text. If this flag is set Views with providers are automatically * added and it is a responsibility of the client to call the APIs of * the provider to determine whether the virtual tree rooted at this View * contains the text, i.e. getting the list of {@link AccessibilityNodeInfo}s * representing the virtual views with this text. * * @see #findViewsWithText(ArrayList, CharSequence, int) * * @hide */ publicstaticfinalint FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS =0x00000004; /** * The undefined cursor position. * * @hide */ publicstaticfinalint ACCESSIBILITY_CURSOR_POSITION_UNDEFINED =-1; /** * Indicates that the screen has changed state and is now off. * * @see #onScreenStateChanged(int) */ publicstaticfinalint SCREEN_STATE_OFF =0x0; /** * Indicates that the screen has changed state and is now on. * * @see #onScreenStateChanged(int) */ publicstaticfinalint SCREEN_STATE_ON =0x1; /** * Indicates no axis of view scrolling. */ publicstaticfinalint SCROLL_AXIS_NONE =0; /** * Indicates scrolling along the horizontal axis. */ publicstaticfinalint SCROLL_AXIS_HORIZONTAL =1<<0; /** * Indicates scrolling along the vertical axis. */ publicstaticfinalint SCROLL_AXIS_VERTICAL =1<<1; /** * Controls the over-scroll mode for this view. * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)}, * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}, * and {@link #OVER_SCROLL_NEVER}. */ privateint mOverScrollMode; /** * The parent this view is attached to. * {@hide} * * @see #getParent() */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedViewParent mParent; /** * {@hide} * * Not available for general use. If you need help, hang up and then dial one of the following * public APIs: * * @see #isAttachedToWindow() for current attach state * @see #onAttachedToWindow() for subclasses performing work when becoming attached * @see #onDetachedFromWindow() for subclasses performing work when becoming detached * @see OnAttachStateChangeListener for other code performing work on attach/detach * @see #getHandler() for posting messages to this view's UI thread/looper * @see #getParent() for interacting with the parent chain * @see #getWindowToken() for the current window token * @see #getRootView() for the view at the root of the attached hierarchy * @see #getDisplay() for the Display this view is presented on * @see #getRootWindowInsets() for the current insets applied to the whole attached window * @see #hasWindowFocus() for whether the attached window is currently focused * @see #getWindowVisibility() for checking the visibility of the attached window */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) AttachInfo mAttachInfo; /** * {@hide} */ @ViewDebug.ExportedProperty(flagMapping ={ @ViewDebug.FlagToString(mask = PFLAG_FORCE_LAYOUT, equals = PFLAG_FORCE_LAYOUT, name ="FORCE_LAYOUT"), @ViewDebug.FlagToString(mask = PFLAG_LAYOUT_REQUIRED, equals = PFLAG_LAYOUT_REQUIRED, name ="LAYOUT_REQUIRED"), @ViewDebug.FlagToString(mask = PFLAG_DRAWING_CACHE_VALID, equals = PFLAG_DRAWING_CACHE_VALID, name ="DRAWING_CACHE_INVALID", outputIf =false), @ViewDebug.FlagToString(mask = PFLAG_DRAWN, equals = PFLAG_DRAWN, name ="DRAWN", outputIf =true), @ViewDebug.FlagToString(mask = PFLAG_DRAWN, equals = PFLAG_DRAWN, name ="NOT_DRAWN", outputIf =false), @ViewDebug.FlagToString(mask = PFLAG_DIRTY_MASK, equals = PFLAG_DIRTY, name ="DIRTY") }, formatToHexString =true) /* @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =123769414) publicint mPrivateFlags; @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =123768943) int mPrivateFlags2; @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =129147060) int mPrivateFlags3; privateint mPrivateFlags4; /** * This view's request for the visibility of the status bar. * @hide */ @ViewDebug.ExportedProperty(flagMapping ={ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LOW_PROFILE, equals = SYSTEM_UI_FLAG_LOW_PROFILE, name ="LOW_PROFILE"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_HIDE_NAVIGATION, equals = SYSTEM_UI_FLAG_HIDE_NAVIGATION, name ="HIDE_NAVIGATION"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_FULLSCREEN, equals = SYSTEM_UI_FLAG_FULLSCREEN, name ="FULLSCREEN"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_STABLE, equals = SYSTEM_UI_FLAG_LAYOUT_STABLE, name ="LAYOUT_STABLE"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION, equals = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION, name ="LAYOUT_HIDE_NAVIGATION"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, equals = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, name ="LAYOUT_FULLSCREEN"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_IMMERSIVE, equals = SYSTEM_UI_FLAG_IMMERSIVE, name ="IMMERSIVE"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_IMMERSIVE_STICKY, equals = SYSTEM_UI_FLAG_IMMERSIVE_STICKY, name ="IMMERSIVE_STICKY"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, equals = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, name ="LIGHT_STATUS_BAR"), @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, equals = SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, name ="LIGHT_NAVIGATION_BAR"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_EXPAND, equals = STATUS_BAR_DISABLE_EXPAND, name ="STATUS_BAR_DISABLE_EXPAND"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_ICONS, equals = STATUS_BAR_DISABLE_NOTIFICATION_ICONS, name ="STATUS_BAR_DISABLE_NOTIFICATION_ICONS"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_ALERTS, equals = STATUS_BAR_DISABLE_NOTIFICATION_ALERTS, name ="STATUS_BAR_DISABLE_NOTIFICATION_ALERTS"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_TICKER, equals = STATUS_BAR_DISABLE_NOTIFICATION_TICKER, name ="STATUS_BAR_DISABLE_NOTIFICATION_TICKER"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SYSTEM_INFO, equals = STATUS_BAR_DISABLE_SYSTEM_INFO, name ="STATUS_BAR_DISABLE_SYSTEM_INFO"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_HOME, equals = STATUS_BAR_DISABLE_HOME, name ="STATUS_BAR_DISABLE_HOME"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_BACK, equals = STATUS_BAR_DISABLE_BACK, name ="STATUS_BAR_DISABLE_BACK"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_CLOCK, equals = STATUS_BAR_DISABLE_CLOCK, name ="STATUS_BAR_DISABLE_CLOCK"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_RECENT, equals = STATUS_BAR_DISABLE_RECENT, name ="STATUS_BAR_DISABLE_RECENT"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH, equals = STATUS_BAR_DISABLE_SEARCH, name ="STATUS_BAR_DISABLE_SEARCH"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, equals = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, name ="STATUS_BAR_DISABLE_ONGOING_CALL_CHIP") }, formatToHexString =true) @SystemUiVisibility int mSystemUiVisibility; /** * @hide */ @IntDef(flag =true, prefix ="", value ={ SYSTEM_UI_FLAG_LOW_PROFILE, SYSTEM_UI_FLAG_HIDE_NAVIGATION, SYSTEM_UI_FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE, SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION, SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, SYSTEM_UI_FLAG_IMMERSIVE, SYSTEM_UI_FLAG_IMMERSIVE_STICKY, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, STATUS_BAR_DISABLE_EXPAND, STATUS_BAR_DISABLE_NOTIFICATION_ICONS, STATUS_BAR_DISABLE_NOTIFICATION_ALERTS, STATUS_BAR_DISABLE_NOTIFICATION_TICKER, STATUS_BAR_DISABLE_SYSTEM_INFO, STATUS_BAR_DISABLE_HOME, STATUS_BAR_DISABLE_BACK, STATUS_BAR_DISABLE_CLOCK, STATUS_BAR_DISABLE_RECENT, STATUS_BAR_DISABLE_SEARCH, STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, }) @Retention(RetentionPolicy.SOURCE) public@interfaceSystemUiVisibility{} /** * Reference count for transient state. * @see #setHasTransientState(boolean) */ int mTransientStateCount =0; /** * Count of how many windows this view has been attached to. */ int mWindowAttachCount; /** * The layout parameters associated with this view and used by the parent * {@link android.view.ViewGroup} to determine how this view should be * laid out. * * The field should not be used directly. Instead {@link #getLayoutParams()} and {@link * #setLayoutParams(ViewGroup.LayoutParams)} should be used. The setter guarantees internal * state correctness of the class. * {@hide} */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedViewGroup.LayoutParams mLayoutParams; /** * The view flags hold various views states. * * Use {@link #setTransitionVisibility(int)} to change the visibility of this view without * triggering updates. * {@hide} */ @ViewDebug.ExportedProperty(formatToHexString =true) @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) int mViewFlags; staticclassTransformationInfo{ /** * The transform matrix for the View. This transform is calculated internally * based on the translation, rotation, and scale properties. * * Do *not* use this variable directly; instead call getMatrix(), which will * load the value from the View's RenderNode. */ privatefinalMatrix mMatrix =newMatrix(); /** * The inverse transform matrix for the View. This transform is calculated * internally based on the translation, rotation, and scale properties. * * Do *not* use this variable directly; instead call getInverseMatrix(), * which will load the value from the View's RenderNode. */ privateMatrix mInverseMatrix; /** * The opacity of the View. This is a value from 0 to 1, where 0 means * completely transparent and 1 means completely opaque. */ @ViewDebug.ExportedProperty privatefloat mAlpha =1f; /** * The opacity of the view as manipulated by the Fade transition. This is a * property only used by transitions, which is composited with the other alpha * values to calculate the final visual alpha value. */ float mTransitionAlpha =1f; } /** @hide */ @UnsupportedAppUsage publicTransformationInfo mTransformationInfo; /** * Current clip bounds. to which all drawing of this view are constrained. */ @ViewDebug.ExportedProperty(category ="drawing") Rect mClipBounds =null; privateboolean mLastIsOpaque; /** * The distance in pixels from the left edge of this view's parent * to the left edge of this view. * {@hide} */ @ViewDebug.ExportedProperty(category ="layout") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedint mLeft; /** * The distance in pixels from the left edge of this view's parent * to the right edge of this view. * {@hide} */ @ViewDebug.ExportedProperty(category ="layout") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedint mRight; /** * The distance in pixels from the top edge of this view's parent * to the top edge of this view. * {@hide} */ @ViewDebug.ExportedProperty(category ="layout") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedint mTop; /** * The distance in pixels from the top edge of this view's parent * to the bottom edge of this view. * {@hide} */ @ViewDebug.ExportedProperty(category ="layout") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedint mBottom; /** * The offset, in pixels, by which the content of this view is scrolled * horizontally. * Please use {@link View#getScrollX()} and {@link View#setScrollX(int)} instead of * accessing these directly. * {@hide} */ @ViewDebug.ExportedProperty(category ="scrolling") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedint mScrollX; /** * The offset, in pixels, by which the content of this view is scrolled * vertically. * Please use {@link View#getScrollY()} and {@link View#setScrollY(int)} instead of * accessing these directly. * {@hide} */ @ViewDebug.ExportedProperty(category ="scrolling") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedint mScrollY; /** * The final computed left padding in pixels that is used for drawing. This is the distance in * pixels between the left edge of this view and the left edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category ="padding") @UnsupportedAppUsage protectedint mPaddingLeft =0; /** * The final computed right padding in pixels that is used for drawing. This is the distance in * pixels between the right edge of this view and the right edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category ="padding") @UnsupportedAppUsage protectedint mPaddingRight =0; /** * The final computed top padding in pixels that is used for drawing. This is the distance in * pixels between the top edge of this view and the top edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category ="padding") @UnsupportedAppUsage protectedint mPaddingTop; /** * The final computed bottom padding in pixels that is used for drawing. This is the distance in * pixels between the bottom edge of this view and the bottom edge of its content. * {@hide} */ @ViewDebug.ExportedProperty(category ="padding") @UnsupportedAppUsage protectedint mPaddingBottom; /** * The layout insets in pixels, that is the distance in pixels between the * visible edges of this view its bounds. */ privateInsets mLayoutInsets; /** * Briefly describes the state of the view and is primarily used for accessibility support. */ privateCharSequence mStateDescription; /** * Briefly describes the view and is primarily used for accessibility support. */ privateCharSequence mContentDescription; /** * If this view represents a distinct part of the window, it can have a title that labels the * area. */ privateCharSequence mAccessibilityPaneTitle; /** * Specifies the id of a view for which this view serves as a label for * accessibility purposes. */ privateint mLabelForId =View.NO_ID; /** * Predicate for matching labeled view id with its label for * accessibility purposes. */ privateMatchLabelForPredicate mMatchLabelForPredicate; /** * Specifies a view before which this one is visited in accessibility traversal. */ privateint mAccessibilityTraversalBeforeId = NO_ID; /** * Specifies a view after which this one is visited in accessibility traversal. */ privateint mAccessibilityTraversalAfterId = NO_ID; /** * Predicate for matching a view by its id. */ privateMatchIdPredicate mMatchIdPredicate; /** * The right padding after RTL resolution, but before taking account of scroll bars. * * @hide */ @ViewDebug.ExportedProperty(category ="padding") protectedint mUserPaddingRight; /** * The resolved bottom padding before taking account of scroll bars. * * @hide */ @ViewDebug.ExportedProperty(category ="padding") protectedint mUserPaddingBottom; /** * The left padding after RTL resolution, but before taking account of scroll bars. * * @hide */ @ViewDebug.ExportedProperty(category ="padding") protectedint mUserPaddingLeft; /** * Cache the paddingStart set by the user to append to the scrollbar's size. * */ @ViewDebug.ExportedProperty(category ="padding") int mUserPaddingStart; /** * Cache the paddingEnd set by the user to append to the scrollbar's size. * */ @ViewDebug.ExportedProperty(category ="padding") int mUserPaddingEnd; /** * The left padding as set by a setter method, a background's padding, or via XML property * resolution. This value is the padding before LTR resolution or taking account of scrollbars. * * @hide */ int mUserPaddingLeftInitial; /** * The right padding as set by a setter method, a background's padding, or via XML property * resolution. This value is the padding before LTR resolution or taking account of scrollbars. * * @hide */ int mUserPaddingRightInitial; /** * Default undefined padding */ privatestaticfinalint UNDEFINED_PADDING =Integer.MIN_VALUE; /** * Cache if a left padding has been defined explicitly via padding, horizontal padding, * or leftPadding in XML, or by setPadding(...) or setRelativePadding(...) */ privateboolean mLeftPaddingDefined =false; /** * Cache if a right padding has been defined explicitly via padding, horizontal padding, * or rightPadding in XML, or by setPadding(...) or setRelativePadding(...) */ privateboolean mRightPaddingDefined =false; /** * @hide */ int mOldWidthMeasureSpec =Integer.MIN_VALUE; /** * @hide */ int mOldHeightMeasureSpec =Integer.MIN_VALUE; privateLongSparseLongArray mMeasureCache; @ViewDebug.ExportedProperty(deepExport =true, prefix ="bg_") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateDrawable mBackground; privateTintInfo mBackgroundTint; @ViewDebug.ExportedProperty(deepExport =true, prefix ="fg_") privateForegroundInfo mForegroundInfo; privateDrawable mScrollIndicatorDrawable; /** * RenderNode used for backgrounds. * <p> * When non-null and valid, this is expected to contain an up-to-date copy * of the background drawable. It is cleared on temporary detach, and reset * on cleanup. * @hide */ RenderNode mBackgroundRenderNode; @UnsupportedAppUsage privateint mBackgroundResource; privateboolean mBackgroundSizeChanged; /** The default focus highlight. * @see #mDefaultFocusHighlightEnabled * @see Drawable#hasFocusStateSpecified() */ privateDrawable mDefaultFocusHighlight; privateDrawable mDefaultFocusHighlightCache; privateboolean mDefaultFocusHighlightSizeChanged; /** * True if the default focus highlight is needed on the target device. */ privatestaticboolean sUseDefaultFocusHighlight; /** * True if zero-sized views can be focused. */ privatestaticboolean sCanFocusZeroSized; /** * Always assign focus if a focusable View is available. */ privatestaticboolean sAlwaysAssignFocus; privateString mTransitionName; staticclassTintInfo{ ColorStateList mTintList; BlendMode mBlendMode; boolean mHasTintMode; boolean mHasTintList; } privatestaticclassForegroundInfo{ privateDrawable mDrawable; privateTintInfo mTintInfo; privateint mGravity =Gravity.FILL; privateboolean mInsidePadding =true; privateboolean mBoundsChanged =true; privatefinalRect mSelfBounds =newRect(); privatefinalRect mOverlayBounds =newRect(); } staticclassListenerInfo{ @UnsupportedAppUsage ListenerInfo(){ } /** * Listener used to dispatch focus change events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ @UnsupportedAppUsage protectedOnFocusChangeListener mOnFocusChangeListener; /** * Listeners for layout change events. */ privateArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners; protectedOnScrollChangeListener mOnScrollChangeListener; /** * Listeners for attach events. */ privateCopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners; /** * Listener used to dispatch click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ @UnsupportedAppUsage publicOnClickListener mOnClickListener; /** * Listener used to dispatch long click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ @UnsupportedAppUsage protectedOnLongClickListener mOnLongClickListener; /** * Listener used to dispatch context click events. This field should be made private, so it * is hidden from the SDK. * {@hide} */ protectedOnContextClickListener mOnContextClickListener; /** * Listener used to build the context menu. * This field should be made private, so it is hidden from the SDK. * {@hide} */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedOnCreateContextMenuListener mOnCreateContextMenuListener; @UnsupportedAppUsage privateOnKeyListener mOnKeyListener; @UnsupportedAppUsage privateOnTouchListener mOnTouchListener; @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateOnHoverListener mOnHoverListener; @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateOnGenericMotionListener mOnGenericMotionListener; @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateOnDragListener mOnDragListener; privateOnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener; OnApplyWindowInsetsListener mOnApplyWindowInsetsListener; OnCapturedPointerListener mOnCapturedPointerListener; privateArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners; WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback; /** * This lives here since it's only valid for interactive views. This list is null * until its first use. */ privateList<Rect> mSystemGestureExclusionRects =null; privateList<Rect> mKeepClearRects =null; privateList<Rect> mUnrestrictedKeepClearRects =null; privateboolean mPreferKeepClear =false; privateRect mHandwritingArea =null; /** * Used to track {@link #mSystemGestureExclusionRects}, {@link #mKeepClearRects} and * {@link #mHandwritingArea}. */ publicRenderNode.PositionUpdateListener mPositionUpdateListener; privateRunnable mPositionChangedUpdate; /** * Allows the application to implement custom scroll capture support. */ ScrollCaptureCallback mScrollCaptureCallback; @Nullable privateOnReceiveContentListener mOnReceiveContentListener; } @UnsupportedAppUsage ListenerInfo mListenerInfo; privatestaticclassTooltipInfo{ /** * Text to be displayed in a tooltip popup. */ @Nullable CharSequence mTooltipText; /** * View-relative position of the tooltip anchor point. */ int mAnchorX; int mAnchorY; /** * The tooltip popup. */ @Nullable TooltipPopup mTooltipPopup; /** * Set to true if the tooltip was shown as a result of a long click. */ boolean mTooltipFromLongClick; /** * Keep these Runnables so that they can be used to reschedule. */ Runnable mShowTooltipRunnable; Runnable mHideTooltipRunnable; /** * Hover move is ignored if it is within this distance in pixels from the previous one. */ int mHoverSlop; /** * Update the anchor position if it significantly (that is by at least mHoverSlop) * different from the previously stored position. Ignoring insignificant changes * filters out the jitter which is typical for such input sources as stylus. * * @return True if the position has been updated. */ privateboolean updateAnchorPos(MotionEvent event){ finalint newAnchorX =(int) event.getX(); finalint newAnchorY =(int) event.getY(); if(Math.abs(newAnchorX - mAnchorX)<= mHoverSlop &&Math.abs(newAnchorY - mAnchorY)<= mHoverSlop){ returnfalse; } mAnchorX = newAnchorX; mAnchorY = newAnchorY; returntrue; } /** * Clear the anchor position to ensure that the next change is considered significant. */ privatevoid clearAnchorPos(){ mAnchorX =Integer.MAX_VALUE; mAnchorY =Integer.MAX_VALUE; } } TooltipInfo mTooltipInfo; // Temporary values used to hold (x,y) coordinates when delegating from the // two-arg performLongClick() method to the legacy no-arg version. privatefloat mLongClickX =Float.NaN; privatefloat mLongClickY =Float.NaN; /** * The application environment this view lives in. * This field should be made private, so it is hidden from the SDK. * {@hide} */ @ViewDebug.ExportedProperty(deepExport =true) @UnsupportedAppUsage @UiContext protectedContext mContext; @UnsupportedAppUsage privatefinalResources mResources; @UnsupportedAppUsage privateScrollabilityCache mScrollCache; privateint[] mDrawableState =null; ViewOutlineProvider mOutlineProvider =ViewOutlineProvider.BACKGROUND; /** * Animator that automatically runs based on state changes. */ privateStateListAnimator mStateListAnimator; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, * the user may specify which view to go to next. */ privateint mNextFocusLeftId =View.NO_ID; /** * When this view has focus and the next focus is {@link #FOCUS_RIGHT}, * the user may specify which view to go to next. */ privateint mNextFocusRightId =View.NO_ID; /** * When this view has focus and the next focus is {@link #FOCUS_UP}, * the user may specify which view to go to next. */ privateint mNextFocusUpId =View.NO_ID; /** * When this view has focus and the next focus is {@link #FOCUS_DOWN}, * the user may specify which view to go to next. */ privateint mNextFocusDownId =View.NO_ID; /** * When this view has focus and the next focus is {@link #FOCUS_FORWARD}, * the user may specify which view to go to next. */ int mNextFocusForwardId =View.NO_ID; /** * User-specified next keyboard navigation cluster in the {@link #FOCUS_FORWARD} direction. * * @see #findUserSetNextKeyboardNavigationCluster(View, int) */ int mNextClusterForwardId =View.NO_ID; /** * Whether this View should use a default focus highlight when it gets focused but doesn't * have {@link android.R.attr#state_focused} defined in its background. */ boolean mDefaultFocusHighlightEnabled =true; privateCheckForLongPress mPendingCheckForLongPress; @UnsupportedAppUsage privateCheckForTap mPendingCheckForTap =null; privatePerformClick mPerformClick; privateSendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent; privateSendAccessibilityEventThrottle mSendStateChangedAccessibilityEvent; privateUnsetPressedState mUnsetPressedState; /** * Whether the long press's action has been invoked. The tap's action is invoked on the * up event while a long press is invoked as soon as the long press duration is reached, so * a long press could be performed before the tap is checked, in which case the tap's action * should not be invoked. */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =115609023) privateboolean mHasPerformedLongPress; /** * Whether a context click button is currently pressed down. This is true when the stylus is * touching the screen and the primary button has been pressed, or if a mouse's right button is * pressed. This is false once the button is released or if the stylus has been lifted. */ privateboolean mInContextButtonPress; /** * Whether the next up event should be ignored for the purposes of gesture recognition. This is * true after a stylus button press has occured, when the next up event should not be recognized * as a tap. */ privateboolean mIgnoreNextUpEvent; /** * The minimum height of the view. We'll try our best to have the height * of this view to at least this amount. */ @ViewDebug.ExportedProperty(category ="measurement") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) privateint mMinHeight; /** * The minimum width of the view. We'll try our best to have the width * of this view to at least this amount. */ @ViewDebug.ExportedProperty(category ="measurement") @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) privateint mMinWidth; /** * The delegate to handle touch events that are physically in this view * but should be handled by another view. */ privateTouchDelegate mTouchDelegate =null; /** * While touch exploration is in use, set to true when hovering across boundaries and * inside the touch area of the delegate at receiving {@link MotionEvent#ACTION_HOVER_ENTER} * or {@link MotionEvent#ACTION_HOVER_MOVE}. False when leaving boundaries or receiving a * {@link MotionEvent#ACTION_HOVER_EXIT}. * Note that children of view group are excluded in the touch area. * @see #dispatchTouchExplorationHoverEvent */ privateboolean mHoveringTouchDelegate =false; /** * Solid color to use as a background when creating the drawing cache. Enables * the cache to use 16 bit bitmaps instead of 32 bit. */ privateint mDrawingCacheBackgroundColor =0; /** * Special tree observer used when mAttachInfo is null. */ privateViewTreeObserver mFloatingTreeObserver; /** * Cache the touch slop from the context that created the view. */ privateint mTouchSlop; /** * Cache the ambiguous gesture multiplier from the context that created the view. */ privatefloat mAmbiguousGestureMultiplier; /** * Object that handles automatic animation of view properties. */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =115609023) privateViewPropertyAnimator mAnimator =null; /** * List of registered FrameMetricsObservers. */ privateArrayList<FrameMetricsObserver> mFrameMetricsObservers; /** * Flag indicating that a drag can cross window boundaries. When * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called * with this flag set, all visible applications with targetSdkVersion >= * {@link android.os.Build.VERSION_CODES#N API 24} will be able to participate * in the drag operation and receive the dragged content. * * <p>If this is the only flag set, then the drag recipient will only have access to text data * and intents contained in the {@link ClipData} object. Access to URIs contained in the * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags</p> */ publicstaticfinalint DRAG_FLAG_GLOBAL =1<<8;// 256 /** * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to * request read access to the content URI(s) contained in the {@link ClipData} object. * @see android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION */ publicstaticfinalint DRAG_FLAG_GLOBAL_URI_READ =Intent.FLAG_GRANT_READ_URI_PERMISSION; /** * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to * request write access to the content URI(s) contained in the {@link ClipData} object. * @see android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION */ publicstaticfinalint DRAG_FLAG_GLOBAL_URI_WRITE =Intent.FLAG_GRANT_WRITE_URI_PERMISSION; /** * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant can be persisted across device * reboots until explicitly revoked with * {@link android.content.Context#revokeUriPermission(Uri, int)} Context.revokeUriPermission}. * @see android.content.Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION */ publicstaticfinalint DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION; /** * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant applies to any URI that is a prefix * match against the original granted URI. * @see android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION */ publicstaticfinalint DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; /** * Flag indicating that the drag shadow will be opaque. When * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called * with this flag set, the drag shadow will be opaque, otherwise, it will be semitransparent. */ publicstaticfinalint DRAG_FLAG_OPAQUE =1<<9; /** * Flag indicating that the drag was initiated with * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START}. When * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called, this * is used by the system to perform a drag without animations. */ publicstaticfinalint DRAG_FLAG_ACCESSIBILITY_ACTION =1<<10; /** * Flag indicating that the caller desires to take ownership of the drag surface for handling * the animation associated with an unhandled drag. It is mainly useful if the view starting * a global drag changes visibility during the gesture and the default animation of animating * the surface back to the origin is not sufficient. * * The calling app must hold the {@link android.Manifest.permission#START_TASKS_FROM_RECENTS} * permission and will receive the drag surface as a part of * {@link action.view.DragEvent#ACTION_DRAG_ENDED} only if the drag event's * {@link action.view.DragEvent#getDragResult()} is {@code false}. The caller is responsible * for removing the surface after its animation. * * This flag has no effect if the system decides that a cancel-drag animation does not need to * occur. * @hide */ publicstaticfinalint DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION =1<<11; /** * Vertical scroll factor cached by {@link #getVerticalScrollFactor}. */ privatefloat mVerticalScrollFactor; /** * Position of the vertical scroll bar. */ @UnsupportedAppUsage privateint mVerticalScrollbarPosition; /** * Position the scroll bar at the default position as determined by the system. */ publicstaticfinalint SCROLLBAR_POSITION_DEFAULT =0; /** * Position the scroll bar along the left edge. */ publicstaticfinalint SCROLLBAR_POSITION_LEFT =1; /** * Position the scroll bar along the right edge. */ publicstaticfinalint SCROLLBAR_POSITION_RIGHT =2; /** * Indicates that the view does not have a layer. * * @see #getLayerType() * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_SOFTWARE * @see #LAYER_TYPE_HARDWARE */ publicstaticfinalint LAYER_TYPE_NONE =0; /** * <p>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.</p> * * <p>Software layers have various usages:</p> * <p>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.</p> * <p>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.</p> * <p>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.)</p> * * @see #getLayerType() * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_HARDWARE */ publicstaticfinalint LAYER_TYPE_SOFTWARE =1; /** * <p>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 #LAYER_TYPE_SOFTWARE software layers}.</p> * * <p>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.</p> * <p>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.</p> * <p>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.</p> * * @see #getLayerType() * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_SOFTWARE */ publicstaticfinalint LAYER_TYPE_HARDWARE =2; /** @hide */ @IntDef(prefix ={"LAYER_TYPE_"}, value ={ LAYER_TYPE_NONE, LAYER_TYPE_SOFTWARE, LAYER_TYPE_HARDWARE }) @Retention(RetentionPolicy.SOURCE) public@interfaceLayerType{} @ViewDebug.ExportedProperty(category ="drawing", mapping ={ @ViewDebug.IntToString(from = LAYER_TYPE_NONE, to ="NONE"), @ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to ="SOFTWARE"), @ViewDebug.IntToString(from = LAYER_TYPE_HARDWARE, to ="HARDWARE") }) int mLayerType = LAYER_TYPE_NONE; Paint mLayerPaint; /** * Set to true when drawing cache is enabled and cannot be created. * * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicboolean mCachingFailed; @UnsupportedAppUsage privateBitmap mDrawingCache; @UnsupportedAppUsage privateBitmap mUnscaledDrawingCache; /** * RenderNode holding View properties, potentially holding a DisplayList of View content. * <p> * When non-null and valid, this is expected to contain an up-to-date copy * of the View content. Its DisplayList content is cleared on temporary detach and reset on * cleanup. */ @UnsupportedAppUsage finalRenderNode mRenderNode; /** * Set to true when the view is sending hover accessibility events because it * is the innermost hovered view. */ privateboolean mSendingHoverAccessibilityEvents; /** * Delegate for injecting accessibility functionality. */ @UnsupportedAppUsage AccessibilityDelegate mAccessibilityDelegate; /** * The view's overlay layer. Developers get a reference to the overlay via getOverlay() * and add/remove objects to/from the overlay directly through the Overlay methods. */ ViewOverlay mOverlay; /** * The currently active parent view for receiving delegated nested scrolling events. * This is set by {@link #startNestedScroll(int)} during a touch interaction and cleared * by {@link #stopNestedScroll()} at the same point where we clear * requestDisallowInterceptTouchEvent. */ privateViewParent mNestedScrollingParent; /** * Consistency verifier for debugging purposes. * @hide */ protectedfinalInputEventConsistencyVerifier mInputEventConsistencyVerifier = InputEventConsistencyVerifier.isInstrumentationEnabled()? newInputEventConsistencyVerifier(this,0):null; privatestaticfinalAtomicInteger sNextGeneratedId =newAtomicInteger(1); privateint[] mTempNestedScrollConsumed; /** * An overlay is going to draw this View instead of being drawn as part of this * View's parent. mGhostView is the View in the Overlay that must be invalidated * when this view is invalidated. */ GhostView mGhostView; /** * Holds pairs of adjacent attribute data: attribute name followed by its value. * @hide */ @ViewDebug.ExportedProperty(category ="attributes", hasAdjacentMapping =true) publicString[] mAttributes; /** * Maps a Resource id to its name. */ privatestaticSparseArray<String> mAttributeMap; /** * Queue of pending runnables. Used to postpone calls to post() until this * view is attached and has a handler. */ privateHandlerActionQueue mRunQueue; /** * The pointer icon when the mouse hovers on this view. The default is null. */ privatePointerIcon mPointerIcon; /** * @hide */ @UnsupportedAppUsage String mStartActivityRequestWho; @Nullable privateRoundScrollbarRenderer mRoundScrollbarRenderer; /** Used to delay visibility updates sent to the autofill manager */ privateHandler mVisibilityChangeForAutofillHandler; /** * Used when app developers explicitly set the {@link ContentCaptureSession} associated with the * view (through {@link #setContentCaptureSession(ContentCaptureSession)}. */ @Nullable privateContentCaptureSession mContentCaptureSession; /** * Whether {@link ContentCaptureSession} is cached, resets on {@link #invalidate()}. */ privateboolean mContentCaptureSessionCached; @LayoutRes privateint mSourceLayoutId = ID_NULL; @Nullable privateSparseIntArray mAttributeSourceResId; @Nullable privateSparseArray<int[]> mAttributeResolutionStacks; @StyleRes privateint mExplicitStyle; /** * Specifies which input source classes should provide unbuffered input events to this view * * @see View#requestUnbufferedDispatch(int) */ @InputSourceClass int mUnbufferedInputSource =InputDevice.SOURCE_CLASS_NONE; @Nullable privateString[] mReceiveContentMimeTypes; @Nullable privateViewTranslationCallback mViewTranslationCallback; @Nullable privateViewTranslationResponse mViewTranslationResponse; /** * Simple constructor to use when creating a view from code. * * @param context The Context the view is running in, through which it can * access the current theme, resources, etc. */ publicView(Context context){ mContext = context; mResources = context !=null? context.getResources():null; mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | FOCUSABLE_AUTO; // Set some flags defaults mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT)| (TEXT_DIRECTION_DEFAULT << PFLAG2_TEXT_DIRECTION_MASK_SHIFT)| (PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT)| (TEXT_ALIGNMENT_DEFAULT << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT)| (PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT)| (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT); mPrivateFlags4 = PFLAG4_AUTO_HANDWRITING_ENABLED; finalViewConfiguration configuration =ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); mAmbiguousGestureMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; mRenderNode =RenderNode.create(getClass().getName(),newViewAnimationHostBridge(this)); if(!sCompatibilityDone && context !=null){ finalint targetSdkVersion = context.getApplicationInfo().targetSdkVersion; // Older apps may need this compatibility hack for measurement. sUseBrokenMakeMeasureSpec = targetSdkVersion <=Build.VERSION_CODES.JELLY_BEAN_MR1; // Older apps expect onMeasure() to always be called on a layout pass, regardless // of whether a layout was requested on that View. sIgnoreMeasureCache = targetSdkVersion <Build.VERSION_CODES.KITKAT; // In M and newer, our widgets can pass a "hint" value in the size // for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers // know what the expected parent size is going to be, so e.g. list items can size // themselves at 1/3 the size of their container. It breaks older apps though, // specifically apps that use some popular open source libraries. sUseZeroUnspecifiedMeasureSpec = targetSdkVersion <Build.VERSION_CODES.M; // Old versions of the platform would give different results from // LinearLayout measurement passes using EXACTLY and non-EXACTLY // modes, so we always need to run an additional EXACTLY pass. sAlwaysRemeasureExactly = targetSdkVersion <=Build.VERSION_CODES.M; // Prior to N, TextureView would silently ignore calls to setBackground/setForeground. // On N+, we throw, but that breaks compatibility with apps that use these methods. sTextureViewIgnoresDrawableSetters = targetSdkVersion <=Build.VERSION_CODES.M; // Prior to N, we would drop margins in LayoutParam conversions. The fix triggers bugs // in apps so we target check it to avoid breaking existing apps. sPreserveMarginParamsInLayoutParamConversion = targetSdkVersion >=Build.VERSION_CODES.N; sCascadedDragDrop = targetSdkVersion <Build.VERSION_CODES.N; sHasFocusableExcludeAutoFocusable = targetSdkVersion <Build.VERSION_CODES.O; sAutoFocusableOffUIThreadWontNotifyParents = targetSdkVersion <Build.VERSION_CODES.O; sUseDefaultFocusHighlight = context.getResources().getBoolean( com.android.internal.R.bool.config_useDefaultFocusHighlight); sThrowOnInvalidFloatProperties = targetSdkVersion >=Build.VERSION_CODES.P; sCanFocusZeroSized = targetSdkVersion <Build.VERSION_CODES.P; sAlwaysAssignFocus = targetSdkVersion <Build.VERSION_CODES.P; sAcceptZeroSizeDragShadow = targetSdkVersion <Build.VERSION_CODES.P; sBrokenInsetsDispatch = targetSdkVersion <Build.VERSION_CODES.R; sBrokenWindowBackground = targetSdkVersion <Build.VERSION_CODES.Q; GradientDrawable.sWrapNegativeAngleMeasurements = targetSdkVersion >=Build.VERSION_CODES.Q; sForceLayoutWhenInsetsChanged = targetSdkVersion <Build.VERSION_CODES.R; sCompatibilityDone =true; } } /** * Constructor that is called when inflating a view from XML. This is called * when a view is being constructed from an XML file, supplying attributes * that were specified in the XML file. This version uses a default style of * 0, so the only attribute values applied are those in the Context's Theme * and the given AttributeSet. * * <p> * The method onFinishInflate() will be called after all children have been * added. * * @param context The Context the view is running in, through which it can * access the current theme, resources, etc. * @param attrs The attributes of the XML tag that is inflating the view. * @see #View(Context, AttributeSet, int) */ publicView(Context context,@NullableAttributeSet attrs){ this(context, attrs,0); } /** * Perform inflation from XML and apply a class-specific base style from a * theme attribute. This constructor of View allows subclasses to use their * own base style when they are inflating. For example, a Button class's * constructor would call this version of the super class constructor and * supply <code>R.attr.buttonStyle</code> for <var>defStyleAttr</var>; this * allows the theme's button style to modify all of the base view attributes * (in particular its background) as well as the Button class's attributes. * * @param context The Context the view is running in, through which it can * access the current theme, resources, etc. * @param attrs The attributes of the XML tag that is inflating the view. * @param defStyleAttr An attribute in the current theme that contains a * reference to a style resource that supplies default values for * the view. Can be 0 to not look for defaults. * @see #View(Context, AttributeSet) */ publicView(Context context,@NullableAttributeSet attrs,int defStyleAttr){ this(context, attrs, defStyleAttr,0); } /** * Perform inflation from XML and apply a class-specific base style from a * theme attribute or style resource. This constructor of View allows * subclasses to use their own base style when they are inflating. * <p> * When determining the final value of a particular attribute, there are * four inputs that come into play: * <ol> * <li>Any attribute values in the given AttributeSet. * <li>The style resource specified in the AttributeSet (named "style"). * <li>The default style specified by <var>defStyleAttr</var>. * <li>The default style specified by <var>defStyleRes</var>. * <li>The base values in this theme. * </ol> * <p> * Each of these inputs is considered in-order, with the first listed taking * precedence over the following ones. In other words, if in the * AttributeSet you have supplied <code>&lt;Button * textColor="#ff000000"&gt;</code> * , then the button's text will <em>always</em> be black, regardless of * what is specified in any of the styles. * * @param context The Context the view is running in, through which it can * access the current theme, resources, etc. * @param attrs The attributes of the XML tag that is inflating the view. * @param defStyleAttr An attribute in the current theme that contains a * reference to a style resource that supplies default values for * the view. Can be 0 to not look for defaults. * @param defStyleRes A resource identifier of a style resource that * supplies default values for the view, used only if * defStyleAttr is 0 or can not be found in the theme. Can be 0 * to not look for defaults. * @see #View(Context, AttributeSet, int) */ publicView(Context context,@NullableAttributeSet attrs,int defStyleAttr,int defStyleRes){ this(context); mSourceLayoutId =Resources.getAttributeSetSourceResId(attrs); finalTypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes); retrieveExplicitStyle(context.getTheme(), attrs); saveAttributeDataForStyleable(context, com.android.internal.R.styleable.View, attrs, a, defStyleAttr, defStyleRes); if(sDebugViewAttributes){ saveAttributeData(attrs, a); } Drawable background =null; int leftPadding =-1; int topPadding =-1; int rightPadding =-1; int bottomPadding =-1; int startPadding = UNDEFINED_PADDING; int endPadding = UNDEFINED_PADDING; int padding =-1; int paddingHorizontal =-1; int paddingVertical =-1; int viewFlagValues =0; int viewFlagMasks =0; boolean setScrollContainer =false; int x =0; int y =0; float tx =0; float ty =0; float tz =0; float elevation =0; float rotation =0; float rotationX =0; float rotationY =0; float sx =1f; float sy =1f; boolean transformSet =false; int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY; int overScrollMode = mOverScrollMode; boolean initializeScrollbars =false; boolean initializeScrollIndicators =false; boolean startPaddingDefined =false; boolean endPaddingDefined =false; boolean leftPaddingDefined =false; boolean rightPaddingDefined =false; finalint targetSdkVersion = context.getApplicationInfo().targetSdkVersion; // Set default values. viewFlagValues |= FOCUSABLE_AUTO; viewFlagMasks |= FOCUSABLE_AUTO; finalint N = a.getIndexCount(); for(int i =0; i < N; i++){ int attr = a.getIndex(i); switch(attr){ case com.android.internal.R.styleable.View_background: background = a.getDrawable(attr); break; case com.android.internal.R.styleable.View_padding: padding = a.getDimensionPixelSize(attr,-1); mUserPaddingLeftInitial = padding; mUserPaddingRightInitial = padding; leftPaddingDefined =true; rightPaddingDefined =true; break; case com.android.internal.R.styleable.View_paddingHorizontal: paddingHorizontal = a.getDimensionPixelSize(attr,-1); mUserPaddingLeftInitial = paddingHorizontal; mUserPaddingRightInitial = paddingHorizontal; leftPaddingDefined =true; rightPaddingDefined =true; break; case com.android.internal.R.styleable.View_paddingVertical: paddingVertical = a.getDimensionPixelSize(attr,-1); break; case com.android.internal.R.styleable.View_paddingLeft: leftPadding = a.getDimensionPixelSize(attr,-1); mUserPaddingLeftInitial = leftPadding; leftPaddingDefined =true; break; case com.android.internal.R.styleable.View_paddingTop: topPadding = a.getDimensionPixelSize(attr,-1); break; case com.android.internal.R.styleable.View_paddingRight: rightPadding = a.getDimensionPixelSize(attr,-1); mUserPaddingRightInitial = rightPadding; rightPaddingDefined =true; break; case com.android.internal.R.styleable.View_paddingBottom: bottomPadding = a.getDimensionPixelSize(attr,-1); break; case com.android.internal.R.styleable.View_paddingStart: startPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING); startPaddingDefined =(startPadding != UNDEFINED_PADDING); break; case com.android.internal.R.styleable.View_paddingEnd: endPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING); endPaddingDefined =(endPadding != UNDEFINED_PADDING); break; case com.android.internal.R.styleable.View_scrollX: x = a.getDimensionPixelOffset(attr,0); break; case com.android.internal.R.styleable.View_scrollY: y = a.getDimensionPixelOffset(attr,0); break; case com.android.internal.R.styleable.View_alpha: setAlpha(a.getFloat(attr,1f)); break; case com.android.internal.R.styleable.View_transformPivotX: setPivotX(a.getDimension(attr,0)); break; case com.android.internal.R.styleable.View_transformPivotY: setPivotY(a.getDimension(attr,0)); break; case com.android.internal.R.styleable.View_translationX: tx = a.getDimension(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_translationY: ty = a.getDimension(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_translationZ: tz = a.getDimension(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_elevation: elevation = a.getDimension(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_rotation: rotation = a.getFloat(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_rotationX: rotationX = a.getFloat(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_rotationY: rotationY = a.getFloat(attr,0); transformSet =true; break; case com.android.internal.R.styleable.View_scaleX: sx = a.getFloat(attr,1f); transformSet =true; break; case com.android.internal.R.styleable.View_scaleY: sy = a.getFloat(attr,1f); transformSet =true; break; case com.android.internal.R.styleable.View_id: mID = a.getResourceId(attr, NO_ID); break; case com.android.internal.R.styleable.View_tag: mTag = a.getText(attr); break; case com.android.internal.R.styleable.View_fitsSystemWindows: if(a.getBoolean(attr,false)){ viewFlagValues |= FITS_SYSTEM_WINDOWS; viewFlagMasks |= FITS_SYSTEM_WINDOWS; } break; case com.android.internal.R.styleable.View_focusable: viewFlagValues =(viewFlagValues &~FOCUSABLE_MASK)| getFocusableAttribute(a); if((viewFlagValues & FOCUSABLE_AUTO)==0){ viewFlagMasks |= FOCUSABLE_MASK; } break; case com.android.internal.R.styleable.View_focusableInTouchMode: if(a.getBoolean(attr,false)){ // unset auto focus since focusableInTouchMode implies explicit focusable viewFlagValues &=~FOCUSABLE_AUTO; viewFlagValues |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE; viewFlagMasks |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE_MASK; } break; case com.android.internal.R.styleable.View_clickable: if(a.getBoolean(attr,false)){ viewFlagValues |= CLICKABLE; viewFlagMasks |= CLICKABLE; } break; case com.android.internal.R.styleable.View_allowClickWhenDisabled: setAllowClickWhenDisabled(a.getBoolean(attr,false)); break; case com.android.internal.R.styleable.View_longClickable: if(a.getBoolean(attr,false)){ viewFlagValues |= LONG_CLICKABLE; viewFlagMasks |= LONG_CLICKABLE; } break; case com.android.internal.R.styleable.View_contextClickable: if(a.getBoolean(attr,false)){ viewFlagValues |= CONTEXT_CLICKABLE; viewFlagMasks |= CONTEXT_CLICKABLE; } break; case com.android.internal.R.styleable.View_saveEnabled: if(!a.getBoolean(attr,true)){ viewFlagValues |= SAVE_DISABLED; viewFlagMasks |= SAVE_DISABLED_MASK; } break; case com.android.internal.R.styleable.View_duplicateParentState: if(a.getBoolean(attr,false)){ viewFlagValues |= DUPLICATE_PARENT_STATE; viewFlagMasks |= DUPLICATE_PARENT_STATE; } break; case com.android.internal.R.styleable.View_visibility: finalint visibility = a.getInt(attr,0); if(visibility !=0){ viewFlagValues |= VISIBILITY_FLAGS[visibility]; viewFlagMasks |= VISIBILITY_MASK; } break; case com.android.internal.R.styleable.View_layoutDirection: // Clear any layout direction flags (included resolved bits) already set mPrivateFlags2 &= ~(PFLAG2_LAYOUT_DIRECTION_MASK | PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK); // Set the layout direction flags depending on the value of the attribute finalint layoutDirection = a.getInt(attr,-1); finalint value =(layoutDirection !=-1)? LAYOUT_DIRECTION_FLAGS[layoutDirection]: LAYOUT_DIRECTION_DEFAULT; mPrivateFlags2 |=(value << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT); break; case com.android.internal.R.styleable.View_drawingCacheQuality: finalint cacheQuality = a.getInt(attr,0); if(cacheQuality !=0){ viewFlagValues |= DRAWING_CACHE_QUALITY_FLAGS[cacheQuality]; viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK; } break; case com.android.internal.R.styleable.View_contentDescription: setContentDescription(a.getString(attr)); break; case com.android.internal.R.styleable.View_accessibilityTraversalBefore: setAccessibilityTraversalBefore(a.getResourceId(attr, NO_ID)); break; case com.android.internal.R.styleable.View_accessibilityTraversalAfter: setAccessibilityTraversalAfter(a.getResourceId(attr, NO_ID)); break; case com.android.internal.R.styleable.View_labelFor: setLabelFor(a.getResourceId(attr, NO_ID)); break; case com.android.internal.R.styleable.View_soundEffectsEnabled: if(!a.getBoolean(attr,true)){ viewFlagValues &=~SOUND_EFFECTS_ENABLED; viewFlagMasks |= SOUND_EFFECTS_ENABLED; } break; case com.android.internal.R.styleable.View_hapticFeedbackEnabled: if(!a.getBoolean(attr,true)){ viewFlagValues &=~HAPTIC_FEEDBACK_ENABLED; viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED; } break; case R.styleable.View_scrollbars: finalint scrollbars = a.getInt(attr, SCROLLBARS_NONE); if(scrollbars != SCROLLBARS_NONE){ viewFlagValues |= scrollbars; viewFlagMasks |= SCROLLBARS_MASK; initializeScrollbars =true; } break; //noinspection deprecation case R.styleable.View_fadingEdge: if(targetSdkVersion >=Build.VERSION_CODES.ICE_CREAM_SANDWICH){ // Ignore the attribute starting with ICS break; } // With builds < ICS, fall through and apply fading edges case R.styleable.View_requiresFadingEdge: finalint fadingEdge = a.getInt(attr, FADING_EDGE_NONE); if(fadingEdge != FADING_EDGE_NONE){ viewFlagValues |= fadingEdge; viewFlagMasks |= FADING_EDGE_MASK; initializeFadingEdgeInternal(a); } break; case R.styleable.View_scrollbarStyle: scrollbarStyle = a.getInt(attr, SCROLLBARS_INSIDE_OVERLAY); if(scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY){ viewFlagValues |= scrollbarStyle & SCROLLBARS_STYLE_MASK; viewFlagMasks |= SCROLLBARS_STYLE_MASK; } break; case R.styleable.View_isScrollContainer: setScrollContainer =true; if(a.getBoolean(attr,false)){ setScrollContainer(true); } break; case com.android.internal.R.styleable.View_keepScreenOn: if(a.getBoolean(attr,false)){ viewFlagValues |= KEEP_SCREEN_ON; viewFlagMasks |= KEEP_SCREEN_ON; } break; case R.styleable.View_filterTouchesWhenObscured: if(a.getBoolean(attr,false)){ viewFlagValues |= FILTER_TOUCHES_WHEN_OBSCURED; viewFlagMasks |= FILTER_TOUCHES_WHEN_OBSCURED; } break; case R.styleable.View_nextFocusLeft: mNextFocusLeftId = a.getResourceId(attr,View.NO_ID); break; case R.styleable.View_nextFocusRight: mNextFocusRightId = a.getResourceId(attr,View.NO_ID); break; case R.styleable.View_nextFocusUp: mNextFocusUpId = a.getResourceId(attr,View.NO_ID); break; case R.styleable.View_nextFocusDown: mNextFocusDownId = a.getResourceId(attr,View.NO_ID); break; case R.styleable.View_nextFocusForward: mNextFocusForwardId = a.getResourceId(attr,View.NO_ID); break; case R.styleable.View_nextClusterForward: mNextClusterForwardId = a.getResourceId(attr,View.NO_ID); break; case R.styleable.View_minWidth: mMinWidth = a.getDimensionPixelSize(attr,0); break; case R.styleable.View_minHeight: mMinHeight = a.getDimensionPixelSize(attr,0); break; case R.styleable.View_onClick: if(context.isRestricted()){ thrownewIllegalStateException("The android:onClick attribute cannot " +"be used within a restricted context"); } finalString handlerName = a.getString(attr); if(handlerName !=null){ setOnClickListener(newDeclaredOnClickListener(this, handlerName)); } break; case R.styleable.View_overScrollMode: overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS); break; case R.styleable.View_verticalScrollbarPosition: mVerticalScrollbarPosition = a.getInt(attr, SCROLLBAR_POSITION_DEFAULT); break; case R.styleable.View_layerType: setLayerType(a.getInt(attr, LAYER_TYPE_NONE),null); break; case R.styleable.View_textDirection: // Clear any text direction flag already set mPrivateFlags2 &=~PFLAG2_TEXT_DIRECTION_MASK; // Set the text direction flags depending on the value of the attribute finalint textDirection = a.getInt(attr,-1); if(textDirection !=-1){ mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_FLAGS[textDirection]; } break; case R.styleable.View_textAlignment: // Clear any text alignment flag already set mPrivateFlags2 &=~PFLAG2_TEXT_ALIGNMENT_MASK; // Set the text alignment flag depending on the value of the attribute finalint textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT); mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_FLAGS[textAlignment]; break; case R.styleable.View_importantForAccessibility: setImportantForAccessibility(a.getInt(attr, IMPORTANT_FOR_ACCESSIBILITY_DEFAULT)); break; case R.styleable.View_accessibilityLiveRegion: setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT)); break; case R.styleable.View_transitionName: setTransitionName(a.getString(attr)); break; case R.styleable.View_nestedScrollingEnabled: setNestedScrollingEnabled(a.getBoolean(attr,false)); break; case R.styleable.View_stateListAnimator: setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, a.getResourceId(attr,0))); break; case R.styleable.View_backgroundTint: // This will get applied later during setBackground(). if(mBackgroundTint ==null){ mBackgroundTint =newTintInfo(); } mBackgroundTint.mTintList = a.getColorStateList( R.styleable.View_backgroundTint); mBackgroundTint.mHasTintList =true; break; case R.styleable.View_backgroundTintMode: // This will get applied later during setBackground(). if(mBackgroundTint ==null){ mBackgroundTint =newTintInfo(); } mBackgroundTint.mBlendMode =Drawable.parseBlendMode(a.getInt( R.styleable.View_backgroundTintMode,-1),null); mBackgroundTint.mHasTintMode =true; break; case R.styleable.View_outlineProvider: setOutlineProviderFromAttribute(a.getInt(R.styleable.View_outlineProvider, PROVIDER_BACKGROUND)); break; case R.styleable.View_foreground: if(targetSdkVersion >=Build.VERSION_CODES.M ||thisinstanceofFrameLayout){ setForeground(a.getDrawable(attr)); } break; case R.styleable.View_foregroundGravity: if(targetSdkVersion >=Build.VERSION_CODES.M ||thisinstanceofFrameLayout){ setForegroundGravity(a.getInt(attr,Gravity.NO_GRAVITY)); } break; case R.styleable.View_foregroundTintMode: if(targetSdkVersion >=Build.VERSION_CODES.M ||thisinstanceofFrameLayout){ setForegroundTintBlendMode( Drawable.parseBlendMode(a.getInt(attr,-1), null)); } break; case R.styleable.View_foregroundTint: if(targetSdkVersion >=Build.VERSION_CODES.M ||thisinstanceofFrameLayout){ setForegroundTintList(a.getColorStateList(attr)); } break; case R.styleable.View_foregroundInsidePadding: if(targetSdkVersion >=Build.VERSION_CODES.M ||thisinstanceofFrameLayout){ if(mForegroundInfo ==null){ mForegroundInfo =newForegroundInfo(); } mForegroundInfo.mInsidePadding = a.getBoolean(attr, mForegroundInfo.mInsidePadding); } break; case R.styleable.View_scrollIndicators: finalint scrollIndicators = (a.getInt(attr,0)<< SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT) & SCROLL_INDICATORS_PFLAG3_MASK; if(scrollIndicators !=0){ mPrivateFlags3 |= scrollIndicators; initializeScrollIndicators =true; } break; case R.styleable.View_pointerIcon: finalint resourceId = a.getResourceId(attr,0); if(resourceId !=0){ setPointerIcon(PointerIcon.load( context.getResources(), resourceId)); }else{ finalint pointerType = a.getInt(attr,PointerIcon.TYPE_NOT_SPECIFIED); if(pointerType !=PointerIcon.TYPE_NOT_SPECIFIED){ setPointerIcon(PointerIcon.getSystemIcon(context, pointerType)); } } break; case R.styleable.View_forceHasOverlappingRendering: if(a.peekValue(attr)!=null){ forceHasOverlappingRendering(a.getBoolean(attr,true)); } break; case R.styleable.View_tooltipText: setTooltipText(a.getText(attr)); break; case R.styleable.View_keyboardNavigationCluster: if(a.peekValue(attr)!=null){ setKeyboardNavigationCluster(a.getBoolean(attr,true)); } break; case R.styleable.View_focusedByDefault: if(a.peekValue(attr)!=null){ setFocusedByDefault(a.getBoolean(attr,true)); } break; case R.styleable.View_autofillHints: if(a.peekValue(attr)!=null){ CharSequence[] rawHints =null; String rawString =null; if(a.getType(attr)==TypedValue.TYPE_REFERENCE){ int resId = a.getResourceId(attr,0); try{ rawHints = a.getTextArray(attr); }catch(Resources.NotFoundException e){ rawString = getResources().getString(resId); } }else{ rawString = a.getString(attr); } if(rawHints ==null){ if(rawString ==null){ thrownewIllegalArgumentException( "Could not resolve autofillHints"); }else{ rawHints = rawString.split(","); } } String[] hints =newString[rawHints.length]; int numHints = rawHints.length; for(int rawHintNum =0; rawHintNum < numHints; rawHintNum++){ hints[rawHintNum]= rawHints[rawHintNum].toString().trim(); } setAutofillHints(hints); } break; case R.styleable.View_importantForAutofill: if(a.peekValue(attr)!=null){ setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO)); } break; case R.styleable.View_importantForContentCapture: if(a.peekValue(attr)!=null){ setImportantForContentCapture(a.getInt(attr, IMPORTANT_FOR_CONTENT_CAPTURE_AUTO)); } case R.styleable.View_defaultFocusHighlightEnabled: if(a.peekValue(attr)!=null){ setDefaultFocusHighlightEnabled(a.getBoolean(attr,true)); } break; case R.styleable.View_screenReaderFocusable: if(a.peekValue(attr)!=null){ setScreenReaderFocusable(a.getBoolean(attr,false)); } break; case R.styleable.View_accessibilityPaneTitle: if(a.peekValue(attr)!=null){ setAccessibilityPaneTitle(a.getString(attr)); } break; case R.styleable.View_outlineSpotShadowColor: setOutlineSpotShadowColor(a.getColor(attr,Color.BLACK)); break; case R.styleable.View_outlineAmbientShadowColor: setOutlineAmbientShadowColor(a.getColor(attr,Color.BLACK)); break; case com.android.internal.R.styleable.View_accessibilityHeading: setAccessibilityHeading(a.getBoolean(attr,false)); break; case R.styleable.View_forceDarkAllowed: mRenderNode.setForceDarkAllowed(a.getBoolean(attr,true)); break; case R.styleable.View_scrollCaptureHint: setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO))); break; case R.styleable.View_clipToOutline: setClipToOutline(a.getBoolean(attr,false)); break; case R.styleable.View_preferKeepClear: setPreferKeepClear(a.getBoolean(attr,false)); break; case R.styleable.View_autoHandwritingEnabled: setAutoHandwritingEnabled(a.getBoolean(attr,true)); break; } } setOverScrollMode(overScrollMode); // Cache start/end user padding as we cannot fully resolve padding here (we don't have yet // the resolved layout direction). Those cached values will be used later during padding // resolution. mUserPaddingStart = startPadding; mUserPaddingEnd = endPadding; if(background !=null){ setBackground(background); } // setBackground above will record that padding is currently provided by the background. // If we have padding specified via xml, record that here instead and use it. mLeftPaddingDefined = leftPaddingDefined; mRightPaddingDefined = rightPaddingDefined; // Valid paddingHorizontal/paddingVertical beats leftPadding, rightPadding, topPadding, // bottomPadding, and padding set by background. Valid padding beats everything. if(padding >=0){ leftPadding = padding; topPadding = padding; rightPadding = padding; bottomPadding = padding; mUserPaddingLeftInitial = padding; mUserPaddingRightInitial = padding; }else{ if(paddingHorizontal >=0){ leftPadding = paddingHorizontal; rightPadding = paddingHorizontal; mUserPaddingLeftInitial = paddingHorizontal; mUserPaddingRightInitial = paddingHorizontal; } if(paddingVertical >=0){ topPadding = paddingVertical; bottomPadding = paddingVertical; } } if(isRtlCompatibilityMode()){ // RTL compatibility mode: pre Jelly Bean MR1 case OR no RTL support case. // left / right padding are used if defined (meaning here nothing to do). If they are not // defined and start / end padding are defined (e.g. in Frameworks resources), then we use // start / end and resolve them as left / right (layout direction is not taken into account). // Padding from the background drawable is stored at this point in mUserPaddingLeftInitial // and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if // defined. if(!mLeftPaddingDefined && startPaddingDefined){ leftPadding = startPadding; } mUserPaddingLeftInitial =(leftPadding >=0)? leftPadding : mUserPaddingLeftInitial; if(!mRightPaddingDefined && endPaddingDefined){ rightPadding = endPadding; } mUserPaddingRightInitial =(rightPadding >=0)? rightPadding : mUserPaddingRightInitial; }else{ // Jelly Bean MR1 and after case: if start/end defined, they will override any left/right // values defined. Otherwise, left /right values are used. // Padding from the background drawable is stored at this point in mUserPaddingLeftInitial // and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if // defined. finalboolean hasRelativePadding = startPaddingDefined || endPaddingDefined; if(mLeftPaddingDefined &&!hasRelativePadding){ mUserPaddingLeftInitial = leftPadding; } if(mRightPaddingDefined &&!hasRelativePadding){ mUserPaddingRightInitial = rightPadding; } } // mPaddingTop and mPaddingBottom may have been set by setBackground(Drawable) so must pass // them on if topPadding or bottomPadding are not valid. internalSetPadding( mUserPaddingLeftInitial, topPadding >=0? topPadding : mPaddingTop, mUserPaddingRightInitial, bottomPadding >=0? bottomPadding : mPaddingBottom); if(viewFlagMasks !=0){ setFlags(viewFlagValues, viewFlagMasks); } if(initializeScrollbars){ initializeScrollbarsInternal(a); } if(initializeScrollIndicators){ initializeScrollIndicatorsInternal(); } a.recycle(); // Needs to be called after mViewFlags is set if(scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY){ recomputePadding(); } if(x !=0|| y !=0){ scrollTo(x, y); } if(transformSet){ setTranslationX(tx); setTranslationY(ty); setTranslationZ(tz); setElevation(elevation); setRotation(rotation); setRotationX(rotationX); setRotationY(rotationY); setScaleX(sx); setScaleY(sy); } if(!setScrollContainer &&(viewFlagValues&SCROLLBARS_VERTICAL)!=0){ setScrollContainer(true); } computeOpaqueFlags(); } /** * Returns the ordered list of resource ID that are considered when resolving attribute values * for this {@link View}. The list will include layout resource ID if the View is inflated from * XML. It will also include a set of explicit styles if specified in XML using * {@code style="..."}. Finally, it will include the default styles resolved from the theme. * * <p> * <b>Note:</b> this method will only return actual values if the view attribute debugging * is enabled in Android developer options. * * @param attribute Attribute resource ID for which the resolution stack should be returned. * @return ordered list of resource ID that are considered when resolving attribute values for * this {@link View}. */ @NonNull publicint[] getAttributeResolutionStack(@AttrResint attribute){ if(!sDebugViewAttributes || mAttributeResolutionStacks ==null || mAttributeResolutionStacks.get(attribute)==null){ returnnewint[0]; } int[] attributeResolutionStack = mAttributeResolutionStacks.get(attribute); int stackSize = attributeResolutionStack.length; if(mSourceLayoutId != ID_NULL){ stackSize++; } int currentIndex =0; int[] stack =newint[stackSize]; if(mSourceLayoutId != ID_NULL){ stack[currentIndex]= mSourceLayoutId; currentIndex++; } for(int i =0; i < attributeResolutionStack.length; i++){ stack[currentIndex]= attributeResolutionStack[i]; currentIndex++; } return stack; } /** * Returns the mapping of attribute resource ID to source resource ID where the attribute value * was set. Source resource ID can either be a layout resource ID, if the value was set in XML * within the View tag, or a style resource ID, if the attribute was set in a style. The source * resource value will be one of the resource IDs from {@link #getAttributeSourceResourceMap()}. * * <p> * <b>Note:</b> this method will only return actual values if the view attribute debugging * is enabled in Android developer options. * * @return mapping of attribute resource ID to source resource ID where the attribute value * was set. */ @NonNull @SuppressWarnings("AndroidFrameworkEfficientCollections") publicMap<Integer,Integer> getAttributeSourceResourceMap(){ HashMap<Integer,Integer> map =newHashMap<>(); if(!sDebugViewAttributes || mAttributeSourceResId ==null){ return map; } for(int i =0; i < mAttributeSourceResId.size(); i++){ map.put(mAttributeSourceResId.keyAt(i), mAttributeSourceResId.valueAt(i)); } return map; } /** * Returns the resource ID for the style specified using {@code style="..."} in the * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise if not * specified or otherwise not applicable. * <p> * Each {@link View} can have an explicit style specified in the layout file. * This style is used first during the {@link View} attribute resolution, then if an attribute * is not defined there the resource system looks at default style and theme as fallbacks. * * <p> * <b>Note:</b> this method will only return actual values if the view attribute debugging * is enabled in Android developer options. * * @return The resource ID for the style specified using {@code style="..."} in the * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise * if not specified or otherwise not applicable. */ @StyleRes publicint getExplicitStyle(){ if(!sDebugViewAttributes){ return ID_NULL; } return mExplicitStyle; } /** * An implementation of OnClickListener that attempts to lazily load a * named click handling method from a parent or ancestor context. */ privatestaticclassDeclaredOnClickListenerimplementsOnClickListener{ privatefinalView mHostView; privatefinalString mMethodName; privateMethod mResolvedMethod; privateContext mResolvedContext; publicDeclaredOnClickListener(@NonNullView hostView,@NonNullString methodName){ mHostView = hostView; mMethodName = methodName; } @Override publicvoid onClick(@NonNullView v){ if(mResolvedMethod ==null){ resolveMethod(mHostView.getContext(), mMethodName); } try{ mResolvedMethod.invoke(mResolvedContext, v); }catch(IllegalAccessException e){ thrownewIllegalStateException( "Could not execute non-public method for android:onClick", e); }catch(InvocationTargetException e){ thrownewIllegalStateException( "Could not execute method for android:onClick", e); } } @NonNull privatevoid resolveMethod(@NullableContext context,@NonNullString name){ while(context !=null){ try{ if(!context.isRestricted()){ finalMethod method = context.getClass().getMethod(mMethodName,View.class); if(method !=null){ mResolvedMethod = method; mResolvedContext = context; return; } } }catch(NoSuchMethodException e){ // Failed to find method, keep searching up the hierarchy. } if(context instanceofContextWrapper){ context =((ContextWrapper) context).getBaseContext(); }else{ // Can't search up the hierarchy, null out and fail. context =null; } } finalint id = mHostView.getId(); finalString idText = id == NO_ID ?"":" with id '" + mHostView.getContext().getResources().getResourceEntryName(id)+"'"; thrownewIllegalStateException("Could not find method "+ mMethodName +"(View) in a parent or ancestor Context for android:onClick " +"attribute defined on view "+ mHostView.getClass()+ idText); } } /** * Non-public constructor for use in testing */ @UnsupportedAppUsage View(){ mResources =null; mRenderNode =RenderNode.create(getClass().getName(),newViewAnimationHostBridge(this)); } /** * Returns {@code true} when the View is attached and the system developer setting to show * the layout bounds is enabled or {@code false} otherwise. */ publicfinalboolean isShowingLayoutBounds(){ return DEBUG_DRAW || mAttachInfo !=null&& mAttachInfo.mDebugLayout; } /** * Used to test isShowingLayoutBounds(). This sets the local value used * by that function. This method does nothing if the layout isn't attached. * * @hide */ @TestApi publicfinalvoid setShowingLayoutBounds(boolean debugLayout){ if(mAttachInfo !=null){ mAttachInfo.mDebugLayout = debugLayout; } } privatestaticSparseArray<String> getAttributeMap(){ if(mAttributeMap ==null){ mAttributeMap =newSparseArray<>(); } return mAttributeMap; } privatevoid retrieveExplicitStyle(@NonNullResources.Theme theme, @NullableAttributeSet attrs){ if(!sDebugViewAttributes){ return; } mExplicitStyle = theme.getExplicitStyle(attrs); } /** * Stores debugging information about attributes. This should be called in a constructor by * every custom {@link View} that uses a custom styleable. If the custom view does not call it, * then the custom attributes used by this view will not be visible in layout inspection tools. * * @param context Context under which this view is created. * @param styleable A reference to styleable array R.styleable.Foo * @param attrs AttributeSet used to construct this view. * @param t Resolved {@link TypedArray} returned by a call to * {@link Resources#obtainAttributes(AttributeSet, int[])}. * @param defStyleAttr Default style attribute passed into the view constructor. * @param defStyleRes Default style resource passed into the view constructor. */ publicfinalvoid saveAttributeDataForStyleable(@NonNullContext context, @NonNullint[] styleable,@NullableAttributeSet attrs,@NonNullTypedArray t, int defStyleAttr,int defStyleRes){ if(!sDebugViewAttributes){ return; } int[] attributeResolutionStack = context.getTheme().getAttributeResolutionStack( defStyleAttr, defStyleRes, mExplicitStyle); if(mAttributeResolutionStacks ==null){ mAttributeResolutionStacks =newSparseArray<>(); } if(mAttributeSourceResId ==null){ mAttributeSourceResId =newSparseIntArray(); } finalint indexCount = t.getIndexCount(); for(int j =0; j < indexCount;++j){ finalint index = t.getIndex(j); mAttributeSourceResId.append(styleable[index], t.getSourceResourceId(index,0)); mAttributeResolutionStacks.append(styleable[index], attributeResolutionStack); } } privatevoid saveAttributeData(@NullableAttributeSet attrs,@NonNullTypedArray t){ finalint attrsCount = attrs ==null?0: attrs.getAttributeCount(); finalint indexCount = t.getIndexCount(); finalString[] attributes =newString[(attrsCount + indexCount)*2]; int i =0; // Store raw XML attributes. for(int j =0; j < attrsCount;++j){ attributes[i]= attrs.getAttributeName(j); attributes[i +1]= attrs.getAttributeValue(j); i +=2; } // Store resolved styleable attributes. finalResources res = t.getResources(); finalSparseArray<String> attributeMap = getAttributeMap(); for(int j =0; j < indexCount;++j){ finalint index = t.getIndex(j); if(!t.hasValueOrEmpty(index)){ // Value is undefined. Skip it. continue; } finalint resourceId = t.getResourceId(index,0); if(resourceId ==0){ // Value is not a reference. Skip it. continue; } String resourceName = attributeMap.get(resourceId); if(resourceName ==null){ try{ resourceName = res.getResourceName(resourceId); }catch(Resources.NotFoundException e){ resourceName ="0x"+Integer.toHexString(resourceId); } attributeMap.put(resourceId, resourceName); } attributes[i]= resourceName; attributes[i +1]= t.getString(index); i +=2; } // Trim to fit contents. finalString[] trimmed =newString[i]; System.arraycopy(attributes,0, trimmed,0, i); mAttributes = trimmed; } @Override publicString toString(){ StringBuilder out =newStringBuilder(256); out.append(getClass().getName()); out.append('{'); out.append(Integer.toHexString(System.identityHashCode(this))); out.append(' '); switch(mViewFlags&VISIBILITY_MASK){ case VISIBLE: out.append('V');break; case INVISIBLE: out.append('I');break; case GONE: out.append('G');break; default: out.append('.');break; } out.append((mViewFlags & FOCUSABLE)== FOCUSABLE ?'F':'.'); out.append((mViewFlags&ENABLED_MASK)== ENABLED ?'E':'.'); out.append((mViewFlags&DRAW_MASK)== WILL_NOT_DRAW ?'.':'D'); out.append((mViewFlags&SCROLLBARS_HORIZONTAL)!=0?'H':'.'); out.append((mViewFlags&SCROLLBARS_VERTICAL)!=0?'V':'.'); out.append((mViewFlags&CLICKABLE)!=0?'C':'.'); out.append((mViewFlags&LONG_CLICKABLE)!=0?'L':'.'); out.append((mViewFlags&CONTEXT_CLICKABLE)!=0?'X':'.'); out.append(' '); out.append((mPrivateFlags&PFLAG_IS_ROOT_NAMESPACE)!=0?'R':'.'); out.append((mPrivateFlags&PFLAG_FOCUSED)!=0?'F':'.'); out.append((mPrivateFlags&PFLAG_SELECTED)!=0?'S':'.'); if((mPrivateFlags&PFLAG_PREPRESSED)!=0){ out.append('p'); }else{ out.append((mPrivateFlags&PFLAG_PRESSED)!=0?'P':'.'); } out.append((mPrivateFlags&PFLAG_HOVERED)!=0?'H':'.'); out.append((mPrivateFlags&PFLAG_ACTIVATED)!=0?'A':'.'); out.append((mPrivateFlags&PFLAG_INVALIDATED)!=0?'I':'.'); out.append((mPrivateFlags&PFLAG_DIRTY_MASK)!=0?'D':'.'); out.append(' '); out.append(mLeft); out.append(','); out.append(mTop); out.append('-'); out.append(mRight); out.append(','); out.append(mBottom); finalint id = getId(); if(id != NO_ID){ out.append(" #"); out.append(Integer.toHexString(id)); finalResources r = mResources; if(id >0&&Resources.resourceHasPackage(id)&& r !=null){ try{ String pkgname; switch(id&0xff000000){ case0x7f000000: pkgname="app"; break; case0x01000000: pkgname="android"; break; default: pkgname = r.getResourcePackageName(id); break; } String typename = r.getResourceTypeName(id); String entryname = r.getResourceEntryName(id); out.append(" "); out.append(pkgname); out.append(":"); out.append(typename); out.append("/"); out.append(entryname); }catch(Resources.NotFoundException e){ } } } if(mAutofillId !=null){ out.append(" aid="); out.append(mAutofillId); } out.append("}"); return out.toString(); } /** * <p> * Initializes the fading edges from a given set of styled attributes. This * method should be called by subclasses that need fading edges and when an * instance of these subclasses is created programmatically rather than * being inflated from XML. This method is automatically called when the XML * is inflated. * </p> * * @param a the styled attributes set to initialize the fading edges from * * @removed */ protectedvoid initializeFadingEdge(TypedArray a){ // This method probably shouldn't have been included in the SDK to begin with. // It relies on 'a' having been initialized using an attribute filter array that is // not publicly available to the SDK. The old method has been renamed // to initializeFadingEdgeInternal and hidden for framework use only; // this one initializes using defaults to make it safe to call for apps. TypedArray arr = mContext.obtainStyledAttributes(com.android.internal.R.styleable.View); initializeFadingEdgeInternal(arr); arr.recycle(); } /** * <p> * Initializes the fading edges from a given set of styled attributes. This * method should be called by subclasses that need fading edges and when an * instance of these subclasses is created programmatically rather than * being inflated from XML. This method is automatically called when the XML * is inflated. * </p> * * @param a the styled attributes set to initialize the fading edges from * @hide This is the real method; the public one is shimmed to be safe to call from apps. */ protectedvoid initializeFadingEdgeInternal(TypedArray a){ initScrollCache(); mScrollCache.fadingEdgeLength = a.getDimensionPixelSize( R.styleable.View_fadingEdgeLength, ViewConfiguration.get(mContext).getScaledFadingEdgeLength()); } /** * Returns the size of the vertical faded edges used to indicate that more * content in this view is visible. * * @return The size in pixels of the vertical faded edge or 0 if vertical * faded edges are not enabled for this view. * @attr ref android.R.styleable#View_fadingEdgeLength */ publicint getVerticalFadingEdgeLength(){ if(isVerticalFadingEdgeEnabled()){ ScrollabilityCache cache = mScrollCache; if(cache !=null){ return cache.fadingEdgeLength; } } return0; } /** * Set the size of the faded edge used to indicate that more content in this * view is available. Will not change whether the fading edge is enabled; use * {@link #setVerticalFadingEdgeEnabled(boolean)} or * {@link #setHorizontalFadingEdgeEnabled(boolean)} to enable the fading edge * for the vertical or horizontal fading edges. * * @param length The size in pixels of the faded edge used to indicate that more * content in this view is visible. */ publicvoid setFadingEdgeLength(int length){ initScrollCache(); mScrollCache.fadingEdgeLength = length; } /** * Returns the size of the horizontal faded edges used to indicate that more * content in this view is visible. * * @return The size in pixels of the horizontal faded edge or 0 if horizontal * faded edges are not enabled for this view. * @attr ref android.R.styleable#View_fadingEdgeLength */ publicint getHorizontalFadingEdgeLength(){ if(isHorizontalFadingEdgeEnabled()){ ScrollabilityCache cache = mScrollCache; if(cache !=null){ return cache.fadingEdgeLength; } } return0; } /** * Returns the width of the vertical scrollbar. * * @return The width in pixels of the vertical scrollbar or 0 if there * is no vertical scrollbar. */ publicint getVerticalScrollbarWidth(){ ScrollabilityCache cache = mScrollCache; if(cache !=null){ ScrollBarDrawable scrollBar = cache.scrollBar; if(scrollBar !=null){ int size = scrollBar.getSize(true); if(size <=0){ size = cache.scrollBarSize; } return size; } return0; } return0; } /** * Returns the height of the horizontal scrollbar. * * @return The height in pixels of the horizontal scrollbar or 0 if * there is no horizontal scrollbar. */ protectedint getHorizontalScrollbarHeight(){ ScrollabilityCache cache = mScrollCache; if(cache !=null){ ScrollBarDrawable scrollBar = cache.scrollBar; if(scrollBar !=null){ int size = scrollBar.getSize(false); if(size <=0){ size = cache.scrollBarSize; } return size; } return0; } return0; } /** * <p> * Initializes the scrollbars from a given set of styled attributes. This * method should be called by subclasses that need scrollbars and when an * instance of these subclasses is created programmatically rather than * being inflated from XML. This method is automatically called when the XML * is inflated. * </p> * * @param a the styled attributes set to initialize the scrollbars from * * @removed */ protectedvoid initializeScrollbars(TypedArray a){ // It's not safe to use this method from apps. The parameter 'a' must have been obtained // using the View filter array which is not available to the SDK. As such, internal // framework usage now uses initializeScrollbarsInternal and we grab a default // TypedArray with the right filter instead here. TypedArray arr = mContext.obtainStyledAttributes(com.android.internal.R.styleable.View); initializeScrollbarsInternal(arr); // We ignored the method parameter. Recycle the one we actually did use. arr.recycle(); } privatevoid initializeScrollBarDrawable(){ initScrollCache(); if(mScrollCache.scrollBar ==null){ mScrollCache.scrollBar =newScrollBarDrawable(); mScrollCache.scrollBar.setState(getDrawableState()); mScrollCache.scrollBar.setCallback(this); } } /** * <p> * Initializes the scrollbars from a given set of styled attributes. This * method should be called by subclasses that need scrollbars and when an * instance of these subclasses is created programmatically rather than * being inflated from XML. This method is automatically called when the XML * is inflated. * </p> * * @param a the styled attributes set to initialize the scrollbars from * @hide */ @UnsupportedAppUsage protectedvoid initializeScrollbarsInternal(TypedArray a){ initScrollCache(); finalScrollabilityCache scrollabilityCache = mScrollCache; if(scrollabilityCache.scrollBar ==null){ scrollabilityCache.scrollBar =newScrollBarDrawable(); scrollabilityCache.scrollBar.setState(getDrawableState()); scrollabilityCache.scrollBar.setCallback(this); } finalboolean fadeScrollbars = a.getBoolean(R.styleable.View_fadeScrollbars,true); if(!fadeScrollbars){ scrollabilityCache.state =ScrollabilityCache.ON; } scrollabilityCache.fadeScrollBars = fadeScrollbars; scrollabilityCache.scrollBarFadeDuration = a.getInt( R.styleable.View_scrollbarFadeDuration,ViewConfiguration .getScrollBarFadeDuration()); scrollabilityCache.scrollBarDefaultDelayBeforeFade = a.getInt( R.styleable.View_scrollbarDefaultDelayBeforeFade, ViewConfiguration.getScrollDefaultDelay()); scrollabilityCache.scrollBarSize = a.getDimensionPixelSize( com.android.internal.R.styleable.View_scrollbarSize, ViewConfiguration.get(mContext).getScaledScrollBarSize()); Drawable track = a.getDrawable(R.styleable.View_scrollbarTrackHorizontal); scrollabilityCache.scrollBar.setHorizontalTrackDrawable(track); Drawable thumb = a.getDrawable(R.styleable.View_scrollbarThumbHorizontal); if(thumb !=null){ scrollabilityCache.scrollBar.setHorizontalThumbDrawable(thumb); } boolean alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawHorizontalTrack, false); if(alwaysDraw){ scrollabilityCache.scrollBar.setAlwaysDrawHorizontalTrack(true); } track = a.getDrawable(R.styleable.View_scrollbarTrackVertical); scrollabilityCache.scrollBar.setVerticalTrackDrawable(track); thumb = a.getDrawable(R.styleable.View_scrollbarThumbVertical); if(thumb !=null){ scrollabilityCache.scrollBar.setVerticalThumbDrawable(thumb); } alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawVerticalTrack, false); if(alwaysDraw){ scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true); } // Apply layout direction to the new Drawables if needed finalint layoutDirection = getLayoutDirection(); if(track !=null){ track.setLayoutDirection(layoutDirection); } if(thumb !=null){ thumb.setLayoutDirection(layoutDirection); } // Re-apply user/background padding so that scrollbar(s) get added resolvePadding(); } /** * Defines the vertical scrollbar thumb drawable * @attr ref android.R.styleable#View_scrollbarThumbVertical * * @see #awakenScrollBars(int) * @see #isVerticalScrollBarEnabled() * @see #setVerticalScrollBarEnabled(boolean) */ publicvoid setVerticalScrollbarThumbDrawable(@NullableDrawable drawable){ initializeScrollBarDrawable(); mScrollCache.scrollBar.setVerticalThumbDrawable(drawable); } /** * Defines the vertical scrollbar track drawable * @attr ref android.R.styleable#View_scrollbarTrackVertical * * @see #awakenScrollBars(int) * @see #isVerticalScrollBarEnabled() * @see #setVerticalScrollBarEnabled(boolean) */ publicvoid setVerticalScrollbarTrackDrawable(@NullableDrawable drawable){ initializeScrollBarDrawable(); mScrollCache.scrollBar.setVerticalTrackDrawable(drawable); } /** * Defines the horizontal thumb drawable * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * * @see #awakenScrollBars(int) * @see #isHorizontalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) */ publicvoid setHorizontalScrollbarThumbDrawable(@NullableDrawable drawable){ initializeScrollBarDrawable(); mScrollCache.scrollBar.setHorizontalThumbDrawable(drawable); } /** * Defines the horizontal track drawable * @attr ref android.R.styleable#View_scrollbarTrackHorizontal * * @see #awakenScrollBars(int) * @see #isHorizontalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) */ publicvoid setHorizontalScrollbarTrackDrawable(@NullableDrawable drawable){ initializeScrollBarDrawable(); mScrollCache.scrollBar.setHorizontalTrackDrawable(drawable); } /** * Returns the currently configured Drawable for the thumb of the vertical scroll bar if it * exists, null otherwise. * * @see #awakenScrollBars(int) * @see #isVerticalScrollBarEnabled() * @see #setVerticalScrollBarEnabled(boolean) */ public@NullableDrawable getVerticalScrollbarThumbDrawable(){ return mScrollCache !=null? mScrollCache.scrollBar.getVerticalThumbDrawable():null; } /** * Returns the currently configured Drawable for the track of the vertical scroll bar if it * exists, null otherwise. * * @see #awakenScrollBars(int) * @see #isVerticalScrollBarEnabled() * @see #setVerticalScrollBarEnabled(boolean) */ public@NullableDrawable getVerticalScrollbarTrackDrawable(){ return mScrollCache !=null? mScrollCache.scrollBar.getVerticalTrackDrawable():null; } /** * Returns the currently configured Drawable for the thumb of the horizontal scroll bar if it * exists, null otherwise. * * @see #awakenScrollBars(int) * @see #isHorizontalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) */ public@NullableDrawable getHorizontalScrollbarThumbDrawable(){ return mScrollCache !=null? mScrollCache.scrollBar.getHorizontalThumbDrawable():null; } /** * Returns the currently configured Drawable for the track of the horizontal scroll bar if it * exists, null otherwise. * * @see #awakenScrollBars(int) * @see #isHorizontalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) */ public@NullableDrawable getHorizontalScrollbarTrackDrawable(){ return mScrollCache !=null? mScrollCache.scrollBar.getHorizontalTrackDrawable():null; } privatevoid initializeScrollIndicatorsInternal(){ // Some day maybe we'll break this into top/left/start/etc. and let the // client control it. Until then, you can have any scroll indicator you // want as long as it's a 1dp foreground-colored rectangle. if(mScrollIndicatorDrawable ==null){ mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material); } } /** * <p> * Initalizes the scrollability cache if necessary. * </p> */ privatevoid initScrollCache(){ if(mScrollCache ==null){ mScrollCache =newScrollabilityCache(ViewConfiguration.get(mContext),this); } } @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateScrollabilityCache getScrollCache(){ initScrollCache(); return mScrollCache; } /** * Set the position of the vertical scroll bar. Should be one of * {@link #SCROLLBAR_POSITION_DEFAULT}, {@link #SCROLLBAR_POSITION_LEFT} or * {@link #SCROLLBAR_POSITION_RIGHT}. * * @param position Where the vertical scroll bar should be positioned. */ publicvoid setVerticalScrollbarPosition(int position){ if(mVerticalScrollbarPosition != position){ mVerticalScrollbarPosition = position; computeOpaqueFlags(); resolvePadding(); } } /** * @return The position where the vertical scroll bar will show, if applicable. * @see #setVerticalScrollbarPosition(int) */ publicint getVerticalScrollbarPosition(){ return mVerticalScrollbarPosition; } boolean isOnScrollbar(float x,float y){ if(mScrollCache ==null){ returnfalse; } x += getScrollX(); y += getScrollY(); finalboolean canScrollVertically = computeVerticalScrollRange()> computeVerticalScrollExtent(); if(isVerticalScrollBarEnabled()&&!isVerticalScrollBarHidden()&& canScrollVertically){ finalRect touchBounds = mScrollCache.mScrollBarTouchBounds; getVerticalScrollBarBounds(null, touchBounds); if(touchBounds.contains((int) x,(int) y)){ returntrue; } } finalboolean canScrollHorizontally = computeHorizontalScrollRange()> computeHorizontalScrollExtent(); if(isHorizontalScrollBarEnabled()&& canScrollHorizontally){ finalRect touchBounds = mScrollCache.mScrollBarTouchBounds; getHorizontalScrollBarBounds(null, touchBounds); if(touchBounds.contains((int) x,(int) y)){ returntrue; } } returnfalse; } @UnsupportedAppUsage boolean isOnScrollbarThumb(float x,float y){ return isOnVerticalScrollbarThumb(x, y)|| isOnHorizontalScrollbarThumb(x, y); } privateboolean isOnVerticalScrollbarThumb(float x,float y){ if(mScrollCache ==null||!isVerticalScrollBarEnabled()|| isVerticalScrollBarHidden()){ returnfalse; } finalint range = computeVerticalScrollRange(); finalint extent = computeVerticalScrollExtent(); if(range > extent){ x += getScrollX(); y += getScrollY(); finalRect bounds = mScrollCache.mScrollBarBounds; finalRect touchBounds = mScrollCache.mScrollBarTouchBounds; getVerticalScrollBarBounds(bounds, touchBounds); finalint offset = computeVerticalScrollOffset(); finalint thumbLength =ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(), extent, range); finalint thumbOffset =ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength, extent, range, offset); finalint thumbTop = bounds.top + thumbOffset; finalint adjust =Math.max(mScrollCache.scrollBarMinTouchTarget - thumbLength,0)/2; if(x >= touchBounds.left && x <= touchBounds.right && y >= thumbTop - adjust && y <= thumbTop + thumbLength + adjust){ returntrue; } } returnfalse; } privateboolean isOnHorizontalScrollbarThumb(float x,float y){ if(mScrollCache ==null||!isHorizontalScrollBarEnabled()){ returnfalse; } finalint range = computeHorizontalScrollRange(); finalint extent = computeHorizontalScrollExtent(); if(range > extent){ x += getScrollX(); y += getScrollY(); finalRect bounds = mScrollCache.mScrollBarBounds; finalRect touchBounds = mScrollCache.mScrollBarTouchBounds; getHorizontalScrollBarBounds(bounds, touchBounds); finalint offset = computeHorizontalScrollOffset(); finalint thumbLength =ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(), extent, range); finalint thumbOffset =ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength, extent, range, offset); finalint thumbLeft = bounds.left + thumbOffset; finalint adjust =Math.max(mScrollCache.scrollBarMinTouchTarget - thumbLength,0)/2; if(x >= thumbLeft - adjust && x <= thumbLeft + thumbLength + adjust && y >= touchBounds.top && y <= touchBounds.bottom){ returntrue; } } returnfalse; } @UnsupportedAppUsage boolean isDraggingScrollBar(){ return mScrollCache !=null && mScrollCache.mScrollBarDraggingState !=ScrollabilityCache.NOT_DRAGGING; } /** * Sets the state of all scroll indicators. * <p> * See {@link #setScrollIndicators(int, int)} for usage information. * * @param indicators a bitmask of indicators that should be enabled, or * {@code 0} to disable all indicators * @see #setScrollIndicators(int, int) * @see #getScrollIndicators() * @attr ref android.R.styleable#View_scrollIndicators */ @RemotableViewMethod publicvoid setScrollIndicators(@ScrollIndicatorsint indicators){ setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT); } /** * Sets the state of the scroll indicators specified by the mask. To change * all scroll indicators at once, see {@link #setScrollIndicators(int)}. * <p> * When a scroll indicator is enabled, it will be displayed if the view * can scroll in the direction of the indicator. * <p> * 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. * <p> * 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: * <ul> * <li>{@link #SCROLL_INDICATOR_TOP}</li> * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li> * <li>{@link #SCROLL_INDICATOR_LEFT}</li> * <li>{@link #SCROLL_INDICATOR_RIGHT}</li> * <li>{@link #SCROLL_INDICATOR_START}</li> * <li>{@link #SCROLL_INDICATOR_END}</li> * </ul> * @see #setScrollIndicators(int) * @see #getScrollIndicators() * @attr ref android.R.styleable#View_scrollIndicators */ publicvoid setScrollIndicators(@ScrollIndicatorsint indicators,@ScrollIndicatorsint mask){ // Shift and sanitize mask. mask <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; mask &= SCROLL_INDICATORS_PFLAG3_MASK; // Shift and mask indicators. indicators <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; indicators &= mask; // Merge with non-masked flags. finalint updatedFlags = indicators |(mPrivateFlags3 &~mask); if(mPrivateFlags3 != updatedFlags){ mPrivateFlags3 = updatedFlags; if(indicators !=0){ initializeScrollIndicatorsInternal(); } invalidate(); } } /** * Returns a bitmask representing the enabled scroll indicators. * <p> * For example, if the top and left scroll indicators are enabled and all * other indicators are disabled, the return value will be * {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}. * <p> * To check whether the bottom scroll indicator is enabled, use the value * of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}. * * @return a bitmask representing the enabled scroll indicators */ @InspectableProperty(flagMapping ={ @FlagEntry(target = SCROLL_INDICATORS_NONE, mask =0xffff_ffff, name ="none"), @FlagEntry(target = SCROLL_INDICATOR_TOP, name ="top"), @FlagEntry(target = SCROLL_INDICATOR_BOTTOM, name ="bottom"), @FlagEntry(target = SCROLL_INDICATOR_LEFT, name ="left"), @FlagEntry(target = SCROLL_INDICATOR_RIGHT, name ="right"), @FlagEntry(target = SCROLL_INDICATOR_START, name ="start"), @FlagEntry(target = SCROLL_INDICATOR_END, name ="end") }) @ScrollIndicators publicint getScrollIndicators(){ return(mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK) >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; } @UnsupportedAppUsage ListenerInfo getListenerInfo(){ if(mListenerInfo !=null){ return mListenerInfo; } mListenerInfo =newListenerInfo(); return mListenerInfo; } /** * Register a callback to be invoked when the scroll X or Y positions of * this view change. * <p> * <b>Note:</b> Some views handle scrolling independently from View and may * have their own separate listeners for scroll-type events. For example, * {@link android.widget.ListView ListView} allows clients to register an * {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener} * to listen for changes in list scroll position. * * @param l The listener to notify when the scroll X or Y position changes. * @see android.view.View#getScrollX() * @see android.view.View#getScrollY() */ publicvoid setOnScrollChangeListener(OnScrollChangeListener l){ getListenerInfo().mOnScrollChangeListener = l; } /** * Register a callback to be invoked when focus of this view changed. * * @param l The callback that will run. */ publicvoid setOnFocusChangeListener(OnFocusChangeListener l){ getListenerInfo().mOnFocusChangeListener = l; } /** * Add a listener that will be called when the bounds of the view change due to * layout processing. * * @param listener The listener that will be called when layout bounds change. */ publicvoid addOnLayoutChangeListener(OnLayoutChangeListener listener){ ListenerInfo li = getListenerInfo(); if(li.mOnLayoutChangeListeners ==null){ li.mOnLayoutChangeListeners =newArrayList<OnLayoutChangeListener>(); } if(!li.mOnLayoutChangeListeners.contains(listener)){ li.mOnLayoutChangeListeners.add(listener); } } /** * Remove a listener for layout changes. * * @param listener The listener for layout bounds change. */ publicvoid removeOnLayoutChangeListener(OnLayoutChangeListener listener){ ListenerInfo li = mListenerInfo; if(li ==null|| li.mOnLayoutChangeListeners ==null){ return; } li.mOnLayoutChangeListeners.remove(listener); } /** * Add a listener for attach state changes. * * This listener will be called whenever this view is attached or detached * from a window. Remove the listener using * {@link #removeOnAttachStateChangeListener(OnAttachStateChangeListener)}. * * @param listener Listener to attach * @see #removeOnAttachStateChangeListener(OnAttachStateChangeListener) */ publicvoid addOnAttachStateChangeListener(OnAttachStateChangeListener listener){ ListenerInfo li = getListenerInfo(); if(li.mOnAttachStateChangeListeners ==null){ li.mOnAttachStateChangeListeners =newCopyOnWriteArrayList<OnAttachStateChangeListener>(); } li.mOnAttachStateChangeListeners.add(listener); } /** * Remove a listener for attach state changes. The listener will receive no further * notification of window attach/detach events. * * @param listener Listener to remove * @see #addOnAttachStateChangeListener(OnAttachStateChangeListener) */ publicvoid removeOnAttachStateChangeListener(OnAttachStateChangeListener listener){ ListenerInfo li = mListenerInfo; if(li ==null|| li.mOnAttachStateChangeListeners ==null){ return; } li.mOnAttachStateChangeListeners.remove(listener); } /** * Returns the focus-change callback registered for this view. * * @return The callback, or null if one is not registered. */ publicOnFocusChangeListener getOnFocusChangeListener(){ ListenerInfo li = mListenerInfo; return li !=null? li.mOnFocusChangeListener :null; } /** * Register a callback to be invoked when this view is clicked. If this view is not * clickable, it becomes clickable. * * @param l The callback that will run * * @see #setClickable(boolean) */ publicvoid setOnClickListener(@NullableOnClickListener l){ if(!isClickable()){ setClickable(true); } getListenerInfo().mOnClickListener = l; } /** * Return whether this view has an attached OnClickListener. Returns * true if there is a listener, false if there is none. */ publicboolean hasOnClickListeners(){ ListenerInfo li = mListenerInfo; return(li !=null&& li.mOnClickListener !=null); } /** * Register a callback to be invoked when this view is clicked and held. If this view is not * long clickable, it becomes long clickable. * * @param l The callback that will run * * @see #setLongClickable(boolean) */ publicvoid setOnLongClickListener(@NullableOnLongClickListener l){ if(!isLongClickable()){ setLongClickable(true); } getListenerInfo().mOnLongClickListener = l; } /** * Return whether this view has an attached OnLongClickListener. Returns * true if there is a listener, false if there is none. */ publicboolean hasOnLongClickListeners(){ ListenerInfo li = mListenerInfo; return(li !=null&& li.mOnLongClickListener !=null); } /** * @return the registered {@link OnLongClickListener} if there is one, {@code null} otherwise. * @hide */ @Nullable publicOnLongClickListener getOnLongClickListener(){ ListenerInfo li = mListenerInfo; return(li !=null)? li.mOnLongClickListener :null; } /** * Register a callback to be invoked when this view is context clicked. If the view is not * context clickable, it becomes context clickable. * * @param l The callback that will run * @see #setContextClickable(boolean) */ publicvoid setOnContextClickListener(@NullableOnContextClickListener l){ if(!isContextClickable()){ setContextClickable(true); } getListenerInfo().mOnContextClickListener = l; } /** * Register a callback to be invoked when the context menu for this view is * being built. If this view is not long clickable, it becomes long clickable. * * @param l The callback that will run * */ publicvoid setOnCreateContextMenuListener(OnCreateContextMenuListener l){ if(!isLongClickable()){ setLongClickable(true); } getListenerInfo().mOnCreateContextMenuListener = l; } /** * Set an observer to collect stats for each frame rendered for this view. * * @hide */ publicvoid addFrameMetricsListener(Window window, Window.OnFrameMetricsAvailableListener listener, Handler handler){ if(mAttachInfo !=null){ if(mAttachInfo.mThreadedRenderer !=null){ if(mFrameMetricsObservers ==null){ mFrameMetricsObservers =newArrayList<>(); } FrameMetricsObserver fmo =newFrameMetricsObserver(window, handler, listener); mFrameMetricsObservers.add(fmo); mAttachInfo.mThreadedRenderer.addObserver(fmo.getRendererObserver()); }else{ Log.w(VIEW_LOG_TAG,"View not hardware-accelerated. Unable to observe frame stats"); } }else{ if(mFrameMetricsObservers ==null){ mFrameMetricsObservers =newArrayList<>(); } FrameMetricsObserver fmo =newFrameMetricsObserver(window, handler, listener); mFrameMetricsObservers.add(fmo); } } /** * Remove observer configured to collect frame stats for this view. * * @hide */ publicvoid removeFrameMetricsListener( Window.OnFrameMetricsAvailableListener listener){ ThreadedRenderer renderer = getThreadedRenderer(); FrameMetricsObserver fmo = findFrameMetricsObserver(listener); if(fmo ==null){ thrownewIllegalArgumentException( "attempt to remove OnFrameMetricsAvailableListener that was never added"); } if(mFrameMetricsObservers !=null){ mFrameMetricsObservers.remove(fmo); if(renderer !=null){ renderer.removeObserver(fmo.getRendererObserver()); } } } privatevoid registerPendingFrameMetricsObservers(){ if(mFrameMetricsObservers !=null){ ThreadedRenderer renderer = getThreadedRenderer(); if(renderer !=null){ for(FrameMetricsObserver fmo : mFrameMetricsObservers){ renderer.addObserver(fmo.getRendererObserver()); } }else{ Log.w(VIEW_LOG_TAG,"View not hardware-accelerated. Unable to observe frame stats"); } } } privateFrameMetricsObserver findFrameMetricsObserver( Window.OnFrameMetricsAvailableListener listener){ if(mFrameMetricsObservers !=null){ for(int i =0; i < mFrameMetricsObservers.size(); i++){ FrameMetricsObserver observer = mFrameMetricsObservers.get(i); if(observer.mListener == listener){ return observer; } } } returnnull; } /** @hide */ publicvoid setNotifyAutofillManagerOnClick(boolean notify){ if(notify){ mPrivateFlags |= PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK; }else{ mPrivateFlags &=~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK; } } privatevoid notifyAutofillManagerOnClick(){ if((mPrivateFlags & PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK)!=0){ try{ getAutofillManager().notifyViewClicked(this); }finally{ // Set it to already called so it's not called twice when called by // performClickInternal() mPrivateFlags &=~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK; } } } /** * Entry point for {@link #performClick()} - other methods on View should call it instead of * {@code performClick()} directly to make sure the autofill manager is notified when * necessary (as subclasses could extend {@code performClick()} without calling the parent's * method). */ privateboolean performClickInternal(){ // Must notify autofill manager before performing the click actions to avoid scenarios where // the app has a click listener that changes the state of views the autofill service might // be interested on. notifyAutofillManagerOnClick(); return performClick(); } /** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing * a sound, etc. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ // NOTE: other methods on View should not call this method directly, but performClickInternal() // instead, to guarantee that the autofill manager is notified when necessary (as subclasses // could extend this method without calling super.performClick()). publicboolean performClick(){ // We still need to call this method to handle the cases where performClick() was called // externally, instead of through performClickInternal() notifyAutofillManagerOnClick(); finalboolean result; finalListenerInfo li = mListenerInfo; if(li !=null&& li.mOnClickListener !=null){ playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result =true; }else{ result =false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); notifyEnterOrExitForAutoFillIfNeeded(true); return result; } /** * Directly call any attached OnClickListener. Unlike {@link #performClick()}, * this only calls the listener, and does not do any associated clicking * actions like reporting an accessibility event. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ publicboolean callOnClick(){ ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnClickListener !=null){ li.mOnClickListener.onClick(this); returntrue; } returnfalse; } /** * Calls this view's OnLongClickListener, if it is defined. Invokes the * context menu if the OnLongClickListener did not consume the event. * * @return {@code true} if one of the above receivers consumed the event, * {@code false} otherwise */ publicboolean performLongClick(){ return performLongClickInternal(mLongClickX, mLongClickY); } /** * Calls this view's OnLongClickListener, if it is defined. Invokes the * context menu if the OnLongClickListener did not consume the event, * anchoring it to an (x,y) coordinate. * * @param x x coordinate of the anchoring touch event, or {@link Float#NaN} * to disable anchoring * @param y y coordinate of the anchoring touch event, or {@link Float#NaN} * to disable anchoring * @return {@code true} if one of the above receivers consumed the event, * {@code false} otherwise */ publicboolean performLongClick(float x,float y){ mLongClickX = x; mLongClickY = y; finalboolean handled = performLongClick(); mLongClickX =Float.NaN; mLongClickY =Float.NaN; return handled; } /** * Calls this view's OnLongClickListener, if it is defined. Invokes the * context menu if the OnLongClickListener did not consume the event, * optionally anchoring it to an (x,y) coordinate. * * @param x x coordinate of the anchoring touch event, or {@link Float#NaN} * to disable anchoring * @param y y coordinate of the anchoring touch event, or {@link Float#NaN} * to disable anchoring * @return {@code true} if one of the above receivers consumed the event, * {@code false} otherwise */ privateboolean performLongClickInternal(float x,float y){ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); boolean handled =false; finalListenerInfo li = mListenerInfo; if(li !=null&& li.mOnLongClickListener !=null){ handled = li.mOnLongClickListener.onLongClick(View.this); } if(!handled){ finalboolean isAnchored =!Float.isNaN(x)&&!Float.isNaN(y); handled = isAnchored ? showContextMenu(x, y): showContextMenu(); } if((mViewFlags & TOOLTIP)== TOOLTIP){ if(!handled){ handled = showLongClickTooltip((int) x,(int) y); } } if(handled){ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } return handled; } /** * Call this view's OnContextClickListener, if it is defined. * * @param x the x coordinate of the context click * @param y the y coordinate of the context click * @return True if there was an assigned OnContextClickListener that consumed the event, false * otherwise. */ publicboolean performContextClick(float x,float y){ return performContextClick(); } /** * Call this view's OnContextClickListener, if it is defined. * * @return True if there was an assigned OnContextClickListener that consumed the event, false * otherwise. */ publicboolean performContextClick(){ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED); boolean handled =false; ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnContextClickListener !=null){ handled = li.mOnContextClickListener.onContextClick(View.this); } if(handled){ performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); } return handled; } /** * Performs button-related actions during a touch down event. * * @param event The event. * @return True if the down was consumed. * * @hide */ protectedboolean performButtonActionOnTouchDown(MotionEvent event){ if(event.isFromSource(InputDevice.SOURCE_MOUSE)&& (event.getButtonState()&MotionEvent.BUTTON_SECONDARY)!=0){ showContextMenu(event.getX(), event.getY()); mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT; returntrue; } returnfalse; } /** * Shows the context menu for this view. * * @return {@code true} if the context menu was shown, {@code false} * otherwise * @see #showContextMenu(float, float) */ publicboolean showContextMenu(){ return getParent().showContextMenuForChild(this); } /** * Shows the context menu for this view anchored to the specified * view-relative coordinate. * * @param x the X coordinate in pixels relative to the view to which the * menu should be anchored, or {@link Float#NaN} to disable anchoring * @param y the Y coordinate in pixels relative to the view to which the * menu should be anchored, or {@link Float#NaN} to disable anchoring * @return {@code true} if the context menu was shown, {@code false} * otherwise */ publicboolean showContextMenu(float x,float y){ return getParent().showContextMenuForChild(this, x, y); } /** * Start an action mode with the default type {@link ActionMode#TYPE_PRIMARY}. * * @param callback Callback that will control the lifecycle of the action mode * @return The new action mode if it is started, null otherwise * * @see ActionMode * @see #startActionMode(android.view.ActionMode.Callback, int) */ publicActionMode startActionMode(ActionMode.Callback callback){ return startActionMode(callback,ActionMode.TYPE_PRIMARY); } /** * Start an action mode with the given type. * * @param callback Callback that will control the lifecycle of the action mode * @param type One of {@link ActionMode#TYPE_PRIMARY} or {@link ActionMode#TYPE_FLOATING}. * @return The new action mode if it is started, null otherwise * * @see ActionMode */ publicActionMode startActionMode(ActionMode.Callback callback,int type){ ViewParent parent = getParent(); if(parent ==null)returnnull; try{ return parent.startActionModeForChild(this, callback, type); }catch(AbstractMethodError ame){ // Older implementations of custom views might not implement this. return parent.startActionModeForChild(this, callback); } } /** * Call {@link Context#startActivityForResult(String, Intent, int, Bundle)} for the View's * Context, creating a unique View identifier to retrieve the result. * * @param intent The Intent to be started. * @param requestCode The request code to use. * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =115609023) publicvoid startActivityForResult(Intent intent,int requestCode){ mStartActivityRequestWho ="@android:view:"+System.identityHashCode(this); getContext().startActivityForResult(mStartActivityRequestWho, intent, requestCode,null); } /** * If this View corresponds to the calling who, dispatches the activity result. * @param who The identifier for the targeted View to receive the result. * @param requestCode The integer request code originally supplied to * startActivityForResult(), allowing you to identify who this * result came from. * @param resultCode The integer result code returned by the child activity * through its setResult(). * @param data An Intent, which can return result data to the caller * (various data can be attached to Intent "extras"). * @return {@code true} if the activity result was dispatched. * @hide */ publicboolean dispatchActivityResult( String who,int requestCode,int resultCode,Intent data){ if(mStartActivityRequestWho !=null&& mStartActivityRequestWho.equals(who)){ onActivityResult(requestCode, resultCode, data); mStartActivityRequestWho =null; returntrue; } returnfalse; } /** * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}. * * @param requestCode The integer request code originally supplied to * startActivityForResult(), allowing you to identify who this * result came from. * @param resultCode The integer result code returned by the child activity * through its setResult(). * @param data An Intent, which can return result data to the caller * (various data can be attached to Intent "extras"). * @hide */ publicvoid onActivityResult(int requestCode,int resultCode,Intent data){ // Do nothing. } /** * Register a callback to be invoked when a hardware key is pressed in this view. * Key presses in software input methods will generally not trigger the methods of * this listener. * @param l the key listener to attach to this view */ publicvoid setOnKeyListener(OnKeyListener l){ getListenerInfo().mOnKeyListener = l; } /** * Register a callback to be invoked when a touch event is sent to this view. * @param l the touch listener to attach to this view */ publicvoid setOnTouchListener(OnTouchListener l){ getListenerInfo().mOnTouchListener = l; } /** * Register a callback to be invoked when a generic motion event is sent to this view. * @param l the generic motion listener to attach to this view */ publicvoid setOnGenericMotionListener(OnGenericMotionListener l){ getListenerInfo().mOnGenericMotionListener = l; } /** * Register a callback to be invoked when a hover event is sent to this view. * @param l the hover listener to attach to this view */ publicvoid setOnHoverListener(OnHoverListener l){ getListenerInfo().mOnHoverListener = l; } /** * Register a drag event listener callback object for this View. The parameter is * an implementation of {@link android.view.View.OnDragListener}. To send a drag event to a * View, the system calls the * {@link android.view.View.OnDragListener#onDrag(View,DragEvent)} method. * @param l An implementation of {@link android.view.View.OnDragListener}. */ publicvoid setOnDragListener(OnDragListener l){ getListenerInfo().mOnDragListener = l; } /** * Give this view focus. This will cause * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} to be called. * * Note: this does not check whether this {@link View} should get focus, it just * gives it focus no matter what. It should only be called internally by framework * code that knows what it is doing, namely {@link #requestFocus(int, Rect)}. * * @param direction values are {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT}. This is the direction which * focus moved when requestFocus() is called. It may not always * apply, in which case use the default View.FOCUS_DOWN. * @param previouslyFocusedRect The rectangle of the view that had focus * prior in this View's coordinate system. */ void handleFocusGainInternal(@FocusRealDirectionint direction,Rect previouslyFocusedRect){ if(DBG){ System.out.println(this+" requestFocus()"); } if((mPrivateFlags & PFLAG_FOCUSED)==0){ mPrivateFlags |= PFLAG_FOCUSED; View oldFocus =(mAttachInfo !=null)? getRootView().findFocus():null; if(mParent !=null){ mParent.requestChildFocus(this,this); updateFocusedInCluster(oldFocus, direction); } if(mAttachInfo !=null){ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus,this); } onFocusChanged(true, direction, previouslyFocusedRect); refreshDrawableState(); } } /** * Sets this view's preference for reveal behavior when it gains focus. * * <p>When set to true, this is a signal to ancestor views in the hierarchy that * this view would prefer to be brought fully into view when it gains focus. * For example, a text field that a user is meant to type into. Other views such * as scrolling containers may prefer to opt-out of this behavior.</p> * * <p>The default value for views is true, though subclasses may change this * based on their preferred behavior.</p> * * @param revealOnFocus true to request reveal on focus in ancestors, false otherwise * * @see #getRevealOnFocusHint() */ publicfinalvoid setRevealOnFocusHint(boolean revealOnFocus){ if(revealOnFocus){ mPrivateFlags3 &=~PFLAG3_NO_REVEAL_ON_FOCUS; }else{ mPrivateFlags3 |= PFLAG3_NO_REVEAL_ON_FOCUS; } } /** * Returns this view's preference for reveal behavior when it gains focus. * * <p>When this method returns true for a child view requesting focus, ancestor * views responding to a focus change in {@link ViewParent#requestChildFocus(View, View)} * should make a best effort to make the newly focused child fully visible to the user. * When it returns false, ancestor views should preferably not disrupt scroll positioning or * other properties affecting visibility to the user as part of the focus change.</p> * * @return true if this view would prefer to become fully visible when it gains focus, * false if it would prefer not to disrupt scroll positioning * * @see #setRevealOnFocusHint(boolean) */ publicfinalboolean getRevealOnFocusHint(){ return(mPrivateFlags3 & PFLAG3_NO_REVEAL_ON_FOCUS)==0; } /** * Populates <code>outRect</code> with the hotspot bounds. By default, * the hotspot bounds are identical to the screen bounds. * * @param outRect rect to populate with hotspot bounds * @hide Only for internal use by views and widgets. */ publicvoid getHotspotBounds(Rect outRect){ finalDrawable background = getBackground(); if(background !=null){ background.getHotspotBounds(outRect); }else{ getBoundsOnScreen(outRect); } } /** * Request that a rectangle of this view be visible on the screen, * scrolling if necessary just enough. * * <p>A View should call this if it maintains some notion of which part * of its content is interesting. For example, a text editing view * should call this when its cursor moves. * <p>The Rectangle passed into this method should be in the View's content coordinate space. * It should not be affected by which part of the View is currently visible or its scroll * position. * * @param rectangle The rectangle in the View's content coordinate space * @return Whether any parent scrolled. */ publicboolean requestRectangleOnScreen(Rect rectangle){ return requestRectangleOnScreen(rectangle,false); } /** * Request that a rectangle of this view be visible on the screen, * scrolling if necessary just enough. * * <p>A View should call this if it maintains some notion of which part * of its content is interesting. For example, a text editing view * should call this when its cursor moves. * <p>The Rectangle passed into this method should be in the View's content coordinate space. * It should not be affected by which part of the View is currently visible or its scroll * position. * <p>When <code>immediate</code> is set to true, scrolling will not be * animated. * * @param rectangle The rectangle in the View's content coordinate space * @param immediate True to forbid animated scrolling, false otherwise * @return Whether any parent scrolled. */ publicboolean requestRectangleOnScreen(Rect rectangle,boolean immediate){ if(mParent ==null){ returnfalse; } View child =this; RectF position =(mAttachInfo !=null)? mAttachInfo.mTmpTransformRect :newRectF(); position.set(rectangle); ViewParent parent = mParent; boolean scrolled =false; while(parent !=null){ rectangle.set((int) position.left,(int) position.top, (int) position.right,(int) position.bottom); scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate); if(!(parent instanceofView)){ break; } // move it from child's content coordinate space to parent's content coordinate space position.offset(child.mLeft - child.getScrollX(), child.mTop -child.getScrollY()); child =(View) parent; parent = child.getParent(); } return scrolled; } /** * Called when this view wants to give up focus. If focus is cleared * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called. * <p> * <strong>Note:</strong> When not in touch-mode, the framework will try to give focus * to the first focusable View from the top after focus is cleared. Hence, if this * View is the first from the top that can take focus, then all callbacks * related to clearing focus will be invoked after which the framework will * give focus to this view. * </p> */ publicvoid clearFocus(){ if(DBG){ System.out.println(this+" clearFocus()"); } finalboolean refocus = sAlwaysAssignFocus ||!isInTouchMode(); clearFocusInternal(null,true, refocus); } /** * Clears focus from the view, optionally propagating the change up through * the parent hierarchy and requesting that the root view place new focus. * * @param propagate whether to propagate the change up through the parent * hierarchy * @param refocus when propagate is true, specifies whether to request the * root view place new focus */ void clearFocusInternal(View focused,boolean propagate,boolean refocus){ if((mPrivateFlags & PFLAG_FOCUSED)!=0){ mPrivateFlags &=~PFLAG_FOCUSED; clearParentsWantFocus(); if(propagate && mParent !=null){ mParent.clearChildFocus(this); } onFocusChanged(false,0,null); refreshDrawableState(); if(propagate &&(!refocus ||!rootViewRequestFocus())){ notifyGlobalFocusCleared(this); } } } void notifyGlobalFocusCleared(View oldFocus){ if(oldFocus !=null&& mAttachInfo !=null){ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus,null); } } boolean rootViewRequestFocus(){ finalView root = getRootView(); return root !=null&& root.requestFocus(); } /** * Called internally by the view system when a new view is getting focus. * This is what clears the old focus. * <p> * <b>NOTE:</b> The parent view's focused child must be updated manually * after calling this method. Otherwise, the view hierarchy may be left in * an inconstent state. */ void unFocus(View focused){ if(DBG){ System.out.println(this+" unFocus()"); } clearFocusInternal(focused,false,false); } /** * Returns true if this view has focus itself, or is the ancestor of the * view that has focus. * * @return True if this view has or contains focus, false otherwise. */ @ViewDebug.ExportedProperty(category ="focus") publicboolean hasFocus(){ return(mPrivateFlags & PFLAG_FOCUSED)!=0; } /** * Returns true if this view is focusable or if it contains a reachable View * for which {@link #hasFocusable()} returns {@code true}. A "reachable hasFocusable()" * is a view whose parents do not block descendants focus. * Only {@link #VISIBLE} views are considered focusable. * * <p>As of {@link Build.VERSION_CODES#O} views that are determined to be focusable * through {@link #FOCUSABLE_AUTO} will also cause this method to return {@code true}. * Apps that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} of * earlier than {@link Build.VERSION_CODES#O} will continue to see this method return * {@code false} for views not explicitly marked as focusable. * Use {@link #hasExplicitFocusable()} if you require the pre-{@link Build.VERSION_CODES#O} * behavior.</p> * * @return {@code true} if the view is focusable or if the view contains a focusable * view, {@code false} otherwise * * @see ViewGroup#FOCUS_BLOCK_DESCENDANTS * @see ViewGroup#getTouchscreenBlocksFocus() * @see #hasExplicitFocusable() */ publicboolean hasFocusable(){ return hasFocusable(!sHasFocusableExcludeAutoFocusable,false); } /** * Returns true if this view is focusable or if it contains a reachable View * for which {@link #hasExplicitFocusable()} returns {@code true}. * A "reachable hasExplicitFocusable()" is a view whose parents do not block descendants focus. * Only {@link #VISIBLE} views for which {@link #getFocusable()} would return * {@link #FOCUSABLE} are considered focusable. * * <p>This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of * {@link #hasFocusable()} in that only views explicitly set focusable will cause * this method to return true. A view set to {@link #FOCUSABLE_AUTO} that resolves * to focusable will not.</p> * * @return {@code true} if the view is focusable or if the view contains a focusable * view, {@code false} otherwise * * @see #hasFocusable() */ publicboolean hasExplicitFocusable(){ return hasFocusable(false,true); } boolean hasFocusable(boolean allowAutoFocus,boolean dispatchExplicit){ if(!isFocusableInTouchMode()){ for(ViewParent p = mParent; p instanceofViewGroup; p = p.getParent()){ finalViewGroup g =(ViewGroup) p; if(g.shouldBlockFocusForTouchscreen()){ returnfalse; } } } // Invisible, gone, or disabled views are never focusable. if((mViewFlags & VISIBILITY_MASK)!= VISIBLE ||(mViewFlags & ENABLED_MASK)!= ENABLED){ returnfalse; } // Only use effective focusable value when allowed. if((allowAutoFocus || getFocusable()!= FOCUSABLE_AUTO)&& isFocusable()){ returntrue; } returnfalse; } /** * Called by the view system when the focus state of this view changes. * When the focus change event is caused by directional navigation, direction * and previouslyFocusedRect provide insight into where the focus is coming from. * When overriding, be sure to call up through to the super class so that * the standard focus handling will occur. * * @param gainFocus True if the View has focus; false otherwise. * @param direction The direction focus has moved when requestFocus() * is called to give this view focus. Values are * {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT}, * {@link #FOCUS_RIGHT}, {@link #FOCUS_FORWARD}, or {@link #FOCUS_BACKWARD}. * It may not always apply, in which case use the default. * @param previouslyFocusedRect The rectangle, in this view's coordinate * system, of the previously focused view. If applicable, this will be * passed in as finer grained information about where the focus is coming * from (in addition to direction). Will be <code>null</code> otherwise. */ @CallSuper protectedvoid onFocusChanged(boolean gainFocus,@FocusDirectionint direction, @NullableRect previouslyFocusedRect){ if(gainFocus){ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); }else{ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } // Here we check whether we still need the default focus highlight, and switch it on/off. switchDefaultFocusHighlight(); if(!gainFocus){ if(isPressed()){ setPressed(false); } if(hasWindowFocus()){ notifyFocusChangeToImeFocusController(false/* hasFocus */); } onFocusLost(); }elseif(hasWindowFocus()){ notifyFocusChangeToImeFocusController(true/* hasFocus */); } invalidate(true); ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnFocusChangeListener !=null){ li.mOnFocusChangeListener.onFocusChange(this, gainFocus); } if(mAttachInfo !=null){ mAttachInfo.mKeyDispatchState.reset(this); } if(mParent !=null){ mParent.onDescendantUnbufferedRequested(); } notifyEnterOrExitForAutoFillIfNeeded(gainFocus); updatePreferKeepClearForFocus(); } /** * Notify {@link ImeFocusController} about the focus change of the {@link View}. * * @param hasFocus {@code true} when the {@link View} is being focused. */ privatevoid notifyFocusChangeToImeFocusController(boolean hasFocus){ if(mAttachInfo ==null){ return; } mAttachInfo.mViewRootImpl.getImeFocusController().onViewFocusChanged(this, hasFocus); } /** @hide */ publicvoid notifyEnterOrExitForAutoFillIfNeeded(boolean enter){ if(canNotifyAutofillEnterExitEvent()){ AutofillManager afm = getAutofillManager(); if(afm !=null){ if(enter){ // We have not been laid out yet, hence cannot evaluate // whether this view is visible to the user, we will do // the evaluation once layout is complete. // Sometimes, views are already laid out, but it's still // not visible to the user, we also do the evaluation once // the view is visible. ex: There is a fade-in animation // for the activity, the view will be laid out when the // animation beginning. On the time, the view is not visible // to the user. And then as the animation progresses, the view // becomes visible to the user. if(!isLaidOut()||!isVisibleToUser()){ mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; }elseif(isVisibleToUser()){ if(isFocused()){ // TODO This is a potential problem that View gets focus before it's // visible to User. Ideally View should handle the event when // isVisibleToUser() becomes true where it should issue // notifyViewEntered(). afm.notifyViewEntered(this); }else{ afm.notifyViewEnteredForFillDialog(this); } } }elseif(!isFocused()){ afm.notifyViewExited(this); } } } } /** * Visually distinct portion of a window with window-like semantics are considered panes for * accessibility purposes. One example is the content view of a fragment that is replaced. * In order for accessibility services to understand a pane's window-like behavior, panes * should have descriptive titles. Views with pane titles produce {@link AccessibilityEvent}s * when they appear, disappear, or change title. * * @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this * View is not a pane. * * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)} * * @attr ref android.R.styleable#View_accessibilityPaneTitle */ publicvoid setAccessibilityPaneTitle(@NullableCharSequence accessibilityPaneTitle){ if(!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)){ boolean currentPaneTitleEmpty = mAccessibilityPaneTitle ==null; boolean newPaneTitleEmpty = accessibilityPaneTitle ==null; mAccessibilityPaneTitle = accessibilityPaneTitle; // Make explicitly important as nulled titles need to be important for DISAPPEARED // events. if(mAccessibilityPaneTitle !=null && getImportantForAccessibility()== IMPORTANT_FOR_ACCESSIBILITY_AUTO){ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } if(currentPaneTitleEmpty){ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED); }elseif(newPaneTitleEmpty){ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); }else{ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE); } } } /** * Get the title of the pane for purposes of accessibility. * * @return The current pane title. * * {@see #setAccessibilityPaneTitle}. * * @attr ref android.R.styleable#View_accessibilityPaneTitle */ @InspectableProperty @Nullable publicCharSequence getAccessibilityPaneTitle(){ return mAccessibilityPaneTitle; } privateboolean isAccessibilityPane(){ return mAccessibilityPaneTitle !=null; } /** * Sends an accessibility event of the given type. If accessibility is * not enabled this method has no effect. The default implementation calls * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)} first * to populate information about the event source (this View), then calls * {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} to * populate the text content of the event source including its descendants, * then for events type {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} * and {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} with * subtype {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_STATE_DESCRIPTION}, * throttle the events, and last calls * {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} * on its parent to request sending of the event to interested parties. * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#sendAccessibilityEvent(View, int)} is * responsible for handling this call. * </p> * <p> * If this view uses {@link AccessibilityNodeProvider} to provide virtual view hierarchy rooted * at this view, this method should not be called to send events from virtual children because * it will populate the events with wrong information and the events should be throttled per * child instead at the virtual root level. To send events from virtual children, call * {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} on the view's * parent to request sending of the event to interested parties. * </p> * * @param eventType The type of the event to send, as defined by several types from * {@link AccessibilityEvent}, such as * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} or * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}. * * @see #onInitializeAccessibilityEvent(AccessibilityEvent) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) * @see ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent) * @see AccessibilityDelegate */ publicvoid sendAccessibilityEvent(int eventType){ if(mAccessibilityDelegate !=null){ mAccessibilityDelegate.sendAccessibilityEvent(this, eventType); }else{ sendAccessibilityEventInternal(eventType); } } /** * Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT} * {@link AccessibilityEvent} to suggest that an accessibility service announce the * specified text to its users. * <p> * Note: The event generated with this API carries no semantic meaning, and is appropriate only * in exceptional situations. Apps can generally achieve correct behavior for accessibility by * accurately supplying the semantics of their UI. * They should not need to specify what exactly is announced to users. * * @param text The announcement text. */ publicvoid announceForAccessibility(CharSequence text){ if(AccessibilityManager.getInstance(mContext).isEnabled()&& mParent !=null){ AccessibilityEvent event =AccessibilityEvent.obtain( AccessibilityEvent.TYPE_ANNOUNCEMENT); onInitializeAccessibilityEvent(event); event.getText().add(text); event.setContentDescription(null); mParent.requestSendAccessibilityEvent(this, event); } } /** * @see #sendAccessibilityEvent(int) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ publicvoid sendAccessibilityEventInternal(int eventType){ if(AccessibilityManager.getInstance(mContext).isEnabled()){ sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); } } /** * This method behaves exactly as {@link #sendAccessibilityEvent(int)} but * takes as an argument an empty {@link AccessibilityEvent} and does not * perform a check whether accessibility is enabled. * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#sendAccessibilityEventUnchecked(View, AccessibilityEvent)} * is responsible for handling this call. * </p> * * @param event The event to send. * * @see #sendAccessibilityEvent(int) */ publicvoid sendAccessibilityEventUnchecked(AccessibilityEvent event){ if(mAccessibilityDelegate !=null){ mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event); }else{ sendAccessibilityEventUncheckedInternal(event); } } /** * @see #sendAccessibilityEventUnchecked(AccessibilityEvent) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ publicvoid sendAccessibilityEventUncheckedInternal(AccessibilityEvent event){ // Panes disappearing are relevant even if though the view is no longer visible. boolean isWindowStateChanged = (event.getEventType()==AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); boolean isWindowDisappearedEvent = isWindowStateChanged &&((event.getContentChangeTypes() &AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)!=0); boolean detached = detached(); if(!isShown()&&!isWindowDisappearedEvent &&!detached){ return; } onInitializeAccessibilityEvent(event); // Only a subset of accessibility events populates text content. if((event.getEventType()& POPULATING_ACCESSIBILITY_EVENT_TYPES)!=0){ dispatchPopulateAccessibilityEvent(event); } SendAccessibilityEventThrottle throttle = getThrottleForAccessibilityEvent(event); if(throttle !=null){ throttle.post(event); }elseif(!isWindowDisappearedEvent && detached){ // Views could be attached soon later. Accessibility events during this temporarily // detached period should be sent too. postDelayed(()->{ if(AccessibilityManager.getInstance(mContext).isEnabled()&& isShown()){ requestParentSendAccessibilityEvent(event); } },ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); }else{ requestParentSendAccessibilityEvent(event); } } privatevoid requestParentSendAccessibilityEvent(AccessibilityEvent event){ ViewParent parent = getParent(); if(parent !=null){ getParent().requestSendAccessibilityEvent(this, event); } } privateSendAccessibilityEventThrottle getThrottleForAccessibilityEvent( AccessibilityEvent event){ if(event.getEventType()==AccessibilityEvent.TYPE_VIEW_SCROLLED){ if(mSendViewScrolledAccessibilityEvent ==null){ mSendViewScrolledAccessibilityEvent =newSendViewScrolledAccessibilityEvent(); } return mSendViewScrolledAccessibilityEvent; } boolean isStateContentChanged =(event.getContentChangeTypes() &AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION)!=0; if(event.getEventType()==AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && isStateContentChanged){ if(mSendStateChangedAccessibilityEvent ==null){ mSendStateChangedAccessibilityEvent =newSendAccessibilityEventThrottle(); } return mSendStateChangedAccessibilityEvent; } returnnull; } privatevoid clearAccessibilityThrottles(){ cancel(mSendViewScrolledAccessibilityEvent); cancel(mSendStateChangedAccessibilityEvent); } /** * Dispatches an {@link AccessibilityEvent} to the {@link View} first and then * to its children for adding their text content to the event. Note that the * event text is populated in a separate dispatch path since we add to the * event not only the text of the source but also the text of all its descendants. * A typical implementation will call * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on the this view * and then call the {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} * on each child. Override this method if custom population of the event text * content is required. * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#dispatchPopulateAccessibilityEvent(View, AccessibilityEvent)} * is responsible for handling this call. * </p> * <p> * <em>Note:</em> Accessibility events of certain types are not dispatched for * populating the event text via this method. For details refer to {@link AccessibilityEvent}. * </p> * * @param event The event. * * @return True if the event population was completed. */ publicboolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event){ if(mAccessibilityDelegate !=null){ return mAccessibilityDelegate.dispatchPopulateAccessibilityEvent(this, event); }else{ return dispatchPopulateAccessibilityEventInternal(event); } } /** * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ publicboolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event){ onPopulateAccessibilityEvent(event); returnfalse; } /** * Called from {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} * giving a chance to this View to populate the accessibility event with its * text content. While this method is free to modify event * attributes other than text content, doing so should normally be performed in * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)}. * <p> * Example: Adding formatted date string to an accessibility event in addition * to the text added by the super implementation: * <pre> 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); * }</pre> * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#onPopulateAccessibilityEvent(View, AccessibilityEvent)} * is responsible for handling this call. * </p> * <p class="note"><strong>Note:</strong> Always call the super implementation before adding * information to the event, in case the default implementation has basic information to add. * </p> * * @param event The accessibility event which to populate. * * @see #sendAccessibilityEvent(int) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ @CallSuper publicvoid onPopulateAccessibilityEvent(AccessibilityEvent event){ if(mAccessibilityDelegate !=null){ mAccessibilityDelegate.onPopulateAccessibilityEvent(this, event); }else{ onPopulateAccessibilityEventInternal(event); } } /** * @see #onPopulateAccessibilityEvent(AccessibilityEvent) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ publicvoid onPopulateAccessibilityEventInternal(AccessibilityEvent event){ if((event.getEventType()==AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) && isAccessibilityPane()){ event.getText().add(getAccessibilityPaneTitle()); } } /** * 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. * <p> * Example: Setting the password property of an event in addition * to properties set by the super implementation: * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) { * super.onInitializeAccessibilityEvent(event); * event.setPassword(true); * }</pre> * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#onInitializeAccessibilityEvent(View, AccessibilityEvent)} * is responsible for handling this call. * </p> * <p class="note"><strong>Note:</strong> Always call the super implementation before adding * information to the event, in case the default implementation has basic information to add. * </p> * @param event The event to initialize. * * @see #sendAccessibilityEvent(int) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ @CallSuper publicvoid onInitializeAccessibilityEvent(AccessibilityEvent event){ if(mAccessibilityDelegate !=null){ mAccessibilityDelegate.onInitializeAccessibilityEvent(this, event); }else{ onInitializeAccessibilityEventInternal(event); } } /** * @see #onInitializeAccessibilityEvent(AccessibilityEvent) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ @UnsupportedAppUsage publicvoid onInitializeAccessibilityEventInternal(AccessibilityEvent event){ event.setSource(this); event.setClassName(getAccessibilityClassName()); event.setPackageName(getContext().getPackageName()); event.setEnabled(isEnabled()); event.setContentDescription(mContentDescription); event.setScrollX(getScrollX()); event.setScrollY(getScrollY()); switch(event.getEventType()){ caseAccessibilityEvent.TYPE_VIEW_FOCUSED:{ ArrayList<View> focusablesTempList =(mAttachInfo !=null) ? mAttachInfo.mTempArrayList :newArrayList<View>(); getRootView().addFocusables(focusablesTempList,View.FOCUS_FORWARD, FOCUSABLES_ALL); event.setItemCount(focusablesTempList.size()); event.setCurrentItemIndex(focusablesTempList.indexOf(this)); if(mAttachInfo !=null){ focusablesTempList.clear(); } }break; caseAccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:{ CharSequence text = getIterableTextForAccessibility(); if(text !=null&& text.length()>0){ event.setFromIndex(getAccessibilitySelectionStart()); event.setToIndex(getAccessibilitySelectionEnd()); event.setItemCount(text.length()); } }break; } } /** * Returns an {@link AccessibilityNodeInfo} representing this view from the * point of view of an {@link android.accessibilityservice.AccessibilityService}. * This method is responsible for obtaining an accessibility node info from a * pool of reusable instances and calling * {@link #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} on this view to * initialize the former. * <p> * Note: The client is responsible for recycling the obtained instance by calling * {@link AccessibilityNodeInfo#recycle()} to minimize object creation. * </p> * * @return A populated {@link AccessibilityNodeInfo}. * * @see AccessibilityNodeInfo */ publicAccessibilityNodeInfo createAccessibilityNodeInfo(){ if(mAccessibilityDelegate !=null){ return mAccessibilityDelegate.createAccessibilityNodeInfo(this); }else{ return createAccessibilityNodeInfoInternal(); } } /** * @see #createAccessibilityNodeInfo() * * @hide */ public@NullableAccessibilityNodeInfo createAccessibilityNodeInfoInternal(){ AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if(provider !=null){ return provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID); }else{ AccessibilityNodeInfo info =AccessibilityNodeInfo.obtain(this); onInitializeAccessibilityNodeInfo(info); return info; } } /** * Initializes an {@link AccessibilityNodeInfo} with information about this view. * The base implementation sets: * <ul> * <li>{@link AccessibilityNodeInfo#setParent(View)},</li> * <li>{@link AccessibilityNodeInfo#setBoundsInParent(Rect)},</li> * <li>{@link AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li> * <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li> * <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li> * <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li> * <li>{@link AccessibilityNodeInfo#setEnabled(boolean)},</li> * <li>{@link AccessibilityNodeInfo#setClickable(boolean)},</li> * <li>{@link AccessibilityNodeInfo#setFocusable(boolean)},</li> * <li>{@link AccessibilityNodeInfo#setFocused(boolean)},</li> * <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},</li> * <li>{@link AccessibilityNodeInfo#setSelected(boolean)},</li> * <li>{@link AccessibilityNodeInfo#setContextClickable(boolean)}</li> * </ul> * <p> * Subclasses should override this method, call the super implementation, * and set additional attributes. * </p> * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)} * is responsible for handling this call. * </p> * * @param info The instance to initialize. */ @CallSuper publicvoid onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info){ if(mAccessibilityDelegate !=null){ mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(this, info); }else{ onInitializeAccessibilityNodeInfoInternal(info); } } /** * Gets the location of this view in screen coordinates. * * @param outRect The output location * @hide */ @UnsupportedAppUsage publicvoid getBoundsOnScreen(Rect outRect){ getBoundsOnScreen(outRect,false); } /** * Gets the location of this view in screen coordinates. * * @param outRect The output location * @param clipToParent Whether to clip child bounds to the parent ones. * @hide */ @UnsupportedAppUsage publicvoid getBoundsOnScreen(Rect outRect,boolean clipToParent){ if(mAttachInfo ==null){ return; } RectF position = mAttachInfo.mTmpTransformRect; getBoundsToScreenInternal(position, clipToParent); outRect.set(Math.round(position.left),Math.round(position.top), Math.round(position.right),Math.round(position.bottom)); } /** * Gets the location of this view in screen coordinates. * * @param outRect The output location * @param clipToParent Whether to clip child bounds to the parent ones. * @hide */ publicvoid getBoundsOnScreen(RectF outRect,boolean clipToParent){ if(mAttachInfo ==null){ return; } RectF position = mAttachInfo.mTmpTransformRect; getBoundsToScreenInternal(position, clipToParent); outRect.set(position.left, position.top, position.right, position.bottom); } privatevoid getBoundsToScreenInternal(RectF position,boolean clipToParent){ position.set(0,0, mRight - mLeft, mBottom - mTop); mapRectFromViewToScreenCoords(position, clipToParent); } /** * Map a rectangle from view-relative coordinates to screen-relative coordinates * * @param rect The rectangle to be mapped * @param clipToParent Whether to clip child bounds to the parent ones. * @hide */ publicvoid mapRectFromViewToScreenCoords(RectF rect,boolean clipToParent){ if(!hasIdentityMatrix()){ getMatrix().mapRect(rect); } rect.offset(mLeft, mTop); ViewParent parent = mParent; while(parent instanceofView){ View parentView =(View) parent; rect.offset(-parentView.mScrollX,-parentView.mScrollY); if(clipToParent){ rect.left =Math.max(rect.left,0); rect.top =Math.max(rect.top,0); rect.right =Math.min(rect.right, parentView.getWidth()); rect.bottom =Math.min(rect.bottom, parentView.getHeight()); } if(!parentView.hasIdentityMatrix()){ parentView.getMatrix().mapRect(rect); } rect.offset(parentView.mLeft, parentView.mTop); parent = parentView.mParent; } if(parent instanceofViewRootImpl){ ViewRootImpl viewRootImpl =(ViewRootImpl) parent; rect.offset(0,-viewRootImpl.mCurScrollY); } rect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); } /** * Return the class name of this object to be used for accessibility purposes. * Subclasses should only override this if they are implementing something that * should be seen as a completely new class of view when used by accessibility, * unrelated to the class it is deriving from. This is used to fill in * {@link AccessibilityNodeInfo#setClassName AccessibilityNodeInfo.setClassName}. */ publicCharSequence getAccessibilityClassName(){ returnView.class.getName(); } /** * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. */ publicvoid onProvideStructure(ViewStructure structure){ onProvideStructure(structure, VIEW_STRUCTURE_FOR_ASSIST,/* flags= */0); } /** * Populates a {@link ViewStructure} to fullfil an autofill request. * * <p>The structure should contain at least the following properties: * <ul> * <li>Autofill id ({@link ViewStructure#setAutofillId(AutofillId, int)}). * <li>Autofill type ({@link ViewStructure#setAutofillType(int)}). * <li>Autofill value ({@link ViewStructure#setAutofillValue(AutofillValue)}). * <li>Whether the data is sensitive ({@link ViewStructure#setDataIsSensitive(boolean)}). * </ul> * * <p>It's also recommended to set the following properties - the more properties the structure * has, the higher the chances of an {@link android.service.autofill.AutofillService} properly * using the structure: * * <ul> * <li>Autofill hints ({@link ViewStructure#setAutofillHints(String[])}). * <li>Autofill options ({@link ViewStructure#setAutofillOptions(CharSequence[])}) when the * view can only be filled with predefined values (typically used when the autofill type * is {@link #AUTOFILL_TYPE_LIST}). * <li>Resource id ({@link ViewStructure#setId(int, String, String, String)}). * <li>Class name ({@link ViewStructure#setClassName(String)}). * <li>Content description ({@link ViewStructure#setContentDescription(CharSequence)}). * <li>Visual properties such as visibility ({@link ViewStructure#setVisibility(int)}), * dimensions ({@link ViewStructure#setDimens(int, int, int, int, int, int)}), and * opacity ({@link ViewStructure#setOpaque(boolean)}). * <li>For views representing text fields, text properties such as the text itself * ({@link ViewStructure#setText(CharSequence)}), text hints * ({@link ViewStructure#setHint(CharSequence)}, input type * ({@link ViewStructure#setInputType(int)}), * <li>For views representing HTML nodes, its web domain * ({@link ViewStructure#setWebDomain(String)}) and HTML properties * (({@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}). * </ul> * * <p>The default implementation of this method already sets most of these properties based on * related {@link View} methods (for example, the autofill id is set using * {@link #getAutofillId()}, the autofill type set using {@link #getAutofillType()}, etc.), * and views in the standard Android widgets library also override it to set their * relevant properties (for example, {@link android.widget.TextView} already sets the text * properties), so it's recommended to only override this method * (and call {@code super.onProvideAutofillStructure()}) when: * * <ul> * <li>The view contents does not include PII (Personally Identifiable Information), so it * can call {@link ViewStructure#setDataIsSensitive(boolean)} passing {@code false}. * <li>The view can only be autofilled with predefined options, so it can call * {@link ViewStructure#setAutofillOptions(CharSequence[])}. * </ul> * * <p><b>Note:</b> The {@code left} and {@code top} values set in * {@link ViewStructure#setDimens(int, int, int, int, int, int)} must be relative to the next * {@link ViewGroup#isImportantForAutofill()} predecessor view included in the structure. * * <p>Views support the Autofill Framework mainly by: * <ul> * <li>Providing the metadata defining what the view means and how it can be autofilled. * <li>Notifying the Android System when the view value changed by calling * {@link AutofillManager#notifyValueChanged(View)}. * <li>Implementing the methods that autofill the view. * </ul> * <p>This method is responsible for the former; {@link #autofill(AutofillValue)} is responsible * for the latter. * * @param structure fill in with structured view data for autofill purposes. * @param flags optional flags. * * @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS */ publicvoid onProvideAutofillStructure(ViewStructure structure,@AutofillFlagsint flags){ onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags); } /** * Populates a {@link ViewStructure} for content capture. * * <p>This method is called after a view that is eligible for content capture * (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is * enabled for the user, and the activity rendering the view is enabled for content capture) * is laid out and is visible. The populated structure is then passed to the service through * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}. * * <p>The default implementation of this method sets the most relevant properties based on * related {@link View} methods, and views in the standard Android widgets library also * override it to set their relevant properties. Therefore, if overriding this method, it * is recommended to call {@code super.onProvideContentCaptureStructure()}. * * <p><b>Note: </b>views that manage a virtual structure under this view must populate just * the node representing this view and return right away, then asynchronously report (not * necessarily in the UI thread) when the children nodes appear, disappear or have their text * changed by calling * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}, * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)} * respectively. The structure for a child must be created using * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the * {@code autofillId} for a child can be obtained either through * {@code childStructure.getAutofillId()} or * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}. * * <p>When the virtual view hierarchy represents a web page, you should also: * * <ul> * <li>Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content * capture events should be generate for that URL. * <li>Create a new {@link ContentCaptureSession} child for every HTML element that * renders a new URL (like an {@code IFRAME}) and use that session to notify events from * that subtree. * </ul> * * <p><b>Note: </b>the following methods of the {@code structure} will be ignored: * <ul> * <li>{@link ViewStructure#setChildCount(int)} * <li>{@link ViewStructure#addChildCount(int)} * <li>{@link ViewStructure#getChildCount()} * <li>{@link ViewStructure#newChild(int)} * <li>{@link ViewStructure#asyncNewChild(int)} * <li>{@link ViewStructure#asyncCommit()} * <li>{@link ViewStructure#setWebDomain(String)} * <li>{@link ViewStructure#newHtmlInfoBuilder(String)} * <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)} * <li>{@link ViewStructure#setDataIsSensitive(boolean)} * <li>{@link ViewStructure#setAlpha(float)} * <li>{@link ViewStructure#setElevation(float)} * <li>{@link ViewStructure#setTransformation(Matrix)} * * </ul> */ publicvoid onProvideContentCaptureStructure(@NonNullViewStructure structure,int flags){ onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags); } /** @hide */ protectedvoid onProvideStructure(@NonNullViewStructure structure, @ViewStructureTypeint viewFor,int flags){ finalint id = mID; if(id != NO_ID &&!isViewIdGenerated(id)){ String pkg, type, entry; try{ finalResources res = getResources(); entry = res.getResourceEntryName(id); type = res.getResourceTypeName(id); pkg = res.getResourcePackageName(id); }catch(Resources.NotFoundException e){ entry = type = pkg =null; } structure.setId(id, pkg, type, entry); }else{ structure.setId(id,null,null,null); } if(viewFor == VIEW_STRUCTURE_FOR_AUTOFILL || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE){ final@AutofillTypeint autofillType = getAutofillType(); // Don't need to fill autofill info if view does not support it. // For example, only TextViews that are editable support autofill if(autofillType != AUTOFILL_TYPE_NONE){ structure.setAutofillType(autofillType); structure.setAutofillHints(getAutofillHints()); structure.setAutofillValue(getAutofillValue()); } structure.setImportantForAutofill(getImportantForAutofill()); structure.setReceiveContentMimeTypes(getReceiveContentMimeTypes()); } int ignoredParentLeft =0; int ignoredParentTop =0; if(viewFor == VIEW_STRUCTURE_FOR_AUTOFILL &&(flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS)==0){ View parentGroup =null; ViewParent viewParent = getParent(); if(viewParent instanceofView){ parentGroup =(View) viewParent; } while(parentGroup !=null&&!parentGroup.isImportantForAutofill()){ ignoredParentLeft += parentGroup.mLeft; ignoredParentTop += parentGroup.mTop; viewParent = parentGroup.getParent(); if(viewParent instanceofView){ parentGroup =(View) viewParent; }else{ break; } } } structure.setDimens(ignoredParentLeft + mLeft, ignoredParentTop + mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop); if(viewFor == VIEW_STRUCTURE_FOR_ASSIST){ if(!hasIdentityMatrix()){ structure.setTransformation(getMatrix()); } structure.setElevation(getZ()); } structure.setVisibility(getVisibility()); structure.setEnabled(isEnabled()); if(isClickable()){ structure.setClickable(true); } if(isFocusable()){ structure.setFocusable(true); } if(isFocused()){ structure.setFocused(true); } if(isAccessibilityFocused()){ structure.setAccessibilityFocused(true); } if(isSelected()){ structure.setSelected(true); } if(isActivated()){ structure.setActivated(true); } if(isLongClickable()){ structure.setLongClickable(true); } if(thisinstanceofCheckable){ structure.setCheckable(true); if(((Checkable)this).isChecked()){ structure.setChecked(true); } } if(isOpaque()){ structure.setOpaque(true); } if(isContextClickable()){ structure.setContextClickable(true); } structure.setClassName(getAccessibilityClassName().toString()); structure.setContentDescription(getContentDescription()); } /** * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to * generate additional virtual structure under this view. The default implementation * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the * view's virtual accessibility nodes, if any. You can override this for a more * optimal implementation providing this data. */ publicvoid onProvideVirtualStructure(ViewStructure structure){ onProvideVirtualStructureCompat(structure,false); } /** * Fallback implementation to populate a ViewStructure from accessibility state. * * @param structure The structure to populate. * @param forAutofill Whether the structure is needed for autofill. */ privatevoid onProvideVirtualStructureCompat(ViewStructure structure,boolean forAutofill){ finalAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if(provider !=null){ if(forAutofill &&Log.isLoggable(AUTOFILL_LOG_TAG,Log.VERBOSE)){ Log.v(AUTOFILL_LOG_TAG,"onProvideVirtualStructureCompat() for "+this); } finalAccessibilityNodeInfo info = createAccessibilityNodeInfo(); structure.setChildCount(1); finalViewStructure root = structure.newChild(0); populateVirtualStructure(root, provider, info, forAutofill); info.recycle(); } } /** * Populates a {@link ViewStructure} containing virtual children to fullfil an autofill * request. * * <p>This method should be used when the view manages a virtual structure under this view. For * example, a view that draws input fields using {@link #draw(Canvas)}. * * <p>When implementing this method, subclasses must follow the rules below: * * <ul> * <li>Add virtual children by calling the {@link ViewStructure#newChild(int)} or * {@link ViewStructure#asyncNewChild(int)} methods, where the {@code id} is an unique id * identifying the children in the virtual structure. * <li>The children hierarchy can have multiple levels if necessary, but ideally it should * exclude intermediate levels that are irrelevant for autofill; that would improve the * autofill performance. * <li>Also implement {@link #autofill(SparseArray)} to autofill the virtual * children. * <li>Set the autofill properties of the child structure as defined by * {@link #onProvideAutofillStructure(ViewStructure, int)}, using * {@link ViewStructure#setAutofillId(AutofillId, int)} to set its autofill id. * <li>Call {@link android.view.autofill.AutofillManager#notifyViewEntered(View, int, Rect)} * and/or {@link android.view.autofill.AutofillManager#notifyViewExited(View, int)} * when the focused virtual child changed. * <li>Override {@link #isVisibleToUserForAutofill(int)} to allow the platform to query * whether a given virtual view is visible to the user in order to support triggering * save when all views of interest go away. * <li>Call * {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)} * when the value of a virtual child changed. * <li>Call {@link * android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)} * when the visibility of a virtual child changed. * <li>Call * {@link android.view.autofill.AutofillManager#notifyViewClicked(View, int)} when a virtual * child is clicked. * <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure * changed and the current context should be committed (for example, when the user tapped * a {@code SUBMIT} button in an HTML page). * <li>Call {@link AutofillManager#cancel()} when the autofill context of the view structure * changed and the current context should be canceled (for example, when the user tapped * a {@code CANCEL} button in an HTML page). * <li>Provide ways for users to manually request autofill by calling * {@link AutofillManager#requestAutofill(View, int, Rect)}. * <li>The {@code left} and {@code top} values set in * {@link ViewStructure#setDimens(int, int, int, int, int, int)} must be relative to the * next {@link ViewGroup#isImportantForAutofill()} predecessor view included in the * structure. * </ul> * * <p>Views with virtual children support the Autofill Framework mainly by: * <ul> * <li>Providing the metadata defining what the virtual children mean and how they can be * autofilled. * <li>Implementing the methods that autofill the virtual children. * </ul> * <p>This method is responsible for the former; {@link #autofill(SparseArray)} is responsible * for the latter. * * @param structure fill in with virtual children data for autofill purposes. * @param flags optional flags. * * @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS */ publicvoid onProvideAutofillVirtualStructure(ViewStructure structure,int flags){ if(mContext.isAutofillCompatibilityEnabled()){ onProvideVirtualStructureCompat(structure,true); } } /** * Sets the listener to be {@link #performReceiveContent used} to handle insertion of * content into this view. * * <p>Depending on the type of view, this listener may be invoked for different scenarios. For * example, for an editable {@link android.widget.TextView}, this listener will be invoked for * the following scenarios: * <ol> * <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the * insertion/selection menu) * <li>Content insertion from the keyboard (from {@link InputConnection#commitContent}) * <li>Drag and drop (drop events from {@link #onDragEvent}) * <li>Autofill * <li>Selection replacement via {@link Intent#ACTION_PROCESS_TEXT} * </ol> * * <p>When setting a listener, clients must also declare the accepted MIME types. * The listener will still be invoked even if the MIME type of the content is not one of the * declared MIME types (e.g. if the user pastes content whose type is not one of the declared * MIME types). * In that case, the listener may reject the content (defer to the default platform behavior) * or execute some other fallback logic (e.g. show an appropriate message to the user). * The declared MIME types serve as a hint to allow different features to optionally alter * their behavior. For example, a soft keyboard may optionally choose to hide its UI for * inserting GIFs for a particular input field if the MIME types set here for that field * don't include "image/gif" or "image/*". * * <p>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC * MIME types. As a result, you should always write your MIME types with lowercase letters, * or use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to * lowercase. * * @param mimeTypes The MIME types accepted by the given listener. These may use patterns * such as "image/*", but may not start with a wildcard. This argument must * not be null or empty if a non-null listener is passed in. * @param listener The listener to use. This can be null to reset to the default behavior. */ publicvoid setOnReceiveContentListener( @SuppressLint("NullableCollection")@NullableString[] mimeTypes, @NullableOnReceiveContentListener listener){ if(listener !=null){ Preconditions.checkArgument(mimeTypes !=null&& mimeTypes.length >0, "When the listener is set, MIME types must also be set"); } if(mimeTypes !=null){ Preconditions.checkArgument(Arrays.stream(mimeTypes).noneMatch(t -> t.startsWith("*")), "A MIME type set here must not start with *: "+Arrays.toString(mimeTypes)); } mReceiveContentMimeTypes =ArrayUtils.isEmpty(mimeTypes)?null: mimeTypes; getListenerInfo().mOnReceiveContentListener = listener; } /** * Receives the given content. If no listener is set, invokes {@link #onReceiveContent}. If a * listener is {@link #setOnReceiveContentListener set}, invokes the listener instead; if the * listener returns a non-null result, invokes {@link #onReceiveContent} to handle it. * * @param payload The content to insert and related metadata. * * @return The portion of the passed-in content that was not accepted (may be all, some, or none * of the passed-in content). */ @Nullable publicContentInfo performReceiveContent(@NonNullContentInfo payload){ finalOnReceiveContentListener listener =(mListenerInfo ==null)?null : getListenerInfo().mOnReceiveContentListener; if(listener !=null){ finalContentInfo remaining = listener.onReceiveContent(this, payload); return(remaining ==null)?null: onReceiveContent(remaining); } return onReceiveContent(payload); } /** * Implements the default behavior for receiving content for this type of view. The default * view implementation is a no-op (returns the passed-in content without acting on it). * * <p>Widgets should override this method to define their default behavior for receiving * content. Apps should {@link #setOnReceiveContentListener set a listener} to provide * app-specific handling for receiving content. * * <p>See {@link #setOnReceiveContentListener} and {@link #performReceiveContent} for more info. * * @param payload The content to insert and related metadata. * * @return The portion of the passed-in content that was not handled (may be all, some, or none * of the passed-in content). */ @Nullable publicContentInfo onReceiveContent(@NonNullContentInfo payload){ return payload; } /** * Returns the MIME types accepted by {@link #performReceiveContent} for this view, as * configured via {@link #setOnReceiveContentListener}. By default returns null. * * <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft * keyboard, etc) may optionally use this metadata to conditionally alter their behavior. For * example, a soft keyboard may choose to hide its UI for inserting GIFs for a particular * input field if the MIME types returned here for that field don't include "image/gif" or * "image/*". * * <p>Note: Comparisons of MIME types should be performed using utilities such as * {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to * correctly handle patterns such as "text/*", "image/*", etc. Note that MIME type matching * in the Android framework is case-sensitive, unlike formal RFC MIME types. As a result, * you should always write your MIME types with lowercase letters, or use * {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to * lowercase. * * @return The MIME types accepted by {@link #performReceiveContent} for this view (may * include patterns such as "image/*"). */ @SuppressLint("NullableCollection") @Nullable publicString[] getReceiveContentMimeTypes(){ return mReceiveContentMimeTypes; } /** * Automatically fills the content of this view with the {@code value}. * * <p>Views support the Autofill Framework mainly by: * <ul> * <li>Providing the metadata defining what the view means and how it can be autofilled. * <li>Implementing the methods that autofill the view. * </ul> * <p>{@link #onProvideAutofillStructure(ViewStructure, int)} is responsible for the former, * this method is responsible for latter. * * <p>This method does nothing by default, but when overridden it typically: * <ol> * <li>Checks if the provided value matches the expected type (which is defined by * {@link #getAutofillType()}). * <li>Checks if the view is editable - if it isn't, it should return right away. * <li>Call the proper getter method on {@link AutofillValue} to fetch the actual value. * <li>Pass the actual value to the equivalent setter in the view. * </ol> * * <p>For example, a text-field view could implement the method this way: * * <pre class="prettyprint"> * &#64;Override * public void autofill(AutofillValue value) { * if (!value.isText() || !this.isEditable()) { * return; * } * CharSequence text = value.getTextValue(); * if (text != null) { * this.setText(text); * } * } * </pre> * * <p>If the value is updated asynchronously, the next call to * {@link AutofillManager#notifyValueChanged(View)} must happen <b>after</b> the value was * changed to the autofilled value. If not, the view will not be considered autofilled. * * <p><b>Note:</b> After this method is called, the value returned by * {@link #getAutofillValue()} must be equal to the {@code value} passed to it, otherwise the * view will not be highlighted as autofilled. * * @param value value to be autofilled. */ publicvoid autofill(@SuppressWarnings("unused")AutofillValue value){ } /** * Automatically fills the content of the virtual children within this view. * * <p>Views with virtual children support the Autofill Framework mainly by: * <ul> * <li>Providing the metadata defining what the virtual children mean and how they can be * autofilled. * <li>Implementing the methods that autofill the virtual children. * </ul> * <p>{@link #onProvideAutofillVirtualStructure(ViewStructure, int)} is responsible for the * former, this method is responsible for the latter - see {@link #autofill(AutofillValue)} and * {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} for more info about autofill. * * <p>If a child value is updated asynchronously, the next call to * {@link AutofillManager#notifyValueChanged(View, int, AutofillValue)} must happen * <b>after</b> the value was changed to the autofilled value. If not, the child will not be * considered autofilled. * * <p><b>Note:</b> To indicate that a virtual view was autofilled, * <code>?android:attr/autofilledHighlight</code> should be drawn over it until the data * changes. * * @param values map of values to be autofilled, keyed by virtual child id. * * @attr ref android.R.styleable#Theme_autofilledHighlight */ publicvoid autofill(@NonNull@SuppressWarnings("unused")SparseArray<AutofillValue> values){ if(!mContext.isAutofillCompatibilityEnabled()){ return; } finalAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if(provider ==null){ return; } finalint valueCount = values.size(); for(int i =0; i < valueCount; i++){ finalAutofillValue value = values.valueAt(i); if(value.isText()){ finalint virtualId = values.keyAt(i); finalCharSequence text = value.getTextValue(); finalBundle arguments =newBundle(); arguments.putCharSequence( AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text); provider.performAction(virtualId,AccessibilityNodeInfo.ACTION_SET_TEXT, arguments); } } } /** * Gets the unique, logical identifier of this view in the activity, for autofill purposes. * * <p>The autofill id is created on demand, unless it is explicitly set by * {@link #setAutofillId(AutofillId)}. * * <p>See {@link #setAutofillId(AutofillId)} for more info. * * @return The View's autofill id. */ publicfinalAutofillId getAutofillId(){ if(mAutofillId ==null){ // The autofill id needs to be unique, but its value doesn't matter, // so it's better to reuse the accessibility id to save space. mAutofillId =newAutofillId(getAutofillViewId()); } return mAutofillId; } /** * Sets the unique, logical identifier of this view in the activity, for autofill purposes. * * <p>The autofill id is created on demand, and this method should only be called when a view is * reused after {@link #dispatchProvideAutofillStructure(ViewStructure, int)} is called, as * that method creates a snapshot of the view that is passed along to the autofill service. * * <p>This method is typically used when view subtrees are recycled to represent different * content* &mdash;in this case, the autofill id can be saved before the view content is swapped * out, and restored later when it's swapped back in. For example: * * <pre> * EditText reusableView = ...; * ViewGroup parentView = ...; * AutofillManager afm = ...; * * // Swap out the view and change its contents * AutofillId oldId = reusableView.getAutofillId(); * CharSequence oldText = reusableView.getText(); * parentView.removeView(reusableView); * AutofillId newId = afm.getNextAutofillId(); * reusableView.setText("New I am"); * reusableView.setAutofillId(newId); * parentView.addView(reusableView); * * // Later, swap the old content back in * parentView.removeView(reusableView); * reusableView.setAutofillId(oldId); * reusableView.setText(oldText); * parentView.addView(reusableView); * </pre> * * <p>NOTE: If this view is a descendant of an {@link android.widget.AdapterView}, the system * may reset its autofill id when this view is recycled. If the autofill ids need to be stable, * they should be set again in * {@link android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)}. * * @param id an autofill ID that is unique in the {@link android.app.Activity} hosting the view, * or {@code null} to reset it. Usually it's an id previously allocated to another view (and * obtained through {@link #getAutofillId()}), or a new value obtained through * {@link AutofillManager#getNextAutofillId()}. * * @throws IllegalStateException if the view is already {@link #isAttachedToWindow() attached to * a window}. * * @throws IllegalArgumentException if the id is an autofill id associated with a virtual view. */ publicvoid setAutofillId(@NullableAutofillId id){ // TODO(b/37566627): add unit / CTS test for all possible combinations below if(Log.isLoggable(AUTOFILL_LOG_TAG,Log.VERBOSE)){ Log.v(AUTOFILL_LOG_TAG,"setAutofill(): from "+ mAutofillId +" to "+ id); } if(isAttachedToWindow()){ thrownewIllegalStateException("Cannot set autofill id when view is attached"); } if(id !=null&&!id.isNonVirtual()){ thrownewIllegalStateException("Cannot set autofill id assigned to virtual views"); } if(id ==null&&(mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET)==0){ // Ignore reset because it was never explicitly set before. return; } mAutofillId = id; if(id !=null){ mAutofillViewId = id.getViewId(); mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET; }else{ mAutofillViewId = NO_ID; mPrivateFlags3 &=~PFLAG3_AUTOFILLID_EXPLICITLY_SET; } } /** * Forces a reset of the autofill ids of the subtree rooted at this view. Like calling * {@link #setAutofillId(AutofillId) setAutofillId(null)} for each view, but works even if the * views are attached to a window. * * <p>This is useful if the views are being recycled, since an autofill id should uniquely * identify a particular piece of content. * * @hide */ publicvoid resetSubtreeAutofillIds(){ if(mAutofillViewId == NO_ID){ return; } if(Log.isLoggable(CONTENT_CAPTURE_LOG_TAG,Log.VERBOSE)){ Log.v(CONTENT_CAPTURE_LOG_TAG,"resetAutofillId() for "+ mAutofillViewId); }elseif(Log.isLoggable(AUTOFILL_LOG_TAG,Log.VERBOSE)){ Log.v(AUTOFILL_LOG_TAG,"resetAutofillId() for "+ mAutofillViewId); } mAutofillId =null; mAutofillViewId = NO_ID; mPrivateFlags3 &=~PFLAG3_AUTOFILLID_EXPLICITLY_SET; } /** * Describes the autofill type of this view, so an * {@link android.service.autofill.AutofillService} can create the proper {@link AutofillValue} * when autofilling the view. * * <p>By default returns {@link #AUTOFILL_TYPE_NONE}, but views should override it to properly * support the Autofill Framework. * * @return either {@link #AUTOFILL_TYPE_NONE}, {@link #AUTOFILL_TYPE_TEXT}, * {@link #AUTOFILL_TYPE_LIST}, {@link #AUTOFILL_TYPE_DATE}, or {@link #AUTOFILL_TYPE_TOGGLE}. * * @see #onProvideAutofillStructure(ViewStructure, int) * @see #autofill(AutofillValue) */ public@AutofillTypeint getAutofillType(){ return AUTOFILL_TYPE_NONE; } /** * Gets the hints that help an {@link android.service.autofill.AutofillService} determine how * to autofill the view with the user's data. * * <p>See {@link #setAutofillHints(String...)} for more info about these hints. * * @return The hints set via the attribute or {@link #setAutofillHints(String...)}, or * {@code null} if no hints were set. * * @attr ref android.R.styleable#View_autofillHints */ @ViewDebug.ExportedProperty() @InspectableProperty @NullablepublicString[] getAutofillHints(){ return mAutofillHints; } /** * @hide */ @TestApi publicboolean isAutofilled(){ return(mPrivateFlags3 & PFLAG3_IS_AUTOFILLED)!=0; } /** * @hide */ publicboolean hideAutofillHighlight(){ return(mPrivateFlags4 & PFLAG4_AUTOFILL_HIDE_HIGHLIGHT)!=0; } /** * Gets the {@link View}'s current autofill value. * * <p>By default returns {@code null}, but subclasses should override it and return an * appropriate value to properly support the Autofill Framework. * * @see #onProvideAutofillStructure(ViewStructure, int) * @see #autofill(AutofillValue) */ @Nullable publicAutofillValue getAutofillValue(){ returnnull; } /** * Gets the mode for determining whether this view is important for autofill. * * <p>See {@link #setImportantForAutofill(int)} and {@link #isImportantForAutofill()} for more * info about this mode. * * @return {@link #IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to * {@link #setImportantForAutofill(int)}. * * @attr ref android.R.styleable#View_importantForAutofill */ @ViewDebug.ExportedProperty(mapping ={ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_AUTO, to ="auto"), @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES, to ="yes"), @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO, to ="no"), @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, to ="yesExcludeDescendants"), @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, to ="noExcludeDescendants")}) @InspectableProperty(enumMapping ={ @EnumEntry(value = IMPORTANT_FOR_AUTOFILL_AUTO, name ="auto"), @EnumEntry(value = IMPORTANT_FOR_AUTOFILL_YES, name ="yes"), @EnumEntry(value = IMPORTANT_FOR_AUTOFILL_NO, name ="no"), @EnumEntry(value = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, name ="yesExcludeDescendants"), @EnumEntry(value = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, name ="noExcludeDescendants"), }) public@AutofillImportanceint getImportantForAutofill(){ return(mPrivateFlags3 & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK)>> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT; } /** * Sets the mode for determining whether this view is considered important for autofill. * * <p>The platform determines the importance for autofill automatically but you * can use this method to customize the behavior. For example: * * <ol> * <li>When the view contents is irrelevant for autofill (for example, a text field used in a * "Captcha" challenge), it should be {@link #IMPORTANT_FOR_AUTOFILL_NO}. * <li>When both the view and its children are irrelevant for autofill (for example, the root * view of an activity containing a spreadhseet editor), it should be * {@link #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}. * <li>When the view content is relevant for autofill but its children aren't (for example, * a credit card expiration date represented by a custom view that overrides the proper * autofill methods and has 2 children representing the month and year), it should * be {@link #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}. * </ol> * * <p><b>Note:</b> Setting the mode as {@link #IMPORTANT_FOR_AUTOFILL_NO} or * {@link #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()} for more details about how the View's importance for * autofill is used. * * @param mode {@link #IMPORTANT_FOR_AUTOFILL_AUTO}, {@link #IMPORTANT_FOR_AUTOFILL_YES}, * {@link #IMPORTANT_FOR_AUTOFILL_NO}, {@link #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, * or {@link #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}. * * @attr ref android.R.styleable#View_importantForAutofill */ publicvoid setImportantForAutofill(@AutofillImportanceint mode){ mPrivateFlags3 &=~PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK; mPrivateFlags3 |=(mode << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT) & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK; } /** * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode} * associated with this view is considered important for autofill purposes. * * <p>Generally speaking, a view is important for autofill if: * <ol> * <li>The view can be autofilled by an {@link android.service.autofill.AutofillService}. * <li>The view contents can help an {@link android.service.autofill.AutofillService} * determine how other views can be autofilled. * <ol> * * <p>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). * * <p>The value returned by this method depends on the value returned by * {@link #getImportantForAutofill()}: * * <ol> * <li>if it returns {@link #IMPORTANT_FOR_AUTOFILL_YES} or * {@link #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, then it returns {@code true} * <li>if it returns {@link #IMPORTANT_FOR_AUTOFILL_NO} or * {@link #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}, then it returns {@code false} * <li>if it returns {@link #IMPORTANT_FOR_AUTOFILL_AUTO}, then it uses some simple heuristics * that can return {@code true} in some cases (like a container with a resource id), * but {@code false} in most. * <li>otherwise, it returns {@code false}. * </ol> * * <p>When a view is considered important for autofill: * <ul> * <li>The view might automatically trigger an autofill request when focused on. * <li>The contents of the view are included in the {@link ViewStructure} used in an autofill * request. * </ul> * * <p>On the other hand, when a view is considered not important for autofill: * <ul> * <li>The view never automatically triggers autofill requests, but it can trigger a manual * request through {@link AutofillManager#requestAutofill(View)}. * <li>The contents of the view are not included in the {@link ViewStructure} used in an * autofill request, unless the request has the * {@link #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} flag. * </ul> * * @return whether the view is considered important for autofill. * * @see #setImportantForAutofill(int) * @see #IMPORTANT_FOR_AUTOFILL_AUTO * @see #IMPORTANT_FOR_AUTOFILL_YES * @see #IMPORTANT_FOR_AUTOFILL_NO * @see #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS * @see #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS * @see AutofillManager#requestAutofill(View) */ publicfinalboolean isImportantForAutofill(){ // Check parent mode to ensure we're not hidden. ViewParent parent = mParent; while(parent instanceofView){ finalint parentImportance =((View) parent).getImportantForAutofill(); if(parentImportance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS || parentImportance == IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS){ if(Log.isLoggable(AUTOFILL_LOG_TAG,Log.VERBOSE)){ Log.v(AUTOFILL_LOG_TAG,"View ("+this+") is not important for autofill " +"because parent "+ parent +"'s importance is "+ parentImportance); } returnfalse; } parent = parent.getParent(); } finalint importance = getImportantForAutofill(); // First, check the explicit states. if(importance == IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS || importance == IMPORTANT_FOR_AUTOFILL_YES){ returntrue; } if(importance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS || importance == IMPORTANT_FOR_AUTOFILL_NO){ if(Log.isLoggable(AUTOFILL_LOG_TAG,Log.VERBOSE)){ Log.v(AUTOFILL_LOG_TAG,"View ("+this+") is not important for autofill " +"because its importance is "+ importance); } returnfalse; } // Then use some heuristics to handle AUTO. if(importance != IMPORTANT_FOR_AUTOFILL_AUTO){ Log.w(AUTOFILL_LOG_TAG,"invalid autofill importance ("+ importance +" on view " +this); returnfalse; } // Always include views that have an explicit resource id. finalint id = mID; if(id != NO_ID &&!isViewIdGenerated(id)){ finalResources res = getResources(); String entry =null; String pkg =null; try{ entry = res.getResourceEntryName(id); pkg = res.getResourcePackageName(id); }catch(Resources.NotFoundException e){ // ignore } if(entry !=null&& pkg !=null&& pkg.equals(mContext.getPackageName())){ returntrue; } } // If the app developer explicitly set hints for it, it's important. if(getAutofillHints()!=null){ returntrue; } // Otherwise, assume it's not important... returnfalse; } /** * Gets the mode for determining whether this view is important for content capture. * * <p>See {@link #setImportantForContentCapture(int)} and * {@link #isImportantForContentCapture()} for more info about this mode. * * @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to * {@link #setImportantForContentCapture(int)}. * * @attr ref android.R.styleable#View_importantForContentCapture */ @ViewDebug.ExportedProperty(mapping ={ @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to ="auto"), @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to ="yes"), @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to ="no"), @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, to ="yesExcludeDescendants"), @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, to ="noExcludeDescendants")}) @InspectableProperty(enumMapping ={ @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name ="auto"), @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name ="yes"), @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name ="no"), @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, name ="yesExcludeDescendants"), @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, name ="noExcludeDescendants"), }) public@ContentCaptureImportanceint getImportantForContentCapture(){ // NOTE: the important for content capture values were the first flags added and are set in // the rightmost position, so we don't need to shift them return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK; } /** * Sets the mode for determining whether this view is considered important for content capture. * * <p>The platform determines the importance for autofill automatically but you * can use this method to customize the behavior. Typically, a view that provides text should * be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}. * * @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}, * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO}, * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS}, * or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}. * * @attr ref android.R.styleable#View_importantForContentCapture */ publicvoid setImportantForContentCapture(@ContentCaptureImportanceint mode){ // Reset first mPrivateFlags4 &=~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK; // Then set again // NOTE: the important for content capture values were the first flags added and are set in // the rightmost position, so we don't need to shift them mPrivateFlags4 |=(mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK); } /** * Hints the Android System whether this view is considered important for content capture, based * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}. * * <p>See {@link ContentCaptureManager} for more info about content capture. * * @return whether the view is considered important for content capture. * * @see #setImportantForContentCapture(int) * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS */ publicfinalboolean isImportantForContentCapture(){ boolean isImportant; if((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED)!=0){ isImportant =(mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE)!=0; return isImportant; } isImportant = calculateIsImportantForContentCapture(); mPrivateFlags4 &=~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; if(isImportant){ mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; } mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED; return isImportant; } /** * Calculates whether the flag is important for content capture so it can be used by * {@link #isImportantForContentCapture()} while the tree is traversed. */ privateboolean calculateIsImportantForContentCapture(){ // Check parent mode to ensure we're important ViewParent parent = mParent; while(parent instanceofView){ finalint parentImportance =((View) parent).getImportantForContentCapture(); if(parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS || parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS){ if(Log.isLoggable(CONTENT_CAPTURE_LOG_TAG,Log.VERBOSE)){ Log.v(CONTENT_CAPTURE_LOG_TAG,"View ("+this+") is not important for " +"content capture because parent "+ parent +"'s importance is " + parentImportance); } returnfalse; } parent = parent.getParent(); } finalint importance = getImportantForContentCapture(); // First, check the explicit states. if(importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS || importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES){ returntrue; } if(importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS || importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO){ if(Log.isLoggable(CONTENT_CAPTURE_LOG_TAG,Log.VERBOSE)){ Log.v(CONTENT_CAPTURE_LOG_TAG,"View ("+this+") is not important for content " +"capture because its importance is "+ importance); } returnfalse; } // Then use some heuristics to handle AUTO. if(importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO){ Log.w(CONTENT_CAPTURE_LOG_TAG,"invalid content capture importance ("+ importance +" on view "+this); returnfalse; } // View group is important if at least one children also is if(thisinstanceofViewGroup){ finalViewGroup group =(ViewGroup)this; for(int i =0; i < group.getChildCount(); i++){ finalView child = group.getChildAt(i); if(child.isImportantForContentCapture()){ returntrue; } } } // If the app developer explicitly set hints or autofill hintsfor it, it's important. if(getAutofillHints()!=null){ returntrue; } // Otherwise, assume it's not important... returnfalse; } /** * Helper used to notify the {@link ContentCaptureManager} when the view is removed or * added, based on whether it's laid out and visible, and without knowing if the parent removed * it from the view hierarchy. * * <p>This method is called from many places (visibility changed, view laid out, view attached * or detached to/from window, etc...) and hence must contain the logic to call the manager, as * described below: * * <ol> * <li>It should only be called when content capture is enabled for the view. * <li>It must call viewAppeared() before viewDisappeared() * <li>viewAppeared() can only be called when the view is visible and laid out * <li>It should not call the same event twice. * </ol> */ privatevoid notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared){ AttachInfo ai = mAttachInfo; // Skip it while the view is being laid out for the first time if(ai !=null&&!ai.mReadyForContentCaptureUpdates)return; // First check if context has client, so it saves a service lookup when it doesn't if(mContext.getContentCaptureOptions()==null)return; if(appeared){ // The appeared event stops sending to AiAi. // 1. The view is hidden. // 2. The same event was sent. // 3. The view is not laid out, and it will be laid out in the future. // Some recycled views cached its layout and a relayout is unnecessary. In this case, // system still needs to notify content capture the view appeared. When a view is // recycled, it will set the flag PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED. finalboolean isRecycledWithoutRelayout = getNotifiedContentCaptureDisappeared() && getVisibility()== VISIBLE &&!isLayoutRequested(); if(getVisibility()!= VISIBLE || getNotifiedContentCaptureAppeared() ||!(isLaidOut()|| isRecycledWithoutRelayout)){ if(DEBUG_CONTENT_CAPTURE){ Log.v(CONTENT_CAPTURE_LOG_TAG,"Ignoring 'appeared' on "+this+": laid=" + isLaidOut()+", visibleToUser="+ isVisibleToUser() +", visible="+(getVisibility()== VISIBLE) +": alreadyNotifiedAppeared="+ getNotifiedContentCaptureAppeared() +", alreadyNotifiedDisappeared=" + getNotifiedContentCaptureDisappeared()); } return; } }else{ if(!getNotifiedContentCaptureAppeared()|| getNotifiedContentCaptureDisappeared()){ if(DEBUG_CONTENT_CAPTURE){ Log.v(CONTENT_CAPTURE_LOG_TAG,"Ignoring 'disappeared' on "+this+": laid=" + isLaidOut()+", visibleToUser="+ isVisibleToUser() +", visible="+(getVisibility()== VISIBLE) +": alreadyNotifiedAppeared="+ getNotifiedContentCaptureAppeared() +", alreadyNotifiedDisappeared=" + getNotifiedContentCaptureDisappeared()); } return; } } ContentCaptureSession session = getContentCaptureSession(); if(session ==null)return; // ... and finally at the view level // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled() if(!isImportantForContentCapture())return; if(appeared){ setNotifiedContentCaptureAppeared(); if(ai !=null){ ai.delayNotifyContentCaptureEvent(session,this, appeared); }else{ if(DEBUG_CONTENT_CAPTURE){ Log.w(CONTENT_CAPTURE_LOG_TAG,"no AttachInfo on appeared for "+this); } } }else{ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; mPrivateFlags4 &=~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; if(ai !=null){ ai.delayNotifyContentCaptureEvent(session,this, appeared); }else{ if(DEBUG_CONTENT_CAPTURE){ Log.v(CONTENT_CAPTURE_LOG_TAG,"no AttachInfo on disappeared for "+this); } } // We reset any translation state as views may be re-used (e.g., as in ListView and // RecyclerView). We only need to do this for views important for content capture since // views unimportant for content capture won't be translated anyway. if(!isTemporarilyDetached()){ clearTranslationState(); } } } privatevoid setNotifiedContentCaptureAppeared(){ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; mPrivateFlags4 &=~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; } /** @hide */ protectedboolean getNotifiedContentCaptureAppeared(){ return(mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED)!=0; } privateboolean getNotifiedContentCaptureDisappeared(){ return(mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED)!=0; } /** * Sets the (optional) {@link ContentCaptureSession} associated with this view. * * <p>This method should be called when you need to associate a {@link ContentCaptureContext} to * the content capture events associated with this view or its view hierarchy (if it's a * {@link ViewGroup}). * * <p>For example, if your activity is associated with a web domain, first you would need to * set the context for the main DOM: * * <pre> * ContentCaptureSession mainSession = rootView.getContentCaptureSession(); * mainSession.setContentCaptureContext(ContentCaptureContext.forLocusId(Uri.parse(myUrl)); * </pre> * * <p>Then if the page had an {@code IFRAME}, you would create a new session for it: * * <pre> * ContentCaptureSession iframeSession = mainSession.createContentCaptureSession( * ContentCaptureContext.forLocusId(Uri.parse(iframeUrl))); * iframeView.setContentCaptureSession(iframeSession); * </pre> * * @param contentCaptureSession a session created by * {@link ContentCaptureSession#createContentCaptureSession( * android.view.contentcapture.ContentCaptureContext)}. */ publicvoid setContentCaptureSession(@NullableContentCaptureSession contentCaptureSession){ mContentCaptureSession = contentCaptureSession; } /** * Gets the session used to notify content capture events. * * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)}, * inherited by ancestors, default session or {@code null} if content capture is disabled for * this view. */ @Nullable publicfinalContentCaptureSession getContentCaptureSession(){ if(mContentCaptureSessionCached){ return mContentCaptureSession; } mContentCaptureSession = getAndCacheContentCaptureSession(); mContentCaptureSessionCached =true; return mContentCaptureSession; } @Nullable privateContentCaptureSession getAndCacheContentCaptureSession(){ // First try the session explicitly set by setContentCaptureSession() if(mContentCaptureSession !=null){ return mContentCaptureSession; } // Then the session explicitly set in an ancestor ContentCaptureSession session =null; if(mParent instanceofView){ session =((View) mParent).getContentCaptureSession(); } // Finally, if no session was explicitly set, use the context's default session. if(session ==null){ finalContentCaptureManager ccm = mContext .getSystemService(ContentCaptureManager.class); return ccm ==null?null: ccm.getMainContentCaptureSession(); } return session; } @Nullable privateAutofillManager getAutofillManager(){ return mContext.getSystemService(AutofillManager.class); } privateboolean isAutofillable(){ if(getAutofillType()== AUTOFILL_TYPE_NONE)returnfalse; if(!isImportantForAutofill()){ // View is not important for "regular" autofill, so we must check if Augmented Autofill // is enabled for the activity finalAutofillOptions options = mContext.getAutofillOptions(); if(options ==null||!options.isAugmentedAutofillEnabled(mContext)){ returnfalse; } finalAutofillManager afm = getAutofillManager(); if(afm ==null)returnfalse; afm.notifyViewEnteredForAugmentedAutofill(this); } return getAutofillViewId()> LAST_APP_AUTOFILL_ID; } /** @hide */ publicboolean canNotifyAutofillEnterExitEvent(){ return isAutofillable()&& isAttachedToWindow(); } privatevoid populateVirtualStructure(ViewStructure structure, AccessibilityNodeProvider provider,AccessibilityNodeInfo info, boolean forAutofill){ structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), null,null, info.getViewIdResourceName()); Rect rect = structure.getTempRect(); info.getBoundsInParent(rect); structure.setDimens(rect.left, rect.top,0,0, rect.width(), rect.height()); structure.setVisibility(VISIBLE); structure.setEnabled(info.isEnabled()); if(info.isClickable()){ structure.setClickable(true); } if(info.isFocusable()){ structure.setFocusable(true); } if(info.isFocused()){ structure.setFocused(true); } if(info.isAccessibilityFocused()){ structure.setAccessibilityFocused(true); } if(info.isSelected()){ structure.setSelected(true); } if(info.isLongClickable()){ structure.setLongClickable(true); } if(info.isCheckable()){ structure.setCheckable(true); if(info.isChecked()){ structure.setChecked(true); } } if(info.isContextClickable()){ structure.setContextClickable(true); } if(forAutofill){ structure.setAutofillId(newAutofillId(getAutofillId(), AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()))); } CharSequence cname = info.getClassName(); structure.setClassName(cname !=null? cname.toString():null); structure.setContentDescription(info.getContentDescription()); if(forAutofill){ finalint maxTextLength = info.getMaxTextLength(); if(maxTextLength !=-1){ structure.setMaxTextLength(maxTextLength); } structure.setHint(info.getHintText()); } CharSequence text = info.getText(); boolean hasText = text !=null|| info.getError()!=null; if(hasText){ structure.setText(text, info.getTextSelectionStart(), info.getTextSelectionEnd()); } if(forAutofill){ if(info.isEditable()){ structure.setDataIsSensitive(true); if(hasText){ structure.setAutofillType(AUTOFILL_TYPE_TEXT); structure.setAutofillValue(AutofillValue.forText(text)); } int inputType = info.getInputType(); if(inputType ==0&& info.isPassword()){ inputType =InputType.TYPE_CLASS_TEXT |InputType.TYPE_TEXT_VARIATION_PASSWORD; } structure.setInputType(inputType); }else{ structure.setDataIsSensitive(false); } } finalint NCHILDREN = info.getChildCount(); if(NCHILDREN >0){ structure.setChildCount(NCHILDREN); for(int i=0; i<NCHILDREN; i++){ if(AccessibilityNodeInfo.getVirtualDescendantId(info.getChildNodeIds().get(i)) ==AccessibilityNodeProvider.HOST_VIEW_ID){ Log.e(VIEW_LOG_TAG,"Virtual view pointing to its host. Ignoring"); continue; } AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo( AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i))); if(cinfo !=null){ ViewStructure child = structure.newChild(i); populateVirtualStructure(child, provider, cinfo, forAutofill); cinfo.recycle(); } } } } /** * Dispatch creation of {@link ViewStructure} down the hierarchy. The default * implementation calls {@link #onProvideStructure} and * {@link #onProvideVirtualStructure}. */ publicvoid dispatchProvideStructure(ViewStructure structure){ dispatchProvideStructure(structure, VIEW_STRUCTURE_FOR_ASSIST,/* flags= */0); } /** * Dispatches creation of a {@link ViewStructure}s for autofill purposes down the hierarchy, * when an Assist structure is being created as part of an autofill request. * * <p>The default implementation does the following: * <ul> * <li>Sets the {@link AutofillId} in the structure. * <li>Calls {@link #onProvideAutofillStructure(ViewStructure, int)}. * <li>Calls {@link #onProvideAutofillVirtualStructure(ViewStructure, int)}. * </ul> * * <p>Typically, this method should only be overridden by subclasses that provide a view * hierarchy (such as {@link ViewGroup}) - other classes should override * {@link #onProvideAutofillStructure(ViewStructure, int)} or * {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} instead. * * <p>When overridden, it must: * * <ul> * <li>Either call * {@code super.dispatchProvideAutofillStructure(structure, flags)} or explicitly * set the {@link AutofillId} in the structure (for example, by calling * {@code structure.setAutofillId(getAutofillId())}). * <li>Decide how to handle the {@link #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} flag - when * set, all views in the structure should be considered important for autofill, * regardless of what {@link #isImportantForAutofill()} returns. We encourage you to * respect this flag to provide a better user experience - this flag is typically used * when an user explicitly requested autofill. If the flag is not set, * then only views marked as important for autofill should be included in the * structure - skipping non-important views optimizes the overall autofill performance. * </ul> * * @param structure fill in with structured view data for autofill purposes. * @param flags optional flags. * * @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS */ publicvoid dispatchProvideAutofillStructure(@NonNullViewStructure structure, @AutofillFlagsint flags){ dispatchProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags); } privatevoid dispatchProvideStructure(@NonNullViewStructure structure, @ViewStructureTypeint viewFor,@AutofillFlagsint flags){ if(viewFor == VIEW_STRUCTURE_FOR_AUTOFILL){ structure.setAutofillId(getAutofillId()); onProvideAutofillStructure(structure, flags); onProvideAutofillVirtualStructure(structure, flags); }elseif(!isAssistBlocked()){ onProvideStructure(structure); onProvideVirtualStructure(structure); }else{ structure.setClassName(getAccessibilityClassName().toString()); structure.setAssistBlocked(true); } } /** * Dispatches the initial content capture events for a view structure. * * @hide */ publicvoid dispatchInitialProvideContentCaptureStructure(){ AttachInfo ai = mAttachInfo; if(ai ==null){ Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): no AttachInfo for "+this); return; } ContentCaptureManager ccm = ai.mContentCaptureManager; if(ccm ==null){ Log.w(CONTENT_CAPTURE_LOG_TAG,"dispatchProvideContentCaptureStructure(): " +"no ContentCaptureManager for "+this); return; } // We must set it before checkign if the view itself is important, because it might // initially not be (for example, if it's empty), although that might change later (for // example, if important views are added) ai.mReadyForContentCaptureUpdates =true; if(!isImportantForContentCapture()){ if(Log.isLoggable(CONTENT_CAPTURE_LOG_TAG,Log.DEBUG)){ Log.d(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): decorView is not important"); } return; } ai.mContentCaptureManager = ccm; ContentCaptureSession session = getContentCaptureSession(); if(session ==null){ if(Log.isLoggable(CONTENT_CAPTURE_LOG_TAG,Log.DEBUG)){ Log.d(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): no session for "+this); } return; } session.internalNotifyViewTreeEvent(/* started= */true); try{ dispatchProvideContentCaptureStructure(); }finally{ session.internalNotifyViewTreeEvent(/* started= */false); } } /** @hide */ void dispatchProvideContentCaptureStructure(){ ContentCaptureSession session = getContentCaptureSession(); if(session !=null){ ViewStructure structure = session.newViewStructure(this); onProvideContentCaptureStructure(structure,/* flags= */0); setNotifiedContentCaptureAppeared(); session.notifyViewAppeared(structure); } } /** * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ publicvoid onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info){ if(mAttachInfo ==null){ return; } Rect bounds = mAttachInfo.mTmpInvalRect; getDrawingRect(bounds); info.setBoundsInParent(bounds); getBoundsOnScreen(bounds,true); info.setBoundsInScreen(bounds); ViewParent parent = getParentForAccessibility(); if(parent instanceofView){ info.setParent((View) parent); } if(mID !=View.NO_ID){ View rootView = getRootView(); if(rootView ==null){ rootView =this; } View label = rootView.findLabelForView(this, mID); if(label !=null){ info.setLabeledBy(label); } if((mAttachInfo.mAccessibilityFetchFlags &AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS)!=0 &&Resources.resourceHasPackage(mID)){ try{ String viewId = getResources().getResourceName(mID); info.setViewIdResourceName(viewId); }catch(Resources.NotFoundException nfe){ /* ignore */ } } } if(mLabelForId !=View.NO_ID){ View rootView = getRootView(); if(rootView ==null){ rootView =this; } View labeled = rootView.findViewInsideOutShouldExist(this, mLabelForId); if(labeled !=null){ info.setLabelFor(labeled); } } if(mAccessibilityTraversalBeforeId !=View.NO_ID){ View rootView = getRootView(); if(rootView ==null){ rootView =this; } View next = rootView.findViewInsideOutShouldExist(this, mAccessibilityTraversalBeforeId); if(next !=null&& next.includeForAccessibility()){ info.setTraversalBefore(next); } } if(mAccessibilityTraversalAfterId !=View.NO_ID){ View rootView = getRootView(); if(rootView ==null){ rootView =this; } View next = rootView.findViewInsideOutShouldExist(this, mAccessibilityTraversalAfterId); if(next !=null&& next.includeForAccessibility()){ info.setTraversalAfter(next); } } info.setVisibleToUser(isVisibleToUser()); info.setImportantForAccessibility(isImportantForAccessibility()); info.setPackageName(mContext.getPackageName()); info.setClassName(getAccessibilityClassName()); info.setStateDescription(getStateDescription()); info.setContentDescription(getContentDescription()); info.setEnabled(isEnabled()); info.setClickable(isClickable()); info.setFocusable(isFocusable()); info.setScreenReaderFocusable(isScreenReaderFocusable()); info.setFocused(isFocused()); info.setAccessibilityFocused(isAccessibilityFocused()); info.setSelected(isSelected()); info.setLongClickable(isLongClickable()); info.setContextClickable(isContextClickable()); info.setLiveRegion(getAccessibilityLiveRegion()); if((mTooltipInfo !=null)&&(mTooltipInfo.mTooltipText !=null)){ info.setTooltipText(mTooltipInfo.mTooltipText); info.addAction((mTooltipInfo.mTooltipPopup ==null) ?AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP :AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP); } // TODO: These make sense only if we are in an AdapterView but all // views can be selected. Maybe from accessibility perspective // we should report as selectable view in an AdapterView. info.addAction(AccessibilityNodeInfo.ACTION_SELECT); info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION); if(isFocusable()){ if(isFocused()){ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS); }else{ info.addAction(AccessibilityNodeInfo.ACTION_FOCUS); } } if(!isAccessibilityFocused()){ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); }else{ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); } if(isClickable()&& isEnabled()){ info.addAction(AccessibilityNodeInfo.ACTION_CLICK); } if(isLongClickable()&& isEnabled()){ info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); } if(isContextClickable()&& isEnabled()){ info.addAction(AccessibilityAction.ACTION_CONTEXT_CLICK); } CharSequence text = getIterableTextForAccessibility(); if(text !=null&& text.length()>0){ info.setTextSelection(getAccessibilitySelectionStart(), getAccessibilitySelectionEnd()); info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION); info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD |AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH); } info.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN); populateAccessibilityNodeInfoDrawingOrderInParent(info); info.setPaneTitle(mAccessibilityPaneTitle); info.setHeading(isAccessibilityHeading()); if(mTouchDelegate !=null){ info.setTouchDelegateInfo(mTouchDelegate.getTouchDelegateInfo()); } if(startedSystemDragForAccessibility()){ info.addAction(AccessibilityAction.ACTION_DRAG_CANCEL); } if(canAcceptAccessibilityDrop()){ info.addAction(AccessibilityAction.ACTION_DRAG_DROP); } } /** * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the * additional data. * <p> * This method only needs overloading if the node is marked as having extra data available. * </p> * * @param info The info to which to add the extra data. Never {@code null}. * @param extraDataKey A key specifying the type of extra data to add to the info. The * extra data should be added to the {@link Bundle} returned by * the info's {@link AccessibilityNodeInfo#getExtras} method. Never * {@code null}. * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be * {@code null} if the service provided no arguments. * * @see AccessibilityNodeInfo#setAvailableExtraData(List) */ publicvoid addExtraDataToAccessibilityNodeInfo( @NonNullAccessibilityNodeInfo info,@NonNullString extraDataKey, @NullableBundle arguments){ } /** * Determine the order in which this view will be drawn relative to its siblings for a11y * * @param info The info whose drawing order should be populated */ privatevoid populateAccessibilityNodeInfoDrawingOrderInParent(AccessibilityNodeInfo info){ /* * If the view's bounds haven't been set yet, layout has not completed. In that situation, * drawing order may not be well-defined, and some Views with custom drawing order may * not be initialized sufficiently to respond properly getChildDrawingOrder. */ if((mPrivateFlags & PFLAG_HAS_BOUNDS)==0){ info.setDrawingOrder(0); return; } int drawingOrderInParent =1; // Iterate up the hierarchy if parents are not important for a11y View viewAtDrawingLevel =this; finalViewParent parent = getParentForAccessibility(); while(viewAtDrawingLevel != parent){ finalViewParent currentParent = viewAtDrawingLevel.getParent(); if(!(currentParent instanceofViewGroup)){ // Should only happen for the Decor drawingOrderInParent =0; break; }else{ finalViewGroup parentGroup =(ViewGroup) currentParent; finalint childCount = parentGroup.getChildCount(); if(childCount >1){ List<View> preorderedList = parentGroup.buildOrderedChildList(); if(preorderedList !=null){ finalint childDrawIndex = preorderedList.indexOf(viewAtDrawingLevel); for(int i =0; i < childDrawIndex; i++){ drawingOrderInParent += numViewsForAccessibility(preorderedList.get(i)); } preorderedList.clear(); }else{ finalint childIndex = parentGroup.indexOfChild(viewAtDrawingLevel); finalboolean customOrder = parentGroup.isChildrenDrawingOrderEnabled(); finalint childDrawIndex =((childIndex >=0)&& customOrder)? parentGroup .getChildDrawingOrder(childCount, childIndex): childIndex; finalint numChildrenToIterate = customOrder ? childCount : childDrawIndex; if(childDrawIndex !=0){ for(int i =0; i < numChildrenToIterate; i++){ finalint otherDrawIndex =(customOrder ? parentGroup.getChildDrawingOrder(childCount, i): i); if(otherDrawIndex < childDrawIndex){ drawingOrderInParent += numViewsForAccessibility(parentGroup.getChildAt(i)); } } } } } } viewAtDrawingLevel =(View) currentParent; } info.setDrawingOrder(drawingOrderInParent); } privatestaticint numViewsForAccessibility(View view){ if(view !=null){ if(view.includeForAccessibility()){ return1; }elseif(view instanceofViewGroup){ return((ViewGroup) view).getNumChildrenForAccessibility(); } } return0; } privateView findLabelForView(View view,int labeledId){ if(mMatchLabelForPredicate ==null){ mMatchLabelForPredicate =newMatchLabelForPredicate(); } mMatchLabelForPredicate.mLabeledId = labeledId; return findViewByPredicateInsideOut(view, mMatchLabelForPredicate); } /** * Computes whether this virtual autofill view is visible to the user. * * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy * view must override it. * * @return Whether the view is visible on the screen. */ publicboolean isVisibleToUserForAutofill(int virtualId){ if(mContext.isAutofillCompatibilityEnabled()){ finalAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if(provider !=null){ finalAccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(virtualId); if(node !=null){ return node.isVisibleToUser(); } // if node is null, assume it's not visible anymore }else{ Log.w(VIEW_LOG_TAG,"isVisibleToUserForAutofill("+ virtualId +"): no provider"); } returnfalse; } returntrue; } /** * Computes whether this view is visible to the user. Such a view is * attached, visible, all its predecessors are visible, it is not clipped * entirely by its predecessors, and has an alpha greater than zero. * * @return Whether the view is visible on the screen. * * @hide */ @UnsupportedAppUsage publicboolean isVisibleToUser(){ return isVisibleToUser(null); } /** * Computes whether the given portion of this view is visible to the user. * Such a view is attached, visible, all its predecessors are visible, * has an alpha greater than zero, and the specified portion is not * clipped entirely by its predecessors. * * @param boundInView the portion of the view to test; coordinates should be relative; may be * <code>null</code>, and the entire view will be tested in this case. * When <code>true</code> is returned by the function, the actual visible * region will be stored in this parameter; that is, if boundInView is fully * contained within the view, no modification will be made, otherwise regions * outside of the visible area of the view will be clipped. * * @return Whether the specified portion of the view is visible on the screen. * * @hide */ @UnsupportedAppUsage(trackingBug =171933273) protectedboolean isVisibleToUser(Rect boundInView){ if(mAttachInfo !=null){ // Attached to invisible window means this view is not visible. if(mAttachInfo.mWindowVisibility !=View.VISIBLE){ returnfalse; } // An invisible predecessor or one with alpha zero means // that this view is not visible to the user. Object current =this; while(current instanceofView){ View view =(View) current; // We have attach info so this view is attached and there is no // need to check whether we reach to ViewRootImpl on the way up. if(view.getAlpha()<=0|| view.getTransitionAlpha()<=0|| view.getVisibility()!= VISIBLE){ returnfalse; } current = view.mParent; } // Check if the view is entirely covered by its predecessors. Rect visibleRect = mAttachInfo.mTmpInvalRect; Point offset = mAttachInfo.mPoint; if(!getGlobalVisibleRect(visibleRect, offset)){ returnfalse; } // Check if the visible portion intersects the rectangle of interest. if(boundInView !=null){ visibleRect.offset(-offset.x,-offset.y); return boundInView.intersect(visibleRect); } returntrue; } returnfalse; } /** * Returns the delegate for implementing accessibility support via * composition. For more details see {@link AccessibilityDelegate}. * * @return The delegate, or null if none set. */ publicAccessibilityDelegate getAccessibilityDelegate(){ return mAccessibilityDelegate; } /** * Sets a delegate for implementing accessibility support via composition * (as opposed to inheritance). For more details, see * {@link AccessibilityDelegate}. * <p> * <strong>Note:</strong> 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 <i>before</i> * host methods. This prevents certain properties such as class name from * being modified by overriding * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)}, * as any changes will be overwritten by the host class. * <p> * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate * methods are called <i>after</i> 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 AccessibilityDelegate */ publicvoid setAccessibilityDelegate(@NullableAccessibilityDelegate delegate){ mAccessibilityDelegate = delegate; } /** * 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. * <p> * If this method returns an instance, this instance is responsible for managing * {@link AccessibilityNodeInfo}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. * </p> * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#getAccessibilityNodeProvider(View)} * is responsible for handling this call. * </p> * * @return The provider. * * @see AccessibilityNodeProvider */ publicAccessibilityNodeProvider getAccessibilityNodeProvider(){ if(mAccessibilityDelegate !=null){ return mAccessibilityDelegate.getAccessibilityNodeProvider(this); }else{ returnnull; } } /** * Gets the unique identifier of this view on the screen for accessibility purposes. * * @return The view accessibility id. * * @hide */ @UnsupportedAppUsage publicint getAccessibilityViewId(){ if(mAccessibilityViewId == NO_ID){ mAccessibilityViewId = sNextAccessibilityViewId++; } return mAccessibilityViewId; } /** * Gets the unique identifier of this view on the screen for autofill purposes. * * @return The view autofill id. * * @hide */ publicint getAutofillViewId(){ if(mAutofillViewId == NO_ID){ mAutofillViewId = mContext.getNextAutofillId(); } return mAutofillViewId; } /** * Gets the unique identifier of the window in which this View resides. * * @return The window accessibility id. * * @hide */ publicint getAccessibilityWindowId(){ return mAttachInfo !=null? mAttachInfo.mAccessibilityWindowId :AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } /** * Returns the {@link View}'s state description. * <p> * <strong>Note:</strong> Do not override this method, as it will have no * effect on the state description presented to accessibility services. * You must call {@link #setStateDescription(CharSequence)} to modify the * state description. * * @return the state description * @see #setStateDescription(CharSequence) */ @ViewDebug.ExportedProperty(category ="accessibility") publicfinal@NullableCharSequence getStateDescription(){ return mStateDescription; } /** * Returns the {@link View}'s content description. * <p> * <strong>Note:</strong> Do not override this method, as it will have no * effect on the content description presented to accessibility services. * You must call {@link #setContentDescription(CharSequence)} to modify the * content description. * * @return the content description * @see #setContentDescription(CharSequence) * @attr ref android.R.styleable#View_contentDescription */ @ViewDebug.ExportedProperty(category ="accessibility") @InspectableProperty publicCharSequence getContentDescription(){ return mContentDescription; } /** * Sets the {@link View}'s state description. * <p> * A state description briefly describes the states of the view and is primarily used * for accessibility support to determine how the states of a view should be presented to * the user. It is a supplement to the boolean states (for example, checked/unchecked) and * it is used for customized state description (for example, "wifi, connected, three bars"). * State description changes frequently while content description should change less often. * State description should be localized. For android widgets which have default state * descriptions, app developers can call this method to override the state descriptions. * Setting state description to null restores the default behavior. * * @param stateDescription The state description. * @see #getStateDescription() */ @RemotableViewMethod publicvoid setStateDescription(@NullableCharSequence stateDescription){ if(mStateDescription ==null){ if(stateDescription ==null){ return; } }elseif(mStateDescription.equals(stateDescription)){ return; } mStateDescription = stateDescription; if(!TextUtils.isEmpty(stateDescription) && getImportantForAccessibility()== IMPORTANT_FOR_ACCESSIBILITY_AUTO){ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } if(AccessibilityManager.getInstance(mContext).isEnabled()){ AccessibilityEvent event =AccessibilityEvent.obtain(); event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION); sendAccessibilityEventUnchecked(event); } } /** * Sets the {@link View}'s content description. * <p> * A content description briefly describes the view and is primarily used * for accessibility support to determine how a view should be presented to * the user. In the case of a view with no textual representation, such as * {@link android.widget.ImageButton}, a useful content description * explains what the view does. For example, an image button with a phone * icon that is used to place a call may use "Call" as its content * description. An image of a floppy disk that is used to save a file may * use "Save". * * @param contentDescription The content description. * @see #getContentDescription() * @attr ref android.R.styleable#View_contentDescription */ @RemotableViewMethod publicvoid setContentDescription(CharSequence contentDescription){ if(mContentDescription ==null){ if(contentDescription ==null){ return; } }elseif(mContentDescription.equals(contentDescription)){ return; } mContentDescription = contentDescription; finalboolean nonEmptyDesc = contentDescription !=null&& contentDescription.length()>0; if(nonEmptyDesc && getImportantForAccessibility()== IMPORTANT_FOR_ACCESSIBILITY_AUTO){ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); notifySubtreeAccessibilityStateChangedIfNeeded(); }else{ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION); } } /** * Sets the id of a view before which this one is visited in accessibility traversal. * A screen-reader must visit the content of this view before the content of the one * it precedes. For example, if view B is set to be before view A, then a screen-reader * will traverse the entire content of B before traversing the entire content of A, * regardles of what traversal strategy it is using. * <p> * Views that do not have specified before/after relationships are traversed in order * determined by the screen-reader. * </p> * <p> * Setting that this view is before a view that is not important for accessibility * or if this view is not important for accessibility will have no effect as the * screen-reader is not aware of unimportant views. * </p> * * @param beforeId The id of a view this one precedes in accessibility traversal. * * @attr ref android.R.styleable#View_accessibilityTraversalBefore * * @see #setImportantForAccessibility(int) */ @RemotableViewMethod publicvoid setAccessibilityTraversalBefore(@IdResint beforeId){ if(mAccessibilityTraversalBeforeId == beforeId){ return; } mAccessibilityTraversalBeforeId = beforeId; notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** * Gets the id of a view before which this one is visited in accessibility traversal. * * @return The id of a view this one precedes in accessibility traversal if * specified, otherwise {@link #NO_ID}. * * @see #setAccessibilityTraversalBefore(int) */ @IdRes @InspectableProperty publicint getAccessibilityTraversalBefore(){ return mAccessibilityTraversalBeforeId; } /** * Sets the id of a view after which this one is visited in accessibility traversal. * A screen-reader must visit the content of the other view before the content of this * one. For example, if view B is set to be after view A, then a screen-reader * will traverse the entire content of A before traversing the entire content of B, * regardles of what traversal strategy it is using. * <p> * Views that do not have specified before/after relationships are traversed in order * determined by the screen-reader. * </p> * <p> * Setting that this view is after a view that is not important for accessibility * or if this view is not important for accessibility will have no effect as the * screen-reader is not aware of unimportant views. * </p> * * @param afterId The id of a view this one succedees in accessibility traversal. * * @attr ref android.R.styleable#View_accessibilityTraversalAfter * * @see #setImportantForAccessibility(int) */ @RemotableViewMethod publicvoid setAccessibilityTraversalAfter(@IdResint afterId){ if(mAccessibilityTraversalAfterId == afterId){ return; } mAccessibilityTraversalAfterId = afterId; notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** * Gets the id of a view after which this one is visited in accessibility traversal. * * @return The id of a view this one succeedes in accessibility traversal if * specified, otherwise {@link #NO_ID}. * * @see #setAccessibilityTraversalAfter(int) */ @IdRes @InspectableProperty publicint getAccessibilityTraversalAfter(){ return mAccessibilityTraversalAfterId; } /** * Gets the id of a view for which this view serves as a label for * accessibility purposes. * * @return The labeled view id. */ @IdRes @ViewDebug.ExportedProperty(category ="accessibility") @InspectableProperty publicint getLabelFor(){ return mLabelForId; } /** * Sets the id of a view for which this view serves as a label for * accessibility purposes. * * @param id The labeled view id. */ @RemotableViewMethod publicvoid setLabelFor(@IdResint id){ if(mLabelForId == id){ return; } mLabelForId = id; if(mLabelForId !=View.NO_ID && mID ==View.NO_ID){ mID = generateViewId(); } notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** * Invoked whenever this view loses focus, either by losing window focus or by losing * focus within its window. This method can be used to clear any state tied to the * focus. For instance, if a button is held pressed with the trackball and the window * loses focus, this method can be used to cancel the press. * * Subclasses of View overriding this method should always call super.onFocusLost(). * * @see #onFocusChanged(boolean, int, android.graphics.Rect) * @see #onWindowFocusChanged(boolean) * * @hide pending API council approval */ @CallSuper @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedvoid onFocusLost(){ resetPressedState(); } privatevoid resetPressedState(){ if((mViewFlags & ENABLED_MASK)== DISABLED){ return; } if(isPressed()){ setPressed(false); if(!mHasPerformedLongPress){ removeLongPressCallback(); } } } /** * Returns true if this view has focus * * @return True if this view has focus, false otherwise. */ @ViewDebug.ExportedProperty(category ="focus") @InspectableProperty(hasAttributeId =false) publicboolean isFocused(){ return(mPrivateFlags & PFLAG_FOCUSED)!=0; } /** * Find the view in the hierarchy rooted at this view that currently has * focus. * * @return The view that currently has focus, or null if no focused view can * be found. */ publicView findFocus(){ return(mPrivateFlags & PFLAG_FOCUSED)!=0?this:null; } /** * Indicates whether this view is one of the set of scrollable containers in * its window. * * @return whether this view is one of the set of scrollable containers in * its window * * @attr ref android.R.styleable#View_isScrollContainer */ @InspectableProperty(name ="isScrollContainer") publicboolean isScrollContainer(){ return(mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED)!=0; } /** * Change whether this view is one of the set of scrollable containers in * its window. This will be used to determine whether the window can * resize or must pan when a soft input area is open -- scrollable * containers allow the window to use resize mode since the container * will appropriately shrink. * * @attr ref android.R.styleable#View_isScrollContainer */ publicvoid setScrollContainer(boolean isScrollContainer){ if(isScrollContainer){ if(mAttachInfo !=null&&(mPrivateFlags&PFLAG_SCROLL_CONTAINER_ADDED)==0){ mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED; } mPrivateFlags |= PFLAG_SCROLL_CONTAINER; }else{ if((mPrivateFlags&PFLAG_SCROLL_CONTAINER_ADDED)!=0){ mAttachInfo.mScrollContainers.remove(this); } mPrivateFlags &=~(PFLAG_SCROLL_CONTAINER|PFLAG_SCROLL_CONTAINER_ADDED); } } /** * Returns the quality of the drawing cache. * * @return One of {@link #DRAWING_CACHE_QUALITY_AUTO}, * {@link #DRAWING_CACHE_QUALITY_LOW}, or {@link #DRAWING_CACHE_QUALITY_HIGH} * * @see #setDrawingCacheQuality(int) * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() * * @attr ref android.R.styleable#View_drawingCacheQuality * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated @DrawingCacheQuality @InspectableProperty(enumMapping ={ @EnumEntry(value = DRAWING_CACHE_QUALITY_LOW, name ="low"), @EnumEntry(value = DRAWING_CACHE_QUALITY_HIGH, name ="high"), @EnumEntry(value = DRAWING_CACHE_QUALITY_AUTO, name ="auto") }) publicint getDrawingCacheQuality(){ return mViewFlags & DRAWING_CACHE_QUALITY_MASK; } /** * Set the drawing cache quality of this view. This value is used only when the * drawing cache is enabled * * @param quality One of {@link #DRAWING_CACHE_QUALITY_AUTO}, * {@link #DRAWING_CACHE_QUALITY_LOW}, or {@link #DRAWING_CACHE_QUALITY_HIGH} * * @see #getDrawingCacheQuality() * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() * * @attr ref android.R.styleable#View_drawingCacheQuality * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid setDrawingCacheQuality(@DrawingCacheQualityint quality){ setFlags(quality, DRAWING_CACHE_QUALITY_MASK); } /** * Returns whether the screen should remain on, corresponding to the current * value of {@link #KEEP_SCREEN_ON}. * * @return Returns true if {@link #KEEP_SCREEN_ON} is set. * * @see #setKeepScreenOn(boolean) * * @attr ref android.R.styleable#View_keepScreenOn */ @InspectableProperty publicboolean getKeepScreenOn(){ return(mViewFlags & KEEP_SCREEN_ON)!=0; } /** * Controls whether the screen should remain on, modifying the * value of {@link #KEEP_SCREEN_ON}. * * @param keepScreenOn Supply true to set {@link #KEEP_SCREEN_ON}. * * @see #getKeepScreenOn() * * @attr ref android.R.styleable#View_keepScreenOn */ publicvoid setKeepScreenOn(boolean keepScreenOn){ setFlags(keepScreenOn ? KEEP_SCREEN_ON :0, KEEP_SCREEN_ON); } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_LEFT}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusLeft */ @IdRes @InspectableProperty(name ="nextFocusLeft") publicint getNextFocusLeftId(){ return mNextFocusLeftId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_LEFT}. * @param nextFocusLeftId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusLeft */ publicvoid setNextFocusLeftId(@IdResint nextFocusLeftId){ mNextFocusLeftId = nextFocusLeftId; } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusRight */ @IdRes @InspectableProperty(name ="nextFocusRight") publicint getNextFocusRightId(){ return mNextFocusRightId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}. * @param nextFocusRightId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusRight */ publicvoid setNextFocusRightId(@IdResint nextFocusRightId){ mNextFocusRightId = nextFocusRightId; } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_UP}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusUp */ @IdRes @InspectableProperty(name ="nextFocusUp") publicint getNextFocusUpId(){ return mNextFocusUpId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_UP}. * @param nextFocusUpId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusUp */ publicvoid setNextFocusUpId(@IdResint nextFocusUpId){ mNextFocusUpId = nextFocusUpId; } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_DOWN}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusDown */ @IdRes @InspectableProperty(name ="nextFocusDown") publicint getNextFocusDownId(){ return mNextFocusDownId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_DOWN}. * @param nextFocusDownId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusDown */ publicvoid setNextFocusDownId(@IdResint nextFocusDownId){ mNextFocusDownId = nextFocusDownId; } /** * Gets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}. * @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically. * * @attr ref android.R.styleable#View_nextFocusForward */ @IdRes @InspectableProperty(name ="nextFocusForward") publicint getNextFocusForwardId(){ return mNextFocusForwardId; } /** * Sets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}. * @param nextFocusForwardId The next focus ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextFocusForward */ publicvoid setNextFocusForwardId(@IdResint nextFocusForwardId){ mNextFocusForwardId = nextFocusForwardId; } /** * Gets the id of the root of the next keyboard navigation cluster. * @return The next keyboard navigation cluster ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextClusterForward */ @IdRes @InspectableProperty(name ="nextClusterForward") publicint getNextClusterForwardId(){ return mNextClusterForwardId; } /** * Sets the id of the view to use as the root of the next keyboard navigation cluster. * @param nextClusterForwardId The next cluster ID, or {@link #NO_ID} if the framework should * decide automatically. * * @attr ref android.R.styleable#View_nextClusterForward */ publicvoid setNextClusterForwardId(@IdResint nextClusterForwardId){ mNextClusterForwardId = nextClusterForwardId; } /** * Returns the visibility of this view and all of its ancestors * * @return True if this view and all of its ancestors are {@link #VISIBLE} */ publicboolean isShown(){ View current =this; //noinspection ConstantConditions do{ if((current.mViewFlags & VISIBILITY_MASK)!= VISIBLE){ returnfalse; } ViewParent parent = current.mParent; if(parent ==null){ returnfalse;// We are not attached to the view root } if(!(parent instanceofView)){ returntrue; } current =(View) parent; }while(current !=null); returnfalse; } privateboolean detached(){ View current =this; //noinspection ConstantConditions do{ if((current.mPrivateFlags4 & PFLAG4_DETACHED)!=0){ returntrue; } ViewParent parent = current.mParent; if(parent ==null){ returnfalse; } if(!(parent instanceofView)){ returnfalse; } current =(View) parent; }while(current !=null); returnfalse; } /** * Called by the view hierarchy when the content insets for a window have * changed, to allow it to adjust its content to fit within those windows. * The content insets tell you the space that the status bar, input method, * and other system windows infringe on the application's window. * * <p>You do not normally need to deal with this function, since the default * window decoration given to applications takes care of applying it to the * content of the window. If you use {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} * or {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} this will not be the case, * and your content can be placed under those system elements. You can then * use this method within your view hierarchy if you have parts of your UI * which you would like to ensure are not being covered. * * <p>The default implementation of this method simply applies the content * insets to the view's padding, consuming that content (modifying the * insets to be 0), and returning true. This behavior is off by default, but can * be enabled through {@link #setFitsSystemWindows(boolean)}. * * <p>This function's traversal down the hierarchy is depth-first. The same content * insets object is propagated down the hierarchy, so any changes made to it will * be seen by all following views (including potentially ones above in * the hierarchy since this is a depth-first traversal). The first view * that returns true will abort the entire traversal. * * <p>The default implementation works well for a situation where it is * used with a container that covers the entire window, allowing it to * apply the appropriate insets to its content on all edges. If you need * a more complicated layout (such as two different views fitting system * windows, one on the top of the window, and one on the bottom), * you can override the method and handle the insets however you would like. * Note that the insets provided by the framework are always relative to the * far edges of the window, not accounting for the location of the called view * within that window. (In fact when this method is called you do not yet know * where the layout will place the view, as it is done before layout happens.) * * <p>Note: unlike many View methods, there is no dispatch phase to this * call. If you are overriding it in a ViewGroup and want to allow the * call to continue to your children, you must be sure to call the super * implementation. * * <p>Here is a sample layout that makes use of fitting system windows * to have controls for a video view placed inside of the window decorations * that it hides and shows. This can be used with code like the second * sample (video player) shown in {@link #setSystemUiVisibility(int)}. * * {@sample development/samples/ApiDemos/res/layout/video_player.xml complete} * * @param insets Current content insets of the window. Prior to * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify * the insets or else you and Android will be unhappy. * * @return {@code true} if this view applied the insets and it should not * continue propagating further down the hierarchy, {@code false} otherwise. * @see #getFitsSystemWindows() * @see #setFitsSystemWindows(boolean) * @see #setSystemUiVisibility(int) * * @deprecated As of API 20 use {@link #dispatchApplyWindowInsets(WindowInsets)} to apply * insets to views. Views should override {@link #onApplyWindowInsets(WindowInsets)} or use * {@link #setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener)} * to implement handling their own insets. */ @Deprecated protectedboolean fitSystemWindows(Rect insets){ if((mPrivateFlags3 & PFLAG3_APPLYING_INSETS)==0){ if(insets ==null){ // Null insets by definition have already been consumed. // This call cannot apply insets since there are none to apply, // so return false. returnfalse; } // If we're not in the process of dispatching the newer apply insets call, // that means we're not in the compatibility path. Dispatch into the newer // apply insets path and take things from there. try{ mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS; return dispatchApplyWindowInsets(newWindowInsets(insets)).isConsumed(); }finally{ mPrivateFlags3 &=~PFLAG3_FITTING_SYSTEM_WINDOWS; } }else{ // We're being called from the newer apply insets path. // Perform the standard fallback behavior. return fitSystemWindowsInt(insets); } } privateboolean fitSystemWindowsInt(Rect insets){ if((mViewFlags & FITS_SYSTEM_WINDOWS)== FITS_SYSTEM_WINDOWS){ Rect localInsets = sThreadLocal.get(); boolean res = computeFitSystemWindows(insets, localInsets); applyInsets(localInsets); return res; } returnfalse; } privatevoid applyInsets(Rect insets){ mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; mUserPaddingLeftInitial = insets.left; mUserPaddingRightInitial = insets.right; internalSetPadding(insets.left, insets.top, insets.right, insets.bottom); } /** * Called when the view should apply {@link WindowInsets} according to its internal policy. * * <p>This method should be overridden by views that wish to apply a policy different from or * in addition to the default behavior. Clients that wish to force a view subtree * to apply insets should call {@link #dispatchApplyWindowInsets(WindowInsets)}.</p> * * <p>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.</p> * * <p>Implementations of this method should either return the insets parameter unchanged * or a new {@link WindowInsets} cloned from the supplied insets with any insets consumed * that this view applied itself. This allows new inset types added in future platform * versions to pass through existing implementations unchanged without being erroneously * consumed.</p> * * <p>By default if a view's {@link #setFitsSystemWindows(boolean) fitsSystemWindows} * property is set then the view will consume the system window insets and apply them * as padding for the view.</p> * * @param insets Insets to apply * @return The supplied insets with any applied insets consumed */ publicWindowInsets onApplyWindowInsets(WindowInsets insets){ if((mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS)!=0 &&(mViewFlags & FITS_SYSTEM_WINDOWS)!=0){ return onApplyFrameworkOptionalFitSystemWindows(insets); } if((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS)==0){ // We weren't called from within a direct call to fitSystemWindows, // call into it as a fallback in case we're in a class that overrides it // and has logic to perform. if(fitSystemWindows(insets.getSystemWindowInsetsAsRect())){ return insets.consumeSystemWindowInsets(); } }else{ // We were called from within a direct call to fitSystemWindows. if(fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())){ return insets.consumeSystemWindowInsets(); } } return insets; } privateWindowInsets onApplyFrameworkOptionalFitSystemWindows(WindowInsets insets){ Rect localInsets = sThreadLocal.get(); WindowInsets result = computeSystemWindowInsets(insets, localInsets); applyInsets(localInsets); return result; } /** * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying * window insets to this view. The listener's * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsets) onApplyWindowInsets} * method will be called instead of the view's * {@link #onApplyWindowInsets(WindowInsets) onApplyWindowInsets} method. * * @param listener Listener to set * * @see #onApplyWindowInsets(WindowInsets) */ publicvoid setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener){ getListenerInfo().mOnApplyWindowInsetsListener = listener; } /** * Request to apply the given window insets to this view or another view in its subtree. * * <p>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.</p> * * <p>Clients wishing to provide custom behavior should override the * {@link #onApplyWindowInsets(WindowInsets)} method or alternatively provide a * {@link OnApplyWindowInsetsListener} via the * {@link #setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener) setOnApplyWindowInsetsListener} * method.</p> * * <p>This method replaces the older {@link #fitSystemWindows(Rect) fitSystemWindows} method. * </p> * * @param insets Insets to apply * @return The provided insets minus the insets that were consumed */ publicWindowInsets dispatchApplyWindowInsets(WindowInsets insets){ try{ mPrivateFlags3 |= PFLAG3_APPLYING_INSETS; if(mListenerInfo !=null&& mListenerInfo.mOnApplyWindowInsetsListener !=null){ return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); }else{ return onApplyWindowInsets(insets); } }finally{ mPrivateFlags3 &=~PFLAG3_APPLYING_INSETS; } } /** * Sets a {@link WindowInsetsAnimation.Callback} to be notified about animations of windows that * cause insets. * <p> * The callback's {@link WindowInsetsAnimation.Callback#getDispatchMode() * dispatch mode} will affect whether animation callbacks are dispatched to the children of * this view. * </p> * @param callback The callback to set. */ publicvoid setWindowInsetsAnimationCallback( @NullableWindowInsetsAnimation.Callback callback){ getListenerInfo().mWindowInsetsAnimationCallback = callback; } /** * @return {@code true} if any {@link WindowInsetsAnimation.Callback} is registered on the view * or view tree of the sub-hierarchy {@code false} otherwise. * @hide */ publicboolean hasWindowInsetsAnimationCallback(){ return getListenerInfo().mWindowInsetsAnimationCallback !=null; } /** * Dispatches {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)} * when Window Insets animation is being prepared. * @param animation current animation * * @see WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation) */ publicvoid dispatchWindowInsetsAnimationPrepare( @NonNullWindowInsetsAnimation animation){ if(mListenerInfo !=null&& mListenerInfo.mWindowInsetsAnimationCallback !=null){ mListenerInfo.mWindowInsetsAnimationCallback.onPrepare(animation); } } /** * Dispatches {@link WindowInsetsAnimation.Callback#onStart(WindowInsetsAnimation, Bounds)} * when Window Insets animation is started. * @param animation current animation * @param bounds the upper and lower {@link Bounds} that provides range of * {@link WindowInsetsAnimation}. * @return the upper and lower {@link Bounds}. */ @NonNull publicBounds dispatchWindowInsetsAnimationStart( @NonNullWindowInsetsAnimation animation,@NonNullBounds bounds){ if(mListenerInfo !=null&& mListenerInfo.mWindowInsetsAnimationCallback !=null){ return mListenerInfo.mWindowInsetsAnimationCallback.onStart(animation, bounds); } return bounds; } /** * Dispatches {@link WindowInsetsAnimation.Callback#onProgress(WindowInsets, List)} * when Window Insets animation makes progress. * @param insets The current {@link WindowInsets}. * @param runningAnimations The currently running {@link WindowInsetsAnimation}s. * @return current {@link WindowInsets}. */ @NonNull publicWindowInsets dispatchWindowInsetsAnimationProgress(@NonNullWindowInsets insets, @NonNullList<WindowInsetsAnimation> runningAnimations){ if(mListenerInfo !=null&& mListenerInfo.mWindowInsetsAnimationCallback !=null){ return mListenerInfo.mWindowInsetsAnimationCallback.onProgress(insets, runningAnimations); }else{ return insets; } } /** * Dispatches {@link WindowInsetsAnimation.Callback#onEnd(WindowInsetsAnimation)} * when Window Insets animation ends. * @param animation The current ongoing {@link WindowInsetsAnimation}. */ publicvoid dispatchWindowInsetsAnimationEnd(@NonNullWindowInsetsAnimation animation){ if(mListenerInfo !=null&& mListenerInfo.mWindowInsetsAnimationCallback !=null){ mListenerInfo.mWindowInsetsAnimationCallback.onEnd(animation); } } /** * Sets a list of areas within this view's post-layout coordinate space where the system * should not intercept touch or other pointing device gestures. <em>This method should * be called by {@link #onLayout(boolean, int, int, int, int)} or {@link #onDraw(Canvas)}.</em> * * <p>Use this to tell the system which specific sub-areas of a view need to receive gesture * input in order to function correctly in the presence of global system gestures that may * conflict. For example, if the system wishes to capture swipe-in-from-screen-edge gestures * to provide system-level navigation functionality, a view such as a navigation drawer * container can mark the left (or starting) edge of itself as requiring gesture capture * priority using this API. The system may then choose to relax its own gesture recognition * to allow the app to consume the user's gesture. It is not necessary for an app to register * exclusion rects for broadly spanning regions such as the entirety of a * <code>ScrollView</code> or for simple press and release click targets such as * <code>Button</code>. Mark an exclusion rect when interacting with a view requires * a precision touch gesture in a small area in either the X or Y dimension, such as * an edge swipe or dragging a <code>SeekBar</code> thumb.</p> * * <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the * exclusions it takes into account. The limit does not apply while the navigation * bar is {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the * {@link android.inputmethodservice.InputMethodService input method} and * {@link Intent#CATEGORY_HOME home activity}. * </p> * * @param rects A list of precision gesture regions that this view needs to function correctly */ publicvoid setSystemGestureExclusionRects(@NonNullList<Rect> rects){ if(rects.isEmpty()&& mListenerInfo ==null)return; finalListenerInfo info = getListenerInfo(); if(info.mSystemGestureExclusionRects !=null){ info.mSystemGestureExclusionRects.clear(); info.mSystemGestureExclusionRects.addAll(rects); }else{ info.mSystemGestureExclusionRects =newArrayList<>(rects); } updatePositionUpdateListener(); postUpdate(this::updateSystemGestureExclusionRects); } privatevoid updatePositionUpdateListener(){ finalListenerInfo info = getListenerInfo(); if(getSystemGestureExclusionRects().isEmpty() && collectPreferKeepClearRects().isEmpty() && collectUnrestrictedPreferKeepClearRects().isEmpty() &&(info.mHandwritingArea ==null||!isAutoHandwritingEnabled())){ if(info.mPositionUpdateListener !=null){ mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener); info.mPositionUpdateListener =null; info.mPositionChangedUpdate =null; } }else{ if(info.mPositionUpdateListener ==null){ info.mPositionChangedUpdate =()->{ updateSystemGestureExclusionRects(); updateKeepClearRects(); updateHandwritingArea(); }; info.mPositionUpdateListener =newRenderNode.PositionUpdateListener(){ @Override publicvoid positionChanged(long n,int l,int t,int r,int b){ postUpdate(info.mPositionChangedUpdate); } @Override publicvoid positionLost(long frameNumber){ postUpdate(info.mPositionChangedUpdate); } }; mRenderNode.addPositionUpdateListener(info.mPositionUpdateListener); } } } /** * WARNING: this can be called by a hwui worker thread, not just the UI thread! */ privatevoid postUpdate(Runnable r){ // Potentially racey from a background thread. It's ok if it's not perfect. finalHandler h = getHandler(); if(h !=null){ h.postAtFrontOfQueue(r); } } void updateSystemGestureExclusionRects(){ finalAttachInfo ai = mAttachInfo; if(ai !=null){ ai.mViewRootImpl.updateSystemGestureExclusionRectsForView(this); } } /** * Retrieve the list of areas within this view's post-layout coordinate space where the system * should not intercept touch or other pointing device gestures. * * <p>Do not modify the returned list.</p> * * @return the list set by {@link #setSystemGestureExclusionRects(List)} */ @NonNull publicList<Rect> getSystemGestureExclusionRects(){ finalListenerInfo info = mListenerInfo; if(info !=null){ finalList<Rect> list = info.mSystemGestureExclusionRects; if(list !=null){ return list; } } returnCollections.emptyList(); } /** * Set a preference to keep the bounds of this view clear from floating windows above this * view's window. This informs the system that the view is considered a vital area for the * user and that ideally it should not be covered. Setting this is only appropriate for UI * where the user would likely take action to uncover it. * <p> * The system will try to respect this preference, but when not possible will ignore it. * <p> * Note: This is independent from {@link #setPreferKeepClearRects}. If both are set, both will * be taken into account. * <p> * @see #setPreferKeepClearRects * @see #isPreferKeepClear * @attr ref android.R.styleable#View_preferKeepClear */ publicfinalvoid setPreferKeepClear(boolean preferKeepClear){ getListenerInfo().mPreferKeepClear = preferKeepClear; updatePositionUpdateListener(); postUpdate(this::updateKeepClearRects); } /** * Retrieve the preference for this view to be kept clear. This is set either by * {@link #setPreferKeepClear} or via the attribute android.R.styleable#View_preferKeepClear. * <p> * If this is {@code true}, the system will ignore the Rects set by * {@link #setPreferKeepClearRects} and try to keep the whole view clear. * <p> * @see #setPreferKeepClear * @attr ref android.R.styleable#View_preferKeepClear */ publicfinalboolean isPreferKeepClear(){ return mListenerInfo !=null&& mListenerInfo.mPreferKeepClear; } /** * Set a preference to keep the provided rects clear from floating windows above this * view's window. This informs the system that these rects are considered vital areas for the * user and that ideally they should not be covered. Setting this is only appropriate for UI * where the user would likely take action to uncover it. * <p> * The system will try to respect this preference, but when not possible will ignore it. * <p> * Note: This is independent from {@link #setPreferKeepClear}. If both are set, both will be * taken into account. * <p> * @see #setPreferKeepClear * @see #getPreferKeepClearRects * * @param rects A list of rects in this view's local coordinate system */ publicfinalvoid setPreferKeepClearRects(@NonNullList<Rect> rects){ finalListenerInfo info = getListenerInfo(); if(info.mKeepClearRects !=null){ info.mKeepClearRects.clear(); info.mKeepClearRects.addAll(rects); }else{ info.mKeepClearRects =newArrayList<>(rects); } updatePositionUpdateListener(); postUpdate(this::updateKeepClearRects); } /** * @return the list of rects, set by {@link #setPreferKeepClearRects}. * * @see #setPreferKeepClearRects */ @NonNull publicfinalList<Rect> getPreferKeepClearRects(){ finalListenerInfo info = mListenerInfo; if(info !=null&& info.mKeepClearRects !=null){ returnnewArrayList(info.mKeepClearRects); } returnCollections.emptyList(); } /** * Set a preference to keep the provided rects clear from floating windows above this * view's window. This informs the system that these rects are considered vital areas for the * user and that ideally they should not be covered. Setting this is only appropriate for UI * where the user would likely take action to uncover it. * <p> * Note: The difference with {@link #setPreferKeepClearRects} is that the system won't apply * restrictions to the rects set here. * <p> * @see #setPreferKeepClear * @see #getPreferKeepClearRects * * @param rects A list of rects in this view's local coordinate system * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS) publicfinalvoid setUnrestrictedPreferKeepClearRects(@NonNullList<Rect> rects){ finalListenerInfo info = getListenerInfo(); if(info.mUnrestrictedKeepClearRects !=null){ info.mUnrestrictedKeepClearRects.clear(); info.mUnrestrictedKeepClearRects.addAll(rects); }else{ info.mUnrestrictedKeepClearRects =newArrayList<>(rects); } updatePositionUpdateListener(); postUpdate(this::updateKeepClearRects); } /** * @return the list of rects, set by {@link #setPreferKeepClearRects}. * * @see #setPreferKeepClearRects * * @hide */ @SystemApi @NonNull publicfinalList<Rect> getUnrestrictedPreferKeepClearRects(){ finalListenerInfo info = mListenerInfo; if(info !=null&& info.mUnrestrictedKeepClearRects !=null){ returnnewArrayList(info.mUnrestrictedKeepClearRects); } returnCollections.emptyList(); } void updateKeepClearRects(){ finalAttachInfo ai = mAttachInfo; if(ai !=null){ ai.mViewRootImpl.updateKeepClearRectsForView(this); } } /** * Retrieve the list of areas within this view's post-layout coordinate space which the * system will try to not cover with other floating elements, like the pip window. */ @NonNull List<Rect> collectPreferKeepClearRects(){ ListenerInfo info = mListenerInfo; boolean keepClearForFocus = isFocused() &&ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled(); boolean keepBoundsClear =(info !=null&& info.mPreferKeepClear)|| keepClearForFocus; boolean hasCustomKeepClearRects = info !=null&& info.mKeepClearRects !=null; if(!keepBoundsClear &&!hasCustomKeepClearRects){ returnCollections.emptyList(); }elseif(keepBoundsClear &&!hasCustomKeepClearRects){ returnCollections.singletonList(newRect(0,0, getWidth(), getHeight())); } finalList<Rect> list =newArrayList<>(); if(keepBoundsClear){ list.add(newRect(0,0, getWidth(), getHeight())); } if(hasCustomKeepClearRects){ list.addAll(info.mKeepClearRects); } return list; } privatevoid updatePreferKeepClearForFocus(){ if(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled()){ updatePositionUpdateListener(); post(this::updateKeepClearRects); } } /** * Retrieve the list of unrestricted areas within this view's post-layout coordinate space * which the system will try to not cover with other floating elements, like the pip window. */ @NonNull List<Rect> collectUnrestrictedPreferKeepClearRects(){ finalListenerInfo info = mListenerInfo; if(info !=null&& info.mUnrestrictedKeepClearRects !=null){ return info.mUnrestrictedKeepClearRects; } returnCollections.emptyList(); } /** * Set a list of handwriting areas in this view. If there is any stylus {@link MotionEvent} * occurs within those areas, it will trigger stylus handwriting mode. This can be disabled by * disabling the auto handwriting initiation by calling * {@link #setAutoHandwritingEnabled(boolean)} with false. * * @attr rects a list of handwriting area in the view's local coordiniates. * * @see android.view.inputmethod.InputMethodManager#startStylusHandwriting(View) * @see #setAutoHandwritingEnabled(boolean) * * @hide */ publicvoid setHandwritingArea(@NullableRect rect){ finalListenerInfo info = getListenerInfo(); info.mHandwritingArea = rect; updatePositionUpdateListener(); postUpdate(this::updateHandwritingArea); } /** * Return the handwriting areas set on this view, in its local coordinates. * @see #setHandwritingArea(Rect) * * @hide */ @Nullable publicRect getHandwritingArea(){ finalListenerInfo info = mListenerInfo; if(info !=null&& info.mHandwritingArea !=null){ returnnewRect(info.mHandwritingArea); } returnnull; } void updateHandwritingArea(){ // If autoHandwritingArea is not enabled, do nothing. if(!isAutoHandwritingEnabled())return; finalAttachInfo ai = mAttachInfo; if(ai !=null){ ai.mViewRootImpl.getHandwritingInitiator().updateHandwritingAreasForView(this); } } /** * Gets the coordinates of this view in the coordinate space of the * {@link Surface} that contains the view. * * <p>In multiple-screen scenarios, if the surface spans multiple screens, * the coordinate space of the surface also spans multiple screens. * * <p>After the method returns, the argument array contains the x and y * coordinates of the view relative to the view's left and top edges, * respectively. * * @param location A two-element integer array in which the view coordinates * are stored. The x-coordinate is at index 0; the y-coordinate, at * index 1. */ publicvoid getLocationInSurface(@NonNull@Size(2)int[] location){ getLocationInWindow(location); if(mAttachInfo !=null&& mAttachInfo.mViewRootImpl !=null){ location[0]+= mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left; location[1]+= mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.top; } } /** * Provide original WindowInsets that are dispatched to the view hierarchy. The insets are * only available if the view is attached. * * @return WindowInsets from the top of the view hierarchy or null if View is detached */ publicWindowInsets getRootWindowInsets(){ if(mAttachInfo !=null){ return mAttachInfo.mViewRootImpl.getWindowInsets(false/* forceConstruct */); } returnnull; } /** * Retrieves the single {@link WindowInsetsController} of the window this view is attached to. * * @return The {@link WindowInsetsController} or {@code null} if the view is neither attached to * a window nor a view tree with a decor. * @see Window#getInsetsController() */ public@NullableWindowInsetsController getWindowInsetsController(){ if(mAttachInfo !=null){ return mAttachInfo.mViewRootImpl.getInsetsController(); } ViewParent parent = getParent(); if(parent instanceofView){ return((View) parent).getWindowInsetsController(); }elseif(parent instanceofViewRootImpl){ // Between WindowManager.addView() and the first traversal AttachInfo isn't set yet. return((ViewRootImpl) parent).getInsetsController(); } returnnull; } /** * Walk up the View hierarchy to find the nearest {@link OnBackInvokedDispatcher}. * * @return The {@link OnBackInvokedDispatcher} from this or the nearest * ancestor, or null if this view is both not attached and have no ancestor providing an * {@link OnBackInvokedDispatcher}. */ @Nullable publicfinalOnBackInvokedDispatcher findOnBackInvokedDispatcher(){ ViewParent parent = getParent(); if(parent !=null){ return parent.findOnBackInvokedDispatcherForChild(this,this); } returnnull; } /** * @hide Compute the insets that should be consumed by this view and the ones * that should propagate to those under it. * * Note: This is used by appcompat's ActionBarOverlayLayout through reflection. * * @param inoutInsets the insets given to this view * @param outLocalInsets the insets that should be applied to this view * @deprecated use {@link #computeSystemWindowInsets} * @return */ @Deprecated @UnsupportedAppUsage protectedboolean computeFitSystemWindows(Rect inoutInsets,Rect outLocalInsets){ WindowInsets innerInsets = computeSystemWindowInsets(newWindowInsets(inoutInsets), outLocalInsets); inoutInsets.set(innerInsets.getSystemWindowInsetsAsRect()); return innerInsets.isSystemWindowInsetsConsumed(); } /** * Compute insets that should be consumed by this view and the ones that should propagate * to those under it. * * @param in Insets currently being processed by this View, likely received as a parameter * to {@link #onApplyWindowInsets(WindowInsets)}. * @param outLocalInsets A Rect that will receive the insets that should be consumed * by this view * @return Insets that should be passed along to views under this one */ publicWindowInsets computeSystemWindowInsets(WindowInsets in,Rect outLocalInsets){ boolean isOptionalFitSystemWindows =(mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS)!=0 ||(mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS)!=0; if(isOptionalFitSystemWindows && mAttachInfo !=null){ OnContentApplyWindowInsetsListener listener = mAttachInfo.mContentOnApplyWindowInsetsListener; if(listener ==null){ // The application wants to take care of fitting system window for // the content. outLocalInsets.setEmpty(); return in; } Pair<Insets,WindowInsets> result = listener.onContentApplyWindowInsets(this, in); outLocalInsets.set(result.first.toRect()); return result.second; }else{ outLocalInsets.set(in.getSystemWindowInsetsAsRect()); return in.consumeSystemWindowInsets().inset(outLocalInsets); } } /** * Sets whether or not this view should account for system screen decorations * such as the status bar and inset its content; that is, controlling whether * the default implementation of {@link #fitSystemWindows(Rect)} will be * executed. See that method for more details. * * <p>Note that if you are providing your own implementation of * {@link #fitSystemWindows(Rect)}, then there is no need to set this * flag to true -- your implementation will be overriding the default * implementation that checks this flag. * * @param fitSystemWindows If true, then the default implementation of * {@link #fitSystemWindows(Rect)} will be executed. * * @attr ref android.R.styleable#View_fitsSystemWindows * @see #getFitsSystemWindows() * @see #fitSystemWindows(Rect) * @see #setSystemUiVisibility(int) */ publicvoid setFitsSystemWindows(boolean fitSystemWindows){ setFlags(fitSystemWindows ? FITS_SYSTEM_WINDOWS :0, FITS_SYSTEM_WINDOWS); } /** * Check for state of {@link #setFitsSystemWindows(boolean)}. If this method * returns {@code true}, the default implementation of {@link #fitSystemWindows(Rect)} * will be executed. * * @return {@code true} if the default implementation of * {@link #fitSystemWindows(Rect)} will be executed. * * @attr ref android.R.styleable#View_fitsSystemWindows * @see #setFitsSystemWindows(boolean) * @see #fitSystemWindows(Rect) * @see #setSystemUiVisibility(int) */ @ViewDebug.ExportedProperty @InspectableProperty publicboolean getFitsSystemWindows(){ return(mViewFlags & FITS_SYSTEM_WINDOWS)== FITS_SYSTEM_WINDOWS; } /** @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicboolean fitsSystemWindows(){ return getFitsSystemWindows(); } /** * Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed. * @deprecated Use {@link #requestApplyInsets()} for newer platform versions. */ @Deprecated publicvoid requestFitSystemWindows(){ if(mParent !=null){ mParent.requestFitSystemWindows(); } } /** * Ask that a new dispatch of {@link #onApplyWindowInsets(WindowInsets)} be performed. */ publicvoid requestApplyInsets(){ requestFitSystemWindows(); } /** * @see #OPTIONAL_FITS_SYSTEM_WINDOWS * @hide */ @UnsupportedAppUsage publicvoid makeOptionalFitsSystemWindows(){ setFlags(OPTIONAL_FITS_SYSTEM_WINDOWS, OPTIONAL_FITS_SYSTEM_WINDOWS); } /** * @see #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS * @hide */ publicvoid makeFrameworkOptionalFitsSystemWindows(){ mPrivateFlags4 |= PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS; } /** * @hide */ publicboolean isFrameworkOptionalFitsSystemWindows(){ return(mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS)!=0; } /** * Returns the visibility status for this view. * * @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. * @attr ref android.R.styleable#View_visibility */ @ViewDebug.ExportedProperty(mapping ={ @ViewDebug.IntToString(from = VISIBLE, to ="VISIBLE"), @ViewDebug.IntToString(from = INVISIBLE, to ="INVISIBLE"), @ViewDebug.IntToString(from = GONE, to ="GONE") }) @InspectableProperty(enumMapping ={ @EnumEntry(value = VISIBLE, name ="visible"), @EnumEntry(value = INVISIBLE, name ="invisible"), @EnumEntry(value = GONE, name ="gone") }) @Visibility publicint getVisibility(){ return mViewFlags & VISIBILITY_MASK; } /** * Set the visibility state of this view. * * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. * @attr ref android.R.styleable#View_visibility */ @RemotableViewMethod publicvoid setVisibility(@Visibilityint visibility){ setFlags(visibility, VISIBILITY_MASK); } /** * Returns the enabled status for this view. The interpretation of the * enabled state varies by subclass. * * @return True if this view is enabled, false otherwise. */ @ViewDebug.ExportedProperty @InspectableProperty publicboolean isEnabled(){ return(mViewFlags & ENABLED_MASK)== ENABLED; } /** * Set the enabled state of this view. The interpretation of the enabled * state varies by subclass. * * @param enabled True if this view is enabled, false otherwise. */ @RemotableViewMethod publicvoid setEnabled(boolean enabled){ if(enabled == isEnabled())return; setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK); /* * The View most likely has to change its appearance, so refresh * the drawable state. */ refreshDrawableState(); // Invalidate too, since the default behavior for views is to be // be drawn at 50% alpha rather than to change the drawable. invalidate(true); if(!enabled){ cancelPendingInputEvents(); } } /** * Set whether this view can receive the focus. * <p> * Setting this to false will also ensure that this view is not focusable * in touch mode. * * @param focusable If true, this view can receive the focus. * * @see #setFocusableInTouchMode(boolean) * @see #setFocusable(int) * @attr ref android.R.styleable#View_focusable */ @RemotableViewMethod publicvoid setFocusable(boolean focusable){ setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE); } /** * Sets whether this view can receive focus. * <p> * Setting this to {@link #FOCUSABLE_AUTO} tells the framework to determine focusability * automatically based on the view's interactivity. This is the default. * <p> * Setting this to NOT_FOCUSABLE will ensure that this view is also not focusable * in touch mode. * * @param focusable One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE}, * or {@link #FOCUSABLE_AUTO}. * @see #setFocusableInTouchMode(boolean) * @attr ref android.R.styleable#View_focusable */ @RemotableViewMethod publicvoid setFocusable(@Focusableint focusable){ if((focusable &(FOCUSABLE_AUTO | FOCUSABLE))==0){ setFlags(0, FOCUSABLE_IN_TOUCH_MODE); } setFlags(focusable, FOCUSABLE_MASK); } /** * Set whether this view can receive focus while in touch mode. * * Setting this to true will also ensure that this view is focusable. * * @param focusableInTouchMode If true, this view can receive the focus while * in touch mode. * * @see #setFocusable(boolean) * @attr ref android.R.styleable#View_focusableInTouchMode */ @RemotableViewMethod publicvoid setFocusableInTouchMode(boolean focusableInTouchMode){ // Focusable in touch mode should always be set before the focusable flag // otherwise, setting the focusable flag will trigger a focusableViewAvailable() // which, in touch mode, will not successfully request focus on this view // because the focusable in touch mode flag is not set setFlags(focusableInTouchMode ? FOCUSABLE_IN_TOUCH_MODE :0, FOCUSABLE_IN_TOUCH_MODE); // Clear FOCUSABLE_AUTO if set. if(focusableInTouchMode){ // Clears FOCUSABLE_AUTO if set. setFlags(FOCUSABLE, FOCUSABLE_MASK); } } /** * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how * to autofill the view with the user's data. * * <p>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. * * <p>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 #AUTOFILL_HINT_USERNAME}, {@link #AUTOFILL_HINT_PASSWORD}, * {@link #AUTOFILL_HINT_EMAIL_ADDRESS}, * {@link #AUTOFILL_HINT_NAME}, * {@link #AUTOFILL_HINT_PHONE}, * {@link #AUTOFILL_HINT_POSTAL_ADDRESS}, {@link #AUTOFILL_HINT_POSTAL_CODE}, * {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER}, {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}, * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}, * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}, * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}. * * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set. * @attr ref android.R.styleable#View_autofillHints */ publicvoid setAutofillHints(@NullableString... autofillHints){ if(autofillHints ==null|| autofillHints.length ==0){ mAutofillHints =null; }else{ mAutofillHints = autofillHints; } } /** * @hide */ @TestApi publicvoid setAutofilled(boolean isAutofilled,boolean hideHighlight){ boolean wasChanged = isAutofilled != isAutofilled(); if(wasChanged){ if(isAutofilled){ mPrivateFlags3 |= PFLAG3_IS_AUTOFILLED; }else{ mPrivateFlags3 &=~PFLAG3_IS_AUTOFILLED; } if(hideHighlight){ mPrivateFlags4 |= PFLAG4_AUTOFILL_HIDE_HIGHLIGHT; }else{ mPrivateFlags4 &=~PFLAG4_AUTOFILL_HIDE_HIGHLIGHT; } invalidate(); } } /** * Set whether this view should have sound effects enabled for events such as * clicking and touching. * * <p>You may wish to disable sound effects for a view if you already play sounds, * for instance, a dial key that plays dtmf tones. * * @param soundEffectsEnabled whether sound effects are enabled for this view. * @see #isSoundEffectsEnabled() * @see #playSoundEffect(int) * @attr ref android.R.styleable#View_soundEffectsEnabled */ publicvoid setSoundEffectsEnabled(boolean soundEffectsEnabled){ setFlags(soundEffectsEnabled ? SOUND_EFFECTS_ENABLED:0, SOUND_EFFECTS_ENABLED); } /** * @return whether this view should have sound effects enabled for events such as * clicking and touching. * * @see #setSoundEffectsEnabled(boolean) * @see #playSoundEffect(int) * @attr ref android.R.styleable#View_soundEffectsEnabled */ @ViewDebug.ExportedProperty @InspectableProperty publicboolean isSoundEffectsEnabled(){ return SOUND_EFFECTS_ENABLED ==(mViewFlags & SOUND_EFFECTS_ENABLED); } /** * Set whether this view should have haptic feedback for events such as * long presses. * * <p>You may wish to disable haptic feedback if your view already controls * its own haptic feedback. * * @param hapticFeedbackEnabled whether haptic feedback enabled for this view. * @see #isHapticFeedbackEnabled() * @see #performHapticFeedback(int) * @attr ref android.R.styleable#View_hapticFeedbackEnabled */ publicvoid setHapticFeedbackEnabled(boolean hapticFeedbackEnabled){ setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED:0, HAPTIC_FEEDBACK_ENABLED); } /** * @return whether this view should have haptic feedback enabled for events * such as long presses. * * @see #setHapticFeedbackEnabled(boolean) * @see #performHapticFeedback(int) * @attr ref android.R.styleable#View_hapticFeedbackEnabled */ @ViewDebug.ExportedProperty @InspectableProperty publicboolean isHapticFeedbackEnabled(){ return HAPTIC_FEEDBACK_ENABLED ==(mViewFlags & HAPTIC_FEEDBACK_ENABLED); } /** * Returns the layout direction for this view. * * @return One of {@link #LAYOUT_DIRECTION_LTR}, * {@link #LAYOUT_DIRECTION_RTL}, * {@link #LAYOUT_DIRECTION_INHERIT} or * {@link #LAYOUT_DIRECTION_LOCALE}. * * @attr ref android.R.styleable#View_layoutDirection * * @hide */ @ViewDebug.ExportedProperty(category ="layout", mapping ={ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to ="LTR"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to ="RTL"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to ="INHERIT"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to ="LOCALE") }) @InspectableProperty(hasAttributeId =false, enumMapping ={ @EnumEntry(value = LAYOUT_DIRECTION_LTR, name ="ltr"), @EnumEntry(value = LAYOUT_DIRECTION_RTL, name ="rtl"), @EnumEntry(value = LAYOUT_DIRECTION_INHERIT, name ="inherit"), @EnumEntry(value = LAYOUT_DIRECTION_LOCALE, name ="locale") }) @LayoutDir publicint getRawLayoutDirection(){ return(mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK)>> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; } /** * 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 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}. * * @attr ref android.R.styleable#View_layoutDirection */ @RemotableViewMethod publicvoid setLayoutDirection(@LayoutDirint layoutDirection){ if(getRawLayoutDirection()!= layoutDirection){ // Reset the current layout direction and the resolved one mPrivateFlags2 &=~PFLAG2_LAYOUT_DIRECTION_MASK; resetRtlProperties(); // Set the new layout direction (filtered) mPrivateFlags2 |= ((layoutDirection << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT)& PFLAG2_LAYOUT_DIRECTION_MASK); // We need to resolve all RTL properties as they all depend on layout direction resolveRtlPropertiesIfNeeded(); requestLayout(); invalidate(true); } } /** * Returns the resolved layout direction for this view. * * @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 {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}. * * @attr ref android.R.styleable#View_layoutDirection */ @ViewDebug.ExportedProperty(category ="layout", mapping ={ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to ="RESOLVED_DIRECTION_LTR"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to ="RESOLVED_DIRECTION_RTL") }) @InspectableProperty(enumMapping ={ @EnumEntry(value = LAYOUT_DIRECTION_LTR, name ="ltr"), @EnumEntry(value = LAYOUT_DIRECTION_RTL, name ="rtl") }) @ResolvedLayoutDir publicint getLayoutDirection(){ finalint targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; if(targetSdkVersion <Build.VERSION_CODES.JELLY_BEAN_MR1){ mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; return LAYOUT_DIRECTION_RESOLVED_DEFAULT; } return((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL)== PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL)? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; } /** * Indicates whether or not this view's layout is right-to-left. This is resolved from * layout attribute and/or the inherited value from the parent * * @return true if the layout is right-to-left. * * @hide */ @ViewDebug.ExportedProperty(category ="layout") @UnsupportedAppUsage publicboolean isLayoutRtl(){ return(getLayoutDirection()== LAYOUT_DIRECTION_RTL); } /** * 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. * * <p>A view with transient state cannot be trivially rebound from an external * data source, such as an adapter binding item views in a list. This may be * because the view is performing an animation, tracking user selection * of content, or similar.</p> * * @return true if the view has transient state */ @ViewDebug.ExportedProperty(category ="layout") publicboolean hasTransientState(){ return(mPrivateFlags2 & PFLAG2_HAS_TRANSIENT_STATE)== PFLAG2_HAS_TRANSIENT_STATE; } /** * Set whether this view is currently tracking transient state that the * framework should attempt to preserve when possible. This flag is reference counted, * so every call to setHasTransientState(true) should be paired with a later call * to setHasTransientState(false). * * <p>A view with transient state cannot be trivially rebound from an external * data source, such as an adapter binding item views in a list. This may be * because the view is performing an animation, tracking user selection * of content, or similar.</p> * * @param hasTransientState true if this view has transient state */ publicvoid setHasTransientState(boolean hasTransientState){ finalboolean oldHasTransientState = hasTransientState(); mTransientStateCount = hasTransientState ? mTransientStateCount +1: mTransientStateCount -1; if(mTransientStateCount <0){ mTransientStateCount =0; Log.e(VIEW_LOG_TAG,"hasTransientState decremented below 0: "+ "unmatched pair of setHasTransientState calls"); }elseif((hasTransientState && mTransientStateCount ==1)|| (!hasTransientState && mTransientStateCount ==0)){ // update flag if we've just incremented up from 0 or decremented down to 0 mPrivateFlags2 =(mPrivateFlags2 &~PFLAG2_HAS_TRANSIENT_STATE)| (hasTransientState ? PFLAG2_HAS_TRANSIENT_STATE :0); finalboolean newHasTransientState = hasTransientState(); if(mParent !=null&& newHasTransientState != oldHasTransientState){ try{ mParent.childHasTransientStateChanged(this, newHasTransientState); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } } } } /** * Set the view is tracking translation transient state. This flag is used to check if the view * need to call setHasTransientState(false) to reset transient state that set when starting * translation. * * @param hasTranslationTransientState true if this view has translation transient state * @hide */ publicvoid setHasTranslationTransientState(boolean hasTranslationTransientState){ if(hasTranslationTransientState){ mPrivateFlags4 |= PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE; }else{ mPrivateFlags4 &=~PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE; } } /** * @hide */ publicboolean hasTranslationTransientState(){ return(mPrivateFlags4 & PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE) == PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE; } /** * @hide */ publicvoid clearTranslationState(){ if(mViewTranslationCallback !=null){ mViewTranslationCallback.onClearTranslation(this); } clearViewTranslationResponse(); if(hasTranslationTransientState()){ setHasTransientState(false); setHasTranslationTransientState(false); } } /** * Returns true if this view is currently attached to a window. */ publicboolean isAttachedToWindow(){ return mAttachInfo !=null; } /** * Returns true if this view has been through at least one layout since it * was last attached to or detached from a window. */ publicboolean isLaidOut(){ return(mPrivateFlags3 & PFLAG3_IS_LAID_OUT)== PFLAG3_IS_LAID_OUT; } /** * @return {@code true} if laid-out and not about to do another layout. */ boolean isLayoutValid(){ return isLaidOut()&&((mPrivateFlags & PFLAG_FORCE_LAYOUT)==0); } /** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. * * Typically, if you override {@link #onDraw(android.graphics.Canvas)} * you should clear this flag. * * @param willNotDraw whether or not this View draw on its own */ publicvoid setWillNotDraw(boolean willNotDraw){ setFlags(willNotDraw ? WILL_NOT_DRAW :0, DRAW_MASK); } /** * Returns whether or not this View draws on its own. * * @return true if this view has nothing to draw, false otherwise */ @ViewDebug.ExportedProperty(category ="drawing") publicboolean willNotDraw(){ return(mViewFlags & DRAW_MASK)== WILL_NOT_DRAW; } /** * When a View's drawing cache is enabled, drawing is redirected to an * offscreen bitmap. Some views, like an ImageView, must be able to * bypass this mechanism if they already draw a single bitmap, to avoid * unnecessary usage of the memory. * * @param willNotCacheDrawing true if this view does not cache its * drawing, false otherwise * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid setWillNotCacheDrawing(boolean willNotCacheDrawing){ setFlags(willNotCacheDrawing ? WILL_NOT_CACHE_DRAWING :0, WILL_NOT_CACHE_DRAWING); } /** * Returns whether or not this View can cache its drawing or not. * * @return true if this view does not cache its drawing, false otherwise * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @ViewDebug.ExportedProperty(category ="drawing") @Deprecated publicboolean willNotCacheDrawing(){ return(mViewFlags & WILL_NOT_CACHE_DRAWING)== WILL_NOT_CACHE_DRAWING; } /** * Indicates whether this view reacts to click events or not. * * @return true if the view is clickable, false otherwise * * @see #setClickable(boolean) * @attr ref android.R.styleable#View_clickable */ @ViewDebug.ExportedProperty @InspectableProperty publicboolean isClickable(){ return(mViewFlags & CLICKABLE)== CLICKABLE; } /** * Enables or disables click events for this view. When a view * is clickable it will change its state to "pressed" on every click. * Subclasses should set the view clickable to visually react to * user's clicks. * * @param clickable true to make the view clickable, false otherwise * * @see #isClickable() * @attr ref android.R.styleable#View_clickable */ publicvoid setClickable(boolean clickable){ setFlags(clickable ? CLICKABLE :0, CLICKABLE); } /** * Enables or disables click events for this view when disabled. * * @param clickableWhenDisabled true to make the view clickable, false otherwise * * @attr ref android.R.styleable#View_allowClickWhenDisabled */ publicvoid setAllowClickWhenDisabled(boolean clickableWhenDisabled){ if(clickableWhenDisabled){ mPrivateFlags4 |= PFLAG4_ALLOW_CLICK_WHEN_DISABLED; }else{ mPrivateFlags4 &=~PFLAG4_ALLOW_CLICK_WHEN_DISABLED; } } /** * Indicates whether this view reacts to long click events or not. * * @return true if the view is long clickable, false otherwise * * @see #setLongClickable(boolean) * @attr ref android.R.styleable#View_longClickable */ @InspectableProperty publicboolean isLongClickable(){ return(mViewFlags & LONG_CLICKABLE)== LONG_CLICKABLE; } /** * Enables or disables long click events for this view. When a view is long * clickable it reacts to the user holding down the button for a longer * duration than a tap. This event can either launch the listener or a * context menu. * * @param longClickable true to make the view long clickable, false otherwise * @see #isLongClickable() * @attr ref android.R.styleable#View_longClickable */ publicvoid setLongClickable(boolean longClickable){ setFlags(longClickable ? LONG_CLICKABLE :0, LONG_CLICKABLE); } /** * Indicates whether this view reacts to context clicks or not. * * @return true if the view is context clickable, false otherwise * @see #setContextClickable(boolean) * @attr ref android.R.styleable#View_contextClickable */ @InspectableProperty publicboolean isContextClickable(){ return(mViewFlags & CONTEXT_CLICKABLE)== CONTEXT_CLICKABLE; } /** * Enables or disables context clicking for this view. This event can launch the listener. * * @param contextClickable true to make the view react to a context click, false otherwise * @see #isContextClickable() * @attr ref android.R.styleable#View_contextClickable */ publicvoid setContextClickable(boolean contextClickable){ setFlags(contextClickable ? CONTEXT_CLICKABLE :0, CONTEXT_CLICKABLE); } /** * Sets the pressed state for this view and provides a touch coordinate for * animation hinting. * * @param pressed Pass true to set the View's internal state to "pressed", * or false to reverts the View's internal state from a * previously set "pressed" state. * @param x The x coordinate of the touch that caused the press * @param y The y coordinate of the touch that caused the press */ privatevoid setPressed(boolean pressed,float x,float y){ if(pressed){ drawableHotspotChanged(x, y); } setPressed(pressed); } /** * Sets the pressed state for this view. * * @see #isClickable() * @see #setClickable(boolean) * * @param pressed Pass true to set the View's internal state to "pressed", or false to reverts * the View's internal state from a previously set "pressed" state. */ publicvoid setPressed(boolean pressed){ finalboolean needsRefresh = pressed !=((mPrivateFlags & PFLAG_PRESSED)== PFLAG_PRESSED); if(pressed){ mPrivateFlags |= PFLAG_PRESSED; }else{ mPrivateFlags &=~PFLAG_PRESSED; } if(needsRefresh){ refreshDrawableState(); } dispatchSetPressed(pressed); } /** * Dispatch setPressed to all of this View's children. * * @see #setPressed(boolean) * * @param pressed The new pressed state */ protectedvoid dispatchSetPressed(boolean pressed){ } /** * Indicates whether the view is currently in pressed state. Unless * {@link #setPressed(boolean)} is explicitly called, only clickable views can enter * the pressed state. * * @see #setPressed(boolean) * @see #isClickable() * @see #setClickable(boolean) * * @return true if the view is currently pressed, false otherwise */ @ViewDebug.ExportedProperty @InspectableProperty(hasAttributeId =false) publicboolean isPressed(){ return(mPrivateFlags & PFLAG_PRESSED)== PFLAG_PRESSED; } /** * @hide * Indicates whether this view will participate in data collection through * {@link ViewStructure}. If true, it will not provide any data * for itself or its children. If false, the normal data collection will be allowed. * * @return Returns false if assist data collection is not blocked, else true. * * @see #setAssistBlocked(boolean) * @attr ref android.R.styleable#View_assistBlocked */ publicboolean isAssistBlocked(){ return(mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED)!=0; } /** * @hide * Controls whether assist data collection from this view and its children is enabled * (that is, whether {@link #onProvideStructure} and * {@link #onProvideVirtualStructure} will be called). The default value is false, * allowing normal assist collection. Setting this to false will disable assist collection. * * @param enabled Set to true to <em>disable</em> assist data collection, or false * (the default) to allow it. * * @see #isAssistBlocked() * @see #onProvideStructure * @see #onProvideVirtualStructure * @attr ref android.R.styleable#View_assistBlocked */ @UnsupportedAppUsage publicvoid setAssistBlocked(boolean enabled){ if(enabled){ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED; }else{ mPrivateFlags3 &=~PFLAG3_ASSIST_BLOCKED; } } /** * Indicates whether this view will save its state (that is, * whether its {@link #onSaveInstanceState} method will be called). * * @return Returns true if the view state saving is enabled, else false. * * @see #setSaveEnabled(boolean) * @attr ref android.R.styleable#View_saveEnabled */ @InspectableProperty publicboolean isSaveEnabled(){ return(mViewFlags & SAVE_DISABLED_MASK)!= SAVE_DISABLED; } /** * Controls whether the saving of this view's state is * enabled (that is, whether its {@link #onSaveInstanceState} method * will be called). Note that even if freezing is enabled, the * view still must have an id assigned to it (via {@link #setId(int)}) * for its state to be saved. This flag can only disable the * saving of this view; any child views may still have their state saved. * * @param enabled Set to false to <em>disable</em> state saving, or true * (the default) to allow it. * * @see #isSaveEnabled() * @see #setId(int) * @see #onSaveInstanceState() * @attr ref android.R.styleable#View_saveEnabled */ publicvoid setSaveEnabled(boolean enabled){ setFlags(enabled ?0: SAVE_DISABLED, SAVE_DISABLED_MASK); } /** * Gets whether the framework should discard touches when the view's * window is obscured by another visible window at the touched location. * Refer to the {@link View} security documentation for more details. * * @return True if touch filtering is enabled. * * @see #setFilterTouchesWhenObscured(boolean) * @attr ref android.R.styleable#View_filterTouchesWhenObscured */ @ViewDebug.ExportedProperty @InspectableProperty publicboolean getFilterTouchesWhenObscured(){ return(mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED)!=0; } /** * Sets whether the framework should discard touches when the view's * window is obscured by another visible window at the touched location. * Refer to the {@link View} security documentation for more details. * * @param enabled True if touch filtering should be enabled. * * @see #getFilterTouchesWhenObscured * @attr ref android.R.styleable#View_filterTouchesWhenObscured */ publicvoid setFilterTouchesWhenObscured(boolean enabled){ setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED :0, FILTER_TOUCHES_WHEN_OBSCURED); } /** * Indicates whether the entire hierarchy under this view will save its * state when a state saving traversal occurs from its parent. The default * is true; if false, these views will not be saved unless * {@link #saveHierarchyState(SparseArray)} is called directly on this view. * * @return Returns true if the view state saving from parent is enabled, else false. * * @see #setSaveFromParentEnabled(boolean) */ publicboolean isSaveFromParentEnabled(){ return(mViewFlags & PARENT_SAVE_DISABLED_MASK)!= PARENT_SAVE_DISABLED; } /** * Controls whether the entire hierarchy under this view will save its * state when a state saving traversal occurs from its parent. The default * is true; if false, these views will not be saved unless * {@link #saveHierarchyState(SparseArray)} is called directly on this view. * * @param enabled Set to false to <em>disable</em> state saving, or true * (the default) to allow it. * * @see #isSaveFromParentEnabled() * @see #setId(int) * @see #onSaveInstanceState() */ publicvoid setSaveFromParentEnabled(boolean enabled){ setFlags(enabled ?0: PARENT_SAVE_DISABLED, PARENT_SAVE_DISABLED_MASK); } /** * Returns whether this View is currently able to take focus. * * @return True if this view can take focus, or false otherwise. */ @ViewDebug.ExportedProperty(category ="focus") publicfinalboolean isFocusable(){ return FOCUSABLE ==(mViewFlags & FOCUSABLE); } /** * Returns the focusable setting for this view. * * @return One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE}, or {@link #FOCUSABLE_AUTO}. * @attr ref android.R.styleable#View_focusable */ @ViewDebug.ExportedProperty(mapping ={ @ViewDebug.IntToString(from = NOT_FOCUSABLE, to ="NOT_FOCUSABLE"), @ViewDebug.IntToString(from = FOCUSABLE, to ="FOCUSABLE"), @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to ="FOCUSABLE_AUTO") }, category ="focus") @InspectableProperty(enumMapping ={ @EnumEntry(value = NOT_FOCUSABLE, name ="false"), @EnumEntry(value = FOCUSABLE, name ="true"), @EnumEntry(value = FOCUSABLE_AUTO, name ="auto") }) @Focusable publicint getFocusable(){ return(mViewFlags & FOCUSABLE_AUTO)>0? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE; } /** * When a view is focusable, it may not want to take focus when in touch mode. * For example, a button would like focus when the user is navigating via a D-pad * so that the user can click on it, but once the user starts touching the screen, * the button shouldn't take focus * @return Whether the view is focusable in touch mode. * @attr ref android.R.styleable#View_focusableInTouchMode */ @ViewDebug.ExportedProperty(category ="focus") @InspectableProperty publicfinalboolean isFocusableInTouchMode(){ return FOCUSABLE_IN_TOUCH_MODE ==(mViewFlags & FOCUSABLE_IN_TOUCH_MODE); } /** * Returns whether the view should be treated as a focusable unit by screen reader * accessibility tools. * @see #setScreenReaderFocusable(boolean) * * @return Whether the view should be treated as a focusable unit by screen reader. * * @attr ref android.R.styleable#View_screenReaderFocusable */ @InspectableProperty publicboolean isScreenReaderFocusable(){ return(mPrivateFlags3 & PFLAG3_SCREEN_READER_FOCUSABLE)!=0; } /** * Sets whether this View should be a focusable element for screen readers * and include non-focusable Views from its subtree when providing feedback. * <p> * Note: this is similar to using <a href="#attr_android:focusable">{@code android:focusable}, * but does not impact input focus behavior. * * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader * accessibility tools. * * @attr ref android.R.styleable#View_screenReaderFocusable */ publicvoid setScreenReaderFocusable(boolean screenReaderFocusable){ updatePflags3AndNotifyA11yIfChanged(PFLAG3_SCREEN_READER_FOCUSABLE, screenReaderFocusable); } /** * Gets whether this view is a heading for accessibility purposes. * * @return {@code true} if the view is a heading, {@code false} otherwise. * * @attr ref android.R.styleable#View_accessibilityHeading */ @InspectableProperty publicboolean isAccessibilityHeading(){ return(mPrivateFlags3 & PFLAG3_ACCESSIBILITY_HEADING)!=0; } /** * Set if view is a heading for a section of content for accessibility purposes. * <p> * Users of some accessibility services can choose to navigate between headings * instead of between paragraphs, words, etc. Apps that provide headings on * sections of text can help the text navigation experience. * * @param isHeading {@code true} if the view is a heading, {@code false} otherwise. * * @attr ref android.R.styleable#View_accessibilityHeading */ publicvoid setAccessibilityHeading(boolean isHeading){ updatePflags3AndNotifyA11yIfChanged(PFLAG3_ACCESSIBILITY_HEADING, isHeading); } privatevoid updatePflags3AndNotifyA11yIfChanged(int mask,boolean newValue){ int pflags3 = mPrivateFlags3; if(newValue){ pflags3 |= mask; }else{ pflags3 &=~mask; } if(pflags3 != mPrivateFlags3){ mPrivateFlags3 = pflags3; notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } /** * Find the nearest view in the specified direction that can take focus. * This does not actually give focus to that view. * * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * * @return The nearest focusable in the specified direction, or null if none * can be found. */ publicView focusSearch(@FocusRealDirectionint direction){ if(mParent !=null){ return mParent.focusSearch(this, direction); }else{ returnnull; } } /** * Returns whether this View is a root of a keyboard navigation cluster. * * @return True if this view is a root of a cluster, or false otherwise. * @attr ref android.R.styleable#View_keyboardNavigationCluster */ @ViewDebug.ExportedProperty(category ="focus") @InspectableProperty publicfinalboolean isKeyboardNavigationCluster(){ return(mPrivateFlags3 & PFLAG3_CLUSTER)!=0; } /** * Searches up the view hierarchy to find the top-most cluster. All deeper/nested clusters * will be ignored. * * @return the keyboard navigation cluster that this view is in (can be this view) * or {@code null} if not in one */ View findKeyboardNavigationCluster(){ if(mParent instanceofView){ View cluster =((View) mParent).findKeyboardNavigationCluster(); if(cluster !=null){ return cluster; }elseif(isKeyboardNavigationCluster()){ returnthis; } } returnnull; } /** * Set whether this view is a root of a keyboard navigation cluster. * * @param isCluster If true, this view is a root of a cluster. * * @attr ref android.R.styleable#View_keyboardNavigationCluster */ publicvoid setKeyboardNavigationCluster(boolean isCluster){ if(isCluster){ mPrivateFlags3 |= PFLAG3_CLUSTER; }else{ mPrivateFlags3 &=~PFLAG3_CLUSTER; } } /** * Sets this View as the one which receives focus the next time cluster navigation jumps * to the cluster containing this View. This does NOT change focus even if the cluster * containing this view is current. * * @hide */ @TestApi publicfinalvoid setFocusedInCluster(){ setFocusedInCluster(findKeyboardNavigationCluster()); } privatevoid setFocusedInCluster(View cluster){ if(thisinstanceofViewGroup){ ((ViewGroup)this).mFocusedInCluster =null; } if(cluster ==this){ return; } ViewParent parent = mParent; View child =this; while(parent instanceofViewGroup){ ((ViewGroup) parent).mFocusedInCluster = child; if(parent == cluster){ break; } child =(View) parent; parent = parent.getParent(); } } privatevoid updateFocusedInCluster(View oldFocus,@FocusDirectionint direction){ if(oldFocus !=null){ View oldCluster = oldFocus.findKeyboardNavigationCluster(); View cluster = findKeyboardNavigationCluster(); if(oldCluster != cluster){ // Going from one cluster to another, so save last-focused. // This covers cluster jumps because they are always FOCUS_DOWN oldFocus.setFocusedInCluster(oldCluster); if(!(oldFocus.mParent instanceofViewGroup)){ return; } if(direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD){ // This is a result of ordered navigation so consider navigation through // the previous cluster "complete" and clear its last-focused memory. ((ViewGroup) oldFocus.mParent).clearFocusedInCluster(oldFocus); }elseif(oldFocus instanceofViewGroup &&((ViewGroup) oldFocus).getDescendantFocusability() ==ViewGroup.FOCUS_AFTER_DESCENDANTS &&ViewRootImpl.isViewDescendantOf(this, oldFocus)){ // This means oldFocus is not focusable since it obviously has a focusable // child (this). Don't restore focus to it in the future. ((ViewGroup) oldFocus.mParent).clearFocusedInCluster(oldFocus); } } } } /** * Returns whether this View should receive focus when the focus is restored for the view * hierarchy containing this view. * <p> * 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. * * @see #restoreDefaultFocus() * * @return {@code true} if this view is the default-focus view, {@code false} otherwise * @attr ref android.R.styleable#View_focusedByDefault */ @ViewDebug.ExportedProperty(category ="focus") @InspectableProperty publicfinalboolean isFocusedByDefault(){ return(mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT)!=0; } /** * Sets whether this View should receive focus when the focus is restored for the view * hierarchy containing this view. * <p> * 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. * * @param isFocusedByDefault {@code true} to set this view as the default-focus view, * {@code false} otherwise. * * @see #restoreDefaultFocus() * * @attr ref android.R.styleable#View_focusedByDefault */ @RemotableViewMethod publicvoid setFocusedByDefault(boolean isFocusedByDefault){ if(isFocusedByDefault ==((mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT)!=0)){ return; } if(isFocusedByDefault){ mPrivateFlags3 |= PFLAG3_FOCUSED_BY_DEFAULT; }else{ mPrivateFlags3 &=~PFLAG3_FOCUSED_BY_DEFAULT; } if(mParent instanceofViewGroup){ if(isFocusedByDefault){ ((ViewGroup) mParent).setDefaultFocus(this); }else{ ((ViewGroup) mParent).clearDefaultFocus(this); } } } /** * Returns whether the view hierarchy with this view as a root contain a default-focus view. * * @return {@code true} if this view has default focus, {@code false} otherwise */ boolean hasDefaultFocus(){ return 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. 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 null if none * can be found */ publicView keyboardNavigationClusterSearch(View currentCluster, @FocusDirectionint direction){ if(isKeyboardNavigationCluster()){ currentCluster =this; } if(isRootNamespace()){ // Root namespace means we should consider ourselves the top of the // tree for group searching; otherwise we could be group searching // into other tabs. see LocalActivityManager and TabHost for more info. returnFocusFinder.getInstance().findNextKeyboardNavigationCluster( this, currentCluster, direction); }elseif(mParent !=null){ return mParent.keyboardNavigationClusterSearch(currentCluster, direction); } returnnull; } /** * This method is the last chance for the focused view and its ancestors to * respond to an arrow key. This is called when the focused view did not * consume the key internally, nor could the view system find a new view in * the requested direction to give focus to. * * @param focused The currently focused view. * @param direction The direction focus wants to move. One of FOCUS_UP, * FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT. * @return True if the this view consumed this unhandled move. */ publicboolean dispatchUnhandledMove(View focused,@FocusRealDirectionint direction){ returnfalse; } /** * Sets whether this View should use a default focus highlight when it gets focused but doesn't * have {@link android.R.attr#state_focused} defined in its background. * * @param defaultFocusHighlightEnabled {@code true} to set this view to use a default focus * highlight, {@code false} otherwise. * * @attr ref android.R.styleable#View_defaultFocusHighlightEnabled */ publicvoid setDefaultFocusHighlightEnabled(boolean defaultFocusHighlightEnabled){ mDefaultFocusHighlightEnabled = defaultFocusHighlightEnabled; } /** * Returns whether this View should use a default focus highlight when it gets focused but * doesn't have {@link android.R.attr#state_focused} defined in its background. * * @return True if this View should use a default focus highlight. * @attr ref android.R.styleable#View_defaultFocusHighlightEnabled */ @ViewDebug.ExportedProperty(category ="focus") @InspectableProperty publicfinalboolean getDefaultFocusHighlightEnabled(){ return mDefaultFocusHighlightEnabled; } /** * If a user manually specified the next view id for a particular direction, * use the root to look up the view. * @param root The root view of the hierarchy containing this view. * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, * or FOCUS_BACKWARD. * @return The user specified next view, or null if there is none. */ View findUserSetNextFocus(View root,@FocusDirectionint direction){ switch(direction){ case FOCUS_LEFT: if(mNextFocusLeftId ==View.NO_ID)returnnull; return findViewInsideOutShouldExist(root, mNextFocusLeftId); case FOCUS_RIGHT: if(mNextFocusRightId ==View.NO_ID)returnnull; return findViewInsideOutShouldExist(root, mNextFocusRightId); case FOCUS_UP: if(mNextFocusUpId ==View.NO_ID)returnnull; return findViewInsideOutShouldExist(root, mNextFocusUpId); case FOCUS_DOWN: if(mNextFocusDownId ==View.NO_ID)returnnull; return findViewInsideOutShouldExist(root, mNextFocusDownId); case FOCUS_FORWARD: if(mNextFocusForwardId ==View.NO_ID)returnnull; return findViewInsideOutShouldExist(root, mNextFocusForwardId); case FOCUS_BACKWARD:{ if(mID ==View.NO_ID)returnnull; finalView rootView = root; finalView startView =this; // Since we have forward links but no backward links, we need to find the view that // forward links to this view. We can't just find the view with the specified ID // because view IDs need not be unique throughout the tree. return root.findViewByPredicateInsideOut(startView, t -> findViewInsideOutShouldExist(rootView, t, t.mNextFocusForwardId) == startView); } } returnnull; } /** * If a user manually specified the next keyboard-navigation cluster for a particular direction, * use the root to look up the view. * * @param root the root view of the hierarchy containing this view * @param direction {@link #FOCUS_FORWARD} or {@link #FOCUS_BACKWARD} * @return the user-specified next cluster, or {@code null} if there is none */ View findUserSetNextKeyboardNavigationCluster(View root,@FocusDirectionint direction){ switch(direction){ case FOCUS_FORWARD: if(mNextClusterForwardId ==View.NO_ID)returnnull; return findViewInsideOutShouldExist(root, mNextClusterForwardId); case FOCUS_BACKWARD:{ if(mID ==View.NO_ID)returnnull; finalint id = mID; return root.findViewByPredicateInsideOut(this, (Predicate<View>) t -> t.mNextClusterForwardId == id); } } returnnull; } privateView findViewInsideOutShouldExist(View root,int id){ return findViewInsideOutShouldExist(root,this, id); } privateView findViewInsideOutShouldExist(View root,View start,int id){ if(mMatchIdPredicate ==null){ mMatchIdPredicate =newMatchIdPredicate(); } mMatchIdPredicate.mId = id; View result = root.findViewByPredicateInsideOut(start, mMatchIdPredicate); if(result ==null){ Log.w(VIEW_LOG_TAG,"couldn't find view with id "+ id); } return result; } /** * Find and return all focusable views that are descendants of this view, * possibly including this view if it is focusable itself. * * @param direction The direction of the focus * @return A list of focusable views */ publicArrayList<View> getFocusables(@FocusDirectionint direction){ ArrayList<View> result =newArrayList<View>(24); addFocusables(result, direction); return result; } /** * Add any focusable views that are descendants of this view (possibly * including this view if it is focusable itself) to views. If we are in touch mode, * only add views that are also focusable in touch mode. * * @param views Focusable views found so far * @param direction The direction of the focus */ publicvoid addFocusables(ArrayList<View> views,@FocusDirectionint direction){ addFocusables(views, direction, isInTouchMode()? FOCUSABLES_TOUCH_MODE : FOCUSABLES_ALL); } /** * Adds any focusable views that are descendants of this view (possibly * including this view if it is focusable itself) to views. This method * adds all focusable views regardless if we are in touch mode or * only views focusable in touch mode if we are in touch mode or * only views that can take accessibility focus if accessibility is enabled * depending on the focusable mode parameter. * * @param views Focusable views found so far or null if all we are interested is * the number of focusables. * @param direction The direction of the focus. * @param focusableMode The type of focusables to be added. * * @see #FOCUSABLES_ALL * @see #FOCUSABLES_TOUCH_MODE */ publicvoid addFocusables(ArrayList<View> views,@FocusDirectionint direction, @FocusableModeint focusableMode){ if(views ==null){ return; } if(!canTakeFocus()){ return; } if((focusableMode & FOCUSABLES_TOUCH_MODE)== FOCUSABLES_TOUCH_MODE &&!isFocusableInTouchMode()){ return; } views.add(this); } /** * Adds any keyboard navigation cluster roots that are descendants of this view (possibly * including this view if it is a cluster root itself) to views. * * @param views Keyboard navigation cluster roots found so far * @param direction Direction to look */ publicvoid addKeyboardNavigationClusters( @NonNullCollection<View> views, int direction){ if(!isKeyboardNavigationCluster()){ return; } if(!hasFocusable()){ return; } views.add(this); } /** * Finds the Views that contain given text. The containment is case insensitive. * The search is performed by either the text that the View renders or the content * description that describes the view for accessibility purposes and the view does * not render or both. Clients can specify how the search is to be performed via * passing the {@link #FIND_VIEWS_WITH_TEXT} and * {@link #FIND_VIEWS_WITH_CONTENT_DESCRIPTION} flags. * * @param outViews The output list of matching Views. * @param searched The text to match against. * * @see #FIND_VIEWS_WITH_TEXT * @see #FIND_VIEWS_WITH_CONTENT_DESCRIPTION * @see #setContentDescription(CharSequence) */ publicvoid findViewsWithText(ArrayList<View> outViews,CharSequence searched, @FindViewFlagsint flags){ if(getAccessibilityNodeProvider()!=null){ if((flags & FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS)!=0){ outViews.add(this); } }elseif((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION)!=0 &&(searched !=null&& searched.length()>0) &&(mContentDescription !=null&& mContentDescription.length()>0)){ String searchedLowerCase = searched.toString().toLowerCase(); String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase(); if(contentDescriptionLowerCase.contains(searchedLowerCase)){ outViews.add(this); } } } /** * Find and return all touchable views that are descendants of this view, * possibly including this view if it is touchable itself. * * @return A list of touchable views */ publicArrayList<View> getTouchables(){ ArrayList<View> result =newArrayList<View>(); addTouchables(result); return result; } /** * Add any touchable views that are descendants of this view (possibly * including this view if it is touchable itself) to views. * * @param views Touchable views found so far */ publicvoid addTouchables(ArrayList<View> views){ finalint viewFlags = mViewFlags; if(((viewFlags & CLICKABLE)== CLICKABLE ||(viewFlags & LONG_CLICKABLE)== LONG_CLICKABLE ||(viewFlags & CONTEXT_CLICKABLE)== CONTEXT_CLICKABLE) &&(viewFlags & ENABLED_MASK)== ENABLED){ views.add(this); } } /** * Returns whether this View is accessibility focused. * * @return True if this View is accessibility focused. */ @InspectableProperty(hasAttributeId =false) publicboolean isAccessibilityFocused(){ return(mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED)!=0; } /** * Call this to try to give accessibility focus to this view. * * A view will not actually take focus if {@link AccessibilityManager#isEnabled()} * returns false or the view is no visible or the view already has accessibility * focus. * * See also {@link #focusSearch(int)}, which is what you call to say that you * have focus, and you want your parent to look for the next one. * * @return Whether this view actually took accessibility focus. * * @hide */ @UnsupportedAppUsage publicboolean requestAccessibilityFocus(){ AccessibilityManager manager =AccessibilityManager.getInstance(mContext); if(!manager.isEnabled()||!manager.isTouchExplorationEnabled()){ returnfalse; } if((mViewFlags & VISIBILITY_MASK)!= VISIBLE){ returnfalse; } if((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED)==0){ mPrivateFlags2 |= PFLAG2_ACCESSIBILITY_FOCUSED; ViewRootImpl viewRootImpl = getViewRootImpl(); if(viewRootImpl !=null){ viewRootImpl.setAccessibilityFocus(this,null); } invalidate(); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); returntrue; } returnfalse; } /** * Call this to try to clear accessibility focus of this view. * * See also {@link #focusSearch(int)}, which is what you call to say that you * have focus, and you want your parent to look for the next one. * * @hide */ @UnsupportedAppUsage publicvoid clearAccessibilityFocus(){ clearAccessibilityFocusNoCallbacks(0); // Clear the global reference of accessibility focus if this view or // any of its descendants had accessibility focus. This will NOT send // an event or update internal state if focus is cleared from a // descendant view, which may leave views in inconsistent states. finalViewRootImpl viewRootImpl = getViewRootImpl(); if(viewRootImpl !=null){ finalView focusHost = viewRootImpl.getAccessibilityFocusedHost(); if(focusHost !=null&&ViewRootImpl.isViewDescendantOf(focusHost,this)){ viewRootImpl.setAccessibilityFocus(null,null); } } } privatevoid sendAccessibilityHoverEvent(int eventType){ // Since we are not delivering to a client accessibility events from not // important views (unless the clinet request that) we need to fire the // event from the deepest view exposed to the client. As a consequence if // the user crosses a not exposed view the client will see enter and exit // of the exposed predecessor followed by and enter and exit of that same // predecessor when entering and exiting the not exposed descendant. This // is fine since the client has a clear idea which view is hovered at the // price of a couple more events being sent. This is a simple and // working solution. View source =this; while(true){ if(source.includeForAccessibility()){ source.sendAccessibilityEvent(eventType); return; } ViewParent parent = source.getParent(); if(parent instanceofView){ source =(View) parent; }else{ return; } } } /** * Clears accessibility focus without calling any callback methods * normally invoked in {@link #clearAccessibilityFocus()}. This method * is used separately from that one for clearing accessibility focus when * giving this focus to another view. * * @param action The action, if any, that led to focus being cleared. Set to * AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS to specify that focus is moving within * the window. */ void clearAccessibilityFocusNoCallbacks(int action){ if((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED)!=0){ mPrivateFlags2 &=~PFLAG2_ACCESSIBILITY_FOCUSED; invalidate(); if(AccessibilityManager.getInstance(mContext).isEnabled()){ AccessibilityEvent event =AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); event.setAction(action); if(mAccessibilityDelegate !=null){ mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event); }else{ sendAccessibilityEventUnchecked(event); } } updatePreferKeepClearForFocus(); } } /** * Call this to try to give focus to a specific view or to one of its * descendants. * * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns * false), or if it can't be focused due to other conditions (not focusable in touch mode * ({@link #isFocusableInTouchMode}) while the device is in touch mode, not visible, not * enabled, or has no size). * * See also {@link #focusSearch(int)}, which is what you call to say that you * have focus, and you want your parent to look for the next one. * * This is equivalent to calling {@link #requestFocus(int, Rect)} with arguments * {@link #FOCUS_DOWN} and <code>null</code>. * * @return Whether this view or one of its descendants actually took focus. */ publicfinalboolean requestFocus(){ return requestFocus(View.FOCUS_DOWN); } /** * This will request focus for whichever View was last focused within this * cluster before a focus-jump out of it. * * @hide */ @TestApi publicboolean restoreFocusInCluster(@FocusRealDirectionint direction){ // Prioritize focusableByDefault over algorithmic focus selection. if(restoreDefaultFocus()){ returntrue; } return requestFocus(direction); } /** * This will request focus for whichever View not in a cluster was last focused before a * focus-jump to a cluster. If no non-cluster View has previously had focus, this will focus * the "first" focusable view it finds. * * @hide */ @TestApi publicboolean restoreFocusNotInCluster(){ return requestFocus(View.FOCUS_DOWN); } /** * Gives focus to the default-focus view in the view hierarchy that has this view as a root. * If the default-focus view cannot be found, falls back to calling {@link #requestFocus(int)}. * * @return Whether this view or one of its descendants actually took focus */ publicboolean restoreDefaultFocus(){ return requestFocus(View.FOCUS_DOWN); } /** * Call this to try to give focus to a specific view or to one of its * descendants and give it a hint about what direction focus is heading. * * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns * false), or if it is focusable and it is not focusable in touch mode * ({@link #isFocusableInTouchMode}) while the device is in touch mode. * * See also {@link #focusSearch(int)}, which is what you call to say that you * have focus, and you want your parent to look for the next one. * * This is equivalent to calling {@link #requestFocus(int, Rect)} with * <code>null</code> set for the previously focused rectangle. * * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * @return Whether this view or one of its descendants actually took focus. */ publicfinalboolean requestFocus(int direction){ return requestFocus(direction,null); } /** * Call this to try to give focus to a specific view or to one of its descendants * and give it hints about the direction and a specific rectangle that the focus * is coming from. The rectangle can help give larger views a finer grained hint * about where focus is coming from, and therefore, where to show selection, or * forward focus change internally. * * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns * false), or if it is focusable and it is not focusable in touch mode * ({@link #isFocusableInTouchMode}) while the device is in touch mode. * * A View will not take focus if it is not visible. * * A View will not take focus if one of its parents has * {@link android.view.ViewGroup#getDescendantFocusability()} equal to * {@link ViewGroup#FOCUS_BLOCK_DESCENDANTS}. * * See also {@link #focusSearch(int)}, which is what you call to say that you * have focus, and you want your parent to look for the next one. * * You may wish to override this method if your custom {@link View} has an internal * {@link View} that it wishes to forward the request to. * * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * @param previouslyFocusedRect The rectangle (in this View's coordinate system) * to give a finer grained hint about where focus is coming from. May be null * if there is no hint. * @return Whether this view or one of its descendants actually took focus. */ publicboolean requestFocus(int direction,Rect previouslyFocusedRect){ return requestFocusNoSearch(direction, previouslyFocusedRect); } privateboolean requestFocusNoSearch(int direction,Rect previouslyFocusedRect){ // need to be focusable if(!canTakeFocus()){ returnfalse; } // need to be focusable in touch mode if in touch mode if(isInTouchMode()&& (FOCUSABLE_IN_TOUCH_MODE !=(mViewFlags & FOCUSABLE_IN_TOUCH_MODE))){ returnfalse; } // need to not have any parents blocking us if(hasAncestorThatBlocksDescendantFocus()){ returnfalse; } if(!isLayoutValid()){ mPrivateFlags |= PFLAG_WANTS_FOCUS; }else{ clearParentsWantFocus(); } handleFocusGainInternal(direction, previouslyFocusedRect); returntrue; } void clearParentsWantFocus(){ if(mParent instanceofView){ ((View) mParent).mPrivateFlags &=~PFLAG_WANTS_FOCUS; ((View) mParent).clearParentsWantFocus(); } } /** * Call this to try to give focus to a specific view or to one of its descendants. This is a * special variant of {@link #requestFocus() } that will allow views that are not focusable in * touch mode to request focus when they are touched. * * @return Whether this view or one of its descendants actually took focus. * * @see #isInTouchMode() * */ publicfinalboolean requestFocusFromTouch(){ // Leave touch mode if we need to if(isInTouchMode()){ ViewRootImpl viewRoot = getViewRootImpl(); if(viewRoot !=null){ viewRoot.ensureTouchMode(false); } } return requestFocus(View.FOCUS_DOWN); } /** * @return Whether any ancestor of this view blocks descendant focus. */ privateboolean hasAncestorThatBlocksDescendantFocus(){ finalboolean focusableInTouchMode = isFocusableInTouchMode(); ViewParent ancestor = mParent; while(ancestor instanceofViewGroup){ finalViewGroup vgAncestor =(ViewGroup) ancestor; if(vgAncestor.getDescendantFocusability()==ViewGroup.FOCUS_BLOCK_DESCENDANTS ||(!focusableInTouchMode && vgAncestor.shouldBlockFocusForTouchscreen())){ returntrue; }else{ ancestor = vgAncestor.getParent(); } } returnfalse; } /** * Gets the mode for determining whether this View is important for accessibility. * A view is important for accessibility if it fires accessibility events and if it * is reported to accessibility services that query the screen. * * @return The mode for determining whether a view is important for accessibility, one * of {@link #IMPORTANT_FOR_ACCESSIBILITY_AUTO}, {@link #IMPORTANT_FOR_ACCESSIBILITY_YES}, * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO}, or * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}. * * @attr ref android.R.styleable#View_importantForAccessibility * * @see #IMPORTANT_FOR_ACCESSIBILITY_YES * @see #IMPORTANT_FOR_ACCESSIBILITY_NO * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO */ @ViewDebug.ExportedProperty(category ="accessibility", mapping ={ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO, to ="auto"), @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES, to ="yes"), @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO, to ="no"), @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, to ="noHideDescendants") }) @InspectableProperty(enumMapping ={ @EnumEntry(value = IMPORTANT_FOR_ACCESSIBILITY_AUTO, name ="auto"), @EnumEntry(value = IMPORTANT_FOR_ACCESSIBILITY_YES, name ="yes"), @EnumEntry(value = IMPORTANT_FOR_ACCESSIBILITY_NO, name ="no"), @EnumEntry(value = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, name ="noHideDescendants"), }) publicint getImportantForAccessibility(){ return(mPrivateFlags2 & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK) >> PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT; } /** * Sets the live region mode for this view. This indicates to accessibility * services whether they should automatically notify the user about changes * to the view's content description or text, or to the content descriptions * or text of the view's children (where applicable). * <p> * 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}. * <p> * To disable change notifications for this view, use * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region * mode for most views. * <p> * To indicate that the user should be notified of changes, use * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. * <p> * If the view's changes should interrupt ongoing speech and notify the user * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}. * * @param mode The live region mode for this view, one of: * <ul> * <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE} * <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE} * <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE} * </ul> * @attr ref android.R.styleable#View_accessibilityLiveRegion */ publicvoid setAccessibilityLiveRegion(int mode){ if(mode != getAccessibilityLiveRegion()){ mPrivateFlags2 &=~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK; mPrivateFlags2 |=(mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT) & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK; notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } /** * Gets the live region mode for this View. * * @return The live region mode for the view. * * @attr ref android.R.styleable#View_accessibilityLiveRegion * * @see #setAccessibilityLiveRegion(int) */ @InspectableProperty(enumMapping ={ @EnumEntry(value = ACCESSIBILITY_LIVE_REGION_NONE, name ="none"), @EnumEntry(value = ACCESSIBILITY_LIVE_REGION_POLITE, name ="polite"), @EnumEntry(value = ACCESSIBILITY_LIVE_REGION_ASSERTIVE, name ="assertive") }) publicint getAccessibilityLiveRegion(){ return(mPrivateFlags2 & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK) >> PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT; } /** * 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. * * @param mode How to determine whether this view is important for accessibility. * * @attr ref android.R.styleable#View_importantForAccessibility * * @see #IMPORTANT_FOR_ACCESSIBILITY_YES * @see #IMPORTANT_FOR_ACCESSIBILITY_NO * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO */ publicvoid setImportantForAccessibility(int mode){ finalint oldMode = getImportantForAccessibility(); if(mode != oldMode){ finalboolean hideDescendants = mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; // If this node or its descendants are no longer important, try to // clear accessibility focus. if(mode == IMPORTANT_FOR_ACCESSIBILITY_NO || hideDescendants){ finalView focusHost = findAccessibilityFocusHost(hideDescendants); if(focusHost !=null){ focusHost.clearAccessibilityFocus(); } } // If we're moving between AUTO and another state, we might not need // to send a subtree changed notification. We'll store the computed // importance, since we'll need to check it later to make sure. finalboolean maySkipNotify = oldMode == IMPORTANT_FOR_ACCESSIBILITY_AUTO || mode == IMPORTANT_FOR_ACCESSIBILITY_AUTO; finalboolean oldIncludeForAccessibility = maySkipNotify && includeForAccessibility(); mPrivateFlags2 &=~PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK; mPrivateFlags2 |=(mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT) & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK; if(!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()){ notifySubtreeAccessibilityStateChangedIfNeeded(); }else{ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } /** * Returns the view within this view's hierarchy that is hosting * accessibility focus. * * @param searchDescendants whether to search for focus in descendant views * @return the view hosting accessibility focus, or {@code null} */ privateView findAccessibilityFocusHost(boolean searchDescendants){ if(isAccessibilityFocusedViewOrHost()){ returnthis; } if(searchDescendants){ finalViewRootImpl viewRoot = getViewRootImpl(); if(viewRoot !=null){ finalView focusHost = viewRoot.getAccessibilityFocusedHost(); if(focusHost !=null&&ViewRootImpl.isViewDescendantOf(focusHost,this)){ return focusHost; } } } returnnull; } /** * 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. * <p> * If an ancestor of this view has importance * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, this method * returns <code>false</code>. * <p> * Otherwise, the value is computed according to the view's * {@link #getImportantForAccessibility()} value: * <ol> * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_NO} or * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, return <code>false * </code> * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_YES}, return <code>true</code> * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_AUTO}, return <code>true</code> if * view satisfies any of the following: * <ul> * <li>Is actionable, e.g. {@link #isClickable()}, * {@link #isLongClickable()}, or {@link #isFocusable()} * <li>Has an {@link AccessibilityDelegate} * <li>Has an interaction listener, e.g. {@link OnTouchListener}, * {@link OnKeyListener}, etc. * <li>Is an accessibility live region, e.g. * {@link #getAccessibilityLiveRegion()} is not * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. * </ul> * <li>Has an accessibility pane title, see {@link #setAccessibilityPaneTitle}</li> * </ol> * * @return Whether the view is exposed for accessibility. * @see #setImportantForAccessibility(int) * @see #getImportantForAccessibility() */ publicboolean isImportantForAccessibility(){ finalint mode = getImportantForAccessibility(); if(mode == IMPORTANT_FOR_ACCESSIBILITY_NO || mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS){ returnfalse; } // Check parent mode to ensure we're not hidden. ViewParent parent = mParent; while(parent instanceofView){ if(((View) parent).getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS){ returnfalse; } parent = parent.getParent(); } return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility() || hasListenersForAccessibility()|| getAccessibilityNodeProvider()!=null || getAccessibilityLiveRegion()!= ACCESSIBILITY_LIVE_REGION_NONE || isAccessibilityPane(); } /** * 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. * * @return The parent for accessibility purposes. */ publicViewParent getParentForAccessibility(){ if(mParent instanceofView){ View parentView =(View) mParent; if(parentView.includeForAccessibility()){ return mParent; }else{ return mParent.getParentForAccessibility(); } } returnnull; } /** @hide */ View getSelfOrParentImportantForA11y(){ if(isImportantForAccessibility())returnthis; ViewParent parent = getParentForAccessibility(); if(parent instanceofView)return(View) parent; returnnull; } /** * Adds the children of this View relevant for accessibility to the given list * as output. Since some Views are not important for accessibility the added * child views are not necessarily direct children of this view, rather they are * the first level of descendants important for accessibility. * * @param outChildren The output list that will receive children for accessibility. */ publicvoid addChildrenForAccessibility(ArrayList<View> outChildren){ } /** * Whether to regard this view for accessibility. A view is regarded for * accessibility if it is important for accessibility or the querying * accessibility service has explicitly requested that view not * important for accessibility are regarded. * * @return Whether to regard the view for accessibility. * * @hide */ @UnsupportedAppUsage publicboolean includeForAccessibility(){ if(mAttachInfo !=null){ return(mAttachInfo.mAccessibilityFetchFlags &AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS)!=0 || isImportantForAccessibility(); } returnfalse; } /** * Returns whether the View is considered actionable from * accessibility perspective. Such view are important for * accessibility. * * @return True if the view is actionable for accessibility. * * @hide */ publicboolean isActionableForAccessibility(){ return(isClickable()|| isLongClickable()|| isFocusable()); } /** * Returns whether the View has registered callbacks which makes it * important for accessibility. * * @return True if the view is actionable for accessibility. */ privateboolean hasListenersForAccessibility(){ ListenerInfo info = getListenerInfo(); return mTouchDelegate !=null|| info.mOnKeyListener !=null || info.mOnTouchListener !=null|| info.mOnGenericMotionListener !=null || info.mOnHoverListener !=null|| info.mOnDragListener !=null; } /** * Notifies that the accessibility state of this view changed. The change * is local to this view and does not represent structural changes such * as children and parent. For example, the view became focusable. The * notification is at at most once every * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()} * to avoid unnecessary load to the system. Also once a view has a pending * notification this method is a NOP until the notification has been sent. * * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicvoid notifyViewAccessibilityStateChangedIfNeeded(int changeType){ if(!AccessibilityManager.getInstance(mContext).isEnabled()|| mAttachInfo ==null){ return; } // Changes to views with a pane title count as window state changes, as the pane title // marks them as significant parts of the UI. A visible view with a nulled title may send // a disappeared event. if((changeType !=AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) &&(isAccessibilityPane() ||(changeType ==AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) && isAggregatedVisible())){ // If the pane isn't visible, content changed events are sufficient unless we're // reporting that the view just disappeared if((isAggregatedVisible()) ||(changeType ==AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)){ finalAccessibilityEvent event =AccessibilityEvent.obtain(); onInitializeAccessibilityEvent(event); event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); event.setContentChangeTypes(changeType); event.setSource(this); onPopulateAccessibilityEvent(event); if(mParent !=null){ try{ mParent.requestSendAccessibilityEvent(this, event); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +" does not fully implement ViewParent", e); } } return; } } // If this is a live region, we should send a subtree change event // from this view immediately. Otherwise, we can let it propagate up. if(getAccessibilityLiveRegion()!= ACCESSIBILITY_LIVE_REGION_NONE){ finalAccessibilityEvent event =AccessibilityEvent.obtain(); event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); event.setContentChangeTypes(changeType); sendAccessibilityEventUnchecked(event); }elseif(mParent !=null){ try{ mParent.notifySubtreeAccessibilityStateChanged(this,this, changeType); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } } } /** * Notifies that the accessibility state of this view changed. The change * is *not* local to this view and does represent structural changes such * as children and parent. For example, the view size changed. The * notification is at at most once every * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()} * to avoid unnecessary load to the system. Also once a view has a pending * notification this method is a NOP until the notification has been sent. * * @hide */ @UnsupportedAppUsage publicvoid notifySubtreeAccessibilityStateChangedIfNeeded(){ if(!AccessibilityManager.getInstance(mContext).isEnabled()|| mAttachInfo ==null){ return; } if((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED)==0){ mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED; if(mParent !=null){ try{ mParent.notifySubtreeAccessibilityStateChanged( this,this,AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } } } } privatevoid notifySubtreeAccessibilityStateChangedByParentIfNeeded(){ if(!AccessibilityManager.getInstance(mContext).isEnabled()){ return; } finalView sendA11yEventView =(View) getParentForAccessibility(); if(sendA11yEventView !=null&& sendA11yEventView.isShown()){ sendA11yEventView.notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * Changes the visibility of this View without triggering any other changes. This should only * be used by animation frameworks, such as {@link android.transition.Transition}, where * visibility changes should not adjust focus or trigger a new layout. Application developers * should use {@link #setVisibility} instead to ensure that the hierarchy is correctly updated. * * <p>Only call this method when a temporary visibility must be applied during an * animation and the original visibility value is guaranteed to be reset after the * animation completes. Use {@link #setVisibility} in all other cases.</p> * * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. * @see #setVisibility(int) */ publicvoid setTransitionVisibility(@Visibilityint visibility){ mViewFlags =(mViewFlags &~View.VISIBILITY_MASK)| visibility; } /** * Reset the flag indicating the accessibility state of the subtree rooted * at this view changed. */ void resetSubtreeAccessibilityStateChanged(){ mPrivateFlags2 &=~PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED; } /** * Report an accessibility action to this view's parents for delegated processing. * * <p>Implementations of {@link #performAccessibilityAction(int, Bundle)} may internally * call this method to delegate an accessibility action to a supporting parent. If the parent * returns true from its * {@link ViewParent#onNestedPrePerformAccessibilityAction(View, int, android.os.Bundle)} * method this method will return true to signify that the action was consumed.</p> * * <p>This method is useful for implementing nested scrolling child views. If * {@link #isNestedScrollingEnabled()} returns true and the action is a scrolling action * a custom view implementation may invoke this method to allow a parent to consume the * scroll first. If this method returns true the custom view should skip its own scrolling * behavior.</p> * * @param action Accessibility action to delegate * @param arguments Optional action arguments * @return true if the action was consumed by a parent */ publicboolean dispatchNestedPrePerformAccessibilityAction(int action, @NullableBundle arguments){ for(ViewParent p = getParent(); p !=null; p = p.getParent()){ if(p.onNestedPrePerformAccessibilityAction(this, action, arguments)){ returntrue; } } returnfalse; } /** * Performs the specified accessibility action on the view. For * possible accessibility actions look at {@link AccessibilityNodeInfo}. * <p> * If an {@link AccessibilityDelegate} has been specified via calling * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)} * is responsible for handling this call. * </p> * * <p>The default implementation will delegate * {@link AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD} and * {@link AccessibilityNodeInfo#ACTION_SCROLL_FORWARD} to nested scrolling parents if * {@link #isNestedScrollingEnabled() nested scrolling is enabled} on this view.</p> * * @param action The action to perform. * @param arguments Optional action arguments. * @return Whether the action was performed. */ publicboolean performAccessibilityAction(int action,@NullableBundle arguments){ if(mAccessibilityDelegate !=null){ return mAccessibilityDelegate.performAccessibilityAction(this, action, arguments); }else{ return performAccessibilityActionInternal(action, arguments); } } /** * @see #performAccessibilityAction(int, Bundle) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */ @UnsupportedAppUsage publicboolean performAccessibilityActionInternal(int action,@NullableBundle arguments){ if(isNestedScrollingEnabled() &&(action ==AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD || action ==AccessibilityNodeInfo.ACTION_SCROLL_FORWARD || action == R.id.accessibilityActionScrollUp || action == R.id.accessibilityActionScrollLeft || action == R.id.accessibilityActionScrollDown || action == R.id.accessibilityActionScrollRight)){ if(dispatchNestedPrePerformAccessibilityAction(action, arguments)){ returntrue; } } switch(action){ caseAccessibilityNodeInfo.ACTION_CLICK:{ if(isClickable()){ performClickInternal(); returntrue; } }break; caseAccessibilityNodeInfo.ACTION_LONG_CLICK:{ if(isLongClickable()){ performLongClick(); returntrue; } }break; caseAccessibilityNodeInfo.ACTION_FOCUS:{ if(!hasFocus()){ // Get out of touch mode since accessibility // wants to move focus around. getViewRootImpl().ensureTouchMode(false); return requestFocus(); } }break; caseAccessibilityNodeInfo.ACTION_CLEAR_FOCUS:{ if(hasFocus()){ clearFocus(); return!isFocused(); } }break; caseAccessibilityNodeInfo.ACTION_SELECT:{ if(!isSelected()){ setSelected(true); return isSelected(); } }break; caseAccessibilityNodeInfo.ACTION_CLEAR_SELECTION:{ if(isSelected()){ setSelected(false); return!isSelected(); } }break; caseAccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:{ if(!isAccessibilityFocused()){ return requestAccessibilityFocus(); } }break; caseAccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:{ if(isAccessibilityFocused()){ clearAccessibilityFocus(); returntrue; } }break; caseAccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:{ if(arguments !=null){ finalint granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); finalboolean extendSelection = arguments.getBoolean( AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN); return traverseAtGranularity(granularity,true, extendSelection); } }break; caseAccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:{ if(arguments !=null){ finalint granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); finalboolean extendSelection = arguments.getBoolean( AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN); return traverseAtGranularity(granularity,false, extendSelection); } }break; caseAccessibilityNodeInfo.ACTION_SET_SELECTION:{ CharSequence text = getIterableTextForAccessibility(); if(text ==null){ returnfalse; } finalint start =(arguments !=null)? arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT,-1):-1; finalint end =(arguments !=null)? arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,-1):-1; // Only cursor position can be specified (selection length == 0) if((getAccessibilitySelectionStart()!= start || getAccessibilitySelectionEnd()!= end) &&(start == end)){ setAccessibilitySelection(start, end); notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); returntrue; } }break; case R.id.accessibilityActionShowOnScreen:{ if(mAttachInfo !=null){ finalRect r = mAttachInfo.mTmpInvalRect; getDrawingRect(r); return requestRectangleOnScreen(r,true); } }break; case R.id.accessibilityActionContextClick:{ if(isContextClickable()){ performContextClick(); returntrue; } }break; case R.id.accessibilityActionShowTooltip:{ if((mTooltipInfo !=null)&&(mTooltipInfo.mTooltipPopup !=null)){ // Tooltip already showing returnfalse; } return showLongClickTooltip(0,0); } case R.id.accessibilityActionHideTooltip:{ if((mTooltipInfo ==null)||(mTooltipInfo.mTooltipPopup ==null)){ // No tooltip showing returnfalse; } hideTooltip(); returntrue; } case R.id.accessibilityActionDragDrop:{ if(!canAcceptAccessibilityDrop()){ returnfalse; } try{ if(mAttachInfo !=null&& mAttachInfo.mSession !=null){ finalint[] location =newint[2]; getLocationInWindow(location); finalint centerX = location[0]+ getWidth()/2; finalint centerY = location[1]+ getHeight()/2; return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow, centerX, centerY); } }catch(RemoteException e){ Log.e(VIEW_LOG_TAG,"Unable to drop for accessibility", e); } returnfalse; } case R.id.accessibilityActionDragCancel:{ if(!startedSystemDragForAccessibility()){ returnfalse; } if(mAttachInfo !=null&& mAttachInfo.mDragToken !=null){ cancelDragAndDrop(); returntrue; } returnfalse; } } returnfalse; } privateboolean canAcceptAccessibilityDrop(){ if(!canAcceptDrag()){ returnfalse; } ListenerInfo li = mListenerInfo; return(li !=null)&&(li.mOnDragListener !=null|| li.mOnReceiveContentListener !=null); } privateboolean traverseAtGranularity(int granularity,boolean forward, boolean extendSelection){ CharSequence text = getIterableTextForAccessibility(); if(text ==null|| text.length()==0){ returnfalse; } TextSegmentIterator iterator = getIteratorForGranularity(granularity); if(iterator ==null){ returnfalse; } int current = getAccessibilitySelectionEnd(); if(current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED){ current = forward ?0: text.length(); } finalint[] range = forward ? iterator.following(current): iterator.preceding(current); if(range ==null){ returnfalse; } finalint segmentStart = range[0]; finalint segmentEnd = range[1]; int selectionStart; int selectionEnd; if(extendSelection && isAccessibilitySelectionExtendable()){ prepareForExtendedAccessibilitySelection(); selectionStart = getAccessibilitySelectionStart(); if(selectionStart == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED){ selectionStart = forward ? segmentStart : segmentEnd; } selectionEnd = forward ? segmentEnd : segmentStart; }else{ selectionStart = selectionEnd= forward ? segmentEnd : segmentStart; } setAccessibilitySelection(selectionStart, selectionEnd); finalint action = forward ?AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY :AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY; sendViewTextTraversedAtGranularityEvent(action, granularity, segmentStart, segmentEnd); returntrue; } /** * Gets the text reported for accessibility purposes. * * @return The accessibility text. * * @hide */ @UnsupportedAppUsage publicCharSequence getIterableTextForAccessibility(){ return getContentDescription(); } /** * Gets whether accessibility selection can be extended. * * @return If selection is extensible. * * @hide */ publicboolean isAccessibilitySelectionExtendable(){ returnfalse; } /** * Prepare for extended selection. * @hide */ publicvoid prepareForExtendedAccessibilitySelection(){ return; } /** * @hide */ publicint getAccessibilitySelectionStart(){ return mAccessibilityCursorPosition; } /** * @hide */ publicint getAccessibilitySelectionEnd(){ return getAccessibilitySelectionStart(); } /** * @hide */ publicvoid setAccessibilitySelection(int start,int end){ if(start == end && end == mAccessibilityCursorPosition){ return; } if(start >=0&& start == end && end <= getIterableTextForAccessibility().length()){ mAccessibilityCursorPosition = start; }else{ mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); } privatevoid sendViewTextTraversedAtGranularityEvent(int action,int granularity, int fromIndex,int toIndex){ if(mParent ==null){ return; } AccessibilityEvent event =AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); onInitializeAccessibilityEvent(event); onPopulateAccessibilityEvent(event); event.setFromIndex(fromIndex); event.setToIndex(toIndex); event.setAction(action); event.setMovementGranularity(granularity); mParent.requestSendAccessibilityEvent(this, event); } /** * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicTextSegmentIterator getIteratorForGranularity(int granularity){ switch(granularity){ caseAccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER:{ CharSequence text = getIterableTextForAccessibility(); if(text !=null&& text.length()>0){ CharacterTextSegmentIterator iterator = CharacterTextSegmentIterator.getInstance( mContext.getResources().getConfiguration().locale); iterator.initialize(text.toString()); return iterator; } }break; caseAccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD:{ CharSequence text = getIterableTextForAccessibility(); if(text !=null&& text.length()>0){ WordTextSegmentIterator iterator = WordTextSegmentIterator.getInstance( mContext.getResources().getConfiguration().locale); iterator.initialize(text.toString()); return iterator; } }break; caseAccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH:{ CharSequence text = getIterableTextForAccessibility(); if(text !=null&& text.length()>0){ ParagraphTextSegmentIterator iterator = ParagraphTextSegmentIterator.getInstance(); iterator.initialize(text.toString()); return iterator; } }break; } returnnull; } /** * Tells whether the {@link View} is in the state between {@link #onStartTemporaryDetach()} * and {@link #onFinishTemporaryDetach()}. * * <p>This method always returns {@code true} when called directly or indirectly from * {@link #onStartTemporaryDetach()}. The return value when called directly or indirectly from * {@link #onFinishTemporaryDetach()}, however, depends on the OS version. * <ul> * <li>{@code true} on {@link android.os.Build.VERSION_CODES#N API 24}</li> * <li>{@code false} on {@link android.os.Build.VERSION_CODES#N_MR1 API 25}} and later</li> * </ul> * </p> * * @return {@code true} when the View is in the state between {@link #onStartTemporaryDetach()} * and {@link #onFinishTemporaryDetach()}. */ publicfinalboolean isTemporarilyDetached(){ return(mPrivateFlags3 & PFLAG3_TEMPORARY_DETACH)!=0; } /** * Dispatch {@link #onStartTemporaryDetach()} to this View and its direct children if this is * a container View. */ @CallSuper publicvoid dispatchStartTemporaryDetach(){ mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH; notifyEnterOrExitForAutoFillIfNeeded(false); notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); onStartTemporaryDetach(); } /** * This is called when a container is going to temporarily detach a child, with * {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}. * It will either be followed by {@link #onFinishTemporaryDetach()} or * {@link #onDetachedFromWindow()} when the container is done. */ publicvoid onStartTemporaryDetach(){ removeUnsetPressCallback(); mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT; } /** * Dispatch {@link #onFinishTemporaryDetach()} to this View and its direct children if this is * a container View. */ @CallSuper publicvoid dispatchFinishTemporaryDetach(){ mPrivateFlags3 &=~PFLAG3_TEMPORARY_DETACH; onFinishTemporaryDetach(); if(hasWindowFocus()&& hasFocus()){ notifyFocusChangeToImeFocusController(true/* hasFocus */); } notifyEnterOrExitForAutoFillIfNeeded(true); notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } /** * Called after {@link #onStartTemporaryDetach} when the container is done * changing the view. */ publicvoid onFinishTemporaryDetach(){ } /** * Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState} * for this view's window. Returns null if the view is not currently attached * to the window. Normally you will not need to use this directly, but * just use the standard high-level event callbacks like * {@link #onKeyDown(int, KeyEvent)}. */ publicKeyEvent.DispatcherState getKeyDispatcherState(){ return mAttachInfo !=null? mAttachInfo.mKeyDispatchState :null; } /** * Dispatch a key event before it is processed by any input method * associated with the view hierarchy. This can be used to intercept * key events in special situations before the IME consumes them; a * typical example would be handling the BACK key to update the application's * UI instead of allowing the IME to see it and close itself. * * @param event The key event to be dispatched. * @return True if the event was handled, false otherwise. */ publicboolean dispatchKeyEventPreIme(KeyEvent event){ return onKeyPreIme(event.getKeyCode(), event); } /** * Dispatch a key event to the next view on the focus path. This path runs * from the top of the view tree down to the currently focused view. If this * view has focus, it will dispatch to itself. Otherwise it will dispatch * the next node down the focus path. This method also fires any key * listeners. * * @param event The key event to be dispatched. * @return True if the event was handled, false otherwise. */ publicboolean dispatchKeyEvent(KeyEvent event){ if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onKeyEvent(event,0); } // Give any attached key listener a first crack at the event. //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnKeyListener !=null&&(mViewFlags & ENABLED_MASK)== ENABLED && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)){ returntrue; } if(event.dispatch(this, mAttachInfo !=null ? mAttachInfo.mKeyDispatchState :null,this)){ returntrue; } if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onUnhandledEvent(event,0); } returnfalse; } /** * Dispatches a key shortcut event. * * @param event The key event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ publicboolean dispatchKeyShortcutEvent(KeyEvent event){ return onKeyShortcut(event.getKeyCode(), event); } /** * Pass the touch screen motion event down to the target view, or this * view if it is the target. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ publicboolean dispatchTouchEvent(MotionEvent event){ // If the event should be handled by accessibility focus first. if(event.isTargetAccessibilityFocus()){ // We don't have focus or no virtual descendant has it, do not handle the event. if(!isAccessibilityFocusedViewOrHost()){ returnfalse; } // We have focus and got the event, then use normal event dispatch. event.setTargetAccessibilityFocus(false); } boolean result =false; if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onTouchEvent(event,0); } finalint actionMasked = event.getActionMasked(); if(actionMasked ==MotionEvent.ACTION_DOWN){ // Defensive cleanup for new gesture stopNestedScroll(); } if(onFilterTouchEventForSecurity(event)){ if((mViewFlags & ENABLED_MASK)== ENABLED && handleScrollBarDragging(event)){ result =true; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnTouchListener !=null &&(mViewFlags & ENABLED_MASK)== ENABLED && li.mOnTouchListener.onTouch(this, event)){ result =true; } if(!result && onTouchEvent(event)){ result =true; } } if(!result && mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onUnhandledEvent(event,0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture. if(actionMasked ==MotionEvent.ACTION_UP || actionMasked ==MotionEvent.ACTION_CANCEL || (actionMasked ==MotionEvent.ACTION_DOWN &&!result)){ stopNestedScroll(); } return result; } boolean isAccessibilityFocusedViewOrHost(){ return isAccessibilityFocused()||(getViewRootImpl()!=null&& getViewRootImpl() .getAccessibilityFocusedHost()==this); } /** * Returns whether this view can receive pointer events. * * @return {@code true} if this view can receive pointer events. * @hide */ protectedboolean canReceivePointerEvents(){ return(mViewFlags & VISIBILITY_MASK)== VISIBLE || getAnimation()!=null; } /** * Filter the touch event to apply security policies. * * @param event The motion event to be filtered. * @return True if the event should be dispatched, false if the event should be dropped. * * @see #getFilterTouchesWhenObscured */ publicboolean onFilterTouchEventForSecurity(MotionEvent event){ //noinspection RedundantIfStatement if((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED)!=0 &&(event.getFlags()&MotionEvent.FLAG_WINDOW_IS_OBSCURED)!=0){ // Window is obscured, drop this touch. returnfalse; } returntrue; } /** * Pass a trackball motion event down to the focused view. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ publicboolean dispatchTrackballEvent(MotionEvent event){ if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onTrackballEvent(event,0); } return onTrackballEvent(event); } /** * Pass a captured pointer event down to the focused view. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ publicboolean dispatchCapturedPointerEvent(MotionEvent event){ if(!hasPointerCapture()){ returnfalse; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnCapturedPointerListener !=null && li.mOnCapturedPointerListener.onCapturedPointer(this, event)){ returntrue; } return onCapturedPointerEvent(event); } /** * Dispatch a generic motion event. * <p> * Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER} * are delivered to the view under the pointer. All other generic motion events are * delivered to the focused view. Hover events are handled specially and are delivered * to {@link #onHoverEvent(MotionEvent)}. * </p> * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ publicboolean dispatchGenericMotionEvent(MotionEvent event){ if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onGenericMotionEvent(event,0); } finalint source = event.getSource(); if((source &InputDevice.SOURCE_CLASS_POINTER)!=0){ finalint action = event.getAction(); if(action ==MotionEvent.ACTION_HOVER_ENTER || action ==MotionEvent.ACTION_HOVER_MOVE || action ==MotionEvent.ACTION_HOVER_EXIT){ if(dispatchHoverEvent(event)){ returntrue; } }elseif(dispatchGenericPointerEvent(event)){ returntrue; } }elseif(dispatchGenericFocusedEvent(event)){ returntrue; } if(dispatchGenericMotionEventInternal(event)){ returntrue; } if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onUnhandledEvent(event,0); } returnfalse; } privateboolean dispatchGenericMotionEventInternal(MotionEvent event){ //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnGenericMotionListener !=null &&(mViewFlags & ENABLED_MASK)== ENABLED && li.mOnGenericMotionListener.onGenericMotion(this, event)){ returntrue; } if(onGenericMotionEvent(event)){ returntrue; } finalint actionButton = event.getActionButton(); switch(event.getActionMasked()){ caseMotionEvent.ACTION_BUTTON_PRESS: if(isContextClickable()&&!mInContextButtonPress &&!mHasPerformedLongPress &&(actionButton ==MotionEvent.BUTTON_STYLUS_PRIMARY || actionButton ==MotionEvent.BUTTON_SECONDARY)){ if(performContextClick(event.getX(), event.getY())){ mInContextButtonPress =true; setPressed(true, event.getX(), event.getY()); removeTapCallback(); removeLongPressCallback(); returntrue; } } break; caseMotionEvent.ACTION_BUTTON_RELEASE: if(mInContextButtonPress &&(actionButton ==MotionEvent.BUTTON_STYLUS_PRIMARY || actionButton ==MotionEvent.BUTTON_SECONDARY)){ mInContextButtonPress =false; mIgnoreNextUpEvent =true; } break; } if(mInputEventConsistencyVerifier !=null){ mInputEventConsistencyVerifier.onUnhandledEvent(event,0); } returnfalse; } /** * Dispatch a hover event. * <p> * Do not call this method directly. * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead. * </p> * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ protectedboolean dispatchHoverEvent(MotionEvent event){ ListenerInfo li = mListenerInfo; //noinspection SimplifiableIfStatement if(li !=null&& li.mOnHoverListener !=null &&(mViewFlags & ENABLED_MASK)== ENABLED && li.mOnHoverListener.onHover(this, event)){ returntrue; } return onHoverEvent(event); } /** * Returns true if the view has a child to which it has recently sent * {@link MotionEvent#ACTION_HOVER_ENTER}. If this view is hovered and * it does not have a hovered child, then it must be the innermost hovered view. * @hide */ protectedboolean hasHoveredChild(){ returnfalse; } /** * Returns true if the given point, in local coordinates, is inside the hovered child. * * @hide */ protectedboolean pointInHoveredChild(MotionEvent event){ returnfalse; } /** * Dispatch a generic motion event to the view under the first pointer. * <p> * Do not call this method directly. * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead. * </p> * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ protectedboolean dispatchGenericPointerEvent(MotionEvent event){ returnfalse; } /** * Dispatch a generic motion event to the currently focused view. * <p> * Do not call this method directly. * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead. * </p> * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ protectedboolean dispatchGenericFocusedEvent(MotionEvent event){ returnfalse; } /** * Dispatch a pointer event. * <p> * Dispatches touch related pointer events to {@link #onTouchEvent(MotionEvent)} and all * other events to {@link #onGenericMotionEvent(MotionEvent)}. This separation of concerns * reinforces the invariant that {@link #onTouchEvent(MotionEvent)} is really about touches * and should not be expected to handle other pointing device features. * </p> * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicfinalboolean dispatchPointerEvent(MotionEvent event){ if(event.isTouchEvent()){ return dispatchTouchEvent(event); }else{ return dispatchGenericMotionEvent(event); } } /** * Called when the window containing this view gains or loses window focus. * ViewGroups should override to route to their children. * * @param hasFocus True if the window containing this view now has focus, * false otherwise. */ publicvoid dispatchWindowFocusChanged(boolean hasFocus){ onWindowFocusChanged(hasFocus); } /** * Called when the window containing this view gains or loses focus. Note * that this is separate from view focus: to receive key events, both * your view and its window must have focus. If a window is displayed * on top of yours that takes input focus, then your own window will lose * focus but the view focus will remain unchanged. * * @param hasWindowFocus True if the window containing this view now has * focus, false otherwise. */ publicvoid onWindowFocusChanged(boolean hasWindowFocus){ if(!hasWindowFocus){ if(isPressed()){ setPressed(false); } mPrivateFlags3 &=~PFLAG3_FINGER_DOWN; if((mPrivateFlags & PFLAG_FOCUSED)!=0){ notifyFocusChangeToImeFocusController(false/* hasFocus */); } removeLongPressCallback(); removeTapCallback(); onFocusLost(); }elseif((mPrivateFlags & PFLAG_FOCUSED)!=0){ notifyFocusChangeToImeFocusController(true/* hasFocus */); } refreshDrawableState(); } /** * Returns true if this view is in a window that currently has window focus. * Note that this is not the same as the view itself having focus. * * @return True if this view is in a window that currently has window focus. */ publicboolean hasWindowFocus(){ return mAttachInfo !=null&& mAttachInfo.mHasWindowFocus; } /** * @return {@code true} if this view is in a window that currently has IME focusable state. * @hide */ publicboolean hasImeFocus(){ return getViewRootImpl()!=null&& getViewRootImpl().getImeFocusController().hasImeFocus(); } /** * Dispatch a view visibility change down the view hierarchy. * ViewGroups should override to route to their children. * @param changedView The view whose visibility changed. Could be 'this' or * an ancestor view. * @param visibility The new visibility of changedView: {@link #VISIBLE}, * {@link #INVISIBLE} or {@link #GONE}. */ protectedvoid dispatchVisibilityChanged(@NonNullView changedView, @Visibilityint visibility){ onVisibilityChanged(changedView, visibility); } /** * Called when the visibility of the view or an ancestor of the view has * changed. * * @param changedView The view whose visibility changed. May be * {@code this} or an ancestor view. * @param visibility The new visibility, one of {@link #VISIBLE}, * {@link #INVISIBLE} or {@link #GONE}. */ protectedvoid onVisibilityChanged(@NonNullView changedView,@Visibilityint visibility){ } /** * Dispatch a hint about whether this view is displayed. For instance, when * a View moves out of the screen, it might receives a display hint indicating * the view is not displayed. Applications should not <em>rely</em> on this hint * as there is no guarantee that they will receive one. * * @param hint A hint about whether or not this view is displayed: * {@link #VISIBLE} or {@link #INVISIBLE}. */ publicvoid dispatchDisplayHint(@Visibilityint hint){ onDisplayHint(hint); } /** * Gives this view a hint about whether is displayed or not. For instance, when * a View moves out of the screen, it might receives a display hint indicating * the view is not displayed. Applications should not <em>rely</em> on this hint * as there is no guarantee that they will receive one. * * @param hint A hint about whether or not this view is displayed: * {@link #VISIBLE} or {@link #INVISIBLE}. */ protectedvoid onDisplayHint(@Visibilityint hint){ } /** * Dispatch a window visibility change down the view hierarchy. * ViewGroups should override to route to their children. * * @param visibility The new visibility of the window. * * @see #onWindowVisibilityChanged(int) */ publicvoid dispatchWindowVisibilityChanged(@Visibilityint visibility){ onWindowVisibilityChanged(visibility); } /** * Called when the window containing has change its visibility * (between {@link #GONE}, {@link #INVISIBLE}, and {@link #VISIBLE}). Note * that this tells you whether or not your window is being made visible * to the window manager; this does <em>not</em> tell you whether or not * your window is obscured by other windows on the screen, even if it * is itself visible. * * @param visibility The new visibility of the window. */ protectedvoid onWindowVisibilityChanged(@Visibilityint visibility){ if(visibility == VISIBLE){ initialAwakenScrollBars(); } } /** * @return true if this view and all ancestors are visible as of the last * {@link #onVisibilityAggregated(boolean)} call. * * @hide */ publicboolean isAggregatedVisible(){ return(mPrivateFlags3 & PFLAG3_AGGREGATED_VISIBLE)!=0; } /** * Internal dispatching method for {@link #onVisibilityAggregated}. Overridden by * ViewGroup. Intended to only be called when {@link #isAttachedToWindow()}, * {@link #getWindowVisibility()} is {@link #VISIBLE} and this view's parent {@link #isShown()}. * * @param isVisible true if this view's visibility to the user is uninterrupted by its * ancestors or by window visibility * @return true if this view is visible to the user, not counting clipping or overlapping */ boolean dispatchVisibilityAggregated(boolean isVisible){ finalboolean thisVisible = getVisibility()== VISIBLE; // If we're not visible but something is telling us we are, ignore it. if(thisVisible ||!isVisible){ onVisibilityAggregated(isVisible); } return thisVisible && isVisible; } /** * Called when the user-visibility of this View is potentially affected by a change * to this view itself, an ancestor view or the window this view is attached to. * * @param isVisible true if this view and all of its ancestors are {@link #VISIBLE} * and this view's window is also visible */ @CallSuper publicvoid onVisibilityAggregated(boolean isVisible){ // Update our internal visibility tracking so we can detect changes boolean oldVisible = isAggregatedVisible(); mPrivateFlags3 = isVisible ?(mPrivateFlags3 | PFLAG3_AGGREGATED_VISIBLE) :(mPrivateFlags3 &~PFLAG3_AGGREGATED_VISIBLE); if(isVisible && mAttachInfo !=null){ initialAwakenScrollBars(); } finalDrawable dr = mBackground; if(dr !=null&& isVisible != dr.isVisible()){ dr.setVisible(isVisible,false); } finalDrawable hl = mDefaultFocusHighlight; if(hl !=null&& isVisible != hl.isVisible()){ hl.setVisible(isVisible,false); } finalDrawable fg = mForegroundInfo !=null? mForegroundInfo.mDrawable :null; if(fg !=null&& isVisible != fg.isVisible()){ fg.setVisible(isVisible,false); } if(isAutofillable()){ AutofillManager afm = getAutofillManager(); if(afm !=null&& getAutofillViewId()> LAST_APP_AUTOFILL_ID){ if(mVisibilityChangeForAutofillHandler !=null){ mVisibilityChangeForAutofillHandler.removeMessages(0); } // If the view is in the background but still part of the hierarchy this is called // with isVisible=false. Hence visibility==false requires further checks if(isVisible){ afm.notifyViewVisibilityChanged(this,true); }else{ if(mVisibilityChangeForAutofillHandler ==null){ mVisibilityChangeForAutofillHandler = newVisibilityChangeForAutofillHandler(afm,this); } // Let current operation (e.g. removal of the view from the hierarchy) // finish before checking state mVisibilityChangeForAutofillHandler.obtainMessage(0,this).sendToTarget(); } } } if(isVisible != oldVisible){ if(isAccessibilityPane()){ notifyViewAccessibilityStateChangedIfNeeded(isVisible ?AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED :AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); } notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible); if(!getSystemGestureExclusionRects().isEmpty()){ postUpdate(this::updateSystemGestureExclusionRects); } if(!collectPreferKeepClearRects().isEmpty()){ postUpdate(this::updateKeepClearRects); } } } /** * Returns the current visibility of the window this view is attached to * (either {@link #GONE}, {@link #INVISIBLE}, or {@link #VISIBLE}). * * @return Returns the current visibility of the view's window. */ @Visibility publicint getWindowVisibility(){ return mAttachInfo !=null? mAttachInfo.mWindowVisibility : GONE; } /** * Retrieve the overall visible display size in which the window this view is * attached to has been positioned in. This takes into account screen * decorations above the window, for both cases where the window itself * is being position inside of them or the window is being placed under * then and covered insets are used for the window to position its content * inside. In effect, this tells you the available area where content can * be placed and remain visible to users. * * @param outRect Filled in with the visible display frame. If the view * is not attached to a window, this is simply the raw display size. */ publicvoid getWindowVisibleDisplayFrame(Rect outRect){ if(mAttachInfo !=null){ mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect); return; } // The view is not attached to a display so we don't have a context. // Make a best guess about the display size. Display d =DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); d.getRectSize(outRect); } /** * Like {@link #getWindowVisibleDisplayFrame}, but returns the "full" display frame this window * is currently in without any insets. * * @hide */ @UnsupportedAppUsage publicvoid getWindowDisplayFrame(Rect outRect){ if(mAttachInfo !=null){ mAttachInfo.mViewRootImpl.getDisplayFrame(outRect); return; } // The view is not attached to a display so we don't have a context. // Make a best guess about the display size. Display d =DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); d.getRectSize(outRect); } /** * Dispatch a notification about a resource configuration change down * the view hierarchy. * ViewGroups should override to route to their children. * * @param newConfig The new resource configuration. * * @see #onConfigurationChanged(android.content.res.Configuration) */ publicvoid dispatchConfigurationChanged(Configuration newConfig){ onConfigurationChanged(newConfig); } /** * Called when the current configuration of the resources being used * by the application have changed. You can use this to decide when * to reload resources that can changed based on orientation and other * configuration characteristics. You only need to use this if you are * not relying on the normal {@link android.app.Activity} mechanism of * recreating the activity instance upon a configuration change. * * @param newConfig The new resource configuration. */ protectedvoid onConfigurationChanged(Configuration newConfig){ } /** * Private function to aggregate all per-view attributes in to the view * root. */ void dispatchCollectViewAttributes(AttachInfo attachInfo,int visibility){ performCollectViewAttributes(attachInfo, visibility); } void performCollectViewAttributes(AttachInfo attachInfo,int visibility){ if((visibility & VISIBILITY_MASK)== VISIBLE){ if((mViewFlags & KEEP_SCREEN_ON)== KEEP_SCREEN_ON){ attachInfo.mKeepScreenOn =true; } attachInfo.mSystemUiVisibility |= mSystemUiVisibility; ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnSystemUiVisibilityChangeListener !=null){ attachInfo.mHasSystemUiListeners =true; } } } void needGlobalAttributesUpdate(boolean force){ finalAttachInfo ai = mAttachInfo; if(ai !=null&&!ai.mRecomputeGlobalAttributes){ if(force || ai.mKeepScreenOn ||(ai.mSystemUiVisibility !=0) || ai.mHasSystemUiListeners){ ai.mRecomputeGlobalAttributes =true; } } } /** * Returns whether the device is currently in touch mode. Touch mode is entered * once the user begins interacting with the device by touch, and affects various * things like whether focus is always visible to the user. * * @return Whether the device is in touch mode. */ @ViewDebug.ExportedProperty publicboolean isInTouchMode(){ if(mAttachInfo !=null){ return mAttachInfo.mInTouchMode; }else{ returnViewRootImpl.isInTouchMode(); } } /** * Returns the context the view is running in, through which it can * access the current theme, resources, etc. * * @return The view's Context. */ @ViewDebug.CapturedViewProperty @UiContext publicfinalContext getContext(){ return mContext; } /** * Handle a key event before it is processed by any input method * associated with the view hierarchy. This can be used to intercept * key events in special situations before the IME consumes them; a * typical example would be handling the BACK key to update the application's * UI instead of allowing the IME to see it and close itself. * * @param keyCode The value in event.getKeyCode(). * @param event Description of the key event. * @return If you handled the event, return true. If you want to allow the * event to be handled by the next receiver, return false. */ publicboolean onKeyPreIme(int keyCode,KeyEvent event){ returnfalse; } /** * Default implementation of {@link KeyEvent.Callback#onKeyDown(int, KeyEvent) * KeyEvent.Callback.onKeyDown()}: perform press of the view * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER} * is released, if the view is enabled and clickable. * <p> * Key presses in software keyboards will generally NOT trigger this * listener, although some may elect to do so in some situations. Do not * rely on this to catch software key presses. * * @param keyCode a key code that represents the button pressed, from * {@link android.view.KeyEvent} * @param event the KeyEvent object that defines the button action */ publicboolean onKeyDown(int keyCode,KeyEvent event){ if(KeyEvent.isConfirmKey(keyCode)&& event.hasNoModifiers()){ if((mViewFlags & ENABLED_MASK)== DISABLED){ returntrue; } if(event.getRepeatCount()==0){ // Long clickable items don't necessarily have to be clickable. finalboolean clickable =(mViewFlags & CLICKABLE)== CLICKABLE ||(mViewFlags & LONG_CLICKABLE)== LONG_CLICKABLE; if(clickable ||(mViewFlags & TOOLTIP)== TOOLTIP){ // For the purposes of menu anchoring and drawable hotspots, // key events are considered to be at the center of the view. finalfloat x = getWidth()/2f; finalfloat y = getHeight()/2f; if(clickable){ setPressed(true, x, y); } checkForLongClick( ViewConfiguration.getLongPressTimeout(), x, y, // This is not a touch gesture -- do not classify it as one. TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION); returntrue; } } } returnfalse; } /** * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent) * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle * the event). * <p>Key presses in software keyboards will generally NOT trigger this listener, * although some may elect to do so in some situations. Do not rely on this to * catch software key presses. */ publicboolean onKeyLongPress(int keyCode,KeyEvent event){ returnfalse; } /** * Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent) * KeyEvent.Callback.onKeyUp()}: perform clicking of the view * when {@link KeyEvent#KEYCODE_DPAD_CENTER}, {@link KeyEvent#KEYCODE_ENTER} * or {@link KeyEvent#KEYCODE_SPACE} is released. * <p>Key presses in software keyboards will generally NOT trigger this listener, * although some may elect to do so in some situations. Do not rely on this to * catch software key presses. * * @param keyCode A key code that represents the button pressed, from * {@link android.view.KeyEvent}. * @param event The KeyEvent object that defines the button action. */ publicboolean onKeyUp(int keyCode,KeyEvent event){ if(KeyEvent.isConfirmKey(keyCode)&& event.hasNoModifiers()){ if((mViewFlags & ENABLED_MASK)== DISABLED){ returntrue; } if((mViewFlags & CLICKABLE)== CLICKABLE && isPressed()){ setPressed(false); if(!mHasPerformedLongPress){ // This is a tap, so remove the longpress check removeLongPressCallback(); if(!event.isCanceled()){ return performClickInternal(); } } } } returnfalse; } /** * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle * the event). * <p>Key presses in software keyboards will generally NOT trigger this listener, * although some may elect to do so in some situations. Do not rely on this to * catch software key presses. * * @param keyCode A key code that represents the button pressed, from * {@link android.view.KeyEvent}. * @param repeatCount The number of times the action was made. * @param event The KeyEvent object that defines the button action. */ publicboolean onKeyMultiple(int keyCode,int repeatCount,KeyEvent event){ returnfalse; } /** * Called on the focused view when a key shortcut event is not handled. * Override this method to implement local key shortcuts for the View. * Key shortcuts can also be implemented by setting the * {@link MenuItem#setShortcut(char, char) shortcut} property of menu items. * * @param keyCode The value in event.getKeyCode(). * @param event Description of the key event. * @return If you handled the event, return true. If you want to allow the * event to be handled by the next receiver, return false. */ publicboolean onKeyShortcut(int keyCode,KeyEvent event){ returnfalse; } /** * Check whether the called view is a text editor, in which case it * would make sense to automatically display a soft input window for * it. Subclasses should override this if they implement * {@link #onCreateInputConnection(EditorInfo)} to return true if * a call on that method would return a non-null InputConnection, and * they are really a first-class editor that the user would normally * start typing on when the go into a window containing your view. * * <p>The default implementation always returns false. This does * <em>not</em> mean that its {@link #onCreateInputConnection(EditorInfo)} * will not be called or the user can not otherwise perform edits on your * view; it is just a hint to the system that this is not the primary * purpose of this view. * * @return Returns true if this view is a text editor, else false. */ publicboolean onCheckIsTextEditor(){ returnfalse; } /** * Create a new InputConnection for an InputMethod to interact * with the view. The default implementation returns null, since it doesn't * support input methods. You can override this to implement such support. * This is only needed for views that take focus and text input. * * <p>When implementing this, you probably also want to implement * {@link #onCheckIsTextEditor()} to indicate you will return a * non-null InputConnection.</p> * * <p>Also, take good care to fill in the {@link android.view.inputmethod.EditorInfo} * object correctly and in its entirety, so that the connected IME can rely * on its values. For example, {@link android.view.inputmethod.EditorInfo#initialSelStart} * and {@link android.view.inputmethod.EditorInfo#initialSelEnd} members * must be filled in with the correct cursor position for IMEs to work correctly * with your application.</p> * * @param outAttrs Fill in with attribute information about the connection. */ publicInputConnection onCreateInputConnection(EditorInfo outAttrs){ returnnull; } /** * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application * that the system has successfully initialized an {@link InputConnection} and it is ready for * use. * * <p>The default implementation does nothing, since a view doesn't support input methods by * default (see {@link #onCreateInputConnection}). * * @param inputConnection The {@link InputConnection} from {@link #onCreateInputConnection}, * after it's been fully initialized by the system. * @param editorInfo The {@link EditorInfo} that was used to create the {@link InputConnection}. * @param handler The dedicated {@link Handler} on which IPC method calls from input methods * will be dispatched. This is the handler returned by {@link InputConnection#getHandler()}. If * that method returns null, this parameter will be null also. * * @hide */ publicvoid onInputConnectionOpenedInternal(@NonNullInputConnection inputConnection, @NonNullEditorInfo editorInfo,@NullableHandler handler){} /** * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application * that the {@link InputConnection} has been closed. * * <p>The default implementation does nothing, since a view doesn't support input methods by * default (see {@link #onCreateInputConnection}). * * <p><strong>Note:</strong> This callback is not invoked if the view is already detached when * the {@link InputConnection} is closed or the connection is not valid and managed by * {@link com.android.server.inputmethod.InputMethodManagerService}. * TODO(b/170645312): Before un-hiding this API, handle the detached view scenario. * * @hide */ publicvoid onInputConnectionClosedInternal(){} /** * Called by the {@link android.view.inputmethod.InputMethodManager} * when a view who is not the current * input connection target is trying to make a call on the manager. The * default implementation returns false; you can override this to return * true for certain views if you are performing InputConnection proxying * to them. * @param view The View that is making the InputMethodManager call. * @return Return true to allow the call, false to reject. */ publicboolean checkInputConnectionProxy(View view){ returnfalse; } /** * Show the context menu for this view. It is not safe to hold on to the * menu after returning from this method. * * You should normally not overload this method. Overload * {@link #onCreateContextMenu(ContextMenu)} or define an * {@link OnCreateContextMenuListener} to add items to the context menu. * * @param menu The context menu to populate */ publicvoid createContextMenu(ContextMenu menu){ ContextMenuInfo menuInfo = getContextMenuInfo(); // Sets the current menu info so all items added to menu will have // my extra info set. ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo); onCreateContextMenu(menu); ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnCreateContextMenuListener !=null){ li.mOnCreateContextMenuListener.onCreateContextMenu(menu,this, menuInfo); } // Clear the extra information so subsequent items that aren't mine don't // have my extra info. ((MenuBuilder)menu).setCurrentMenuInfo(null); if(mParent !=null){ mParent.createContextMenu(menu); } } /** * Views should implement this if they have extra information to associate * with the context menu. The return result is supplied as a parameter to * the {@link OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} * callback. * * @return Extra information about the item for which the context menu * should be shown. This information will vary across different * subclasses of View. */ protectedContextMenuInfo getContextMenuInfo(){ returnnull; } /** * Views should implement this if the view itself is going to add items to * the context menu. * * @param menu the context menu to populate */ protectedvoid onCreateContextMenu(ContextMenu menu){ } /** * Implement this method to handle trackball motion events. The * <em>relative</em> movement of the trackball since the last event * can be retrieve with {@link MotionEvent#getX MotionEvent.getX()} and * {@link MotionEvent#getY MotionEvent.getY()}. These are normalized so * that a movement of 1 corresponds to the user pressing one DPAD key (so * they will often be fractional values, representing the more fine-grained * movement information available from a trackball). * * @param event The motion event. * @return True if the event was handled, false otherwise. */ publicboolean onTrackballEvent(MotionEvent event){ returnfalse; } /** * Implement this method to handle generic motion events. * <p> * Generic motion events describe joystick movements, mouse hovers, track pad * touches, scroll wheel movements and other input events. The * {@link MotionEvent#getSource() source} of the motion event specifies * the class of input that was received. Implementations of this method * must examine the bits in the source before processing the event. * The following code example shows how this is done. * </p><p> * Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER} * are delivered to the view under the pointer. All other generic motion events are * delivered to the focused view. * </p> * <pre> public boolean onGenericMotionEvent(MotionEvent event) { * if (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) { * if (event.getAction() == MotionEvent.ACTION_MOVE) { * // process the joystick movement... * return true; * } * } * if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { * switch (event.getAction()) { * case MotionEvent.ACTION_HOVER_MOVE: * // process the mouse hover movement... * return true; * case MotionEvent.ACTION_SCROLL: * // process the scroll wheel movement... * return true; * } * } * return super.onGenericMotionEvent(event); * }</pre> * * @param event The generic motion event being processed. * @return True if the event was handled, false otherwise. */ publicboolean onGenericMotionEvent(MotionEvent event){ returnfalse; } /** * Dispatching hover events to {@link TouchDelegate} to improve accessibility. * <p> * This method is dispatching hover events to the delegate target to support explore by touch. * Similar to {@link ViewGroup#dispatchTouchEvent}, this method send proper hover events to * the delegate target according to the pointer and the touch area of the delegate while touch * exploration enabled. * </p> * * @param event The motion event dispatch to the delegate target. * @return True if the event was handled, false otherwise. * * @see #onHoverEvent */ privateboolean dispatchTouchExplorationHoverEvent(MotionEvent event){ finalAccessibilityManager manager =AccessibilityManager.getInstance(mContext); if(!manager.isEnabled()||!manager.isTouchExplorationEnabled()){ returnfalse; } finalboolean oldHoveringTouchDelegate = mHoveringTouchDelegate; finalint action = event.getActionMasked(); boolean pointInDelegateRegion =false; boolean handled =false; finalAccessibilityNodeInfo.TouchDelegateInfo info = mTouchDelegate.getTouchDelegateInfo(); for(int i =0; i < info.getRegionCount(); i++){ Region r = info.getRegionAt(i); if(r.contains((int) event.getX(),(int) event.getY())){ pointInDelegateRegion =true; } } // Explore by touch should dispatch events to children under the pointer first if any // before dispatching to TouchDelegate. For non-hoverable views that do not consume // hover events but receive accessibility focus, it should also not delegate to these // views when hovered. if(!oldHoveringTouchDelegate){ if((action ==MotionEvent.ACTION_HOVER_ENTER || action ==MotionEvent.ACTION_HOVER_MOVE) &&!pointInHoveredChild(event) && pointInDelegateRegion){ mHoveringTouchDelegate =true; } }else{ if(action ==MotionEvent.ACTION_HOVER_EXIT ||(action ==MotionEvent.ACTION_HOVER_MOVE &&(pointInHoveredChild(event)||!pointInDelegateRegion))){ mHoveringTouchDelegate =false; } } switch(action){ caseMotionEvent.ACTION_HOVER_MOVE: if(oldHoveringTouchDelegate && mHoveringTouchDelegate){ // Inside bounds, dispatch as is. handled = mTouchDelegate.onTouchExplorationHoverEvent(event); }elseif(!oldHoveringTouchDelegate && mHoveringTouchDelegate){ // Moving inbound, synthesize hover enter. MotionEvent eventNoHistory =(event.getHistorySize()==0) ? event :MotionEvent.obtainNoHistory(event); eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER); handled = mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory); eventNoHistory.setAction(action); handled |= mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory); }elseif(oldHoveringTouchDelegate &&!mHoveringTouchDelegate){ // Moving outbound, synthesize hover exit. finalboolean hoverExitPending = event.isHoverExitPending(); event.setHoverExitPending(true); mTouchDelegate.onTouchExplorationHoverEvent(event); MotionEvent eventNoHistory =(event.getHistorySize()==0) ? event :MotionEvent.obtainNoHistory(event); eventNoHistory.setHoverExitPending(hoverExitPending); eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT); mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory); }// else: outside bounds, do nothing. break; caseMotionEvent.ACTION_HOVER_ENTER: if(!oldHoveringTouchDelegate && mHoveringTouchDelegate){ handled = mTouchDelegate.onTouchExplorationHoverEvent(event); } break; caseMotionEvent.ACTION_HOVER_EXIT: if(oldHoveringTouchDelegate){ mTouchDelegate.onTouchExplorationHoverEvent(event); } break; } return handled; } /** * Implement this method to handle hover events. * <p> * This method is called whenever a pointer is hovering into, over, or out of the * bounds of a view and the view is not currently being touched. * Hover events are represented as pointer events with action * {@link MotionEvent#ACTION_HOVER_ENTER}, {@link MotionEvent#ACTION_HOVER_MOVE}, * or {@link MotionEvent#ACTION_HOVER_EXIT}. * </p> * <ul> * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_ENTER} * when the pointer enters the bounds of the view.</li> * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_MOVE} * when the pointer has already entered the bounds of the view and has moved.</li> * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_EXIT} * when the pointer has exited the bounds of the view or when the pointer is * about to go down due to a button click, tap, or similar user action that * causes the view to be touched.</li> * </ul> * <p> * The view should implement this method to return true to indicate that it is * handling the hover event, such as by changing its drawable state. * </p><p> * The default implementation calls {@link #setHovered} to update the hovered state * of the view when a hover enter or hover exit event is received, if the view * is enabled and is clickable. The default implementation also sends hover * accessibility events. * </p> * * @param event The motion event that describes the hover. * @return True if the view handled the hover event. * * @see #isHovered * @see #setHovered * @see #onHoverChanged */ publicboolean onHoverEvent(MotionEvent event){ if(mTouchDelegate !=null&& dispatchTouchExplorationHoverEvent(event)){ returntrue; } // The root view may receive hover (or touch) events that are outside the bounds of // the window. This code ensures that we only send accessibility events for // hovers that are actually within the bounds of the root view. finalint action = event.getActionMasked(); if(!mSendingHoverAccessibilityEvents){ if((action ==MotionEvent.ACTION_HOVER_ENTER || action ==MotionEvent.ACTION_HOVER_MOVE) &&!hasHoveredChild() && pointInView(event.getX(), event.getY())){ sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); mSendingHoverAccessibilityEvents =true; } }else{ if(action ==MotionEvent.ACTION_HOVER_EXIT ||(action ==MotionEvent.ACTION_HOVER_MOVE &&!pointInView(event.getX(), event.getY()))){ mSendingHoverAccessibilityEvents =false; sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); } } if((action ==MotionEvent.ACTION_HOVER_ENTER || action ==MotionEvent.ACTION_HOVER_MOVE) && event.isFromSource(InputDevice.SOURCE_MOUSE) && isOnScrollbar(event.getX(), event.getY())){ awakenScrollBars(); } // If we consider ourself hoverable, or if we we're already hovered, // handle changing state in response to ENTER and EXIT events. if(isHoverable()|| isHovered()){ switch(action){ caseMotionEvent.ACTION_HOVER_ENTER: setHovered(true); break; caseMotionEvent.ACTION_HOVER_EXIT: setHovered(false); break; } // Dispatch the event to onGenericMotionEvent before returning true. // This is to provide compatibility with existing applications that // handled HOVER_MOVE events in onGenericMotionEvent and that would // break because of the new default handling for hoverable views // in onHoverEvent. // Note that onGenericMotionEvent will be called by default when // onHoverEvent returns false (refer to dispatchGenericMotionEvent). dispatchGenericMotionEventInternal(event); // The event was already handled by calling setHovered(), so always // return true. returntrue; } returnfalse; } /** * Returns true if the view should handle {@link #onHoverEvent} * by calling {@link #setHovered} to change its hovered state. * * @return True if the view is hoverable. */ privateboolean isHoverable(){ finalint viewFlags = mViewFlags; if((viewFlags & ENABLED_MASK)== DISABLED){ returnfalse; } return(viewFlags & CLICKABLE)== CLICKABLE ||(viewFlags & LONG_CLICKABLE)== LONG_CLICKABLE ||(viewFlags & CONTEXT_CLICKABLE)== CONTEXT_CLICKABLE; } /** * Returns true if the view is currently hovered. * * @return True if the view is currently hovered. * * @see #setHovered * @see #onHoverChanged */ @ViewDebug.ExportedProperty publicboolean isHovered(){ return(mPrivateFlags & PFLAG_HOVERED)!=0; } /** * Sets whether the view is currently hovered. * <p> * Calling this method also changes the drawable state of the view. This * enables the view to react to hover by using different drawable resources * to change its appearance. * </p><p> * The {@link #onHoverChanged} method is called when the hovered state changes. * </p> * * @param hovered True if the view is hovered. * * @see #isHovered * @see #onHoverChanged */ publicvoid setHovered(boolean hovered){ if(hovered){ if((mPrivateFlags & PFLAG_HOVERED)==0){ mPrivateFlags |= PFLAG_HOVERED; refreshDrawableState(); onHoverChanged(true); } }else{ if((mPrivateFlags & PFLAG_HOVERED)!=0){ mPrivateFlags &=~PFLAG_HOVERED; refreshDrawableState(); onHoverChanged(false); } } } /** * Implement this method to handle hover state changes. * <p> * This method is called whenever the hover state changes as a result of a * call to {@link #setHovered}. * </p> * * @param hovered The current hover state, as returned by {@link #isHovered}. * * @see #isHovered * @see #setHovered */ publicvoid onHoverChanged(boolean hovered){ } /** * Handles scroll bar dragging by mouse input. * * @hide * @param event The motion event. * * @return true if the event was handled as a scroll bar dragging, false otherwise. */ protectedboolean handleScrollBarDragging(MotionEvent event){ if(mScrollCache ==null){ returnfalse; } finalfloat x = event.getX(); finalfloat y = event.getY(); finalint action = event.getAction(); if((mScrollCache.mScrollBarDraggingState ==ScrollabilityCache.NOT_DRAGGING && action !=MotionEvent.ACTION_DOWN) ||!event.isFromSource(InputDevice.SOURCE_MOUSE) ||!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)){ mScrollCache.mScrollBarDraggingState =ScrollabilityCache.NOT_DRAGGING; returnfalse; } switch(action){ caseMotionEvent.ACTION_MOVE: if(mScrollCache.mScrollBarDraggingState ==ScrollabilityCache.NOT_DRAGGING){ returnfalse; } if(mScrollCache.mScrollBarDraggingState ==ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR){ finalRect bounds = mScrollCache.mScrollBarBounds; getVerticalScrollBarBounds(bounds,null); finalint range = computeVerticalScrollRange(); finalint offset = computeVerticalScrollOffset(); finalint extent = computeVerticalScrollExtent(); finalint thumbLength =ScrollBarUtils.getThumbLength( bounds.height(), bounds.width(), extent, range); finalint thumbOffset =ScrollBarUtils.getThumbOffset( bounds.height(), thumbLength, extent, range, offset); finalfloat diff = y - mScrollCache.mScrollBarDraggingPos; finalfloat maxThumbOffset = bounds.height()- thumbLength; finalfloat newThumbOffset = Math.min(Math.max(thumbOffset + diff,0.0f), maxThumbOffset); finalint height = getHeight(); if(Math.round(newThumbOffset)!= thumbOffset && maxThumbOffset >0 && height >0&& extent >0){ finalint newY =Math.round((range - extent) /((float)extent / height)*(newThumbOffset / maxThumbOffset)); if(newY != getScrollY()){ mScrollCache.mScrollBarDraggingPos = y; setScrollY(newY); } } returntrue; } if(mScrollCache.mScrollBarDraggingState ==ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR){ finalRect bounds = mScrollCache.mScrollBarBounds; getHorizontalScrollBarBounds(bounds,null); finalint range = computeHorizontalScrollRange(); finalint offset = computeHorizontalScrollOffset(); finalint extent = computeHorizontalScrollExtent(); finalint thumbLength =ScrollBarUtils.getThumbLength( bounds.width(), bounds.height(), extent, range); finalint thumbOffset =ScrollBarUtils.getThumbOffset( bounds.width(), thumbLength, extent, range, offset); finalfloat diff = x - mScrollCache.mScrollBarDraggingPos; finalfloat maxThumbOffset = bounds.width()- thumbLength; finalfloat newThumbOffset = Math.min(Math.max(thumbOffset + diff,0.0f), maxThumbOffset); finalint width = getWidth(); if(Math.round(newThumbOffset)!= thumbOffset && maxThumbOffset >0 && width >0&& extent >0){ finalint newX =Math.round((range - extent) /((float)extent / width)*(newThumbOffset / maxThumbOffset)); if(newX != getScrollX()){ mScrollCache.mScrollBarDraggingPos = x; setScrollX(newX); } } returntrue; } caseMotionEvent.ACTION_DOWN: if(mScrollCache.state ==ScrollabilityCache.OFF){ returnfalse; } if(isOnVerticalScrollbarThumb(x, y)){ mScrollCache.mScrollBarDraggingState = ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR; mScrollCache.mScrollBarDraggingPos = y; returntrue; } if(isOnHorizontalScrollbarThumb(x, y)){ mScrollCache.mScrollBarDraggingState = ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR; mScrollCache.mScrollBarDraggingPos = x; returntrue; } } mScrollCache.mScrollBarDraggingState =ScrollabilityCache.NOT_DRAGGING; returnfalse; } /** * Implement this method to handle touch screen motion events. * <p> * If this method is used to detect click actions, it is recommended that * the actions be performed by implementing and calling * {@link #performClick()}. This will ensure consistent system behavior, * including: * <ul> * <li>obeying click sound preferences * <li>dispatching OnClickListener calls * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when * accessibility features are enabled * </ul> * * @param event The motion event. * @return True if the event was handled, false otherwise. */ publicboolean onTouchEvent(MotionEvent event){ finalfloat x = event.getX(); finalfloat y = event.getY(); finalint viewFlags = mViewFlags; finalint action = event.getAction(); finalboolean clickable =((viewFlags & CLICKABLE)== CLICKABLE ||(viewFlags & LONG_CLICKABLE)== LONG_CLICKABLE) ||(viewFlags & CONTEXT_CLICKABLE)== CONTEXT_CLICKABLE; if((viewFlags & ENABLED_MASK)== DISABLED &&(mPrivateFlags4 & PFLAG4_ALLOW_CLICK_WHEN_DISABLED)==0){ if(action ==MotionEvent.ACTION_UP &&(mPrivateFlags & PFLAG_PRESSED)!=0){ setPressed(false); } mPrivateFlags3 &=~PFLAG3_FINGER_DOWN; // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return clickable; } if(mTouchDelegate !=null){ if(mTouchDelegate.onTouchEvent(event)){ returntrue; } } if(clickable ||(viewFlags & TOOLTIP)== TOOLTIP){ switch(action){ caseMotionEvent.ACTION_UP: mPrivateFlags3 &=~PFLAG3_FINGER_DOWN; if((viewFlags & TOOLTIP)== TOOLTIP){ handleTooltipUp(); } if(!clickable){ removeTapCallback(); removeLongPressCallback(); mInContextButtonPress =false; mHasPerformedLongPress =false; mIgnoreNextUpEvent =false; break; } boolean prepressed =(mPrivateFlags & PFLAG_PREPRESSED)!=0; if((mPrivateFlags & PFLAG_PRESSED)!=0|| prepressed){ // take focus if we don't have it already and we should in // touch mode. boolean focusTaken =false; if(isFocusable()&& isFocusableInTouchMode()&&!isFocused()){ focusTaken = requestFocus(); } if(prepressed){ // The button is being released before we actually // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. setPressed(true, x, y); } if(!mHasPerformedLongPress &&!mIgnoreNextUpEvent){ // This is a tap, so remove the longpress check removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if(!focusTaken){ // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if(mPerformClick ==null){ mPerformClick =newPerformClick(); } if(!post(mPerformClick)){ performClickInternal(); } } } if(mUnsetPressedState ==null){ mUnsetPressedState =newUnsetPressedState(); } if(prepressed){ postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); }elseif(!post(mUnsetPressedState)){ // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } mIgnoreNextUpEvent =false; break; caseMotionEvent.ACTION_DOWN: if(event.getSource()==InputDevice.SOURCE_TOUCHSCREEN){ mPrivateFlags3 |= PFLAG3_FINGER_DOWN; } mHasPerformedLongPress =false; if(!clickable){ checkForLongClick( ViewConfiguration.getLongPressTimeout(), x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); break; } if(performButtonActionOnTouchDown(event)){ break; } // Walk up the hierarchy to determine if we're inside a scrolling container. boolean isInScrollingContainer = isInScrollingContainer(); // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if(isInScrollingContainer){ mPrivateFlags |= PFLAG_PREPRESSED; if(mPendingCheckForTap ==null){ mPendingCheckForTap =newCheckForTap(); } mPendingCheckForTap.x = event.getX(); mPendingCheckForTap.y = event.getY(); postDelayed(mPendingCheckForTap,ViewConfiguration.getTapTimeout()); }else{ // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y); checkForLongClick( ViewConfiguration.getLongPressTimeout(), x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } break; caseMotionEvent.ACTION_CANCEL: if(clickable){ setPressed(false); } removeTapCallback(); removeLongPressCallback(); mInContextButtonPress =false; mHasPerformedLongPress =false; mIgnoreNextUpEvent =false; mPrivateFlags3 &=~PFLAG3_FINGER_DOWN; break; caseMotionEvent.ACTION_MOVE: if(clickable){ drawableHotspotChanged(x, y); } finalint motionClassification = event.getClassification(); finalboolean ambiguousGesture = motionClassification ==MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE; int touchSlop = mTouchSlop; if(ambiguousGesture && hasPendingLongPressCallback()){ if(!pointInView(x, y, touchSlop)){ // The default action here is to cancel long press. But instead, we // just extend the timeout here, in case the classification // stays ambiguous. removeLongPressCallback(); long delay =(long)(ViewConfiguration.getLongPressTimeout() * mAmbiguousGestureMultiplier); // Subtract the time already spent delay -= event.getEventTime()- event.getDownTime(); checkForLongClick( delay, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } touchSlop *= mAmbiguousGestureMultiplier; } // Be lenient about moving outside of buttons if(!pointInView(x, y, touchSlop)){ // Outside button // Remove any future long press/tap checks removeTapCallback(); removeLongPressCallback(); if((mPrivateFlags & PFLAG_PRESSED)!=0){ setPressed(false); } mPrivateFlags3 &=~PFLAG3_FINGER_DOWN; } finalboolean deepPress = motionClassification ==MotionEvent.CLASSIFICATION_DEEP_PRESS; if(deepPress && hasPendingLongPressCallback()){ // process the long click action immediately removeLongPressCallback(); checkForLongClick( 0/* send immediately */, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS); } break; } returntrue; } returnfalse; } /** * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicboolean isInScrollingContainer(){ ViewParent p = getParent(); while(p !=null&& p instanceofViewGroup){ if(((ViewGroup) p).shouldDelayChildPressedState()){ returntrue; } p = p.getParent(); } returnfalse; } /** * Remove the longpress detection timer. */ privatevoid removeLongPressCallback(){ if(mPendingCheckForLongPress !=null){ removeCallbacks(mPendingCheckForLongPress); } } /** * Return true if the long press callback is scheduled to run sometime in the future. * Return false if there is no scheduled long press callback at the moment. */ privateboolean hasPendingLongPressCallback(){ if(mPendingCheckForLongPress ==null){ returnfalse; } finalAttachInfo attachInfo = mAttachInfo; if(attachInfo ==null){ returnfalse; } return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress); } /** * Remove the pending click action */ @UnsupportedAppUsage privatevoid removePerformClickCallback(){ if(mPerformClick !=null){ removeCallbacks(mPerformClick); } } /** * Remove the prepress detection timer. */ privatevoid removeUnsetPressCallback(){ if((mPrivateFlags & PFLAG_PRESSED)!=0&& mUnsetPressedState !=null){ setPressed(false); removeCallbacks(mUnsetPressedState); } } /** * Remove the tap detection timer. */ privatevoid removeTapCallback(){ if(mPendingCheckForTap !=null){ mPrivateFlags &=~PFLAG_PREPRESSED; removeCallbacks(mPendingCheckForTap); } } /** * Cancels a pending long press. Your subclass can use this if you * want the context menu to come up if the user presses and holds * at the same place, but you don't want it to come up if they press * and then move around enough to cause scrolling. */ publicvoid cancelLongPress(){ removeLongPressCallback(); /* * The prepressed state handled by the tap callback is a display * construct, but the tap callback will post a long press callback * less its own timeout. Remove it here. */ removeTapCallback(); } /** * Sets the TouchDelegate for this View. */ publicvoid setTouchDelegate(TouchDelegate delegate){ mTouchDelegate = delegate; } /** * Gets the TouchDelegate for this View. */ publicTouchDelegate getTouchDelegate(){ return mTouchDelegate; } /** * Request unbuffered dispatch of the given stream of MotionEvents to this View. * * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input * system not batch {@link MotionEvent}s but instead deliver them as soon as they're * available. This method should only be called for touch events. * * <p class="note">This API is not intended for most applications. Buffered dispatch * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent * streams will not improve your input latency. Side effects include: increased latency, * jittery scrolls and inability to take advantage of system resampling. Talk to your input * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for * you.</p> * * To receive unbuffered events for arbitrary input device source classes, use * {@link #requestUnbufferedDispatch(int)}, * * @see View#requestUnbufferedDispatch(int) */ publicfinalvoid requestUnbufferedDispatch(MotionEvent event){ finalint action = event.getAction(); if(mAttachInfo ==null || action !=MotionEvent.ACTION_DOWN && action !=MotionEvent.ACTION_MOVE ||!event.isTouchEvent()){ return; } mAttachInfo.mUnbufferedDispatchRequested =true; } /** * Request unbuffered dispatch of the given event source class to this view. * This is similar to {@link View#requestUnbufferedDispatch(MotionEvent)}, but does not * automatically terminate, and allows the specification of arbitrary input source classes. * * @param source The combined input source class to request unbuffered dispatch for. All * events coming from these source classes will not be buffered. Set to * {@link InputDevice#SOURCE_CLASS_NONE} in order to return to default behaviour. * * @see View#requestUnbufferedDispatch(MotionEvent) */ publicfinalvoid requestUnbufferedDispatch(@InputSourceClassint source){ if(mUnbufferedInputSource == source){ return; } mUnbufferedInputSource = source; if(mParent !=null){ mParent.onDescendantUnbufferedRequested(); } } privateboolean hasSize(){ return(mBottom > mTop)&&(mRight > mLeft); } privateboolean canTakeFocus(){ return((mViewFlags & VISIBILITY_MASK)== VISIBLE) &&((mViewFlags & FOCUSABLE)== FOCUSABLE) &&((mViewFlags & ENABLED_MASK)== ENABLED) &&(sCanFocusZeroSized ||!isLayoutValid()|| hasSize()); } /** * Set flags controlling behavior of this view. * * @param flags Constant indicating the value which should be set * @param mask Constant indicating the bit range that should be changed */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =115609023) void setFlags(int flags,int mask){ finalboolean accessibilityEnabled = AccessibilityManager.getInstance(mContext).isEnabled(); finalboolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility(); int old = mViewFlags; mViewFlags =(mViewFlags &~mask)|(flags & mask); int changed = mViewFlags ^ old; if(changed ==0){ return; } int privateFlags = mPrivateFlags; boolean shouldNotifyFocusableAvailable =false; // If focusable is auto, update the FOCUSABLE bit. int focusableChangedByAuto =0; if(((mViewFlags & FOCUSABLE_AUTO)!=0) &&(changed &(FOCUSABLE_MASK | CLICKABLE))!=0){ // Heuristic only takes into account whether view is clickable. finalint newFocus; if((mViewFlags & CLICKABLE)!=0){ newFocus = FOCUSABLE; }else{ newFocus = NOT_FOCUSABLE; } mViewFlags =(mViewFlags &~FOCUSABLE)| newFocus; focusableChangedByAuto =(old & FOCUSABLE)^(newFocus & FOCUSABLE); changed =(changed &~FOCUSABLE)| focusableChangedByAuto; } /* Check if the FOCUSABLE bit has changed */ if(((changed & FOCUSABLE)!=0)&&((privateFlags & PFLAG_HAS_BOUNDS)!=0)){ if(((old & FOCUSABLE)== FOCUSABLE) &&((privateFlags & PFLAG_FOCUSED)!=0)){ /* Give up focus if we are no longer focusable */ clearFocus(); if(mParent instanceofViewGroup){ ((ViewGroup) mParent).clearFocusedInCluster(); } }elseif(((old & FOCUSABLE)== NOT_FOCUSABLE) &&((privateFlags & PFLAG_FOCUSED)==0)){ /* * Tell the view system that we are now available to take focus * if no one else already has it. */ if(mParent !=null){ ViewRootImpl viewRootImpl = getViewRootImpl(); if(!sAutoFocusableOffUIThreadWontNotifyParents || focusableChangedByAuto ==0 || viewRootImpl ==null || viewRootImpl.mThread ==Thread.currentThread()){ shouldNotifyFocusableAvailable = canTakeFocus(); } } } } finalint newVisibility = flags & VISIBILITY_MASK; if(newVisibility == VISIBLE){ if((changed & VISIBILITY_MASK)!=0){ /* * If this view is becoming visible, invalidate it in case it changed while * it was not visible. Marking it drawn ensures that the invalidation will * go through. */ mPrivateFlags |= PFLAG_DRAWN; invalidate(true); needGlobalAttributesUpdate(true); // a view becoming visible is worth notifying the parent about in case nothing has // focus. Even if this specific view isn't focusable, it may contain something that // is, so let the root view try to give this focus if nothing else does. shouldNotifyFocusableAvailable = hasSize(); } } if((changed & ENABLED_MASK)!=0){ if((mViewFlags & ENABLED_MASK)== ENABLED){ // a view becoming enabled should notify the parent as long as the view is also // visible and the parent wasn't already notified by becoming visible during this // setFlags invocation. shouldNotifyFocusableAvailable = canTakeFocus(); }else{ if(isFocused()) clearFocus(); } } if(shouldNotifyFocusableAvailable && mParent !=null){ mParent.focusableViewAvailable(this); } /* Check if the GONE bit has changed */ if((changed & GONE)!=0){ needGlobalAttributesUpdate(false); requestLayout(); if(((mViewFlags & VISIBILITY_MASK)== GONE)){ if(hasFocus()){ clearFocus(); if(mParent instanceofViewGroup){ ((ViewGroup) mParent).clearFocusedInCluster(); } } clearAccessibilityFocus(); destroyDrawingCache(); if(mParent instanceofView){ // GONE views noop invalidation, so invalidate the parent ((View) mParent).invalidate(true); } // Mark the view drawn to ensure that it gets invalidated properly the next // time it is visible and gets invalidated mPrivateFlags |= PFLAG_DRAWN; } if(mAttachInfo !=null){ mAttachInfo.mViewVisibilityChanged =true; } } /* Check if the VISIBLE bit has changed */ if((changed & INVISIBLE)!=0){ needGlobalAttributesUpdate(false); /* * If this view is becoming invisible, set the DRAWN flag so that * the next invalidate() will not be skipped. */ mPrivateFlags |= PFLAG_DRAWN; if(((mViewFlags & VISIBILITY_MASK)== INVISIBLE)){ // root view becoming invisible shouldn't clear focus and accessibility focus if(getRootView()!=this){ if(hasFocus()){ clearFocus(); if(mParent instanceofViewGroup){ ((ViewGroup) mParent).clearFocusedInCluster(); } } clearAccessibilityFocus(); } } if(mAttachInfo !=null){ mAttachInfo.mViewVisibilityChanged =true; } } if((changed & VISIBILITY_MASK)!=0){ // If the view is invisible, cleanup its display list to free up resources if(newVisibility != VISIBLE && mAttachInfo !=null){ cleanupDraw(); } if(mParent instanceofViewGroup){ ViewGroup parent =(ViewGroup) mParent; parent.onChildVisibilityChanged(this,(changed & VISIBILITY_MASK), newVisibility); parent.invalidate(true); }elseif(mParent !=null){ mParent.invalidateChild(this,null); } if(mAttachInfo !=null){ dispatchVisibilityChanged(this, newVisibility); // Aggregated visibility changes are dispatched to attached views // in visible windows where the parent is currently shown/drawn // or the parent is not a ViewGroup (and therefore assumed to be a ViewRoot), // discounting clipping or overlapping. This makes it a good place // to change animation states. if(mParent !=null&& getWindowVisibility()== VISIBLE && ((!(mParent instanceofViewGroup))||((ViewGroup) mParent).isShown())){ dispatchVisibilityAggregated(newVisibility == VISIBLE); } // If this view is invisible from visible, then sending the A11y event by its // parent which is shown and has the accessibility important. if((old & VISIBILITY_MASK)== VISIBLE){ notifySubtreeAccessibilityStateChangedByParentIfNeeded(); }else{ notifySubtreeAccessibilityStateChangedIfNeeded(); } } } if((changed & WILL_NOT_CACHE_DRAWING)!=0){ destroyDrawingCache(); } if((changed & DRAWING_CACHE_ENABLED)!=0){ destroyDrawingCache(); mPrivateFlags &=~PFLAG_DRAWING_CACHE_VALID; invalidateParentCaches(); } if((changed & DRAWING_CACHE_QUALITY_MASK)!=0){ destroyDrawingCache(); mPrivateFlags &=~PFLAG_DRAWING_CACHE_VALID; } if((changed & DRAW_MASK)!=0){ if((mViewFlags & WILL_NOT_DRAW)!=0){ if(mBackground !=null || mDefaultFocusHighlight !=null ||(mForegroundInfo !=null&& mForegroundInfo.mDrawable !=null)){ mPrivateFlags &=~PFLAG_SKIP_DRAW; }else{ mPrivateFlags |= PFLAG_SKIP_DRAW; } }else{ mPrivateFlags &=~PFLAG_SKIP_DRAW; } requestLayout(); invalidate(true); } if((changed & KEEP_SCREEN_ON)!=0){ if(mParent !=null&& mAttachInfo !=null&&!mAttachInfo.mRecomputeGlobalAttributes){ mParent.recomputeViewAttributes(this); } } if(accessibilityEnabled){ // If we're an accessibility pane and the visibility changed, we already have sent // a state change, so we really don't need to report other changes. if(isAccessibilityPane()){ changed &=~VISIBILITY_MASK; } if((changed & FOCUSABLE)!=0||(changed & VISIBILITY_MASK)!=0 ||(changed & CLICKABLE)!=0||(changed & LONG_CLICKABLE)!=0 ||(changed & CONTEXT_CLICKABLE)!=0){ if(oldIncludeForAccessibility != includeForAccessibility()){ notifySubtreeAccessibilityStateChangedIfNeeded(); }else{ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } }elseif((changed & ENABLED_MASK)!=0){ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } /** * Change the view's z order in the tree, so it's on top of other sibling * views. This ordering change may affect layout, if the parent container * uses an order-dependent layout scheme (e.g., LinearLayout). Prior * to {@link android.os.Build.VERSION_CODES#KITKAT} this * method should be followed by calls to {@link #requestLayout()} and * {@link View#invalidate()} on the view's parent to force the parent to redraw * with the new child ordering. * * @see ViewGroup#bringChildToFront(View) */ publicvoid bringToFront(){ if(mParent !=null){ mParent.bringChildToFront(this); } } /** * This is called in response to an internal scroll in this view (i.e., the * view scrolled its own contents). This is typically as a result of * {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been * called. * * @param l Current horizontal scroll origin. * @param t Current vertical scroll origin. * @param oldl Previous horizontal scroll origin. * @param oldt Previous vertical scroll origin. */ protectedvoid onScrollChanged(int l,int t,int oldl,int oldt){ notifySubtreeAccessibilityStateChangedIfNeeded(); postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt); mBackgroundSizeChanged =true; mDefaultFocusHighlightSizeChanged =true; if(mForegroundInfo !=null){ mForegroundInfo.mBoundsChanged =true; } finalAttachInfo ai = mAttachInfo; if(ai !=null){ ai.mViewScrollChanged =true; } if(mListenerInfo !=null&& mListenerInfo.mOnScrollChangeListener !=null){ mListenerInfo.mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt); } } /** * Interface definition for a callback to be invoked when the scroll * X or Y positions of a view change. * <p> * <b>Note:</b> Some views handle scrolling independently from View and may * have their own separate listeners for scroll-type events. For example, * {@link android.widget.ListView ListView} allows clients to register an * {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener} * to listen for changes in list scroll position. * * @see #setOnScrollChangeListener(View.OnScrollChangeListener) */ publicinterfaceOnScrollChangeListener{ /** * Called when the scroll position of a view changes. * * @param v The view whose scroll position has changed. * @param scrollX Current horizontal scroll origin. * @param scrollY Current vertical scroll origin. * @param oldScrollX Previous horizontal scroll origin. * @param oldScrollY Previous vertical scroll origin. */ void onScrollChange(View v,int scrollX,int scrollY,int oldScrollX,int oldScrollY); } /** * Interface definition for a callback to be invoked when the layout bounds of a view * changes due to layout processing. */ publicinterfaceOnLayoutChangeListener{ /** * Called when the layout bounds of a view changes due to layout processing. * * @param v The view whose bounds have changed. * @param left The new value of the view's left property. * @param top The new value of the view's top property. * @param right The new value of the view's right property. * @param bottom The new value of the view's bottom property. * @param oldLeft The previous value of the view's left property. * @param oldTop The previous value of the view's top property. * @param oldRight The previous value of the view's right property. * @param oldBottom The previous value of the view's bottom property. */ void onLayoutChange(View v,int left,int top,int right,int bottom, int oldLeft,int oldTop,int oldRight,int oldBottom); } /** * This is called during layout when the size of this view has changed. If * you were just added to the view hierarchy, you're called with the old * values of 0. * * @param w Current width of this view. * @param h Current height of this view. * @param oldw Old width of this view. * @param oldh Old height of this view. */ protectedvoid onSizeChanged(int w,int h,int oldw,int oldh){ } /** * Called by draw to draw the child views. This may be overridden * by derived classes to gain control just before its children are drawn * (but after its own view has been drawn). * @param canvas the canvas on which to draw the view */ protectedvoid dispatchDraw(Canvas canvas){ } /** * Gets the parent of this view. Note that the parent is a * ViewParent and not necessarily a View. * * @return Parent of this view. */ publicfinalViewParent getParent(){ return mParent; } /** * Set the horizontal scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param value the x position to scroll to */ publicvoid setScrollX(int value){ scrollTo(value, mScrollY); } /** * Set the vertical scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param value the y position to scroll to */ publicvoid setScrollY(int value){ scrollTo(mScrollX, value); } /** * Return the scrolled left position of this view. This is the left edge of * the displayed part of your view. You do not need to draw any pixels * farther left, since those are outside of the frame of your view on * screen. * * @return The left edge of the displayed part of your view, in pixels. */ @InspectableProperty publicfinalint getScrollX(){ return mScrollX; } /** * Return the scrolled top position of this view. This is the top edge of * the displayed part of your view. You do not need to draw any pixels above * it, since those are outside of the frame of your view on screen. * * @return The top edge of the displayed part of your view, in pixels. */ @InspectableProperty publicfinalint getScrollY(){ return mScrollY; } /** * Return the width of your view. * * @return The width of your view, in pixels. */ @ViewDebug.ExportedProperty(category ="layout") publicfinalint getWidth(){ return mRight - mLeft; } /** * Return the height of your view. * * @return The height of your view, in pixels. */ @ViewDebug.ExportedProperty(category ="layout") publicfinalint getHeight(){ return mBottom - mTop; } /** * Return the visible drawing bounds of your view. Fills in the output * rectangle with the values from getScrollX(), getScrollY(), * getWidth(), and getHeight(). These bounds do not account for any * transformation properties currently set on the view, such as * {@link #setScaleX(float)} or {@link #setRotation(float)}. * * @param outRect The (scrolled) drawing bounds of the view. */ publicvoid getDrawingRect(Rect outRect){ outRect.left = mScrollX; outRect.top = mScrollY; outRect.right = mScrollX +(mRight - mLeft); outRect.bottom = mScrollY +(mBottom - mTop); } /** * Like {@link #getMeasuredWidthAndState()}, but only returns the * raw width component (that is the result is masked by * {@link #MEASURED_SIZE_MASK}). * * @return The raw measured width of this view. */ publicfinalint getMeasuredWidth(){ return mMeasuredWidth & MEASURED_SIZE_MASK; } /** * Return the full width measurement information for this view as computed * by the most recent call to {@link #measure(int, int)}. This result is a bit mask * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. * This should be used during measurement and layout calculations only. Use * {@link #getWidth()} to see how wide a view is after layout. * * @return The measured width of this view as a bit mask. */ @ViewDebug.ExportedProperty(category ="measurement", flagMapping ={ @ViewDebug.FlagToString(mask = MEASURED_STATE_MASK, equals = MEASURED_STATE_TOO_SMALL, name ="MEASURED_STATE_TOO_SMALL"), }) publicfinalint getMeasuredWidthAndState(){ return mMeasuredWidth; } /** * Like {@link #getMeasuredHeightAndState()}, but only returns the * raw height component (that is the result is masked by * {@link #MEASURED_SIZE_MASK}). * * @return The raw measured height of this view. */ publicfinalint getMeasuredHeight(){ return mMeasuredHeight & MEASURED_SIZE_MASK; } /** * Return the full height measurement information for this view as computed * by the most recent call to {@link #measure(int, int)}. This result is a bit mask * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. * This should be used during measurement and layout calculations only. Use * {@link #getHeight()} to see how high a view is after layout. * * @return The measured height of this view as a bit mask. */ @ViewDebug.ExportedProperty(category ="measurement", flagMapping ={ @ViewDebug.FlagToString(mask = MEASURED_STATE_MASK, equals = MEASURED_STATE_TOO_SMALL, name ="MEASURED_STATE_TOO_SMALL"), }) publicfinalint getMeasuredHeightAndState(){ return mMeasuredHeight; } /** * Return only the state bits of {@link #getMeasuredWidthAndState()} * and {@link #getMeasuredHeightAndState()}, combined into one integer. * The width component is in the regular bits {@link #MEASURED_STATE_MASK} * and the height component is at the shifted bits * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}. */ publicfinalint getMeasuredState(){ return(mMeasuredWidth&MEASURED_STATE_MASK) |((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT) &(MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); } /** * The transform matrix of this view, which is calculated based on the current * rotation, scale, and pivot properties. * * @see #getRotation() * @see #getScaleX() * @see #getScaleY() * @see #getPivotX() * @see #getPivotY() * @return The current transform matrix for the view */ publicMatrix getMatrix(){ ensureTransformationInfo(); finalMatrix matrix = mTransformationInfo.mMatrix; mRenderNode.getMatrix(matrix); return matrix; } /** * Returns true if the transform matrix is the identity matrix. * Recomputes the matrix if necessary. * * @return True if the transform matrix is the identity matrix, false otherwise. * @hide */ @UnsupportedAppUsage publicfinalboolean hasIdentityMatrix(){ return mRenderNode.hasIdentityMatrix(); } @UnsupportedAppUsage void ensureTransformationInfo(){ if(mTransformationInfo ==null){ mTransformationInfo =newTransformationInfo(); } } /** * Utility method to retrieve the inverse of the current mMatrix property. * We cache the matrix to avoid recalculating it when transform properties * have not changed. * * @return The inverse of the current matrix of this view. * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicfinalMatrix getInverseMatrix(){ ensureTransformationInfo(); if(mTransformationInfo.mInverseMatrix ==null){ mTransformationInfo.mInverseMatrix =newMatrix(); } finalMatrix matrix = mTransformationInfo.mInverseMatrix; mRenderNode.getInverseMatrix(matrix); return matrix; } /** * Gets the distance along the Z axis from the camera to this view. * * @see #setCameraDistance(float) * * @return The distance along the Z axis. */ publicfloat getCameraDistance(){ finalfloat dpi = mResources.getDisplayMetrics().densityDpi; return mRenderNode.getCameraDistance()* dpi; } /** * <p>Sets the distance along the Z axis (orthogonal to the X/Y plane on which * views are drawn) from the camera to this view. The camera's distance * affects 3D transformations, for instance rotations around the X and Y * axis. If the rotationX or rotationY properties are changed and this view is * large (more than half the size of the screen), it is recommended to always * use a camera distance that's greater than the height (X axis rotation) or * the width (Y axis rotation) of this view.</p> * * <p>The distance of the camera from the view plane can have an affect on the * perspective distortion of the view when it is rotated around the x or y axis. * For example, a large distance will result in a large viewing angle, and there * will not be much perspective distortion of the view as it rotates. A short * distance may cause much more perspective distortion upon rotation, and can * also result in some drawing artifacts if the rotated view ends up partially * behind the camera (which is why the recommendation is to use a distance at * least as far as the size of the view, if the view is to be rotated.)</p> * * <p>The distance is expressed in "depth pixels." The default distance depends * on the screen density. For instance, on a medium density display, the * default distance is 1280. On a high density display, the default distance * is 1920.</p> * * <p>If you want to specify a distance that leads to visually consistent * results across various densities, use the following formula:</p> * <pre> * float scale = context.getResources().getDisplayMetrics().density; * view.setCameraDistance(distance * scale); * </pre> * * <p>The density scale factor of a high density display is 1.5, * and 1920 = 1280 * 1.5.</p> * * @param distance The distance in "depth pixels", if negative the opposite * value is used * * @see #setRotationX(float) * @see #setRotationY(float) */ publicvoid setCameraDistance(float distance){ finalfloat dpi = mResources.getDisplayMetrics().densityDpi; invalidateViewProperty(true,false); mRenderNode.setCameraDistance(Math.abs(distance)/ dpi); invalidateViewProperty(false,false); invalidateParentIfNeededAndWasQuickRejected(); } /** * The degrees that the view is rotated around the pivot point. * * @see #setRotation(float) * @see #getPivotX() * @see #getPivotY() * * @return The degrees of rotation. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getRotation(){ return mRenderNode.getRotationZ(); } /** * Sets the degrees that the view is rotated around the pivot point. Increasing values * result in clockwise rotation. * * @param rotation The degrees of rotation. * * @see #getRotation() * @see #getPivotX() * @see #getPivotY() * @see #setRotationX(float) * @see #setRotationY(float) * * @attr ref android.R.styleable#View_rotation */ @RemotableViewMethod publicvoid setRotation(float rotation){ if(rotation != getRotation()){ // Double-invalidation is necessary to capture view's old and new areas invalidateViewProperty(true,false); mRenderNode.setRotationZ(rotation); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The degrees that the view is rotated around the vertical axis through the pivot point. * * @see #getPivotX() * @see #getPivotY() * @see #setRotationY(float) * * @return The degrees of Y rotation. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getRotationY(){ return mRenderNode.getRotationY(); } /** * Sets the degrees that the view is rotated around the vertical axis through the pivot point. * Increasing values result in counter-clockwise rotation from the viewpoint of looking * down the y axis. * * When rotating large views, it is recommended to adjust the camera distance * accordingly. Refer to {@link #setCameraDistance(float)} for more information. * * @param rotationY The degrees of Y rotation. * * @see #getRotationY() * @see #getPivotX() * @see #getPivotY() * @see #setRotation(float) * @see #setRotationX(float) * @see #setCameraDistance(float) * * @attr ref android.R.styleable#View_rotationY */ @RemotableViewMethod publicvoid setRotationY(float rotationY){ if(rotationY != getRotationY()){ invalidateViewProperty(true,false); mRenderNode.setRotationY(rotationY); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The degrees that the view is rotated around the horizontal axis through the pivot point. * * @see #getPivotX() * @see #getPivotY() * @see #setRotationX(float) * * @return The degrees of X rotation. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getRotationX(){ return mRenderNode.getRotationX(); } /** * Sets the degrees that the view is rotated around the horizontal axis through the pivot point. * Increasing values result in clockwise rotation from the viewpoint of looking down the * x axis. * * When rotating large views, it is recommended to adjust the camera distance * accordingly. Refer to {@link #setCameraDistance(float)} for more information. * * @param rotationX The degrees of X rotation. * * @see #getRotationX() * @see #getPivotX() * @see #getPivotY() * @see #setRotation(float) * @see #setRotationY(float) * @see #setCameraDistance(float) * * @attr ref android.R.styleable#View_rotationX */ @RemotableViewMethod publicvoid setRotationX(float rotationX){ if(rotationX != getRotationX()){ invalidateViewProperty(true,false); mRenderNode.setRotationX(rotationX); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The amount that the view is scaled in x around the pivot point, as a proportion of * the view's unscaled width. A value of 1, the default, means that no scaling is applied. * * <p>By default, this is 1.0f. * * @see #getPivotX() * @see #getPivotY() * @return The scaling factor. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getScaleX(){ return mRenderNode.getScaleX(); } /** * Sets the amount that the view is scaled in x around the pivot point, as a proportion of * the view's unscaled width. A value of 1 means that no scaling is applied. * * @param scaleX The scaling factor. * @see #getPivotX() * @see #getPivotY() * * @attr ref android.R.styleable#View_scaleX */ @RemotableViewMethod publicvoid setScaleX(float scaleX){ if(scaleX != getScaleX()){ scaleX = sanitizeFloatPropertyValue(scaleX,"scaleX"); invalidateViewProperty(true,false); mRenderNode.setScaleX(scaleX); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The amount that the view is scaled in y around the pivot point, as a proportion of * the view's unscaled height. A value of 1, the default, means that no scaling is applied. * * <p>By default, this is 1.0f. * * @see #getPivotX() * @see #getPivotY() * @return The scaling factor. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getScaleY(){ return mRenderNode.getScaleY(); } /** * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of * the view's unscaled width. A value of 1 means that no scaling is applied. * * @param scaleY The scaling factor. * @see #getPivotX() * @see #getPivotY() * * @attr ref android.R.styleable#View_scaleY */ @RemotableViewMethod publicvoid setScaleY(float scaleY){ if(scaleY != getScaleY()){ scaleY = sanitizeFloatPropertyValue(scaleY,"scaleY"); invalidateViewProperty(true,false); mRenderNode.setScaleY(scaleY); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The x location of the point around which the view is {@link #setRotation(float) rotated} * and {@link #setScaleX(float) scaled}. * * @see #getRotation() * @see #getScaleX() * @see #getScaleY() * @see #getPivotY() * @return The x location of the pivot point. * * @attr ref android.R.styleable#View_transformPivotX */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty(name ="transformPivotX") publicfloat getPivotX(){ return mRenderNode.getPivotX(); } /** * Sets the x location of the point around which the view is * {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}. * By default, the pivot point is centered on the object. * Setting this property disables this behavior and causes the view to use only the * explicitly set pivotX and pivotY values. * * @param pivotX The x location of the pivot point. * @see #getRotation() * @see #getScaleX() * @see #getScaleY() * @see #getPivotY() * * @attr ref android.R.styleable#View_transformPivotX */ @RemotableViewMethod publicvoid setPivotX(float pivotX){ if(!mRenderNode.isPivotExplicitlySet()|| pivotX != getPivotX()){ invalidateViewProperty(true,false); mRenderNode.setPivotX(pivotX); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); } } /** * The y location of the point around which the view is {@link #setRotation(float) rotated} * and {@link #setScaleY(float) scaled}. * * @see #getRotation() * @see #getScaleX() * @see #getScaleY() * @see #getPivotY() * @return The y location of the pivot point. * * @attr ref android.R.styleable#View_transformPivotY */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty(name ="transformPivotY") publicfloat getPivotY(){ return mRenderNode.getPivotY(); } /** * Sets the y location of the point around which the view is {@link #setRotation(float) rotated} * and {@link #setScaleY(float) scaled}. By default, the pivot point is centered on the object. * Setting this property disables this behavior and causes the view to use only the * explicitly set pivotX and pivotY values. * * @param pivotY The y location of the pivot point. * @see #getRotation() * @see #getScaleX() * @see #getScaleY() * @see #getPivotY() * * @attr ref android.R.styleable#View_transformPivotY */ @RemotableViewMethod publicvoid setPivotY(float pivotY){ if(!mRenderNode.isPivotExplicitlySet()|| pivotY != getPivotY()){ invalidateViewProperty(true,false); mRenderNode.setPivotY(pivotY); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); } } /** * Returns whether or not a pivot has been set by a call to {@link #setPivotX(float)} or * {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center * of the view. * * @return True if a pivot has been set, false if the default pivot is being used */ publicboolean isPivotSet(){ return mRenderNode.isPivotExplicitlySet(); } /** * Clears any pivot previously set by a call to {@link #setPivotX(float)} or * {@link #setPivotY(float)}. After calling this {@link #isPivotSet()} will be false * and the pivot used for rotation will return to default of being centered on the view. */ publicvoid resetPivot(){ if(mRenderNode.resetPivot()){ invalidateViewProperty(false,false); } } /** * 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. * * <p>By default this is 1.0f. * @return The opacity of the view. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getAlpha(){ return mTransformationInfo !=null? mTransformationInfo.mAlpha :1; } /** * Sets the behavior for overlapping rendering for this view (see {@link * #hasOverlappingRendering()} for more details on this behavior). Calling this method * is an alternative to overriding {@link #hasOverlappingRendering()} in a subclass, * providing the value which is then used internally. That is, when {@link * #forceHasOverlappingRendering(boolean)} is called, the value of {@link * #hasOverlappingRendering()} is ignored and the value passed into this method is used * instead. * * @param hasOverlappingRendering The value for overlapping rendering to be used internally * instead of that returned by {@link #hasOverlappingRendering()}. * * @attr ref android.R.styleable#View_forceHasOverlappingRendering */ publicvoid forceHasOverlappingRendering(boolean hasOverlappingRendering){ mPrivateFlags3 |= PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED; if(hasOverlappingRendering){ mPrivateFlags3 |= PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE; }else{ mPrivateFlags3 &=~PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE; } } /** * Returns the value for overlapping rendering that is used internally. This is either * the value passed into {@link #forceHasOverlappingRendering(boolean)}, if called, or * the return value of {@link #hasOverlappingRendering()}, otherwise. * * @return The value for overlapping rendering being used internally. */ publicfinalboolean getHasOverlappingRendering(){ return(mPrivateFlags3 & PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED)!=0? (mPrivateFlags3 & PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE)!=0: hasOverlappingRendering(); } /** * Returns whether this View has content which overlaps. * * <p>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.</p> * * <p><strong>Note:</strong> The return value of this method is ignored if {@link * #forceHasOverlappingRendering(boolean)} has been called on this view.</p> * * @return true if the content in this view might overlap, false otherwise. */ @ViewDebug.ExportedProperty(category ="drawing") publicboolean hasOverlappingRendering(){ returntrue; } /** * Sets the opacity of the view to a value from 0 to 1, where 0 means the view is * completely transparent and 1 means the view is completely opaque. * * <p class="note"><strong>Note:</strong> 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.</p> * * <p>For a view with a frequently changing alpha, such as during a fading animation, it is * strongly recommended for performance reasons to either override * {@link #hasOverlappingRendering()} to return <code>false</code> if appropriate, or setting a * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view for the duration * of the animation. On versions {@link android.os.Build.VERSION_CODES#M} and below, * the default path for rendering an unlayered View with alpha could add multiple milliseconds * of rendering cost, even for simple or small views. Starting with * {@link android.os.Build.VERSION_CODES#M}, {@link #LAYER_TYPE_HARDWARE} is automatically * applied to the view at the rendering level.</p> * * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is * responsible for applying the opacity itself.</p> * * <p>On versions {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and below, note that if * the view is backed by a {@link #setLayerType(int, android.graphics.Paint) layer} and is * associated with a {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an * alpha value less than 1.0 will supersede the alpha of the layer paint.</p> * * <p>Starting with {@link android.os.Build.VERSION_CODES#M}, setting a translucent alpha * value will clip a View to its bounds, unless the View returns <code>false</code> from * {@link #hasOverlappingRendering}.</p> * * @param alpha The opacity of the view. * * @see #hasOverlappingRendering() * @see #setLayerType(int, android.graphics.Paint) * * @attr ref android.R.styleable#View_alpha */ @RemotableViewMethod publicvoid setAlpha(@FloatRange(from=0.0, to=1.0)float alpha){ ensureTransformationInfo(); if(mTransformationInfo.mAlpha != alpha){ setAlphaInternal(alpha); if(onSetAlpha((int)(alpha *255))){ mPrivateFlags |= PFLAG_ALPHA_SET; // subclass is handling alpha - don't optimize rendering cache invalidation invalidateParentCaches(); invalidate(true); }else{ mPrivateFlags &=~PFLAG_ALPHA_SET; invalidateViewProperty(true,false); mRenderNode.setAlpha(getFinalAlpha()); } } } /** * Faster version of setAlpha() which performs the same steps except there are * no calls to invalidate(). The caller of this function should perform proper invalidation * on the parent and this object. The return value indicates whether the subclass handles * alpha (the return value for onSetAlpha()). * * @param alpha The new value for the alpha property * @return true if the View subclass handles alpha (the return value for onSetAlpha()) and * the new value for the alpha property is different from the old value */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =123768435) boolean setAlphaNoInvalidation(float alpha){ ensureTransformationInfo(); if(mTransformationInfo.mAlpha != alpha){ setAlphaInternal(alpha); boolean subclassHandlesAlpha = onSetAlpha((int)(alpha *255)); if(subclassHandlesAlpha){ mPrivateFlags |= PFLAG_ALPHA_SET; returntrue; }else{ mPrivateFlags &=~PFLAG_ALPHA_SET; mRenderNode.setAlpha(getFinalAlpha()); } } returnfalse; } void setAlphaInternal(float alpha){ float oldAlpha = mTransformationInfo.mAlpha; mTransformationInfo.mAlpha = alpha; // Report visibility changes, which can affect children, to accessibility if((alpha ==0)^(oldAlpha ==0)){ notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * This property is intended only for use by the Fade transition, which animates it * to produce a visual translucency that does not side-effect (or get affected by) * the real alpha property. This value is composited with the other alpha value * (and the AlphaAnimation value, when that is present) to produce a final visual * translucency result, which is what is passed into the DisplayList. */ publicvoid setTransitionAlpha(float alpha){ ensureTransformationInfo(); if(mTransformationInfo.mTransitionAlpha != alpha){ mTransformationInfo.mTransitionAlpha = alpha; mPrivateFlags &=~PFLAG_ALPHA_SET; invalidateViewProperty(true,false); mRenderNode.setAlpha(getFinalAlpha()); } } /** * Calculates the visual alpha of this view, which is a combination of the actual * alpha value and the transitionAlpha value (if set). */ privatefloat getFinalAlpha(){ if(mTransformationInfo !=null){ return mTransformationInfo.mAlpha * mTransformationInfo.mTransitionAlpha; } return1; } /** * This property is intended only for use by the Fade transition, which animates * it to produce a visual translucency that does not side-effect (or get affected * by) the real alpha property. This value is composited with the other alpha * value (and the AlphaAnimation value, when that is present) to produce a final * visual translucency result, which is what is passed into the DisplayList. */ @ViewDebug.ExportedProperty(category ="drawing") publicfloat getTransitionAlpha(){ return mTransformationInfo !=null? mTransformationInfo.mTransitionAlpha :1; } /** * Sets whether or not to allow force dark to apply to this view. * * Setting this to false will disable the auto-dark feature on everything this view * draws, including any descendants. * * Setting this to true will allow this view to be automatically made dark, however * a value of 'true' will not override any 'false' value in its parent chain nor will * it prevent any 'false' in any of its children. * * The default behavior of force dark is also influenced by the Theme's * {@link android.R.styleable#Theme_isLightTheme isLightTheme} attribute. * If a theme is isLightTheme="false", then force dark is globally disabled for that theme. * * @param allow Whether or not to allow force dark. */ publicvoid setForceDarkAllowed(boolean allow){ if(mRenderNode.setForceDarkAllowed(allow)){ // Currently toggling force-dark requires a new display list push to apply // TODO: Make it not clobber the display list so this is just a damageSelf() instead invalidate(); } } /** * See {@link #setForceDarkAllowed(boolean)} * * @return true if force dark is allowed (default), false if it is disabled */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicboolean isForceDarkAllowed(){ return mRenderNode.isForceDarkAllowed(); } /** * Top position of this view relative to its parent. * * @return The top of this view, in pixels. */ @ViewDebug.CapturedViewProperty publicfinalint getTop(){ return mTop; } /** * Sets the top position of this view relative to its parent. This method is meant to be called * by the layout system and should not generally be called otherwise, because the property * may be changed at any time by the layout. * * @param top The top of this view, in pixels. */ publicfinalvoid setTop(int top){ if(top != mTop){ finalboolean matrixIsIdentity = hasIdentityMatrix(); if(matrixIsIdentity){ if(mAttachInfo !=null){ int minTop; int yLoc; if(top < mTop){ minTop = top; yLoc = top - mTop; }else{ minTop = mTop; yLoc =0; } invalidate(0, yLoc, mRight - mLeft, mBottom - minTop); } }else{ // Double-invalidation is necessary to capture view's old and new areas invalidate(true); } int width = mRight - mLeft; int oldHeight = mBottom - mTop; mTop = top; mRenderNode.setTop(mTop); sizeChange(width, mBottom - mTop, width, oldHeight); if(!matrixIsIdentity){ mPrivateFlags |= PFLAG_DRAWN;// force another invalidation with the new orientation invalidate(true); } mBackgroundSizeChanged =true; mDefaultFocusHighlightSizeChanged =true; if(mForegroundInfo !=null){ mForegroundInfo.mBoundsChanged =true; } invalidateParentIfNeeded(); } } /** * Bottom position of this view relative to its parent. * * @return The bottom of this view, in pixels. */ @ViewDebug.CapturedViewProperty publicfinalint getBottom(){ return mBottom; } /** * True if this view has changed since the last time being drawn. * * @return The dirty state of this view. */ publicboolean isDirty(){ return(mPrivateFlags & PFLAG_DIRTY_MASK)!=0; } /** * Sets the bottom position of this view relative to its parent. This method is meant to be * called by the layout system and should not generally be called otherwise, because the * property may be changed at any time by the layout. * * @param bottom The bottom of this view, in pixels. */ publicfinalvoid setBottom(int bottom){ if(bottom != mBottom){ finalboolean matrixIsIdentity = hasIdentityMatrix(); if(matrixIsIdentity){ if(mAttachInfo !=null){ int maxBottom; if(bottom < mBottom){ maxBottom = mBottom; }else{ maxBottom = bottom; } invalidate(0,0, mRight - mLeft, maxBottom - mTop); } }else{ // Double-invalidation is necessary to capture view's old and new areas invalidate(true); } int width = mRight - mLeft; int oldHeight = mBottom - mTop; mBottom = bottom; mRenderNode.setBottom(mBottom); sizeChange(width, mBottom - mTop, width, oldHeight); if(!matrixIsIdentity){ mPrivateFlags |= PFLAG_DRAWN;// force another invalidation with the new orientation invalidate(true); } mBackgroundSizeChanged =true; mDefaultFocusHighlightSizeChanged =true; if(mForegroundInfo !=null){ mForegroundInfo.mBoundsChanged =true; } invalidateParentIfNeeded(); } } /** * Left position of this view relative to its parent. * * @return The left edge of this view, in pixels. */ @ViewDebug.CapturedViewProperty publicfinalint getLeft(){ return mLeft; } /** * Sets the left position of this view relative to its parent. This method is meant to be called * by the layout system and should not generally be called otherwise, because the property * may be changed at any time by the layout. * * @param left The left of this view, in pixels. */ publicfinalvoid setLeft(int left){ if(left != mLeft){ finalboolean matrixIsIdentity = hasIdentityMatrix(); if(matrixIsIdentity){ if(mAttachInfo !=null){ int minLeft; int xLoc; if(left < mLeft){ minLeft = left; xLoc = left - mLeft; }else{ minLeft = mLeft; xLoc =0; } invalidate(xLoc,0, mRight - minLeft, mBottom - mTop); } }else{ // Double-invalidation is necessary to capture view's old and new areas invalidate(true); } int oldWidth = mRight - mLeft; int height = mBottom - mTop; mLeft = left; mRenderNode.setLeft(left); sizeChange(mRight - mLeft, height, oldWidth, height); if(!matrixIsIdentity){ mPrivateFlags |= PFLAG_DRAWN;// force another invalidation with the new orientation invalidate(true); } mBackgroundSizeChanged =true; mDefaultFocusHighlightSizeChanged =true; if(mForegroundInfo !=null){ mForegroundInfo.mBoundsChanged =true; } invalidateParentIfNeeded(); } } /** * Right position of this view relative to its parent. * * @return The right edge of this view, in pixels. */ @ViewDebug.CapturedViewProperty publicfinalint getRight(){ return mRight; } /** * Sets the right position of this view relative to its parent. This method is meant to be called * by the layout system and should not generally be called otherwise, because the property * may be changed at any time by the layout. * * @param right The right of this view, in pixels. */ publicfinalvoid setRight(int right){ if(right != mRight){ finalboolean matrixIsIdentity = hasIdentityMatrix(); if(matrixIsIdentity){ if(mAttachInfo !=null){ int maxRight; if(right < mRight){ maxRight = mRight; }else{ maxRight = right; } invalidate(0,0, maxRight - mLeft, mBottom - mTop); } }else{ // Double-invalidation is necessary to capture view's old and new areas invalidate(true); } int oldWidth = mRight - mLeft; int height = mBottom - mTop; mRight = right; mRenderNode.setRight(mRight); sizeChange(mRight - mLeft, height, oldWidth, height); if(!matrixIsIdentity){ mPrivateFlags |= PFLAG_DRAWN;// force another invalidation with the new orientation invalidate(true); } mBackgroundSizeChanged =true; mDefaultFocusHighlightSizeChanged =true; if(mForegroundInfo !=null){ mForegroundInfo.mBoundsChanged =true; } invalidateParentIfNeeded(); } } privatestaticfloat sanitizeFloatPropertyValue(float value,String propertyName){ return sanitizeFloatPropertyValue(value, propertyName,-Float.MAX_VALUE,Float.MAX_VALUE); } privatestaticfloat sanitizeFloatPropertyValue(float value,String propertyName, float min,float max){ // The expected "nothing bad happened" path if(value >= min && value <= max)return value; if(value < min || value ==Float.NEGATIVE_INFINITY){ if(sThrowOnInvalidFloatProperties){ thrownewIllegalArgumentException("Cannot set '"+ propertyName +"' to " + value +", the value must be >= "+ min); } return min; } if(value > max || value ==Float.POSITIVE_INFINITY){ if(sThrowOnInvalidFloatProperties){ thrownewIllegalArgumentException("Cannot set '"+ propertyName +"' to " + value +", the value must be <= "+ max); } return max; } if(Float.isNaN(value)){ if(sThrowOnInvalidFloatProperties){ thrownewIllegalArgumentException( "Cannot set '"+ propertyName +"' to Float.NaN"); } return0;// Unclear which direction this NaN went so... 0? } // Shouldn't be possible to reach this. thrownewIllegalStateException("How do you get here?? "+ value); } /** * The visual x position of this view, in pixels. This is equivalent to the * {@link #setTranslationX(float) translationX} property plus the current * {@link #getLeft() left} property. * * @return The visual x position of this view, in pixels. */ @ViewDebug.ExportedProperty(category ="drawing") publicfloat getX(){ return mLeft + getTranslationX(); } /** * Sets the visual x position of this view, in pixels. This is equivalent to setting the * {@link #setTranslationX(float) translationX} property to be the difference between * the x value passed in and the current {@link #getLeft() left} property. * * @param x The visual x position of this view, in pixels. */ publicvoid setX(float x){ setTranslationX(x - mLeft); } /** * The visual y position of this view, in pixels. This is equivalent to the * {@link #setTranslationY(float) translationY} property plus the current * {@link #getTop() top} property. * * @return The visual y position of this view, in pixels. */ @ViewDebug.ExportedProperty(category ="drawing") publicfloat getY(){ return mTop + getTranslationY(); } /** * Sets the visual y position of this view, in pixels. This is equivalent to setting the * {@link #setTranslationY(float) translationY} property to be the difference between * the y value passed in and the current {@link #getTop() top} property. * * @param y The visual y position of this view, in pixels. */ publicvoid setY(float y){ setTranslationY(y - mTop); } /** * The visual z position of this view, in pixels. This is equivalent to the * {@link #setTranslationZ(float) translationZ} property plus the current * {@link #getElevation() elevation} property. * * @return The visual z position of this view, in pixels. */ @ViewDebug.ExportedProperty(category ="drawing") publicfloat getZ(){ return getElevation()+ getTranslationZ(); } /** * Sets the visual z position of this view, in pixels. This is equivalent to setting the * {@link #setTranslationZ(float) translationZ} property to be the difference between * the z value passed in and the current {@link #getElevation() elevation} property. * * @param z The visual z position of this view, in pixels. */ publicvoid setZ(float z){ setTranslationZ(z - getElevation()); } /** * The base elevation of this view relative to its parent, in pixels. * * @return The base depth position of the view, in pixels. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getElevation(){ return mRenderNode.getElevation(); } /** * Sets the base elevation of this view, in pixels. * * @attr ref android.R.styleable#View_elevation */ @RemotableViewMethod publicvoid setElevation(float elevation){ if(elevation != getElevation()){ elevation = sanitizeFloatPropertyValue(elevation,"elevation"); invalidateViewProperty(true,false); mRenderNode.setElevation(elevation); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); } } /** * The horizontal location of this view relative to its {@link #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. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getTranslationX(){ return mRenderNode.getTranslationX(); } /** * Sets the horizontal location of this view relative to its {@link #getLeft() left} position. * This effectively positions the object post-layout, in addition to wherever the object's * layout placed it. * * @param translationX The horizontal position of this view relative to its left position, * in pixels. * * @attr ref android.R.styleable#View_translationX */ @RemotableViewMethod publicvoid setTranslationX(float translationX){ if(translationX != getTranslationX()){ invalidateViewProperty(true,false); mRenderNode.setTranslationX(translationX); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The vertical location of this view relative to its {@link #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. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getTranslationY(){ return mRenderNode.getTranslationY(); } /** * Sets the vertical location of this view relative to its {@link #getTop() top} position. * This effectively positions the object post-layout, in addition to wherever the object's * layout placed it. * * @param translationY The vertical position of this view relative to its top position, * in pixels. * * @attr ref android.R.styleable#View_translationY */ @RemotableViewMethod publicvoid setTranslationY(float translationY){ if(translationY != getTranslationY()){ invalidateViewProperty(true,false); mRenderNode.setTranslationY(translationY); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * The depth location of this view relative to its {@link #getElevation() elevation}. * * @return The depth of this view relative to its elevation. */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty publicfloat getTranslationZ(){ return mRenderNode.getTranslationZ(); } /** * Sets the depth location of this view relative to its {@link #getElevation() elevation}. * * @attr ref android.R.styleable#View_translationZ */ @RemotableViewMethod publicvoid setTranslationZ(float translationZ){ if(translationZ != getTranslationZ()){ translationZ = sanitizeFloatPropertyValue(translationZ,"translationZ"); invalidateViewProperty(true,false); mRenderNode.setTranslationZ(translationZ); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); } } /** * Changes the transformation matrix on the view. This is used in animation frameworks, * such as {@link android.transition.Transition}. When the animation finishes, the matrix * should be cleared by calling this method with <code>null</code> as the matrix parameter. * Application developers should use transformation methods like {@link #setRotation(float)}, * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}} * and {@link #setTranslationY(float)} (float)}} instead. * * @param matrix The matrix, null indicates that the matrix should be cleared. * @see #getAnimationMatrix() */ publicvoid setAnimationMatrix(@NullableMatrix matrix){ invalidateViewProperty(true,false); mRenderNode.setAnimationMatrix(matrix); invalidateViewProperty(false,true); invalidateParentIfNeededAndWasQuickRejected(); } /** * Return the current transformation matrix of the view. This is used in animation frameworks, * such as {@link android.transition.Transition}. Returns <code>null</code> when there is no * transformation provided by {@link #setAnimationMatrix(Matrix)}. * Application developers should use transformation methods like {@link #setRotation(float)}, * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}} * and {@link #setTranslationY(float)} (float)}} instead. * * @return the Matrix, null indicates there is no transformation * @see #setAnimationMatrix(Matrix) */ @Nullable publicMatrix getAnimationMatrix(){ return mRenderNode.getAnimationMatrix(); } /** * Returns the current StateListAnimator if exists. * * @return StateListAnimator or null if it does not exists * @see #setStateListAnimator(android.animation.StateListAnimator) */ @InspectableProperty publicStateListAnimator getStateListAnimator(){ return mStateListAnimator; } /** * Attaches the provided StateListAnimator to this View. * <p> * Any previously attached StateListAnimator will be detached. * * @param stateListAnimator The StateListAnimator to update the view * @see android.animation.StateListAnimator */ publicvoid setStateListAnimator(StateListAnimator stateListAnimator){ if(mStateListAnimator == stateListAnimator){ return; } if(mStateListAnimator !=null){ mStateListAnimator.setTarget(null); } mStateListAnimator = stateListAnimator; if(stateListAnimator !=null){ stateListAnimator.setTarget(this); if(isAttachedToWindow()){ stateListAnimator.setState(getDrawableState()); } } } /** * Returns whether the Outline should be used to clip the contents of the View. * <p> * Note that this flag will only be respected if the View's Outline returns true from * {@link Outline#canClip()}. * * @see #setOutlineProvider(ViewOutlineProvider) * @see #setClipToOutline(boolean) */ publicfinalboolean getClipToOutline(){ return mRenderNode.getClipToOutline(); } /** * Sets whether the View's Outline should be used to clip the contents of the View. * <p> * Only a single non-rectangular clip can be applied on a View at any time. * Circular clips from a {@link ViewAnimationUtils#createCircularReveal(View, int, int, float, float) * circular reveal} animation take priority over Outline clipping, and * child Outline clipping takes priority over Outline clipping done by a * parent. * <p> * Note that this flag will only be respected if the View's Outline returns true from * {@link Outline#canClip()}. * * @see #setOutlineProvider(ViewOutlineProvider) * @see #getClipToOutline() * * @attr ref android.R.styleable#View_clipToOutline */ @RemotableViewMethod publicvoid setClipToOutline(boolean clipToOutline){ damageInParent(); if(getClipToOutline()!= clipToOutline){ mRenderNode.setClipToOutline(clipToOutline); } } // correspond to the enum values of View_outlineProvider privatestaticfinalint PROVIDER_BACKGROUND =0; privatestaticfinalint PROVIDER_NONE =1; privatestaticfinalint PROVIDER_BOUNDS =2; privatestaticfinalint PROVIDER_PADDED_BOUNDS =3; privatevoid setOutlineProviderFromAttribute(int providerInt){ switch(providerInt){ case PROVIDER_BACKGROUND: setOutlineProvider(ViewOutlineProvider.BACKGROUND); break; case PROVIDER_NONE: setOutlineProvider(null); break; case PROVIDER_BOUNDS: setOutlineProvider(ViewOutlineProvider.BOUNDS); break; case PROVIDER_PADDED_BOUNDS: setOutlineProvider(ViewOutlineProvider.PADDED_BOUNDS); break; } } /** * Sets the {@link ViewOutlineProvider} of the view, which generates the Outline that defines * the shape of the shadow it casts, and enables outline clipping. * <p> * The default ViewOutlineProvider, {@link ViewOutlineProvider#BACKGROUND}, queries the Outline * from the View's background drawable, via {@link Drawable#getOutline(Outline)}. Changing the * outline provider with this method allows this behavior to be overridden. * <p> * If the ViewOutlineProvider is null, if querying it for an outline returns false, * or if the produced Outline is {@link Outline#isEmpty()}, shadows will not be cast. * <p> * Only outlines that return true from {@link Outline#canClip()} may be used for clipping. * * @see #setClipToOutline(boolean) * @see #getClipToOutline() * @see #getOutlineProvider() */ publicvoid setOutlineProvider(ViewOutlineProvider provider){ mOutlineProvider = provider; invalidateOutline(); } /** * Returns the current {@link ViewOutlineProvider} of the view, which generates the Outline * that defines the shape of the shadow it casts, and enables outline clipping. * * @see #setOutlineProvider(ViewOutlineProvider) */ @InspectableProperty publicViewOutlineProvider getOutlineProvider(){ return mOutlineProvider; } /** * Called to rebuild this View's Outline from its {@link ViewOutlineProvider outline provider} * * @see #setOutlineProvider(ViewOutlineProvider) */ publicvoid invalidateOutline(){ rebuildOutline(); notifySubtreeAccessibilityStateChangedIfNeeded(); invalidateViewProperty(false,false); } /** * Internal version of {@link #invalidateOutline()} which invalidates the * outline without invalidating the view itself. This is intended to be called from * within methods in the View class itself which are the result of the view being * invalidated already. For example, when we are drawing the background of a View, * we invalidate the outline in case it changed in the meantime, but we do not * need to invalidate the view because we're already drawing the background as part * of drawing the view in response to an earlier invalidation of the view. */ privatevoid rebuildOutline(){ // Unattached views ignore this signal, and outline is recomputed in onAttachedToWindow() if(mAttachInfo ==null)return; if(mOutlineProvider ==null){ // no provider, remove outline mRenderNode.setOutline(null); }else{ finalOutline outline = mAttachInfo.mTmpOutline; outline.setEmpty(); outline.setAlpha(1.0f); mOutlineProvider.getOutline(this, outline); mRenderNode.setOutline(outline); } } /** * HierarchyViewer only * * @hide */ @ViewDebug.ExportedProperty(category ="drawing") publicboolean hasShadow(){ return mRenderNode.hasShadow(); } /** * Sets the color of the spot shadow that is drawn when the view has a positive Z or * elevation value. * <p> * By default the shadow color is black. Generally, this color will be opaque so the intensity * of the shadow is consistent between different views with different colors. * <p> * The opacity of the final spot shadow is a function of the shadow caster height, the * alpha channel of the outlineSpotShadowColor (typically opaque), and the * {@link android.R.attr#spotShadowAlpha} theme attribute. * * @attr ref android.R.styleable#View_outlineSpotShadowColor * @param color The color this View will cast for its elevation spot shadow. */ publicvoid setOutlineSpotShadowColor(@ColorIntint color){ if(mRenderNode.setSpotShadowColor(color)){ invalidateViewProperty(true,true); } } /** * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing * was set */ @InspectableProperty public@ColorIntint getOutlineSpotShadowColor(){ return mRenderNode.getSpotShadowColor(); } /** * Sets the color of the ambient shadow that is drawn when the view has a positive Z or * elevation value. * <p> * By default the shadow color is black. Generally, this color will be opaque so the intensity * of the shadow is consistent between different views with different colors. * <p> * The opacity of the final ambient shadow is a function of the shadow caster height, the * alpha channel of the outlineAmbientShadowColor (typically opaque), and the * {@link android.R.attr#ambientShadowAlpha} theme attribute. * * @attr ref android.R.styleable#View_outlineAmbientShadowColor * @param color The color this View will cast for its elevation shadow. */ publicvoid setOutlineAmbientShadowColor(@ColorIntint color){ if(mRenderNode.setAmbientShadowColor(color)){ invalidateViewProperty(true,true); } } /** * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if * nothing was set */ @InspectableProperty public@ColorIntint getOutlineAmbientShadowColor(){ return mRenderNode.getAmbientShadowColor(); } /** @hide */ publicvoid setRevealClip(boolean shouldClip,float x,float y,float radius){ mRenderNode.setRevealClip(shouldClip, x, y, radius); invalidateViewProperty(false,false); } /** * Hit rectangle in parent's coordinates * * @param outRect The hit rectangle of the view. */ publicvoid getHitRect(Rect outRect){ if(hasIdentityMatrix()|| mAttachInfo ==null){ outRect.set(mLeft, mTop, mRight, mBottom); }else{ finalRectF tmpRect = mAttachInfo.mTmpTransformRect; tmpRect.set(0,0, getWidth(), getHeight()); getMatrix().mapRect(tmpRect);// TODO: mRenderNode.mapRect(tmpRect) outRect.set((int) tmpRect.left + mLeft,(int) tmpRect.top + mTop, (int) tmpRect.right + mLeft,(int) tmpRect.bottom + mTop); } } /** * Determines whether the given point, in local coordinates is inside the view. */ /*package*/finalboolean pointInView(float localX,float localY){ return pointInView(localX, localY,0); } /** * Utility method to determine whether the given point, in local coordinates, * is inside the view, where the area of the view is expanded by the slop factor. * This method is called while processing touch-move events to determine if the event * is still within the view. * * @hide */ @UnsupportedAppUsage publicboolean pointInView(float localX,float localY,float slop){ return localX >=-slop && localY >=-slop && localX <((mRight - mLeft)+ slop)&& localY <((mBottom - mTop)+ slop); } /** * When a view has focus and the user navigates away from it, the next view is searched for * starting from the rectangle filled in by this method. * * By default, the rectangle is the {@link #getDrawingRect(android.graphics.Rect)}) * of the view. However, if your view maintains some idea of internal selection, * such as a cursor, or a selected row or column, you should override this method and * fill in a more specific rectangle. * * @param r The rectangle to fill in, in this view's coordinates. */ publicvoid getFocusedRect(Rect r){ getDrawingRect(r); } /** * Sets {@code r} to the coordinates of the non-clipped area of this view in * the coordinate space of the view's root view. Sets {@code globalOffset} * to the offset of the view's x and y coordinates from the coordinate space * origin, which is the top left corner of the root view irrespective of * screen decorations and system UI elements. * * <p>To convert {@code r} to coordinates relative to the top left corner of * this view (without taking view rotations into account), offset {@code r} * by the inverse values of * {@code globalOffset}&mdash;{@code r.offset(-globalOffset.x, * -globalOffset.y)}&mdash;which is equivalent to calling * {@link #getLocalVisibleRect(Rect) getLocalVisibleRect(Rect)}. * * <p><b>Note:</b> Do not use this method to determine the size of a window * in multi-window mode; use * {@link WindowManager#getCurrentWindowMetrics()}. * * @param r If the method returns true, contains the coordinates of the * visible portion of this view in the coordinate space of the view's * root view. If the method returns false, the contents of {@code r} * are undefined. * @param globalOffset If the method returns true, contains the offset of * the x and y coordinates of this view from the top left corner of the * view's root view. If the method returns false, the contents of * {@code globalOffset} are undefined. The argument can be null (see * {@link #getGlobalVisibleRect(Rect) getGlobalVisibleRect(Rect)}. * @return true if at least part of the view is visible within the root * view; false if the view is completely clipped or translated out of * the visible area of the root view. * * @see #getLocalVisibleRect(Rect) */ publicboolean getGlobalVisibleRect(Rect r,Point globalOffset){ int width = mRight - mLeft; int height = mBottom - mTop; if(width >0&& height >0){ r.set(0,0, width, height); if(globalOffset !=null){ globalOffset.set(-mScrollX,-mScrollY); } return mParent ==null|| mParent.getChildVisibleRect(this, r, globalOffset); } returnfalse; } /** * Sets {@code r} to the coordinates of the non-clipped area of this view in * the coordinate space of the view's root view. * * <p>See {@link #getGlobalVisibleRect(Rect, Point) * getGlobalVisibleRect(Rect, Point)} for more information. * * @param r If the method returns true, contains the coordinates of the * visible portion of this view in the coordinate space of the view's * root view. If the method returns false, the contents of {@code r} * are undefined. * @return true if at least part of the view is visible within the root * view; otherwise false. */ publicfinalboolean getGlobalVisibleRect(Rect r){ return getGlobalVisibleRect(r,null); } /** * Sets {@code r} to the coordinates of the non-clipped area of this view * relative to the top left corner of the view. * * <p>If the view is clipped on the left or top, the left and top * coordinates are offset from 0 by the clipped amount. For example, if the * view is off screen 50px on the left and 30px at the top, the left and top * coordinates are 50 and 30 respectively. * * <p>If the view is clipped on the right or bottom, the right and bottom * coordinates are reduced by the clipped amount. For example, if the view * is off screen 40px on the right and 20px at the bottom, the right * coordinate is the view width - 40, and the bottom coordinate is the view * height - 20. * * @param r If the method returns true, contains the coordinates of the * visible portion of this view relative to the top left corner of the * view. If the method returns false, the contents of {@code r} are * undefined. * @return true if at least part of the view is visible; false if the view * is completely clipped or translated out of the visible area. * * @see #getGlobalVisibleRect(Rect, Point) */ publicfinalboolean getLocalVisibleRect(Rect r){ finalPoint offset = mAttachInfo !=null? mAttachInfo.mPoint :newPoint(); if(getGlobalVisibleRect(r, offset)){ r.offset(-offset.x,-offset.y);// make r local returntrue; } returnfalse; } /** * Offset this view's vertical location by the specified number of pixels. * * @param offset the number of pixels to offset the view by */ publicvoid offsetTopAndBottom(int offset){ if(offset !=0){ finalboolean matrixIsIdentity = hasIdentityMatrix(); if(matrixIsIdentity){ if(isHardwareAccelerated()){ invalidateViewProperty(false,false); }else{ finalViewParent p = mParent; if(p !=null&& mAttachInfo !=null){ finalRect r = mAttachInfo.mTmpInvalRect; int minTop; int maxBottom; int yLoc; if(offset <0){ minTop = mTop + offset; maxBottom = mBottom; yLoc = offset; }else{ minTop = mTop; maxBottom = mBottom + offset; yLoc =0; } r.set(0, yLoc, mRight - mLeft, maxBottom - minTop); p.invalidateChild(this, r); } } }else{ invalidateViewProperty(false,false); } mTop += offset; mBottom += offset; mRenderNode.offsetTopAndBottom(offset); if(isHardwareAccelerated()){ invalidateViewProperty(false,false); invalidateParentIfNeededAndWasQuickRejected(); }else{ if(!matrixIsIdentity){ invalidateViewProperty(false,true); } invalidateParentIfNeeded(); } notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * Offset this view's horizontal location by the specified amount of pixels. * * @param offset the number of pixels to offset the view by */ publicvoid offsetLeftAndRight(int offset){ if(offset !=0){ finalboolean matrixIsIdentity = hasIdentityMatrix(); if(matrixIsIdentity){ if(isHardwareAccelerated()){ invalidateViewProperty(false,false); }else{ finalViewParent p = mParent; if(p !=null&& mAttachInfo !=null){ finalRect r = mAttachInfo.mTmpInvalRect; int minLeft; int maxRight; if(offset <0){ minLeft = mLeft + offset; maxRight = mRight; }else{ minLeft = mLeft; maxRight = mRight + offset; } r.set(0,0, maxRight - minLeft, mBottom - mTop); p.invalidateChild(this, r); } } }else{ invalidateViewProperty(false,false); } mLeft += offset; mRight += offset; mRenderNode.offsetLeftAndRight(offset); if(isHardwareAccelerated()){ invalidateViewProperty(false,false); invalidateParentIfNeededAndWasQuickRejected(); }else{ if(!matrixIsIdentity){ invalidateViewProperty(false,true); } invalidateParentIfNeeded(); } notifySubtreeAccessibilityStateChangedIfNeeded(); } } /** * Get the LayoutParams associated with this view. All views should have * layout parameters. These supply parameters to the <i>parent</i> of this * view specifying how it should be arranged. There are many subclasses of * ViewGroup.LayoutParams, and these correspond to the different subclasses * of ViewGroup that are responsible for arranging their children. * * This method may return null if this View is not attached to a parent * ViewGroup or {@link #setLayoutParams(android.view.ViewGroup.LayoutParams)} * was not invoked successfully. When a View is attached to a parent * ViewGroup, this method must not return null. * * @return The LayoutParams associated with this view, or null if no * parameters have been set yet */ @ViewDebug.ExportedProperty(deepExport =true, prefix ="layout_") publicViewGroup.LayoutParams getLayoutParams(){ return mLayoutParams; } /** * Set the layout parameters associated with this view. These supply * parameters to the <i>parent</i> of this view specifying how it should be * arranged. There are many subclasses of ViewGroup.LayoutParams, and these * correspond to the different subclasses of ViewGroup that are responsible * for arranging their children. * * @param params The layout parameters for this view, cannot be null */ publicvoid setLayoutParams(ViewGroup.LayoutParams params){ if(params ==null){ thrownewNullPointerException("Layout parameters cannot be null"); } mLayoutParams = params; resolveLayoutParams(); if(mParent instanceofViewGroup){ ((ViewGroup) mParent).onSetLayoutParams(this, params); } requestLayout(); } /** * Resolve the layout parameters depending on the resolved layout direction * * @hide */ publicvoid resolveLayoutParams(){ if(mLayoutParams !=null){ mLayoutParams.resolveLayoutDirection(getLayoutDirection()); } } /** * Set the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the x position to scroll to * @param y the y position to scroll to */ publicvoid scrollTo(int x,int y){ if(mScrollX != x || mScrollY != y){ int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if(!awakenScrollBars()){ postInvalidateOnAnimation(); } } } /** * Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally * @param y the amount of pixels to scroll by vertically */ publicvoid scrollBy(int x,int y){ scrollTo(mScrollX + x, mScrollY + y); } /** * <p>Trigger the scrollbars to draw. When invoked this method starts an * animation to fade the scrollbars out after a default delay. If a subclass * provides animated scrolling, the start delay should equal the duration * of the scrolling animation.</p> * * <p>The animation starts only if at least one of the scrollbars is * enabled, as specified by {@link #isHorizontalScrollBarEnabled()} and * {@link #isVerticalScrollBarEnabled()}. When the animation is started, * this method returns true, and false otherwise. If the animation is * started, this method calls {@link #invalidate()}; in that case the * caller should not call {@link #invalidate()}.</p> * * <p>This method should be invoked every time a subclass directly updates * the scroll parameters.</p> * * <p>This method is automatically invoked by {@link #scrollBy(int, int)} * and {@link #scrollTo(int, int)}.</p> * * @return true if the animation is played, false otherwise * * @see #awakenScrollBars(int) * @see #scrollBy(int, int) * @see #scrollTo(int, int) * @see #isHorizontalScrollBarEnabled() * @see #isVerticalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) * @see #setVerticalScrollBarEnabled(boolean) */ protectedboolean awakenScrollBars(){ return mScrollCache !=null&& awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade,true); } /** * Trigger the scrollbars to draw. * This method differs from awakenScrollBars() only in its default duration. * initialAwakenScrollBars() will show the scroll bars for longer than * usual to give the user more of a chance to notice them. * * @return true if the animation is played, false otherwise. */ privateboolean initialAwakenScrollBars(){ return mScrollCache !=null&& awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade *4,true); } /** * <p> * Trigger the scrollbars to draw. When invoked this method starts an * animation to fade the scrollbars out after a fixed delay. If a subclass * provides animated scrolling, the start delay should equal the duration of * the scrolling animation. * </p> * * <p> * The animation starts only if at least one of the scrollbars is enabled, * as specified by {@link #isHorizontalScrollBarEnabled()} and * {@link #isVerticalScrollBarEnabled()}. When the animation is started, * this method returns true, and false otherwise. If the animation is * started, this method calls {@link #invalidate()}; in that case the caller * should not call {@link #invalidate()}. * </p> * * <p> * This method should be invoked every time a subclass directly updates the * scroll parameters. * </p> * * @param startDelay the delay, in milliseconds, after which the animation * should start; when the delay is 0, the animation starts * immediately * @return true if the animation is played, false otherwise * * @see #scrollBy(int, int) * @see #scrollTo(int, int) * @see #isHorizontalScrollBarEnabled() * @see #isVerticalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) * @see #setVerticalScrollBarEnabled(boolean) */ protectedboolean awakenScrollBars(int startDelay){ return awakenScrollBars(startDelay,true); } /** * <p> * Trigger the scrollbars to draw. When invoked this method starts an * animation to fade the scrollbars out after a fixed delay. If a subclass * provides animated scrolling, the start delay should equal the duration of * the scrolling animation. * </p> * * <p> * The animation starts only if at least one of the scrollbars is enabled, * as specified by {@link #isHorizontalScrollBarEnabled()} and * {@link #isVerticalScrollBarEnabled()}. When the animation is started, * this method returns true, and false otherwise. If the animation is * started, this method calls {@link #invalidate()} if the invalidate parameter * is set to true; in that case the caller * should not call {@link #invalidate()}. * </p> * * <p> * This method should be invoked every time a subclass directly updates the * scroll parameters. * </p> * * @param startDelay the delay, in milliseconds, after which the animation * should start; when the delay is 0, the animation starts * immediately * * @param invalidate Whether this method should call invalidate * * @return true if the animation is played, false otherwise * * @see #scrollBy(int, int) * @see #scrollTo(int, int) * @see #isHorizontalScrollBarEnabled() * @see #isVerticalScrollBarEnabled() * @see #setHorizontalScrollBarEnabled(boolean) * @see #setVerticalScrollBarEnabled(boolean) */ protectedboolean awakenScrollBars(int startDelay,boolean invalidate){ finalScrollabilityCache scrollCache = mScrollCache; if(scrollCache ==null||!scrollCache.fadeScrollBars){ returnfalse; } if(scrollCache.scrollBar ==null){ scrollCache.scrollBar =newScrollBarDrawable(); scrollCache.scrollBar.setState(getDrawableState()); scrollCache.scrollBar.setCallback(this); } if(isHorizontalScrollBarEnabled()|| isVerticalScrollBarEnabled()){ if(invalidate){ // Invalidate to show the scrollbars postInvalidateOnAnimation(); } if(scrollCache.state ==ScrollabilityCache.OFF){ // FIXME: this is copied from WindowManagerService. // We should get this value from the system when it // is possible to do so. finalint KEY_REPEAT_FIRST_DELAY =750; startDelay =Math.max(KEY_REPEAT_FIRST_DELAY, startDelay); } // Tell mScrollCache when we should start fading. This may // extend the fade start time if one was already scheduled long fadeStartTime =AnimationUtils.currentAnimationTimeMillis()+ startDelay; scrollCache.fadeStartTime = fadeStartTime; scrollCache.state =ScrollabilityCache.ON; // Schedule our fader to run, unscheduling any old ones first if(mAttachInfo !=null){ mAttachInfo.mHandler.removeCallbacks(scrollCache); mAttachInfo.mHandler.postAtTime(scrollCache, fadeStartTime); } returntrue; } returnfalse; } /** * Do not invalidate views which are not visible and which are not running an animation. They * will not get drawn and they should not set dirty flags as if they will be drawn */ privateboolean skipInvalidate(){ return(mViewFlags & VISIBILITY_MASK)!= VISIBLE && mCurrentAnimation ==null&& (!(mParent instanceofViewGroup)|| !((ViewGroup) mParent).isViewTransitioning(this)); } /** * Mark the area defined by dirty as needing to be drawn. If the view is * visible, {@link #onDraw(android.graphics.Canvas)} will be called at some * point in the future. * <p> * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. * <p> * <b>WARNING:</b> In API 19 and below, this method may be destructive to * {@code dirty}. * * @param dirty the rectangle representing the bounds of the dirty region * * @deprecated The switch to hardware accelerated rendering in API 14 reduced * the importance of the dirty rectangle. In API 21 the given rectangle is * ignored entirely in favor of an internally-calculated area instead. * Because of this, clients are encouraged to just call {@link #invalidate()}. */ @Deprecated publicvoid invalidate(Rect dirty){ finalint scrollX = mScrollX; finalint scrollY = mScrollY; invalidateInternal(dirty.left - scrollX, dirty.top - scrollY, dirty.right - scrollX, dirty.bottom - scrollY,true,false); } /** * Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The * coordinates of the dirty rect are relative to the view. If the view is * visible, {@link #onDraw(android.graphics.Canvas)} will be called at some * point in the future. * <p> * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. * * @param l the left position of the dirty region * @param t the top position of the dirty region * @param r the right position of the dirty region * @param b the bottom position of the dirty region * * @deprecated The switch to hardware accelerated rendering in API 14 reduced * the importance of the dirty rectangle. In API 21 the given rectangle is * ignored entirely in favor of an internally-calculated area instead. * Because of this, clients are encouraged to just call {@link #invalidate()}. */ @Deprecated publicvoid invalidate(int l,int t,int r,int b){ finalint scrollX = mScrollX; finalint scrollY = mScrollY; invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY,true,false); } /** * Invalidate the whole view. If the view is visible, * {@link #onDraw(android.graphics.Canvas)} will be called at some point in * the future. * <p> * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. */ publicvoid invalidate(){ invalidate(true); } /** * This is where the invalidate() work actually happens. A full invalidate() * causes the drawing cache to be invalidated, but this function can be * called with invalidateCache set to false to skip that invalidation step * for cases that do not need it (for example, a component that remains at * the same dimensions with the same content). * * @param invalidateCache Whether the drawing cache for this view should be * invalidated as well. This is usually true for a full * invalidate, but may be set to false if the View's contents or * dimensions have not changed. * @hide */ @UnsupportedAppUsage publicvoid invalidate(boolean invalidateCache){ invalidateInternal(0,0, mRight - mLeft, mBottom - mTop, invalidateCache,true); } void invalidateInternal(int l,int t,int r,int b,boolean invalidateCache, boolean fullInvalidate){ if(mGhostView !=null){ mGhostView.invalidate(true); return; } if(skipInvalidate()){ return; } // Reset content capture caches mPrivateFlags4 &=~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; mContentCaptureSessionCached =false; if((mPrivateFlags &(PFLAG_DRAWN | PFLAG_HAS_BOUNDS))==(PFLAG_DRAWN | PFLAG_HAS_BOUNDS) ||(invalidateCache &&(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID)== PFLAG_DRAWING_CACHE_VALID) ||(mPrivateFlags & PFLAG_INVALIDATED)!= PFLAG_INVALIDATED ||(fullInvalidate && isOpaque()!= mLastIsOpaque)){ if(fullInvalidate){ mLastIsOpaque = isOpaque(); mPrivateFlags &=~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; if(invalidateCache){ mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &=~PFLAG_DRAWING_CACHE_VALID; } // Propagate the damage rectangle to the parent view. finalAttachInfo ai = mAttachInfo; finalViewParent p = mParent; if(p !=null&& ai !=null&& l < r && t < b){ finalRect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); p.invalidateChild(this, damage); } // Damage the entire projection receiver, if necessary. if(mBackground !=null&& mBackground.isProjected()){ finalView receiver = getProjectionReceiver(); if(receiver !=null){ receiver.damageInParent(); } } } } /** * @return this view's projection receiver, or {@code null} if none exists */ privateView getProjectionReceiver(){ ViewParent p = getParent(); while(p !=null&& p instanceofView){ finalView v =(View) p; if(v.isProjectionReceiver()){ return v; } p = p.getParent(); } returnnull; } /** * @return whether the view is a projection receiver */ privateboolean isProjectionReceiver(){ return mBackground !=null; } /** * Quick invalidation for View property changes (alpha, translationXY, etc.). We don't want to * set any flags or handle all of the cases handled by the default invalidation methods. * Instead, we just want to schedule a traversal in ViewRootImpl with the appropriate * dirty rect. This method calls into fast invalidation methods in ViewGroup that * walk up the hierarchy, transforming the dirty rect as necessary. * * The method also handles normal invalidation logic if display list properties are not * being used in this view. The invalidateParent and forceRedraw flags are used by that * backup approach, to handle these cases used in the various property-setting methods. * * @param invalidateParent Force a call to invalidateParentCaches() if display list properties * are not being used in this view * @param forceRedraw Mark the view as DRAWN to force the invalidation to propagate, if display * list properties are not being used in this view */ @UnsupportedAppUsage void invalidateViewProperty(boolean invalidateParent,boolean forceRedraw){ if(!isHardwareAccelerated() ||!mRenderNode.hasDisplayList() ||(mPrivateFlags & PFLAG_DRAW_ANIMATION)!=0){ if(invalidateParent){ invalidateParentCaches(); } if(forceRedraw){ mPrivateFlags |= PFLAG_DRAWN;// force another invalidation with the new orientation } invalidate(false); }else{ damageInParent(); } } /** * Tells the parent view to damage this view's bounds. * * @hide */ protectedvoid damageInParent(){ if(mParent !=null&& mAttachInfo !=null){ mParent.onDescendantInvalidated(this,this); } } /** * Used to indicate that the parent of this view should clear its caches. This functionality * is used to force the parent to rebuild its display list (when hardware-accelerated), * which is necessary when various parent-managed properties of the view change, such as * alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method only * clears the parent caches and does not causes an invalidate event. * * @hide */ @UnsupportedAppUsage protectedvoid invalidateParentCaches(){ if(mParent instanceofView){ ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED; } } /** * Used to indicate that the parent of this view should be invalidated. This functionality * is used to force the parent to rebuild its display list (when hardware-accelerated), * which is necessary when various parent-managed properties of the view change, such as * alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method will propagate * an invalidation event to the parent. * * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedvoid invalidateParentIfNeeded(){ if(isHardwareAccelerated()&& mParent instanceofView){ ((View) mParent).invalidate(true); } } /** * @hide */ protectedvoid invalidateParentIfNeededAndWasQuickRejected(){ if((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED)!=0){ // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } } /** * Indicates whether this View is opaque. An opaque View guarantees that it will * draw all the pixels overlapping its bounds using a fully opaque color. * * Subclasses of View should override this method whenever possible to indicate * whether an instance is opaque. Opaque Views are treated in a special way by * the View hierarchy, possibly allowing it to perform optimizations during * invalidate/draw passes. * * @return True if this View is guaranteed to be fully opaque, false otherwise. */ @ViewDebug.ExportedProperty(category ="drawing") publicboolean isOpaque(){ return(mPrivateFlags & PFLAG_OPAQUE_MASK)== PFLAG_OPAQUE_MASK && getFinalAlpha()>=1.0f; } /** * @hide */ @UnsupportedAppUsage protectedvoid computeOpaqueFlags(){ // Opaque if: // - Has a background // - Background is opaque // - Doesn't have scrollbars or scrollbars overlay if(mBackground !=null&& mBackground.getOpacity()==PixelFormat.OPAQUE){ mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND; }else{ mPrivateFlags &=~PFLAG_OPAQUE_BACKGROUND; } finalint flags = mViewFlags; if(((flags & SCROLLBARS_VERTICAL)==0&&(flags & SCROLLBARS_HORIZONTAL)==0)|| (flags & SCROLLBARS_STYLE_MASK)== SCROLLBARS_INSIDE_OVERLAY || (flags & SCROLLBARS_STYLE_MASK)== SCROLLBARS_OUTSIDE_OVERLAY){ mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS; }else{ mPrivateFlags &=~PFLAG_OPAQUE_SCROLLBARS; } } /** * @hide */ protectedboolean hasOpaqueScrollbars(){ return(mPrivateFlags & PFLAG_OPAQUE_SCROLLBARS)== PFLAG_OPAQUE_SCROLLBARS; } /** * @return A handler associated with the thread running the View. This * handler can be used to pump events in the UI events queue. */ publicHandler getHandler(){ finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ return attachInfo.mHandler; } returnnull; } /** * Returns the queue of runnable for this view. * * @return the queue of runnables for this view */ privateHandlerActionQueue getRunQueue(){ if(mRunQueue ==null){ mRunQueue =newHandlerActionQueue(); } return mRunQueue; } /** * Gets the view root associated with the View. * @return The view root, or null if none. * @hide */ @UnsupportedAppUsage publicViewRootImpl getViewRootImpl(){ if(mAttachInfo !=null){ return mAttachInfo.mViewRootImpl; } returnnull; } /** * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicThreadedRenderer getThreadedRenderer(){ return mAttachInfo !=null? mAttachInfo.mThreadedRenderer :null; } /** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * * @see #postDelayed * @see #removeCallbacks */ publicboolean post(Runnable action){ finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); returntrue; } /** * <p>Causes the Runnable to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. * * @see #post * @see #removeCallbacks */ publicboolean postDelayed(Runnable action,long delayMillis){ finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ return attachInfo.mHandler.postDelayed(action, delayMillis); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().postDelayed(action, delayMillis); returntrue; } /** * <p>Causes the Runnable to execute on the next animation time step. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * * @see #postOnAnimationDelayed * @see #removeCallbacks */ publicvoid postOnAnimation(Runnable action){ finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ attachInfo.mViewRootImpl.mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, action,null); }else{ // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().post(action); } } /** * <p>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.</p> * * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @see #postOnAnimation * @see #removeCallbacks */ publicvoid postOnAnimationDelayed(Runnable action,long delayMillis){ finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ attachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed( Choreographer.CALLBACK_ANIMATION, action,null, delayMillis); }else{ // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().postDelayed(action, delayMillis); } } /** * <p>Removes the specified Runnable from the message queue.</p> * * @param action The Runnable to remove from the message handling queue * * @return true if this view could ask the Handler to remove the Runnable, * false otherwise. When the returned value is true, the Runnable * may or may not have been actually removed from the message queue * (for instance, if the Runnable was not in the queue already.) * * @see #post * @see #postDelayed * @see #postOnAnimation * @see #postOnAnimationDelayed */ publicboolean removeCallbacks(Runnable action){ if(action !=null){ finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ attachInfo.mHandler.removeCallbacks(action); attachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION, action,null); } getRunQueue().removeCallbacks(action); } returntrue; } /** * <p>Cause an invalidate to happen on a subsequent cycle through the event loop. * Use this to invalidate the View from a non-UI thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @see #invalidate() * @see #postInvalidateDelayed(long) */ publicvoid postInvalidate(){ postInvalidateDelayed(0); } /** * <p>Cause an invalidate of the specified area to happen on a subsequent cycle * through the event loop. Use this to invalidate the View from a non-UI thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @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. * * @see #invalidate(int, int, int, int) * @see #invalidate(Rect) * @see #postInvalidateDelayed(long, int, int, int, int) */ publicvoid postInvalidate(int left,int top,int right,int bottom){ postInvalidateDelayed(0, left, top, right, bottom); } /** * <p>Cause an invalidate to happen on a subsequent cycle through the event * loop. Waits for the specified amount of time.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param delayMilliseconds the duration in milliseconds to delay the * invalidation by * * @see #invalidate() * @see #postInvalidate() */ publicvoid postInvalidateDelayed(long delayMilliseconds){ // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } } /** * <p>Cause an invalidate of the specified area to happen on a subsequent cycle * through the event loop. Waits for the specified amount of time.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param delayMilliseconds the duration in milliseconds to delay the * invalidation by * @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. * * @see #invalidate(int, int, int, int) * @see #invalidate(Rect) * @see #postInvalidate(int, int, int, int) */ publicvoid postInvalidateDelayed(long delayMilliseconds,int left,int top, int right,int bottom){ // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ finalAttachInfo.InvalidateInfo info =AttachInfo.InvalidateInfo.obtain(); info.target =this; info.left = left; info.top = top; info.right = right; info.bottom = bottom; attachInfo.mViewRootImpl.dispatchInvalidateRectDelayed(info, delayMilliseconds); } } /** * <p>Cause an invalidate to happen on the next animation time step, typically the * next display frame.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @see #invalidate() */ publicvoid postInvalidateOnAnimation(){ // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ attachInfo.mViewRootImpl.dispatchInvalidateOnAnimation(this); } } /** * <p>Cause an invalidate of the specified area to happen on the next animation * time step, typically the next display frame.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @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. * * @see #invalidate(int, int, int, int) * @see #invalidate(Rect) */ publicvoid postInvalidateOnAnimation(int left,int top,int right,int bottom){ // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window finalAttachInfo attachInfo = mAttachInfo; if(attachInfo !=null){ finalAttachInfo.InvalidateInfo info =AttachInfo.InvalidateInfo.obtain(); info.target =this; info.left = left; info.top = top; info.right = right; info.bottom = bottom; attachInfo.mViewRootImpl.dispatchInvalidateRectOnAnimation(info); } } /** * Post a callback to send a {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. * This event is sent at most once every * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. */ privatevoid postSendViewScrolledAccessibilityEventCallback(int dx,int dy){ if(AccessibilityManager.getInstance(mContext).isEnabled()){ AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); event.setScrollDeltaX(dx); event.setScrollDeltaY(dy); sendAccessibilityEventUnchecked(event); } } /** * Called by a parent to request that a child update its values for mScrollX * and mScrollY if necessary. This will typically be done if the child is * animating a scroll using a {@link android.widget.Scroller Scroller} * object. */ publicvoid computeScroll(){ } /** * <p>Indicate whether the horizontal edges are faded when the view is * scrolled horizontally.</p> * * @return true if the horizontal edges should are faded on scroll, false * otherwise * * @see #setHorizontalFadingEdgeEnabled(boolean) * * @attr ref android.R.styleable#View_requiresFadingEdge */ publicboolean isHorizontalFadingEdgeEnabled(){ return(mViewFlags & FADING_EDGE_HORIZONTAL)== FADING_EDGE_HORIZONTAL; } /** * <p>Define whether the horizontal edges should be faded when this view * is scrolled horizontally.</p> * * @param horizontalFadingEdgeEnabled true if the horizontal edges should * be faded when the view is scrolled * horizontally * * @see #isHorizontalFadingEdgeEnabled() * * @attr ref android.R.styleable#View_requiresFadingEdge */ publicvoid setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled){ if(isHorizontalFadingEdgeEnabled()!= horizontalFadingEdgeEnabled){ if(horizontalFadingEdgeEnabled){ initScrollCache(); } mViewFlags ^= FADING_EDGE_HORIZONTAL; } } /** * <p>Indicate whether the vertical edges are faded when the view is * scrolled horizontally.</p> * * @return true if the vertical edges should are faded on scroll, false * otherwise * * @see #setVerticalFadingEdgeEnabled(boolean) * * @attr ref android.R.styleable#View_requiresFadingEdge */ publicboolean isVerticalFadingEdgeEnabled(){ return(mViewFlags & FADING_EDGE_VERTICAL)== FADING_EDGE_VERTICAL; } /** * <p>Define whether the vertical edges should be faded when this view * is scrolled vertically.</p> * * @param verticalFadingEdgeEnabled true if the vertical edges should * be faded when the view is scrolled * vertically * * @see #isVerticalFadingEdgeEnabled() * * @attr ref android.R.styleable#View_requiresFadingEdge */ publicvoid setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled){ if(isVerticalFadingEdgeEnabled()!= verticalFadingEdgeEnabled){ if(verticalFadingEdgeEnabled){ initScrollCache(); } mViewFlags ^= FADING_EDGE_VERTICAL; } } /** * Get the fading edge flags, used for inspection. * * @return One of {@link #FADING_EDGE_NONE}, {@link #FADING_EDGE_VERTICAL}, * or {@link #FADING_EDGE_HORIZONTAL} * @hide */ @InspectableProperty(name ="requiresFadingEdge", flagMapping ={ @FlagEntry(target = FADING_EDGE_NONE, mask = FADING_EDGE_MASK, name ="none"), @FlagEntry(target = FADING_EDGE_VERTICAL, name ="vertical"), @FlagEntry(target = FADING_EDGE_HORIZONTAL, name ="horizontal") }) publicint getFadingEdge(){ return mViewFlags & FADING_EDGE_MASK; } /** * Get the fading edge length, used for inspection * * @return The fading edge length or 0 * @hide */ @InspectableProperty publicint getFadingEdgeLength(){ if(mScrollCache !=null&&(mViewFlags & FADING_EDGE_MASK)!= FADING_EDGE_NONE){ return mScrollCache.fadingEdgeLength; } return0; } /** * Returns the strength, or intensity, of the top faded edge. The strength is * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation * returns 0.0 or 1.0 but no value in between. * * Subclasses should override this method to provide a smoother fade transition * when scrolling occurs. * * @return the intensity of the top fade as a float between 0.0f and 1.0f */ protectedfloat getTopFadingEdgeStrength(){ return computeVerticalScrollOffset()>0?1.0f:0.0f; } /** * Returns the strength, or intensity, of the bottom faded edge. The strength is * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation * returns 0.0 or 1.0 but no value in between. * * Subclasses should override this method to provide a smoother fade transition * when scrolling occurs. * * @return the intensity of the bottom fade as a float between 0.0f and 1.0f */ protectedfloat getBottomFadingEdgeStrength(){ return computeVerticalScrollOffset()+ computeVerticalScrollExtent()< computeVerticalScrollRange()?1.0f:0.0f; } /** * Returns the strength, or intensity, of the left faded edge. The strength is * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation * returns 0.0 or 1.0 but no value in between. * * Subclasses should override this method to provide a smoother fade transition * when scrolling occurs. * * @return the intensity of the left fade as a float between 0.0f and 1.0f */ protectedfloat getLeftFadingEdgeStrength(){ return computeHorizontalScrollOffset()>0?1.0f:0.0f; } /** * Returns the strength, or intensity, of the right faded edge. The strength is * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation * returns 0.0 or 1.0 but no value in between. * * Subclasses should override this method to provide a smoother fade transition * when scrolling occurs. * * @return the intensity of the right fade as a float between 0.0f and 1.0f */ protectedfloat getRightFadingEdgeStrength(){ return computeHorizontalScrollOffset()+ computeHorizontalScrollExtent()< computeHorizontalScrollRange()?1.0f:0.0f; } /** * <p>Indicate whether the horizontal scrollbar should be drawn or not. The * scrollbar is not drawn by default.</p> * * @return true if the horizontal scrollbar should be painted, false * otherwise * * @see #setHorizontalScrollBarEnabled(boolean) */ publicboolean isHorizontalScrollBarEnabled(){ return(mViewFlags & SCROLLBARS_HORIZONTAL)== SCROLLBARS_HORIZONTAL; } /** * <p>Define whether the horizontal scrollbar should be drawn or not. The * scrollbar is not drawn by default.</p> * * @param horizontalScrollBarEnabled true if the horizontal scrollbar should * be painted * * @see #isHorizontalScrollBarEnabled() */ publicvoid setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled){ if(isHorizontalScrollBarEnabled()!= horizontalScrollBarEnabled){ mViewFlags ^= SCROLLBARS_HORIZONTAL; computeOpaqueFlags(); resolvePadding(); } } /** * <p>Indicate whether the vertical scrollbar should be drawn or not. The * scrollbar is not drawn by default.</p> * * @return true if the vertical scrollbar should be painted, false * otherwise * * @see #setVerticalScrollBarEnabled(boolean) */ publicboolean isVerticalScrollBarEnabled(){ return(mViewFlags & SCROLLBARS_VERTICAL)== SCROLLBARS_VERTICAL; } /** * <p>Define whether the vertical scrollbar should be drawn or not. The * scrollbar is not drawn by default.</p> * * @param verticalScrollBarEnabled true if the vertical scrollbar should * be painted * * @see #isVerticalScrollBarEnabled() */ publicvoid setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled){ if(isVerticalScrollBarEnabled()!= verticalScrollBarEnabled){ mViewFlags ^= SCROLLBARS_VERTICAL; computeOpaqueFlags(); resolvePadding(); } } /** * @hide */ @UnsupportedAppUsage protectedvoid recomputePadding(){ internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom); } /** * Define whether scrollbars will fade when the view is not scrolling. * * @param fadeScrollbars whether to enable fading * * @attr ref android.R.styleable#View_fadeScrollbars */ publicvoid setScrollbarFadingEnabled(boolean fadeScrollbars){ initScrollCache(); finalScrollabilityCache scrollabilityCache = mScrollCache; scrollabilityCache.fadeScrollBars = fadeScrollbars; if(fadeScrollbars){ scrollabilityCache.state =ScrollabilityCache.OFF; }else{ scrollabilityCache.state =ScrollabilityCache.ON; } } /** * * Returns true if scrollbars will fade when this view is not scrolling * * @return true if scrollbar fading is enabled * * @attr ref android.R.styleable#View_fadeScrollbars */ publicboolean isScrollbarFadingEnabled(){ return mScrollCache !=null&& mScrollCache.fadeScrollBars; } /** * * Returns the delay before scrollbars fade. * * @return the delay before scrollbars fade * * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade */ @InspectableProperty(name ="scrollbarDefaultDelayBeforeFade") publicint getScrollBarDefaultDelayBeforeFade(){ return mScrollCache ==null?ViewConfiguration.getScrollDefaultDelay(): mScrollCache.scrollBarDefaultDelayBeforeFade; } /** * Define the delay before scrollbars fade. * * @param scrollBarDefaultDelayBeforeFade - the delay before scrollbars fade * * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade */ publicvoid setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade){ getScrollCache().scrollBarDefaultDelayBeforeFade = scrollBarDefaultDelayBeforeFade; } /** * * Returns the scrollbar fade duration. * * @return the scrollbar fade duration, in milliseconds * * @attr ref android.R.styleable#View_scrollbarFadeDuration */ @InspectableProperty(name ="scrollbarFadeDuration") publicint getScrollBarFadeDuration(){ return mScrollCache ==null?ViewConfiguration.getScrollBarFadeDuration(): mScrollCache.scrollBarFadeDuration; } /** * Define the scrollbar fade duration. * * @param scrollBarFadeDuration - the scrollbar fade duration, in milliseconds * * @attr ref android.R.styleable#View_scrollbarFadeDuration */ publicvoid setScrollBarFadeDuration(int scrollBarFadeDuration){ getScrollCache().scrollBarFadeDuration = scrollBarFadeDuration; } /** * * Returns the scrollbar size. * * @return the scrollbar size * * @attr ref android.R.styleable#View_scrollbarSize */ @InspectableProperty(name ="scrollbarSize") publicint getScrollBarSize(){ return mScrollCache ==null?ViewConfiguration.get(mContext).getScaledScrollBarSize(): mScrollCache.scrollBarSize; } /** * Define the scrollbar size. * * @param scrollBarSize - the scrollbar size * * @attr ref android.R.styleable#View_scrollbarSize */ publicvoid setScrollBarSize(int scrollBarSize){ getScrollCache().scrollBarSize = scrollBarSize; } /** * <p>Specify the style of the scrollbars. The scrollbars can be overlaid or * inset. When inset, they add to the padding of the view. And the scrollbars * can be drawn inside the padding area or on the edge of the view. For example, * if a view has a background drawable and you want to draw the scrollbars * inside the padding specified by the drawable, you can use * SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to * appear at the edge of the view, ignoring the padding, then you can use * SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.</p> * @param style the style of the scrollbars. Should be one of * SCROLLBARS_INSIDE_OVERLAY, SCROLLBARS_INSIDE_INSET, * SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET. * @see #SCROLLBARS_INSIDE_OVERLAY * @see #SCROLLBARS_INSIDE_INSET * @see #SCROLLBARS_OUTSIDE_OVERLAY * @see #SCROLLBARS_OUTSIDE_INSET * * @attr ref android.R.styleable#View_scrollbarStyle */ publicvoid setScrollBarStyle(@ScrollBarStyleint style){ if(style !=(mViewFlags & SCROLLBARS_STYLE_MASK)){ mViewFlags =(mViewFlags &~SCROLLBARS_STYLE_MASK)|(style & SCROLLBARS_STYLE_MASK); computeOpaqueFlags(); resolvePadding(); } } /** * <p>Returns the current scrollbar style.</p> * @return the current scrollbar style * @see #SCROLLBARS_INSIDE_OVERLAY * @see #SCROLLBARS_INSIDE_INSET * @see #SCROLLBARS_OUTSIDE_OVERLAY * @see #SCROLLBARS_OUTSIDE_INSET * * @attr ref android.R.styleable#View_scrollbarStyle */ @ViewDebug.ExportedProperty(mapping ={ @ViewDebug.IntToString(from = SCROLLBARS_INSIDE_OVERLAY, to ="INSIDE_OVERLAY"), @ViewDebug.IntToString(from = SCROLLBARS_INSIDE_INSET, to ="INSIDE_INSET"), @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_OVERLAY, to ="OUTSIDE_OVERLAY"), @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_INSET, to ="OUTSIDE_INSET") }) @InspectableProperty(name ="scrollbarStyle", enumMapping ={ @EnumEntry(value = SCROLLBARS_INSIDE_OVERLAY, name ="insideOverlay"), @EnumEntry(value = SCROLLBARS_INSIDE_INSET, name ="insideInset"), @EnumEntry(value = SCROLLBARS_OUTSIDE_OVERLAY, name ="outsideOverlay"), @EnumEntry(value = SCROLLBARS_OUTSIDE_INSET, name ="outsideInset") }) @ScrollBarStyle publicint getScrollBarStyle(){ return mViewFlags & SCROLLBARS_STYLE_MASK; } /** * <p>Compute the horizontal range that the horizontal scrollbar * represents.</p> * * <p>The range is expressed in arbitrary units that must be the same as the * units used by {@link #computeHorizontalScrollExtent()} and * {@link #computeHorizontalScrollOffset()}.</p> * * <p>The default range is the drawing width of this view.</p> * * @return the total horizontal range represented by the horizontal * scrollbar * * @see #computeHorizontalScrollExtent() * @see #computeHorizontalScrollOffset() */ protectedint computeHorizontalScrollRange(){ return getWidth(); } /** * <p>Compute the horizontal offset of the horizontal scrollbar's thumb * within the horizontal range. This value is used to compute the position * of the thumb within the scrollbar's track.</p> * * <p>The range is expressed in arbitrary units that must be the same as the * units used by {@link #computeHorizontalScrollRange()} and * {@link #computeHorizontalScrollExtent()}.</p> * * <p>The default offset is the scroll offset of this view.</p> * * @return the horizontal offset of the scrollbar's thumb * * @see #computeHorizontalScrollRange() * @see #computeHorizontalScrollExtent() */ protectedint computeHorizontalScrollOffset(){ return mScrollX; } /** * <p>Compute the horizontal extent of the horizontal scrollbar's thumb * within the horizontal range. This value is used to compute the length * of the thumb within the scrollbar's track.</p> * * <p>The range is expressed in arbitrary units that must be the same as the * units used by {@link #computeHorizontalScrollRange()} and * {@link #computeHorizontalScrollOffset()}.</p> * * <p>The default extent is the drawing width of this view.</p> * * @return the horizontal extent of the scrollbar's thumb * * @see #computeHorizontalScrollRange() * @see #computeHorizontalScrollOffset() */ protectedint computeHorizontalScrollExtent(){ return getWidth(); } /** * <p>Compute the vertical range that the vertical scrollbar represents.</p> * * <p>The range is expressed in arbitrary units that must be the same as the * units used by {@link #computeVerticalScrollExtent()} and * {@link #computeVerticalScrollOffset()}.</p> * * @return the total vertical range represented by the vertical scrollbar * * <p>The default range is the drawing height of this view.</p> * * @see #computeVerticalScrollExtent() * @see #computeVerticalScrollOffset() */ protectedint computeVerticalScrollRange(){ return getHeight(); } /** * <p>Compute the vertical offset of the vertical scrollbar's thumb * within the horizontal range. This value is used to compute the position * of the thumb within the scrollbar's track.</p> * * <p>The range is expressed in arbitrary units that must be the same as the * units used by {@link #computeVerticalScrollRange()} and * {@link #computeVerticalScrollExtent()}.</p> * * <p>The default offset is the scroll offset of this view.</p> * * @return the vertical offset of the scrollbar's thumb * * @see #computeVerticalScrollRange() * @see #computeVerticalScrollExtent() */ protectedint computeVerticalScrollOffset(){ return mScrollY; } /** * <p>Compute the vertical extent of the vertical scrollbar's thumb * within the vertical range. This value is used to compute the length * of the thumb within the scrollbar's track.</p> * * <p>The range is expressed in arbitrary units that must be the same as the * units used by {@link #computeVerticalScrollRange()} and * {@link #computeVerticalScrollOffset()}.</p> * * <p>The default extent is the drawing height of this view.</p> * * @return the vertical extent of the scrollbar's thumb * * @see #computeVerticalScrollRange() * @see #computeVerticalScrollOffset() */ protectedint computeVerticalScrollExtent(){ return getHeight(); } /** * Check if this view can be scrolled horizontally in a certain direction. * * <p>This is without regard to whether the view is enabled or not, or if it will scroll * in response to user input or not. * * @param direction Negative to check scrolling left, positive to check scrolling right. * @return true if this view can be scrolled in the specified direction, false otherwise. */ publicboolean canScrollHorizontally(int direction){ finalint offset = computeHorizontalScrollOffset(); finalint range = computeHorizontalScrollRange()- computeHorizontalScrollExtent(); if(range ==0)returnfalse; if(direction <0){ return offset >0; }else{ return offset < range -1; } } /** * Check if this view can be scrolled vertically in a certain direction. * * <p>This is without regard to whether the view is enabled or not, or if it will scroll * in response to user input or not. * * @param direction Negative to check scrolling up, positive to check scrolling down. * @return true if this view can be scrolled in the specified direction, false otherwise. */ publicboolean canScrollVertically(int direction){ finalint offset = computeVerticalScrollOffset(); finalint range = computeVerticalScrollRange()- computeVerticalScrollExtent(); if(range ==0)returnfalse; if(direction <0){ return offset >0; }else{ return offset < range -1; } } void getScrollIndicatorBounds(@NonNullRect out){ out.left = mScrollX; out.right = mScrollX + mRight - mLeft; out.top = mScrollY; out.bottom = mScrollY + mBottom - mTop; } privatevoid onDrawScrollIndicators(Canvas c){ if((mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK)==0){ // No scroll indicators enabled. return; } finalDrawable dr = mScrollIndicatorDrawable; if(dr ==null){ // Scroll indicators aren't supported here. return; } if(mAttachInfo ==null){ // View is not attached. return; } finalint h = dr.getIntrinsicHeight(); finalint w = dr.getIntrinsicWidth(); finalRect rect = mAttachInfo.mTmpInvalRect; getScrollIndicatorBounds(rect); if((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_TOP)!=0){ finalboolean canScrollUp = canScrollVertically(-1); if(canScrollUp){ dr.setBounds(rect.left, rect.top, rect.right, rect.top + h); dr.draw(c); } } if((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_BOTTOM)!=0){ finalboolean canScrollDown = canScrollVertically(1); if(canScrollDown){ dr.setBounds(rect.left, rect.bottom - h, rect.right, rect.bottom); dr.draw(c); } } finalint leftRtl; finalint rightRtl; if(getLayoutDirection()== LAYOUT_DIRECTION_RTL){ leftRtl = PFLAG3_SCROLL_INDICATOR_END; rightRtl = PFLAG3_SCROLL_INDICATOR_START; }else{ leftRtl = PFLAG3_SCROLL_INDICATOR_START; rightRtl = PFLAG3_SCROLL_INDICATOR_END; } finalint leftMask = PFLAG3_SCROLL_INDICATOR_LEFT | leftRtl; if((mPrivateFlags3 & leftMask)!=0){ finalboolean canScrollLeft = canScrollHorizontally(-1); if(canScrollLeft){ dr.setBounds(rect.left, rect.top, rect.left + w, rect.bottom); dr.draw(c); } } finalint rightMask = PFLAG3_SCROLL_INDICATOR_RIGHT | rightRtl; if((mPrivateFlags3 & rightMask)!=0){ finalboolean canScrollRight = canScrollHorizontally(1); if(canScrollRight){ dr.setBounds(rect.right - w, rect.top, rect.right, rect.bottom); dr.draw(c); } } } privatevoid getHorizontalScrollBarBounds(@NullableRect drawBounds, @NullableRect touchBounds){ finalRect bounds = drawBounds !=null? drawBounds : touchBounds; if(bounds ==null){ return; } finalint inside =(mViewFlags & SCROLLBARS_OUTSIDE_MASK)==0?~0:0; finalboolean drawVerticalScrollBar = isVerticalScrollBarEnabled() &&!isVerticalScrollBarHidden(); finalint size = getHorizontalScrollbarHeight(); finalint verticalScrollBarGap = drawVerticalScrollBar ? getVerticalScrollbarWidth():0; finalint width = mRight - mLeft; finalint height = mBottom - mTop; bounds.top = mScrollY + height - size -(mUserPaddingBottom & inside); bounds.left = mScrollX +(mPaddingLeft & inside); bounds.right = mScrollX + width -(mUserPaddingRight & inside)- verticalScrollBarGap; bounds.bottom = bounds.top + size; if(touchBounds ==null){ return; } if(touchBounds != bounds){ touchBounds.set(bounds); } finalint minTouchTarget = mScrollCache.scrollBarMinTouchTarget; if(touchBounds.height()< minTouchTarget){ finalint adjust =(minTouchTarget - touchBounds.height())/2; touchBounds.bottom =Math.min(touchBounds.bottom + adjust, mScrollY + height); touchBounds.top = touchBounds.bottom - minTouchTarget; } if(touchBounds.width()< minTouchTarget){ finalint adjust =(minTouchTarget - touchBounds.width())/2; touchBounds.left -= adjust; touchBounds.right = touchBounds.left + minTouchTarget; } } privatevoid getVerticalScrollBarBounds(@NullableRect bounds,@NullableRect touchBounds){ if(mRoundScrollbarRenderer ==null){ getStraightVerticalScrollBarBounds(bounds, touchBounds); }else{ getRoundVerticalScrollBarBounds(bounds !=null? bounds : touchBounds); } } privatevoid getRoundVerticalScrollBarBounds(Rect bounds){ finalint width = mRight - mLeft; finalint height = mBottom - mTop; // Do not take padding into account as we always want the scrollbars // to hug the screen for round wearable devices. bounds.left = mScrollX; bounds.top = mScrollY; bounds.right = bounds.left + width; bounds.bottom = mScrollY + height; } privatevoid getStraightVerticalScrollBarBounds(@NullableRect drawBounds, @NullableRect touchBounds){ finalRect bounds = drawBounds !=null? drawBounds : touchBounds; if(bounds ==null){ return; } finalint inside =(mViewFlags & SCROLLBARS_OUTSIDE_MASK)==0?~0:0; finalint size = getVerticalScrollbarWidth(); int verticalScrollbarPosition = mVerticalScrollbarPosition; if(verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT){ verticalScrollbarPosition = isLayoutRtl()? SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT; } finalint width = mRight - mLeft; finalint height = mBottom - mTop; switch(verticalScrollbarPosition){ default: case SCROLLBAR_POSITION_RIGHT: bounds.left = mScrollX + width - size -(mUserPaddingRight & inside); break; case SCROLLBAR_POSITION_LEFT: bounds.left = mScrollX +(mUserPaddingLeft & inside); break; } bounds.top = mScrollY +(mPaddingTop & inside); bounds.right = bounds.left + size; bounds.bottom = mScrollY + height -(mUserPaddingBottom & inside); if(touchBounds ==null){ return; } if(touchBounds != bounds){ touchBounds.set(bounds); } finalint minTouchTarget = mScrollCache.scrollBarMinTouchTarget; if(touchBounds.width()< minTouchTarget){ finalint adjust =(minTouchTarget - touchBounds.width())/2; if(verticalScrollbarPosition == SCROLLBAR_POSITION_RIGHT){ touchBounds.right =Math.min(touchBounds.right + adjust, mScrollX + width); touchBounds.left = touchBounds.right - minTouchTarget; }else{ touchBounds.left =Math.max(touchBounds.left + adjust, mScrollX); touchBounds.right = touchBounds.left + minTouchTarget; } } if(touchBounds.height()< minTouchTarget){ finalint adjust =(minTouchTarget - touchBounds.height())/2; touchBounds.top -= adjust; touchBounds.bottom = touchBounds.top + minTouchTarget; } } /** * <p>Request the drawing of the horizontal and the vertical scrollbar. The * scrollbars are painted only if they have been awakened first.</p> * * @param canvas the canvas on which to draw the scrollbars * * @see #awakenScrollBars(int) */ protectedfinalvoid onDrawScrollBars(Canvas canvas){ // scrollbars are drawn only when the animation is running finalScrollabilityCache cache = mScrollCache; if(cache !=null){ int state = cache.state; if(state ==ScrollabilityCache.OFF){ return; } boolean invalidate =false; if(state ==ScrollabilityCache.FADING){ // We're fading -- get our fade interpolation if(cache.interpolatorValues ==null){ cache.interpolatorValues =newfloat[1]; } float[] values = cache.interpolatorValues; // Stops the animation if we're done if(cache.scrollBarInterpolator.timeToValues(values)== Interpolator.Result.FREEZE_END){ cache.state =ScrollabilityCache.OFF; }else{ cache.scrollBar.mutate().setAlpha(Math.round(values[0])); } // This will make the scroll bars inval themselves after // drawing. We only want this when we're fading so that // we prevent excessive redraws invalidate =true; }else{ // We're just on -- but we may have been fading before so // reset alpha cache.scrollBar.mutate().setAlpha(255); } finalboolean drawHorizontalScrollBar = isHorizontalScrollBarEnabled(); finalboolean drawVerticalScrollBar = isVerticalScrollBarEnabled() &&!isVerticalScrollBarHidden(); // Fork out the scroll bar drawing for round wearable devices. if(mRoundScrollbarRenderer !=null){ if(drawVerticalScrollBar){ finalRect bounds = cache.mScrollBarBounds; getVerticalScrollBarBounds(bounds,null); mRoundScrollbarRenderer.drawRoundScrollbars( canvas,(float) cache.scrollBar.getAlpha()/255f, bounds); if(invalidate){ invalidate(); } } // Do not draw horizontal scroll bars for round wearable devices. }elseif(drawVerticalScrollBar || drawHorizontalScrollBar){ finalScrollBarDrawable scrollBar = cache.scrollBar; if(drawHorizontalScrollBar){ scrollBar.setParameters(computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(),false); finalRect bounds = cache.mScrollBarBounds; getHorizontalScrollBarBounds(bounds,null); onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top, bounds.right, bounds.bottom); if(invalidate){ invalidate(bounds); } } if(drawVerticalScrollBar){ scrollBar.setParameters(computeVerticalScrollRange(), computeVerticalScrollOffset(), computeVerticalScrollExtent(),true); finalRect bounds = cache.mScrollBarBounds; getVerticalScrollBarBounds(bounds,null); onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top, bounds.right, bounds.bottom); if(invalidate){ invalidate(bounds); } } } } } /** * Override this if the vertical scrollbar needs to be hidden in a subclass, like when * FastScroller is visible. * @return whether to temporarily hide the vertical scrollbar * @hide */ protectedboolean isVerticalScrollBarHidden(){ returnfalse; } /** * <p>Draw the horizontal scrollbar if * {@link #isHorizontalScrollBarEnabled()} returns true.</p> * * @param canvas the canvas on which to draw the scrollbar * @param scrollBar the scrollbar's drawable * * @see #isHorizontalScrollBarEnabled() * @see #computeHorizontalScrollRange() * @see #computeHorizontalScrollExtent() * @see #computeHorizontalScrollOffset() * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedvoid onDrawHorizontalScrollBar(Canvas canvas,Drawable scrollBar, int l,int t,int r,int b){ scrollBar.setBounds(l, t, r, b); scrollBar.draw(canvas); } /** * <p>Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()} * returns true.</p> * * @param canvas the canvas on which to draw the scrollbar * @param scrollBar the scrollbar's drawable * * @see #isVerticalScrollBarEnabled() * @see #computeVerticalScrollRange() * @see #computeVerticalScrollExtent() * @see #computeVerticalScrollOffset() * @hide */ @UnsupportedAppUsage protectedvoid onDrawVerticalScrollBar(Canvas canvas,Drawable scrollBar, int l,int t,int r,int b){ scrollBar.setBounds(l, t, r, b); scrollBar.draw(canvas); } /** * Implement this to do your drawing. * * @param canvas the canvas on which the background will be drawn */ protectedvoid onDraw(Canvas canvas){ } /* * Caller is responsible for calling requestLayout if necessary. * (This allows addViewInLayout to not request a new layout.) */ @UnsupportedAppUsage void assignParent(ViewParent parent){ if(mParent ==null){ mParent = parent; }elseif(parent ==null){ mParent =null; }else{ thrownewRuntimeException("view "+this+" being added, but" +" it already has a parent"); } } /** * This is called when the view is attached to a window. At this point it * has a Surface and will start drawing. Note that this function is * guaranteed to be called before {@link #onDraw(android.graphics.Canvas)}, * however it may be called any time before the first onDraw -- including * before or after {@link #onMeasure(int, int)}. * * @see #onDetachedFromWindow() */ @CallSuper protectedvoid onAttachedToWindow(){ if(mParent !=null&&(mPrivateFlags & PFLAG_REQUEST_TRANSPARENT_REGIONS)!=0){ mParent.requestTransparentRegion(this); } mPrivateFlags3 &=~PFLAG3_IS_LAID_OUT; jumpDrawablesToCurrentState(); AccessibilityNodeIdManager.getInstance().registerViewWithId(this, getAccessibilityViewId()); resetSubtreeAccessibilityStateChanged(); // rebuild, since Outline not maintained while View is detached rebuildOutline(); if(isFocused()){ notifyFocusChangeToImeFocusController(true/* hasFocus */); } } /** * Resolve all RTL related properties. * * @return true if resolution of RTL properties has been done * * @hide */ publicboolean resolveRtlPropertiesIfNeeded(){ if(!needRtlPropertiesResolution())returnfalse; // Order is important here: LayoutDirection MUST be resolved first if(!isLayoutDirectionResolved()){ resolveLayoutDirection(); resolveLayoutParams(); } // ... then we can resolve the others properties depending on the resolved LayoutDirection. if(!isTextDirectionResolved()){ resolveTextDirection(); } if(!isTextAlignmentResolved()){ resolveTextAlignment(); } // Should resolve Drawables before Padding because we need the layout direction of the // Drawable to correctly resolve Padding. if(!areDrawablesResolved()){ resolveDrawables(); } if(!isPaddingResolved()){ resolvePadding(); } onRtlPropertiesChanged(getLayoutDirection()); returntrue; } /** * Reset resolution of all RTL related properties. * * @hide */ @TestApi publicvoid resetRtlProperties(){ resetResolvedLayoutDirection(); resetResolvedTextDirection(); resetResolvedTextAlignment(); resetResolvedPadding(); resetResolvedDrawables(); } /** * @see #onScreenStateChanged(int) */ void dispatchScreenStateChanged(int screenState){ onScreenStateChanged(screenState); } /** * This method is called whenever the state of the screen this view is * attached to changes. A state change will usually occurs when the screen * turns on or off (whether it happens automatically or the user does it * manually.) * * @param screenState The new state of the screen. Can be either * {@link #SCREEN_STATE_ON} or {@link #SCREEN_STATE_OFF} */ publicvoid onScreenStateChanged(int screenState){ } /** * @see #onMovedToDisplay(int, Configuration) */ void dispatchMovedToDisplay(Display display,Configuration config){ mAttachInfo.mDisplay = display; mAttachInfo.mDisplayState = display.getState(); onMovedToDisplay(display.getDisplayId(), config); } /** * Called by the system when the hosting activity is moved from one display to another without * recreation. This means that the activity is declared to handle all changes to configuration * that happened when it was switched to another display, so it wasn't destroyed and created * again. * * <p>This call will be followed by {@link #onConfigurationChanged(Configuration)} if the * applied configuration actually changed. It is up to app developer to choose whether to handle * the change in this method or in the following {@link #onConfigurationChanged(Configuration)} * call. * * <p>Use this callback to track changes to the displays if some functionality relies on an * association with some display properties. * * @param displayId The id of the display to which the view was moved. * @param config Configuration of the resources on new display after move. * * @see #onConfigurationChanged(Configuration) * @hide */ publicvoid onMovedToDisplay(int displayId,Configuration config){ } /** * Return true if the application tag in the AndroidManifest has set "supportRtl" to true */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privateboolean hasRtlSupport(){ return mContext.getApplicationInfo().hasRtlSupport(); } /** * Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or * RTL not supported) */ privateboolean isRtlCompatibilityMode(){ finalint targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; return targetSdkVersion <Build.VERSION_CODES.JELLY_BEAN_MR1 ||!hasRtlSupport(); } /** * @return true if RTL properties need resolution. * */ privateboolean needRtlPropertiesResolution(){ return(mPrivateFlags2 & ALL_RTL_PROPERTIES_RESOLVED)!= ALL_RTL_PROPERTIES_RESOLVED; } /** * Called when any RTL property (layout direction or text direction or text alignment) has * been changed. * * Subclasses need to override this method to take care of cached information that depends on the * resolved layout direction, or to inform child views that inherit their layout direction. * * The default implementation does nothing. * * @param layoutDirection the direction of the layout * * @see #LAYOUT_DIRECTION_LTR * @see #LAYOUT_DIRECTION_RTL */ publicvoid onRtlPropertiesChanged(@ResolvedLayoutDirint layoutDirection){ } /** * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing * that the parent directionality can and will be resolved before its children. * * @return true if resolution has been done, false otherwise. * * @hide */ publicboolean resolveLayoutDirection(){ // Clear any previous layout direction resolution mPrivateFlags2 &=~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK; if(hasRtlSupport()){ // Set resolved depending on layout direction switch((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK)>> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT){ case LAYOUT_DIRECTION_INHERIT: // We cannot resolve yet. LTR is by default and let the resolution happen again // later to get the correct resolved value if(!canResolveLayoutDirection())returnfalse; // Parent has not yet resolved, LTR is still the default try{ if(!mParent.isLayoutDirectionResolved())returnfalse; if(mParent.getLayoutDirection()== LAYOUT_DIRECTION_RTL){ mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; } }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } break; case LAYOUT_DIRECTION_RTL: mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; break; case LAYOUT_DIRECTION_LOCALE: if((LAYOUT_DIRECTION_RTL == TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()))){ mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; } break; default: // Nothing to do, LTR by default } } // Set to resolved mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; returntrue; } /** * Check if layout direction resolution can be done. * * @return true if layout direction resolution can be done otherwise return false. */ publicboolean canResolveLayoutDirection(){ switch(getRawLayoutDirection()){ case LAYOUT_DIRECTION_INHERIT: if(mParent !=null){ try{ return mParent.canResolveLayoutDirection(); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } } returnfalse; default: returntrue; } } /** * Reset the resolved layout direction. Layout direction will be resolved during a call to * {@link #onMeasure(int, int)}. * * @hide */ @TestApi publicvoid resetResolvedLayoutDirection(){ // Reset the current resolved bits mPrivateFlags2 &=~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK; } /** * @return true if the layout direction is inherited. * * @hide */ publicboolean isLayoutDirectionInherited(){ return(getRawLayoutDirection()== LAYOUT_DIRECTION_INHERIT); } /** * @return true if layout direction has been resolved. */ publicboolean isLayoutDirectionResolved(){ return(mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED)== PFLAG2_LAYOUT_DIRECTION_RESOLVED; } /** * Return if padding has been resolved * * @hide */ @UnsupportedAppUsage boolean isPaddingResolved(){ return(mPrivateFlags2 & PFLAG2_PADDING_RESOLVED)== PFLAG2_PADDING_RESOLVED; } /** * Resolves padding depending on layout direction, if applicable, and * recomputes internal padding values to adjust for scroll bars. * * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicvoid resolvePadding(){ finalint resolvedLayoutDirection = getLayoutDirection(); if(!isRtlCompatibilityMode()){ // Post Jelly Bean MR1 case: we need to take the resolved layout direction into account. // If start / end padding are defined, they will be resolved (hence overriding) to // left / right or right / left depending on the resolved layout direction. // If start / end padding are not defined, use the left / right ones. if(mBackground !=null&&(!mLeftPaddingDefined ||!mRightPaddingDefined)){ Rect padding = sThreadLocal.get(); if(padding ==null){ padding =newRect(); sThreadLocal.set(padding); } mBackground.getPadding(padding); if(!mLeftPaddingDefined){ mUserPaddingLeftInitial = padding.left; } if(!mRightPaddingDefined){ mUserPaddingRightInitial = padding.right; } } switch(resolvedLayoutDirection){ case LAYOUT_DIRECTION_RTL: if(mUserPaddingStart != UNDEFINED_PADDING){ mUserPaddingRight = mUserPaddingStart; }else{ mUserPaddingRight = mUserPaddingRightInitial; } if(mUserPaddingEnd != UNDEFINED_PADDING){ mUserPaddingLeft = mUserPaddingEnd; }else{ mUserPaddingLeft = mUserPaddingLeftInitial; } break; case LAYOUT_DIRECTION_LTR: default: if(mUserPaddingStart != UNDEFINED_PADDING){ mUserPaddingLeft = mUserPaddingStart; }else{ mUserPaddingLeft = mUserPaddingLeftInitial; } if(mUserPaddingEnd != UNDEFINED_PADDING){ mUserPaddingRight = mUserPaddingEnd; }else{ mUserPaddingRight = mUserPaddingRightInitial; } } mUserPaddingBottom =(mUserPaddingBottom >=0)? mUserPaddingBottom : mPaddingBottom; } internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom); onRtlPropertiesChanged(resolvedLayoutDirection); mPrivateFlags2 |= PFLAG2_PADDING_RESOLVED; } /** * Reset the resolved layout direction. * * @hide */ @TestApi publicvoid resetResolvedPadding(){ resetResolvedPaddingInternal(); } /** * Used when we only want to reset *this* view's padding and not trigger overrides * in ViewGroup that reset children too. */ void resetResolvedPaddingInternal(){ mPrivateFlags2 &=~PFLAG2_PADDING_RESOLVED; } /** * This is called when the view is detached from a window. At this point it * no longer has a surface for drawing. * * @see #onAttachedToWindow() */ @CallSuper protectedvoid onDetachedFromWindow(){ } /** * This is a framework-internal mirror of onDetachedFromWindow() that's called * after onDetachedFromWindow(). * * If you override this you *MUST* call super.onDetachedFromWindowInternal()! * The super method should be called at the end of the overridden method to ensure * subclasses are destroyed first * * @hide */ @CallSuper @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedvoid onDetachedFromWindowInternal(){ mPrivateFlags &=~PFLAG_CANCEL_NEXT_UP_EVENT; mPrivateFlags3 &=~PFLAG3_IS_LAID_OUT; mPrivateFlags3 &=~PFLAG3_TEMPORARY_DETACH; removeUnsetPressCallback(); removeLongPressCallback(); removePerformClickCallback(); clearAccessibilityThrottles(); stopNestedScroll(); // Anything that started animating right before detach should already // be in its final state when re-attached. jumpDrawablesToCurrentState(); destroyDrawingCache(); cleanupDraw(); mCurrentAnimation =null; if((mViewFlags & TOOLTIP)== TOOLTIP){ hideTooltip(); } AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId()); if(mBackgroundRenderNode !=null){ mBackgroundRenderNode.forceEndAnimators(); } mRenderNode.forceEndAnimators(); } privatevoid cleanupDraw(){ resetDisplayList(); if(mAttachInfo !=null){ mAttachInfo.mViewRootImpl.cancelInvalidate(this); } } void invalidateInheritedLayoutMode(int layoutModeOfRoot){ } /** * @return The number of times this view has been attached to a window */ protectedint getWindowAttachCount(){ return mWindowAttachCount; } /** * Retrieve a unique token identifying the window this view is attached to. * @return Return the window's token for use in * {@link WindowManager.LayoutParams#token WindowManager.LayoutParams.token}. */ publicIBinder getWindowToken(){ return mAttachInfo !=null? mAttachInfo.mWindowToken :null; } /** * Retrieve the {@link WindowId} for the window this view is * currently attached to. */ publicWindowId getWindowId(){ AttachInfo ai = mAttachInfo; if(ai ==null){ returnnull; } if(ai.mWindowId ==null){ try{ ai.mIWindowId = ai.mSession.getWindowId(ai.mWindowToken); if(ai.mIWindowId !=null){ ai.mWindowId =newWindowId(ai.mIWindowId); } }catch(RemoteException e){ } } return ai.mWindowId; } /** * Retrieve a unique token identifying the top-level "real" window of * the window that this view is attached to. That is, this is like * {@link #getWindowToken}, except if the window this view in is a panel * window (attached to another containing window), then the token of * the containing window is returned instead. * * @return Returns the associated window token, either * {@link #getWindowToken()} or the containing window's token. */ publicIBinder getApplicationWindowToken(){ AttachInfo ai = mAttachInfo; if(ai !=null){ IBinder appWindowToken = ai.mPanelParentWindowToken; if(appWindowToken ==null){ appWindowToken = ai.mWindowToken; } return appWindowToken; } returnnull; } /** * Gets the logical display to which the view's window has been attached. * * @return The logical display, or null if the view is not currently attached to a window. */ publicDisplay getDisplay(){ return mAttachInfo !=null? mAttachInfo.mDisplay :null; } /** * Retrieve private session object this view hierarchy is using to * communicate with the window manager. * @return the session object to communicate with the window manager */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =115609023) /*package*/IWindowSession getWindowSession(){ return mAttachInfo !=null? mAttachInfo.mSession :null; } /** * Return the window this view is currently attached to. * @hide */ protectedIWindow getWindow(){ return mAttachInfo !=null? mAttachInfo.mWindow :null; } /** * Return the visibility value of the least visible component passed. */ int combineVisibility(int vis1,int vis2){ // This works because VISIBLE < INVISIBLE < GONE. returnMath.max(vis1, vis2); } /** * @param info the {@link android.view.View.AttachInfo} to associated with * this view */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) void dispatchAttachedToWindow(AttachInfo info,int visibility){ mAttachInfo = info; if(mOverlay !=null){ mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); } mWindowAttachCount++; // We will need to evaluate the drawable state at least once. mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY; if(mFloatingTreeObserver !=null){ info.mTreeObserver.merge(mFloatingTreeObserver); mFloatingTreeObserver =null; } registerPendingFrameMetricsObservers(); if((mPrivateFlags&PFLAG_SCROLL_CONTAINER)!=0){ mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED; } // Transfer all pending runnables. if(mRunQueue !=null){ mRunQueue.executeActions(info.mHandler); mRunQueue =null; } performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); ListenerInfo li = mListenerInfo; finalCopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li !=null? li.mOnAttachStateChangeListeners :null; if(listeners !=null&& listeners.size()>0){ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. for(OnAttachStateChangeListener listener : listeners){ listener.onViewAttachedToWindow(this); } } int vis = info.mWindowVisibility; if(vis != GONE){ onWindowVisibilityChanged(vis); if(isShown()){ // Calling onVisibilityAggregated directly here since the subtree will also // receive dispatchAttachedToWindow and this same call onVisibilityAggregated(vis == VISIBLE); } } // Send onVisibilityChanged directly instead of dispatchVisibilityChanged. // As all views in the subtree will already receive dispatchAttachedToWindow // traversing the subtree again here is not desired. onVisibilityChanged(this, visibility); if((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY)!=0){ // If nobody has evaluated the drawable state yet, then do it now. refreshDrawableState(); } needGlobalAttributesUpdate(false); notifyEnterOrExitForAutoFillIfNeeded(true); notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) void dispatchDetachedFromWindow(){ AttachInfo info = mAttachInfo; if(info !=null){ int vis = info.mWindowVisibility; if(vis != GONE){ onWindowVisibilityChanged(GONE); if(isShown()){ // Invoking onVisibilityAggregated directly here since the subtree // will also receive detached from window onVisibilityAggregated(false); } } } onDetachedFromWindow(); onDetachedFromWindowInternal(); if(info !=null){ info.mViewRootImpl.getImeFocusController().onViewDetachedFromWindow(this); } ListenerInfo li = mListenerInfo; finalCopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li !=null? li.mOnAttachStateChangeListeners :null; if(listeners !=null&& listeners.size()>0){ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. for(OnAttachStateChangeListener listener : listeners){ listener.onViewDetachedFromWindow(this); } } if((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED)!=0){ mAttachInfo.mScrollContainers.remove(this); mPrivateFlags &=~PFLAG_SCROLL_CONTAINER_ADDED; } notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); mAttachInfo =null; if(mOverlay !=null){ mOverlay.getOverlayView().dispatchDetachedFromWindow(); } notifyEnterOrExitForAutoFillIfNeeded(false); if(info !=null&&!collectPreferKeepClearRects().isEmpty()){ info.mViewRootImpl.updateKeepClearRectsForView(this); } } /** * Cancel any deferred high-level input events that were previously posted to the event queue. * * <p>Many views post high-level events such as click handlers to the event queue * to run deferred in order to preserve a desired user experience - clearing visible * pressed states before executing, etc. This method will abort any events of this nature * that are currently in flight.</p> * * <p>Custom views that generate their own high-level deferred input events should override * {@link #onCancelPendingInputEvents()} and remove those pending events from the queue.</p> * * <p>This will also cancel pending input events for any child views.</p> * * <p>Note that this may not be sufficient as a debouncing strategy for clicks in all cases. * This will not impact newer events posted after this call that may occur as a result of * lower-level input events still waiting in the queue. If you are trying to prevent * double-submitted events for the duration of some sort of asynchronous transaction * you should also take other steps to protect against unexpected double inputs e.g. calling * {@link #setEnabled(boolean) setEnabled(false)} and re-enabling the view when * the transaction completes, tracking already submitted transaction IDs, etc.</p> */ publicfinalvoid cancelPendingInputEvents(){ dispatchCancelPendingInputEvents(); } /** * Called by {@link #cancelPendingInputEvents()} to cancel input events in flight. * Overridden by ViewGroup to dispatch. Package scoped to prevent app-side meddling. */ void dispatchCancelPendingInputEvents(){ mPrivateFlags3 &=~PFLAG3_CALLED_SUPER; onCancelPendingInputEvents(); if((mPrivateFlags3 & PFLAG3_CALLED_SUPER)!= PFLAG3_CALLED_SUPER){ thrownewSuperNotCalledException("View "+ getClass().getSimpleName()+ " did not call through to super.onCancelPendingInputEvents()"); } } /** * Called as the result of a call to {@link #cancelPendingInputEvents()} on this view or * a parent view. * * <p>This method is responsible for removing any pending high-level input events that were * posted to the event queue to run later. Custom view classes that post their own deferred * high-level events via {@link #post(Runnable)}, {@link #postDelayed(Runnable, long)} or * {@link android.os.Handler} should override this method, call * <code>super.onCancelPendingInputEvents()</code> and remove those callbacks as appropriate. * </p> */ publicvoid onCancelPendingInputEvents(){ removePerformClickCallback(); cancelLongPress(); mPrivateFlags3 |= PFLAG3_CALLED_SUPER; } /** * Store this view hierarchy's frozen state into the given container. * * @param container The SparseArray in which to save the view's state. * * @see #restoreHierarchyState(android.util.SparseArray) * @see #dispatchSaveInstanceState(android.util.SparseArray) * @see #onSaveInstanceState() */ publicvoid saveHierarchyState(SparseArray<Parcelable> container){ dispatchSaveInstanceState(container); } /** * Called by {@link #saveHierarchyState(android.util.SparseArray)} to store the state for * this view and its children. May be overridden to modify how freezing happens to a * view's children; for example, some views may want to not store state for their children. * * @param container The SparseArray in which to save the view's state. * * @see #dispatchRestoreInstanceState(android.util.SparseArray) * @see #saveHierarchyState(android.util.SparseArray) * @see #onSaveInstanceState() */ protectedvoid dispatchSaveInstanceState(SparseArray<Parcelable> container){ if(mID != NO_ID &&(mViewFlags & SAVE_DISABLED_MASK)==0){ mPrivateFlags &=~PFLAG_SAVE_STATE_CALLED; Parcelable state = onSaveInstanceState(); if((mPrivateFlags & PFLAG_SAVE_STATE_CALLED)==0){ thrownewIllegalStateException( "Derived class did not call super.onSaveInstanceState()"); } if(state !=null){ // Log.i("View", "Freezing #" + Integer.toHexString(mID) // + ": " + state); container.put(mID, state); } } } /** * Hook allowing a view to generate a representation of its internal state * that can later be used to create a new instance with that same state. * This state should only contain information that is not persistent or can * not be reconstructed later. For example, you will never store your * current position on screen because that will be computed again when a * new instance of the view is placed in its view hierarchy. * <p> * Some examples of things you may store here: the current cursor position * in a text view (but usually not the text itself since that is stored in a * content provider or other persistent storage), the currently selected * item in a list view. * * @return Returns a Parcelable object containing the view's current dynamic * state, or null if there is nothing interesting to save. * @see #onRestoreInstanceState(Parcelable) * @see #saveHierarchyState(SparseArray) * @see #dispatchSaveInstanceState(SparseArray) * @see #setSaveEnabled(boolean) */ @CallSuper @NullableprotectedParcelable onSaveInstanceState(){ mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; if(mStartActivityRequestWho !=null|| isAutofilled() || mAutofillViewId > LAST_APP_AUTOFILL_ID){ BaseSavedState state =newBaseSavedState(AbsSavedState.EMPTY_STATE); if(mStartActivityRequestWho !=null){ state.mSavedData |=BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED; } if(isAutofilled()){ state.mSavedData |=BaseSavedState.IS_AUTOFILLED; } if(mAutofillViewId > LAST_APP_AUTOFILL_ID){ state.mSavedData |=BaseSavedState.AUTOFILL_ID; } state.mStartActivityRequestWhoSaved = mStartActivityRequestWho; state.mIsAutofilled = isAutofilled(); state.mHideHighlight = hideAutofillHighlight(); state.mAutofillViewId = mAutofillViewId; return state; } returnBaseSavedState.EMPTY_STATE; } /** * Restore this view hierarchy's frozen state from the given container. * * @param container The SparseArray which holds previously frozen states. * * @see #saveHierarchyState(android.util.SparseArray) * @see #dispatchRestoreInstanceState(android.util.SparseArray) * @see #onRestoreInstanceState(android.os.Parcelable) */ publicvoid restoreHierarchyState(SparseArray<Parcelable> container){ dispatchRestoreInstanceState(container); } /** * Called by {@link #restoreHierarchyState(android.util.SparseArray)} to retrieve the * state for this view and its children. May be overridden to modify how restoring * happens to a view's children; for example, some views may want to not store state * for their children. * * @param container The SparseArray which holds previously saved state. * * @see #dispatchSaveInstanceState(android.util.SparseArray) * @see #restoreHierarchyState(android.util.SparseArray) * @see #onRestoreInstanceState(android.os.Parcelable) */ protectedvoid dispatchRestoreInstanceState(SparseArray<Parcelable> container){ if(mID != NO_ID){ Parcelable state = container.get(mID); if(state !=null){ // Log.i("View", "Restoreing #" + Integer.toHexString(mID) // + ": " + state); mPrivateFlags &=~PFLAG_SAVE_STATE_CALLED; onRestoreInstanceState(state); if((mPrivateFlags & PFLAG_SAVE_STATE_CALLED)==0){ thrownewIllegalStateException( "Derived class did not call super.onRestoreInstanceState()"); } } } } /** * Hook allowing a view to re-apply a representation of its internal state that had previously * been generated by {@link #onSaveInstanceState}. This function will never be called with a * null state. * * @param state The frozen state that had previously been returned by * {@link #onSaveInstanceState}. * * @see #onSaveInstanceState() * @see #restoreHierarchyState(android.util.SparseArray) * @see #dispatchRestoreInstanceState(android.util.SparseArray) */ @CallSuper protectedvoid onRestoreInstanceState(Parcelable state){ mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; if(state !=null&&!(state instanceofAbsSavedState)){ thrownewIllegalArgumentException("Wrong state class, expecting View State but " +"received "+ state.getClass().toString()+" instead. This usually happens " +"when two views of different type have the same id in the same hierarchy. " +"This view's id is "+ViewDebug.resolveId(mContext, getId())+". Make sure " +"other views do not use the same id."); } if(state !=null&& state instanceofBaseSavedState){ BaseSavedState baseState =(BaseSavedState) state; if((baseState.mSavedData &BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED)!=0){ mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved; } if((baseState.mSavedData &BaseSavedState.IS_AUTOFILLED)!=0){ setAutofilled(baseState.mIsAutofilled, baseState.mHideHighlight); } if((baseState.mSavedData &BaseSavedState.AUTOFILL_ID)!=0){ // It can happen that views have the same view id and the restoration path will not // be able to distinguish between them. The autofill id needs to be unique though. // Hence prevent the same autofill view id from being restored multiple times. ((BaseSavedState) state).mSavedData &=~BaseSavedState.AUTOFILL_ID; if((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET)!=0){ // Ignore when view already set it through setAutofillId(); if(Log.isLoggable(AUTOFILL_LOG_TAG,Log.DEBUG)){ Log.d(AUTOFILL_LOG_TAG,"onRestoreInstanceState(): not setting autofillId " +"to "+ baseState.mAutofillViewId +" because view explicitly set" +" it to "+ mAutofillId); } }else{ mAutofillViewId = baseState.mAutofillViewId; mAutofillId =null;// will be set on demand by getAutofillId() } } } } /** * <p>Return the time at which the drawing of the view hierarchy started.</p> * * @return the drawing start time in milliseconds */ publiclong getDrawingTime(){ return mAttachInfo !=null? mAttachInfo.mDrawingTime :0; } /** * <p>Enables or disables the duplication of the parent's state into this view. When * duplication is enabled, this view gets its drawable state from its parent rather * than from its own internal properties.</p> * * <p>Note: in the current implementation, setting this property to true after the * view was added to a ViewGroup might have no effect at all. This property should * always be used from XML or set to true before adding this view to a ViewGroup.</p> * * <p>Note: if this view's parent addStateFromChildren property is enabled and this * property is enabled, an exception will be thrown.</p> * * <p>Note: if the child view uses and updates additional states which are unknown to the * parent, these states should not be affected by this method.</p> * * @param enabled True to enable duplication of the parent's drawable state, false * to disable it. * * @see #getDrawableState() * @see #isDuplicateParentStateEnabled() */ publicvoid setDuplicateParentStateEnabled(boolean enabled){ setFlags(enabled ? DUPLICATE_PARENT_STATE :0, DUPLICATE_PARENT_STATE); } /** * <p>Indicates whether this duplicates its drawable state from its parent.</p> * * @return True if this view's drawable state is duplicated from the parent, * false otherwise * * @see #getDrawableState() * @see #setDuplicateParentStateEnabled(boolean) */ @InspectableProperty(name ="duplicateParentState") publicboolean isDuplicateParentStateEnabled(){ return(mViewFlags & DUPLICATE_PARENT_STATE)== DUPLICATE_PARENT_STATE; } /** * <p>Specifies the type of layer backing this view. The layer can be * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or * {@link #LAYER_TYPE_HARDWARE}.</p> * * <p>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:</p> * <ul> * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li> * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> * </ul> * * <p>If this view has an alpha value set to < 1.0 by calling * {@link #setAlpha(float)}, the alpha value of the layer's paint is superseded * by this view's alpha value.</p> * * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE}, * {@link #LAYER_TYPE_SOFTWARE} and {@link #LAYER_TYPE_HARDWARE} * for more information on when and how to use layers.</p> * * @param layerType The type of layer to use with this view, must be one of * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or * {@link #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 #LAYER_TYPE_NONE} * * @see #getLayerType() * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_SOFTWARE * @see #LAYER_TYPE_HARDWARE * @see #setAlpha(float) * * @attr ref android.R.styleable#View_layerType */ publicvoid setLayerType(@LayerTypeint layerType,@NullablePaint paint){ if(layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE){ thrownewIllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, " +"LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE"); } boolean typeChanged = mRenderNode.setLayerType(layerType); if(!typeChanged){ setLayerPaint(paint); return; } if(layerType != LAYER_TYPE_SOFTWARE){ // Destroy any previous software drawing cache if present // NOTE: even if previous layer type is HW, we do this to ensure we've cleaned up // drawing cache created in View#draw when drawing to a SW canvas. destroyDrawingCache(); } mLayerType = layerType; mLayerPaint = mLayerType == LAYER_TYPE_NONE ?null: paint; mRenderNode.setLayerPaint(mLayerPaint); // draw() behaves differently if we are on a layer, so we need to // invalidate() here invalidateParentCaches(); invalidate(true); } /** * Configure the {@link android.graphics.RenderEffect} to apply to this View. * This will apply a visual effect to the results of the View before it is drawn. For example if * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)} * is provided, the contents will be drawn in a separate layer, then this layer will be blurred * when this View is drawn. * @param renderEffect to be applied to the View. Passing null clears the previously configured * {@link RenderEffect} */ publicvoid setRenderEffect(@NullableRenderEffect renderEffect){ if(mRenderNode.setRenderEffect(renderEffect)){ invalidateViewProperty(true,true); } } /** * Updates the {@link Paint} object used with the current layer (used only if the current * layer type is not set to {@link #LAYER_TYPE_NONE}). Changed properties of the Paint * provided to {@link #setLayerType(int, android.graphics.Paint)} will be used the next time * the View is redrawn, but {@link #setLayerPaint(android.graphics.Paint)} must be called to * ensure that the view gets redrawn immediately. * * <p>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:</p> * <ul> * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li> * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> * </ul> * * <p>If this view has an alpha value set to < 1.0 by calling {@link #setAlpha(float)}, the * alpha value of the layer's paint is superseded by this view's alpha value.</p> * * @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 #LAYER_TYPE_NONE} * * @see #setLayerType(int, android.graphics.Paint) */ publicvoid setLayerPaint(@NullablePaint paint){ int layerType = getLayerType(); if(layerType != LAYER_TYPE_NONE){ mLayerPaint = paint; if(layerType == LAYER_TYPE_HARDWARE){ if(mRenderNode.setLayerPaint(paint)){ invalidateViewProperty(false,false); } }else{ invalidate(); } } } /** * 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 #LAYER_TYPE_NONE}. * Refer to the documentation of {@link #setLayerType(int, android.graphics.Paint)} * for more information on the different types of layers. * * @return {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or * {@link #LAYER_TYPE_HARDWARE} * * @see #setLayerType(int, android.graphics.Paint) * @see #buildLayer() * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_SOFTWARE * @see #LAYER_TYPE_HARDWARE */ @InspectableProperty(enumMapping ={ @EnumEntry(value = LAYER_TYPE_NONE, name ="none"), @EnumEntry(value = LAYER_TYPE_SOFTWARE, name ="software"), @EnumEntry(value = LAYER_TYPE_HARDWARE, name ="hardware") }) @LayerType publicint getLayerType(){ return mLayerType; } /** * Forces this view's layer to be created and this view to be rendered * into its layer. If this view's layer type is set to {@link #LAYER_TYPE_NONE}, * invoking this method will have no effect. * * This method can for instance be used to render a view into its layer before * starting an animation. If this view is complex, rendering into the layer * before starting the animation will avoid skipping frames. * * @throws IllegalStateException If this view is not attached to a window * * @see #setLayerType(int, android.graphics.Paint) */ publicvoid buildLayer(){ if(mLayerType == LAYER_TYPE_NONE)return; finalAttachInfo attachInfo = mAttachInfo; if(attachInfo ==null){ thrownewIllegalStateException("This view must be attached to a window first"); } if(getWidth()==0|| getHeight()==0){ return; } switch(mLayerType){ case LAYER_TYPE_HARDWARE: updateDisplayListIfDirty(); if(attachInfo.mThreadedRenderer !=null&& mRenderNode.hasDisplayList()){ attachInfo.mThreadedRenderer.buildLayer(mRenderNode); } break; case LAYER_TYPE_SOFTWARE: buildDrawingCache(true); break; } } /** * Destroys all hardware rendering resources. This method is invoked * when the system needs to reclaim resources. Upon execution of this * method, you should free any OpenGL resources created by the view. * * Note: you <strong>must</strong> call * <code>super.destroyHardwareResources()</code> when overriding * this method. * * @hide */ @CallSuper @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) protectedvoid destroyHardwareResources(){ if(mOverlay !=null){ mOverlay.getOverlayView().destroyHardwareResources(); } if(mGhostView !=null){ mGhostView.destroyHardwareResources(); } } /** * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a * bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when * the cache is enabled. To benefit from the cache, you must request the drawing cache by * calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not * null.</p> * * <p>Enabling the drawing cache is similar to * {@link #setLayerType(int, android.graphics.Paint) setting a layer} when hardware * acceleration is turned off. When hardware acceleration is turned on, enabling the * drawing cache has no effect on rendering because the system uses a different mechanism * for acceleration which ignores the flag. If you want to use a Bitmap for the view, even * when hardware acceleration is enabled, see {@link #setLayerType(int, android.graphics.Paint)} * for information on how to enable software and hardware layers.</p> * * <p>This API can be used to manually generate * a bitmap copy of this view, by setting the flag to <code>true</code> and calling * {@link #getDrawingCache()}.</p> * * @param enabled true to enable the drawing cache, false otherwise * * @see #isDrawingCacheEnabled() * @see #getDrawingCache() * @see #buildDrawingCache() * @see #setLayerType(int, android.graphics.Paint) * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid setDrawingCacheEnabled(boolean enabled){ mCachingFailed =false; setFlags(enabled ? DRAWING_CACHE_ENABLED :0, DRAWING_CACHE_ENABLED); } /** * <p>Indicates whether the drawing cache is enabled for this view.</p> * * @return true if the drawing cache is enabled * * @see #setDrawingCacheEnabled(boolean) * @see #getDrawingCache() * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated @ViewDebug.ExportedProperty(category ="drawing") publicboolean isDrawingCacheEnabled(){ return(mViewFlags & DRAWING_CACHE_ENABLED)== DRAWING_CACHE_ENABLED; } /** * Debugging utility which recursively outputs the dirty state of a view and its * descendants. * * @hide */ @SuppressWarnings({"UnusedDeclaration"}) publicvoid outputDirtyFlags(String indent,boolean clear,int clearMask){ Log.d(VIEW_LOG_TAG, indent +this+" DIRTY(" +(mPrivateFlags &View.PFLAG_DIRTY_MASK) +") DRAWN("+(mPrivateFlags & PFLAG_DRAWN)+")"+" CACHE_VALID(" +(mPrivateFlags &View.PFLAG_DRAWING_CACHE_VALID) +") INVALIDATED("+(mPrivateFlags & PFLAG_INVALIDATED)+")"); if(clear){ mPrivateFlags &= clearMask; } if(thisinstanceofViewGroup){ ViewGroup parent =(ViewGroup)this; finalint count = parent.getChildCount(); for(int i =0; i < count; i++){ finalView child = parent.getChildAt(i); child.outputDirtyFlags(indent +" ", clear, clearMask); } } } /** * This method is used by ViewGroup to cause its children to restore or recreate their * display lists. It is called by getDisplayList() when the parent ViewGroup does not need * to recreate its own display list, which would happen if it went through the normal * draw/dispatchDraw mechanisms. * * @hide */ protectedvoid dispatchGetDisplayList(){} /** * A view that is not attached or hardware accelerated cannot create a display list. * This method checks these conditions and returns the appropriate result. * * @return true if view has the ability to create a display list, false otherwise. * * @hide */ publicboolean canHaveDisplayList(){ return!(mAttachInfo ==null|| mAttachInfo.mThreadedRenderer ==null); } /** * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported) * @hide */ @NonNull @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicRenderNode updateDisplayListIfDirty(){ finalRenderNode renderNode = mRenderNode; if(!canHaveDisplayList()){ // can't populate RenderNode, don't try return renderNode; } if((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID)==0 ||!renderNode.hasDisplayList() ||(mRecreateDisplayList)){ // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs if(renderNode.hasDisplayList() &&!mRecreateDisplayList){ mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &=~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode;// no work needed } // If we got here, we're recreating it. Mark it as such to ensure that // we copy in child display lists into ours in drawChild() mRecreateDisplayList =true; int width = mRight - mLeft; int height = mBottom - mTop; int layerType = getLayerType(); // Hacky hack: Reset any stretch effects as those are applied during the draw pass // instead of being "stateful" like other RenderNode properties renderNode.clearStretch(); finalRecordingCanvas canvas = renderNode.beginRecording(width, height); try{ if(layerType == LAYER_TYPE_SOFTWARE){ buildDrawingCache(true); Bitmap cache = getDrawingCache(true); if(cache !=null){ canvas.drawBitmap(cache,0,0, mLayerPaint); } }else{ computeScroll(); canvas.translate(-mScrollX,-mScrollY); mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &=~PFLAG_DIRTY_MASK; // Fast path for layouts with no backgrounds if((mPrivateFlags & PFLAG_SKIP_DRAW)== PFLAG_SKIP_DRAW){ dispatchDraw(canvas); drawAutofilledHighlight(canvas); if(mOverlay !=null&&!mOverlay.isEmpty()){ mOverlay.getOverlayView().draw(canvas); } if(isShowingLayoutBounds()){ debugDrawFocus(canvas); } }else{ draw(canvas); } } }finally{ renderNode.endRecording(); setDisplayListProperties(renderNode); } }else{ mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &=~PFLAG_DIRTY_MASK; } return renderNode; } @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privatevoid resetDisplayList(){ mRenderNode.discardDisplayList(); if(mBackgroundRenderNode !=null){ mBackgroundRenderNode.discardDisplayList(); } } /** * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> * * @return A non-scaled bitmap representing this view or null if cache is disabled. * * @see #getDrawingCache(boolean) * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicBitmap getDrawingCache(){ return getDrawingCache(false); } /** * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap * is null when caching is disabled. If caching is enabled and the cache is not ready, * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not * draw from the cache when the cache is enabled. To benefit from the cache, you must * request the drawing cache by calling this method and draw it on screen if the * returned bitmap is not null.</p> * * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, * this method will create a bitmap of the same size as this view. Because this bitmap * will be drawn scaled by the parent ViewGroup, the result on screen might show * scaling artifacts. To avoid such artifacts, you should call this method by setting * the auto scaling to true. Doing so, however, will generate a bitmap of a different * size than the view. This implies that your application must be able to handle this * size.</p> * * @param autoScale Indicates whether the generated bitmap should be scaled based on * the current density of the screen when the application is in compatibility * mode. * * @return A bitmap representing this view or null if cache is disabled. * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() * @see #buildDrawingCache(boolean) * @see #destroyDrawingCache() * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicBitmap getDrawingCache(boolean autoScale){ if((mViewFlags & WILL_NOT_CACHE_DRAWING)== WILL_NOT_CACHE_DRAWING){ returnnull; } if((mViewFlags & DRAWING_CACHE_ENABLED)== DRAWING_CACHE_ENABLED){ buildDrawingCache(autoScale); } return autoScale ? mDrawingCache : mUnscaledDrawingCache; } /** * <p>Frees the resources used by the drawing cache. If you call * {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache with this method afterwards.</p> * * @see #setDrawingCacheEnabled(boolean) * @see #buildDrawingCache() * @see #getDrawingCache() * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid destroyDrawingCache(){ if(mDrawingCache !=null){ mDrawingCache.recycle(); mDrawingCache =null; } if(mUnscaledDrawingCache !=null){ mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache =null; } } /** * Setting a solid background color for the drawing cache's bitmaps will improve * performance and memory usage. Note, though that this should only be used if this * view will always be drawn on top of a solid color. * * @param color The background color to use for the drawing cache's bitmap * * @see #setDrawingCacheEnabled(boolean) * @see #buildDrawingCache() * @see #getDrawingCache() * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid setDrawingCacheBackgroundColor(@ColorIntint color){ if(color != mDrawingCacheBackgroundColor){ mDrawingCacheBackgroundColor = color; mPrivateFlags &=~PFLAG_DRAWING_CACHE_VALID; } } /** * @see #setDrawingCacheBackgroundColor(int) * * @return The background color to used for the drawing cache's bitmap * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated @ColorInt publicint getDrawingCacheBackgroundColor(){ return mDrawingCacheBackgroundColor; } /** * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p> * * @see #buildDrawingCache(boolean) * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid buildDrawingCache(){ buildDrawingCache(false); } /** * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p> * * <p>If you call {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p> * * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, * this method will create a bitmap of the same size as this view. Because this bitmap * will be drawn scaled by the parent ViewGroup, the result on screen might show * scaling artifacts. To avoid such artifacts, you should call this method by setting * the auto scaling to true. Doing so, however, will generate a bitmap of a different * size than the view. This implies that your application must be able to handle this * size.</p> * * <p>You should avoid calling this method when hardware acceleration is enabled. If * you do not need the drawing cache bitmap, calling this method will increase memory * usage and cause the view to be rendered in software once, thus negatively impacting * performance.</p> * * @see #getDrawingCache() * @see #destroyDrawingCache() * * @deprecated The view drawing cache was largely made obsolete with the introduction of * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer. In the rare cases where caching layers are useful, * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware * rendering. For software-rendered snapshots of a small part of the View hierarchy or * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these * software-rendered usages are discouraged and have compatibility issues with hardware-only * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE} * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback * reports or unit testing the {@link PixelCopy} API is recommended. */ @Deprecated publicvoid buildDrawingCache(boolean autoScale){ if((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID)==0||(autoScale ? mDrawingCache ==null: mUnscaledDrawingCache ==null)){ if(Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)){ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "buildDrawingCache/SW Layer for "+ getClass().getSimpleName()); } try{ buildDrawingCacheImpl(autoScale); }finally{ Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } } /** * private, internal implementation of buildDrawingCache, used to enable tracing */ privatevoid buildDrawingCacheImpl(boolean autoScale){ mCachingFailed =false; int width = mRight - mLeft; int height = mBottom - mTop; finalAttachInfo attachInfo = mAttachInfo; finalboolean scalingRequired = attachInfo !=null&& attachInfo.mScalingRequired; if(autoScale && scalingRequired){ width =(int)((width * attachInfo.mApplicationScale)+0.5f); height =(int)((height * attachInfo.mApplicationScale)+0.5f); } finalint drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; finalboolean opaque = drawingCacheBackgroundColor !=0|| isOpaque(); finalboolean use32BitCache = attachInfo !=null&& attachInfo.mUse32BitDrawingCache; finallong projectedBitmapSize = width * height *(opaque &&!use32BitCache ?2:4); finallong drawingCacheSize = ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize(); if(width <=0|| height <=0|| projectedBitmapSize > drawingCacheSize){ if(width >0&& height >0){ Log.w(VIEW_LOG_TAG, getClass().getSimpleName()+" not displayed because it is" +" too large to fit into a software layer (or drawing cache), needs " + projectedBitmapSize +" bytes, only " + drawingCacheSize +" available"); } destroyDrawingCache(); mCachingFailed =true; return; } boolean clear =true; Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache; if(bitmap ==null|| bitmap.getWidth()!= width || bitmap.getHeight()!= height){ Bitmap.Config quality; if(!opaque){ // Never pick ARGB_4444 because it looks awful // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case switch(mViewFlags & DRAWING_CACHE_QUALITY_MASK){ case DRAWING_CACHE_QUALITY_AUTO: case DRAWING_CACHE_QUALITY_LOW: case DRAWING_CACHE_QUALITY_HIGH: default: quality =Bitmap.Config.ARGB_8888; break; } }else{ // Optimization for translucent windows // If the window is translucent, use a 32 bits bitmap to benefit from memcpy() quality = use32BitCache ?Bitmap.Config.ARGB_8888 :Bitmap.Config.RGB_565; } // Try to cleanup memory if(bitmap !=null) bitmap.recycle(); try{ bitmap =Bitmap.createBitmap(mResources.getDisplayMetrics(), width, height, quality); bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); if(autoScale){ mDrawingCache = bitmap; }else{ mUnscaledDrawingCache = bitmap; } if(opaque && use32BitCache) bitmap.setHasAlpha(false); }catch(OutOfMemoryError e){ // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy if(autoScale){ mDrawingCache =null; }else{ mUnscaledDrawingCache =null; } mCachingFailed =true; return; } clear = drawingCacheBackgroundColor !=0; } Canvas canvas; if(attachInfo !=null){ canvas = attachInfo.mCanvas; if(canvas ==null){ canvas =newCanvas(); } canvas.setBitmap(bitmap); // Temporarily clobber the cached Canvas in case one of our children // is also using a drawing cache. Without this, the children would // steal the canvas by attaching their own bitmap to it and bad, bad // thing would happen (invisible views, corrupted drawings, etc.) attachInfo.mCanvas =null; }else{ // This case should hopefully never or seldom happen canvas =newCanvas(bitmap); } if(clear){ bitmap.eraseColor(drawingCacheBackgroundColor); } computeScroll(); finalint restoreCount = canvas.save(); if(autoScale && scalingRequired){ finalfloat scale = attachInfo.mApplicationScale; canvas.scale(scale, scale); } canvas.translate(-mScrollX,-mScrollY); mPrivateFlags |= PFLAG_DRAWN; if(mAttachInfo ==null||!mAttachInfo.mHardwareAccelerated || mLayerType != LAYER_TYPE_NONE){ mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID; } // Fast path for layouts with no backgrounds if((mPrivateFlags & PFLAG_SKIP_DRAW)== PFLAG_SKIP_DRAW){ mPrivateFlags &=~PFLAG_DIRTY_MASK; dispatchDraw(canvas); drawAutofilledHighlight(canvas); if(mOverlay !=null&&!mOverlay.isEmpty()){ mOverlay.getOverlayView().draw(canvas); } }else{ draw(canvas); } canvas.restoreToCount(restoreCount); canvas.setBitmap(null); if(attachInfo !=null){ // Restore the cached Canvas for our siblings attachInfo.mCanvas = canvas; } } /** * Create a snapshot of the view into a bitmap. We should probably make * some form of this public, but should think about the API. * * @hide */ @UnsupportedAppUsage publicBitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider,boolean skipChildren){ int width = mRight - mLeft; int height = mBottom - mTop; finalAttachInfo attachInfo = mAttachInfo; finalfloat scale = attachInfo !=null? attachInfo.mApplicationScale :1.0f; width =(int)((width * scale)+0.5f); height =(int)((height * scale)+0.5f); Canvas oldCanvas =null; try{ Canvas canvas = canvasProvider.getCanvas(this, width >0? width :1, height >0? height :1); if(attachInfo !=null){ oldCanvas = attachInfo.mCanvas; // Temporarily clobber the cached Canvas in case one of our children // is also using a drawing cache. Without this, the children would // steal the canvas by attaching their own bitmap to it and bad, bad // things would happen (invisible views, corrupted drawings, etc.) attachInfo.mCanvas =null; } computeScroll(); finalint restoreCount = canvas.save(); canvas.scale(scale, scale); canvas.translate(-mScrollX,-mScrollY); // Temporarily remove the dirty mask int flags = mPrivateFlags; mPrivateFlags &=~PFLAG_DIRTY_MASK; // Fast path for layouts with no backgrounds if((mPrivateFlags & PFLAG_SKIP_DRAW)== PFLAG_SKIP_DRAW){ dispatchDraw(canvas); drawAutofilledHighlight(canvas); if(mOverlay !=null&&!mOverlay.isEmpty()){ mOverlay.getOverlayView().draw(canvas); } }else{ draw(canvas); } mPrivateFlags = flags; canvas.restoreToCount(restoreCount); return canvasProvider.createBitmap(); }finally{ if(oldCanvas !=null){ attachInfo.mCanvas = oldCanvas; } } } /** * Indicates whether this View is currently in edit mode. A View is usually * in edit mode when displayed within a developer tool. For instance, if * this View is being drawn by a visual user interface builder, this method * should return true. * * Subclasses should check the return value of this method to provide * different behaviors if their normal behavior might interfere with the * host environment. For instance: the class spawns a thread in its * constructor, the drawing code relies on device-specific features, etc. * * This method is usually checked in the drawing code of custom widgets. * * @return True if this View is in edit mode, false otherwise. */ publicboolean isInEditMode(){ returnfalse; } /** * If the View draws content inside its padding and enables fading edges, * it needs to support padding offsets. Padding offsets are added to the * fading edges to extend the length of the fade so that it covers pixels * drawn inside the padding. * * Subclasses of this class should override this method if they need * to draw content inside the padding. * * @return True if padding offset must be applied, false otherwise. * * @see #getLeftPaddingOffset() * @see #getRightPaddingOffset() * @see #getTopPaddingOffset() * @see #getBottomPaddingOffset() * * @since CURRENT */ protectedboolean isPaddingOffsetRequired(){ returnfalse; } /** * Amount by which to extend the left fading region. Called only when * {@link #isPaddingOffsetRequired()} returns true. * * @return The left padding offset in pixels. * * @see #isPaddingOffsetRequired() * * @since CURRENT */ protectedint getLeftPaddingOffset(){ return0; } /** * Amount by which to extend the right fading region. Called only when * {@link #isPaddingOffsetRequired()} returns true. * * @return The right padding offset in pixels. * * @see #isPaddingOffsetRequired() * * @since CURRENT */ protectedint getRightPaddingOffset(){ return0; } /** * Amount by which to extend the top fading region. Called only when * {@link #isPaddingOffsetRequired()} returns true. * * @return The top padding offset in pixels. * * @see #isPaddingOffsetRequired() * * @since CURRENT */ protectedint getTopPaddingOffset(){ return0; } /** * Amount by which to extend the bottom fading region. Called only when * {@link #isPaddingOffsetRequired()} returns true. * * @return The bottom padding offset in pixels. * * @see #isPaddingOffsetRequired() * * @since CURRENT */ protectedint getBottomPaddingOffset(){ return0; } /** * @hide * @param offsetRequired */ protectedint getFadeTop(boolean offsetRequired){ int top = mPaddingTop; if(offsetRequired) top += getTopPaddingOffset(); return top; } /** * @hide * @param offsetRequired */ protectedint getFadeHeight(boolean offsetRequired){ int padding = mPaddingTop; if(offsetRequired) padding += getTopPaddingOffset(); return mBottom - mTop - mPaddingBottom - padding; } /** * <p>Indicates whether this view is attached to a hardware accelerated * window or not.</p> * * <p>Even if this method returns true, it does not mean that every call * to {@link #draw(android.graphics.Canvas)} will be made with an hardware * accelerated {@link android.graphics.Canvas}. For instance, if this view * is drawn onto an offscreen {@link android.graphics.Bitmap} and its * window is hardware accelerated, * {@link android.graphics.Canvas#isHardwareAccelerated()} will likely * return false, and this method will return true.</p> * * @return True if the view is attached to a window and the window is * hardware accelerated; false in any other case. */ @ViewDebug.ExportedProperty(category ="drawing") publicboolean isHardwareAccelerated(){ return mAttachInfo !=null&& mAttachInfo.mHardwareAccelerated; } /** * 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. * * @param clipBounds The rectangular area, in the local coordinates of * this view, to which future drawing operations will be clipped. */ publicvoid setClipBounds(Rect clipBounds){ if(clipBounds == mClipBounds ||(clipBounds !=null&& clipBounds.equals(mClipBounds))){ return; } if(clipBounds !=null){ if(mClipBounds ==null){ mClipBounds =newRect(clipBounds); }else{ mClipBounds.set(clipBounds); } }else{ mClipBounds =null; } mRenderNode.setClipRect(mClipBounds); invalidateViewProperty(false,false); } /** * Returns a copy of the current {@link #setClipBounds(Rect) clipBounds}. * * @return A copy of the current clip bounds if clip bounds are set, * otherwise null. */ publicRect getClipBounds(){ return(mClipBounds !=null)?newRect(mClipBounds):null; } /** * Populates an output rectangle with the clip bounds of the view, * returning {@code true} if successful or {@code false} if the view's * clip bounds are {@code null}. * * @param outRect rectangle in which to place the clip bounds of the view * @return {@code true} if successful or {@code false} if the view's * clip bounds are {@code null} */ publicboolean getClipBounds(Rect outRect){ if(mClipBounds !=null){ outRect.set(mClipBounds); returntrue; } returnfalse; } /** * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common * case of an active Animation being run on the view. */ privateboolean applyLegacyAnimation(ViewGroup parent,long drawingTime, Animation a,boolean scalingRequired){ Transformation invalidationTransform; finalint flags = parent.mGroupFlags; finalboolean initialized = a.isInitialized(); if(!initialized){ a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight()); a.initializeInvalidateRegion(0,0, mRight - mLeft, mBottom - mTop); if(mAttachInfo !=null) a.setListenerHandler(mAttachInfo.mHandler); onAnimationStart(); } finalTransformation t = parent.getChildTransformation(); boolean more = a.getTransformation(drawingTime, t,1f); if(scalingRequired && mAttachInfo.mApplicationScale !=1f){ if(parent.mInvalidationTransformation ==null){ parent.mInvalidationTransformation =newTransformation(); } invalidationTransform = parent.mInvalidationTransformation; a.getTransformation(drawingTime, invalidationTransform,1f); }else{ invalidationTransform = t; } if(more){ if(!a.willChangeBounds()){ if((flags &(ViewGroup.FLAG_OPTIMIZE_INVALIDATE |ViewGroup.FLAG_ANIMATION_DONE))== ViewGroup.FLAG_OPTIMIZE_INVALIDATE){ parent.mGroupFlags |=ViewGroup.FLAG_INVALIDATE_REQUIRED; }elseif((flags &ViewGroup.FLAG_INVALIDATE_REQUIRED)==0){ // The child need to draw an animation, potentially offscreen, so // make sure we do not cancel invalidate requests parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION; parent.invalidate(mLeft, mTop, mRight, mBottom); } }else{ if(parent.mInvalidateRegion ==null){ parent.mInvalidateRegion =newRectF(); } finalRectF region = parent.mInvalidateRegion; a.getInvalidateRegion(0,0, mRight - mLeft, mBottom - mTop, region, invalidationTransform); // The child need to draw an animation, potentially offscreen, so // make sure we do not cancel invalidate requests parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION; finalint left = mLeft +(int) region.left; finalint top = mTop +(int) region.top; parent.invalidate(left, top, left +(int)(region.width()+.5f), top +(int)(region.height()+.5f)); } } return more; } /** * This method is called by getDisplayList() when a display list is recorded for a View. * It pushes any properties to the RenderNode that aren't managed by the RenderNode. */ void setDisplayListProperties(RenderNode renderNode){ if(renderNode !=null){ renderNode.setHasOverlappingRendering(getHasOverlappingRendering()); renderNode.setClipToBounds(mParent instanceofViewGroup &&((ViewGroup) mParent).getClipChildren()); float alpha =1; if(mParent instanceofViewGroup&&(((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS)!=0){ ViewGroup parentVG =(ViewGroup) mParent; finalTransformation t = parentVG.getChildTransformation(); if(parentVG.getChildStaticTransformation(this, t)){ finalint transformType = t.getTransformationType(); if(transformType !=Transformation.TYPE_IDENTITY){ if((transformType &Transformation.TYPE_ALPHA)!=0){ alpha = t.getAlpha(); } if((transformType &Transformation.TYPE_MATRIX)!=0){ renderNode.setStaticMatrix(t.getMatrix()); } } } } if(mTransformationInfo !=null){ alpha *= getFinalAlpha(); if(alpha <1){ finalint multipliedAlpha =(int)(255* alpha); if(onSetAlpha(multipliedAlpha)){ alpha =1; } } renderNode.setAlpha(alpha); }elseif(alpha <1){ renderNode.setAlpha(alpha); } } } /** * If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList. * * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't * HW accelerated, it can't handle drawing RenderNodes. * * @hide */ protectedfinalboolean drawsWithRenderNode(Canvas canvas){ return mAttachInfo !=null && mAttachInfo.mHardwareAccelerated && canvas.isHardwareAccelerated(); } /** * This method is called by ViewGroup.drawChild() to have each child view draw itself. * * This is where the View specializes rendering behavior based on layer type, * and hardware acceleration. */ boolean draw(Canvas canvas,ViewGroup parent,long drawingTime){ finalboolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); boolean drawingWithRenderNode = drawsWithRenderNode(canvas); boolean more =false; finalboolean childHasIdentityMatrix = hasIdentityMatrix(); finalint parentFlags = parent.mGroupFlags; if((parentFlags &ViewGroup.FLAG_CLEAR_TRANSFORMATION)!=0){ parent.getChildTransformation().clear(); parent.mGroupFlags &=~ViewGroup.FLAG_CLEAR_TRANSFORMATION; } Transformation transformToApply =null; boolean concatMatrix =false; finalboolean scalingRequired = mAttachInfo !=null&& mAttachInfo.mScalingRequired; finalAnimation a = getAnimation(); if(a !=null){ more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); concatMatrix = a.willChangeTransformationMatrix(); if(concatMatrix){ mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } transformToApply = parent.getChildTransformation(); }else{ if((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM)!=0){ // No longer animating: clear out old animation matrix mRenderNode.setAnimationMatrix(null); mPrivateFlags3 &=~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } if(!drawingWithRenderNode &&(parentFlags &ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS)!=0){ finalTransformation t = parent.getChildTransformation(); finalboolean hasTransform = parent.getChildStaticTransformation(this, t); if(hasTransform){ finalint transformType = t.getTransformationType(); transformToApply = transformType !=Transformation.TYPE_IDENTITY ? t :null; concatMatrix =(transformType &Transformation.TYPE_MATRIX)!=0; } } } concatMatrix |=!childHasIdentityMatrix; // Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations mPrivateFlags |= PFLAG_DRAWN; if(!concatMatrix && (parentFlags &(ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS | ViewGroup.FLAG_CLIP_CHILDREN))==ViewGroup.FLAG_CLIP_CHILDREN && canvas.quickReject(mLeft, mTop, mRight, mBottom)&& (mPrivateFlags & PFLAG_DRAW_ANIMATION)==0){ mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED; return more; } mPrivateFlags2 &=~PFLAG2_VIEW_QUICK_REJECTED; if(hardwareAcceleratedCanvas){ // Clear INVALIDATED flag to allow invalidation to occur during rendering, but // retain the flag's value temporarily in the mRecreateDisplayList flag mRecreateDisplayList =(mPrivateFlags & PFLAG_INVALIDATED)!=0; mPrivateFlags &=~PFLAG_INVALIDATED; } RenderNode renderNode =null; Bitmap cache =null; int layerType = getLayerType();// TODO: signify cache state with just 'cache' local if(layerType == LAYER_TYPE_SOFTWARE ||!drawingWithRenderNode){ if(layerType != LAYER_TYPE_NONE){ // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); } cache = getDrawingCache(true); } if(drawingWithRenderNode){ // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view renderNode = updateDisplayListIfDirty(); if(!renderNode.hasDisplayList()){ // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. renderNode =null; drawingWithRenderNode =false; } } int sx =0; int sy =0; if(!drawingWithRenderNode){ computeScroll(); sx = mScrollX; sy = mScrollY; } finalboolean drawingWithDrawingCache = cache !=null&&!drawingWithRenderNode; finalboolean offsetForScroll = cache ==null&&!drawingWithRenderNode; int restoreTo =-1; if(!drawingWithRenderNode || transformToApply !=null){ restoreTo = canvas.save(); } if(offsetForScroll){ canvas.translate(mLeft - sx, mTop - sy); }else{ if(!drawingWithRenderNode){ canvas.translate(mLeft, mTop); } if(scalingRequired){ if(drawingWithRenderNode){ // TODO: Might not need this if we put everything inside the DL restoreTo = canvas.save(); } // mAttachInfo cannot be null, otherwise scalingRequired == false finalfloat scale =1.0f/ mAttachInfo.mApplicationScale; canvas.scale(scale, scale); } } float alpha = drawingWithRenderNode ?1:(getAlpha()* getTransitionAlpha()); if(transformToApply !=null || alpha <1 ||!hasIdentityMatrix() ||(mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA)!=0){ if(transformToApply !=null||!childHasIdentityMatrix){ int transX =0; int transY =0; if(offsetForScroll){ transX =-sx; transY =-sy; } if(transformToApply !=null){ if(concatMatrix){ if(drawingWithRenderNode){ renderNode.setAnimationMatrix(transformToApply.getMatrix()); }else{ // Undo the scroll translation, apply the transformation matrix, // then redo the scroll translate to get the correct result. canvas.translate(-transX,-transY); canvas.concat(transformToApply.getMatrix()); canvas.translate(transX, transY); } parent.mGroupFlags |=ViewGroup.FLAG_CLEAR_TRANSFORMATION; } float transformAlpha = transformToApply.getAlpha(); if(transformAlpha <1){ alpha *= transformAlpha; parent.mGroupFlags |=ViewGroup.FLAG_CLEAR_TRANSFORMATION; } } if(!childHasIdentityMatrix &&!drawingWithRenderNode){ canvas.translate(-transX,-transY); canvas.concat(getMatrix()); canvas.translate(transX, transY); } } // Deal with alpha if it is or used to be <1 if(alpha <1||(mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA)!=0){ if(alpha <1){ mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA; }else{ mPrivateFlags3 &=~PFLAG3_VIEW_IS_ANIMATING_ALPHA; } parent.mGroupFlags |=ViewGroup.FLAG_CLEAR_TRANSFORMATION; if(!drawingWithDrawingCache){ finalint multipliedAlpha =(int)(255* alpha); if(!onSetAlpha(multipliedAlpha)){ if(drawingWithRenderNode){ renderNode.setAlpha(alpha * getAlpha()* getTransitionAlpha()); }elseif(layerType == LAYER_TYPE_NONE){ canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(), multipliedAlpha); } }else{ // Alpha is handled by the child directly, clobber the layer's alpha mPrivateFlags |= PFLAG_ALPHA_SET; } } } }elseif((mPrivateFlags & PFLAG_ALPHA_SET)== PFLAG_ALPHA_SET){ onSetAlpha(255); mPrivateFlags &=~PFLAG_ALPHA_SET; } if(!drawingWithRenderNode){ // apply clips directly, since RenderNode won't do it for this draw if((parentFlags &ViewGroup.FLAG_CLIP_CHILDREN)!=0&& cache ==null){ if(offsetForScroll){ canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight()); }else{ if(!scalingRequired || cache ==null){ canvas.clipRect(0,0, getWidth(), getHeight()); }else{ canvas.clipRect(0,0, cache.getWidth(), cache.getHeight()); } } } if(mClipBounds !=null){ // clip bounds ignore scroll canvas.clipRect(mClipBounds); } } if(!drawingWithDrawingCache){ if(drawingWithRenderNode){ mPrivateFlags &=~PFLAG_DIRTY_MASK; ((RecordingCanvas) canvas).drawRenderNode(renderNode); }else{ // Fast path for layouts with no backgrounds if((mPrivateFlags & PFLAG_SKIP_DRAW)== PFLAG_SKIP_DRAW){ mPrivateFlags &=~PFLAG_DIRTY_MASK; dispatchDraw(canvas); }else{ draw(canvas); } } }elseif(cache !=null){ mPrivateFlags &=~PFLAG_DIRTY_MASK; if(layerType == LAYER_TYPE_NONE || mLayerPaint ==null){ // no layer paint, use temporary paint to draw bitmap Paint cachePaint = parent.mCachePaint; if(cachePaint ==null){ cachePaint =newPaint(); cachePaint.setDither(false); parent.mCachePaint = cachePaint; } cachePaint.setAlpha((int)(alpha *255)); canvas.drawBitmap(cache,0.0f,0.0f, cachePaint); }else{ // use layer paint to draw the bitmap, merging the two alphas, but also restore int layerPaintAlpha = mLayerPaint.getAlpha(); if(alpha <1){ mLayerPaint.setAlpha((int)(alpha * layerPaintAlpha)); } canvas.drawBitmap(cache,0.0f,0.0f, mLayerPaint); if(alpha <1){ mLayerPaint.setAlpha(layerPaintAlpha); } } } if(restoreTo >=0){ canvas.restoreToCount(restoreTo); } if(a !=null&&!more){ if(!hardwareAcceleratedCanvas &&!a.getFillAfter()){ onSetAlpha(255); } parent.finishAnimatingView(this, a); } if(more && hardwareAcceleratedCanvas){ if(a.hasAlpha()&&(mPrivateFlags & PFLAG_ALPHA_SET)== PFLAG_ALPHA_SET){ // alpha animations should cause the child to recreate its display list invalidate(true); } } mRecreateDisplayList =false; return more; } staticPaint getDebugPaint(){ if(sDebugPaint ==null){ sDebugPaint =newPaint(); sDebugPaint.setAntiAlias(false); } return sDebugPaint; } finalint dipsToPixels(int dips){ float scale = getContext().getResources().getDisplayMetrics().density; return(int)(dips * scale +0.5f); } finalprivatevoid debugDrawFocus(Canvas canvas){ if(isFocused()){ finalint cornerSquareSize = dipsToPixels(DEBUG_CORNERS_SIZE_DIP); finalint l = mScrollX; finalint r = l + mRight - mLeft; finalint t = mScrollY; finalint b = t + mBottom - mTop; finalPaint paint = getDebugPaint(); paint.setColor(DEBUG_CORNERS_COLOR); // Draw squares in corners. paint.setStyle(Paint.Style.FILL); canvas.drawRect(l, t, l + cornerSquareSize, t + cornerSquareSize, paint); canvas.drawRect(r - cornerSquareSize, t, r, t + cornerSquareSize, paint); canvas.drawRect(l, b - cornerSquareSize, l + cornerSquareSize, b, paint); canvas.drawRect(r - cornerSquareSize, b - cornerSquareSize, r, b, paint); // Draw big X across the view. paint.setStyle(Paint.Style.STROKE); canvas.drawLine(l, t, r, b, paint); canvas.drawLine(l, b, r, t, paint); } } /** * Manually render this view (and all of its children) to the given Canvas. * The view must have already done a full layout before this function is * called. When implementing a view, implement * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method. * If you do need to override this method, call the superclass version. * * @param canvas The Canvas to which the View is rendered. */ @CallSuper publicvoid draw(Canvas canvas){ finalint privateFlags = mPrivateFlags; mPrivateFlags =(privateFlags &~PFLAG_DIRTY_MASK)| PFLAG_DRAWN; /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) * 7. If necessary, draw the default focus highlight */ // Step 1, draw the background, if needed int saveCount; drawBackground(canvas); // skip step 2 & 5 if possible (common case) finalint viewFlags = mViewFlags; boolean horizontalEdges =(viewFlags & FADING_EDGE_HORIZONTAL)!=0; boolean verticalEdges =(viewFlags & FADING_EDGE_VERTICAL)!=0; if(!verticalEdges &&!horizontalEdges){ // Step 3, draw the content onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); drawAutofilledHighlight(canvas); // Overlay is part of the content and draws beneath Foreground if(mOverlay !=null&&!mOverlay.isEmpty()){ mOverlay.getOverlayView().dispatchDraw(canvas); } // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); if(isShowingLayoutBounds()){ debugDrawFocus(canvas); } // we're done... return; } /* * Here we do the full fledged routine... * (this is an uncommon case where speed matters less, * this is why we repeat some of the tests that have been * done above) */ boolean drawTop =false; boolean drawBottom =false; boolean drawLeft =false; boolean drawRight =false; float topFadeStrength =0.0f; float bottomFadeStrength =0.0f; float leftFadeStrength =0.0f; float rightFadeStrength =0.0f; // Step 2, save the canvas' layers int paddingLeft = mPaddingLeft; finalboolean offsetRequired = isPaddingOffsetRequired(); if(offsetRequired){ paddingLeft += getLeftPaddingOffset(); } int left = mScrollX + paddingLeft; int right = left + mRight - mLeft - mPaddingRight - paddingLeft; int top = mScrollY + getFadeTop(offsetRequired); int bottom = top + getFadeHeight(offsetRequired); if(offsetRequired){ right += getRightPaddingOffset(); bottom += getBottomPaddingOffset(); } finalScrollabilityCache scrollabilityCache = mScrollCache; finalfloat fadeHeight = scrollabilityCache.fadingEdgeLength; int length =(int) fadeHeight; // clip the fade length if top and bottom fades overlap // overlapping fades produce odd-looking artifacts if(verticalEdges &&(top + length > bottom - length)){ length =(bottom - top)/2; } // also clip horizontal fades if necessary if(horizontalEdges &&(left + length > right - length)){ length =(right - left)/2; } if(verticalEdges){ topFadeStrength =Math.max(0.0f,Math.min(1.0f, getTopFadingEdgeStrength())); drawTop = topFadeStrength * fadeHeight >1.0f; bottomFadeStrength =Math.max(0.0f,Math.min(1.0f, getBottomFadingEdgeStrength())); drawBottom = bottomFadeStrength * fadeHeight >1.0f; } if(horizontalEdges){ leftFadeStrength =Math.max(0.0f,Math.min(1.0f, getLeftFadingEdgeStrength())); drawLeft = leftFadeStrength * fadeHeight >1.0f; rightFadeStrength =Math.max(0.0f,Math.min(1.0f, getRightFadingEdgeStrength())); drawRight = rightFadeStrength * fadeHeight >1.0f; } saveCount = canvas.getSaveCount(); int topSaveCount =-1; int bottomSaveCount =-1; int leftSaveCount =-1; int rightSaveCount =-1; int solidColor = getSolidColor(); if(solidColor ==0){ if(drawTop){ topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length); } if(drawBottom){ bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom); } if(drawLeft){ leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom); } if(drawRight){ rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom); } }else{ scrollabilityCache.setFadeColor(solidColor); } // Step 3, draw the content onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers finalPaint p = scrollabilityCache.paint; finalMatrix matrix = scrollabilityCache.matrix; finalShader fade = scrollabilityCache.shader; // must be restored in the reverse order that they were saved if(drawRight){ matrix.setScale(1, fadeHeight * rightFadeStrength); matrix.postRotate(90); matrix.postTranslate(right, top); fade.setLocalMatrix(matrix); p.setShader(fade); if(solidColor ==0){ canvas.restoreUnclippedLayer(rightSaveCount, p); }else{ canvas.drawRect(right - length, top, right, bottom, p); } } if(drawLeft){ matrix.setScale(1, fadeHeight * leftFadeStrength); matrix.postRotate(-90); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); p.setShader(fade); if(solidColor ==0){ canvas.restoreUnclippedLayer(leftSaveCount, p); }else{ canvas.drawRect(left, top, left + length, bottom, p); } } if(drawBottom){ matrix.setScale(1, fadeHeight * bottomFadeStrength); matrix.postRotate(180); matrix.postTranslate(left, bottom); fade.setLocalMatrix(matrix); p.setShader(fade); if(solidColor ==0){ canvas.restoreUnclippedLayer(bottomSaveCount, p); }else{ canvas.drawRect(left, bottom - length, right, bottom, p); } } if(drawTop){ matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); p.setShader(fade); if(solidColor ==0){ canvas.restoreUnclippedLayer(topSaveCount, p); }else{ canvas.drawRect(left, top, right, top + length, p); } } canvas.restoreToCount(saveCount); drawAutofilledHighlight(canvas); // Overlay is part of the content and draws beneath Foreground if(mOverlay !=null&&!mOverlay.isEmpty()){ mOverlay.getOverlayView().dispatchDraw(canvas); } // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); if(isShowingLayoutBounds()){ debugDrawFocus(canvas); } } /** * Draws the background onto the specified canvas. * * @param canvas Canvas on which to draw the background */ @UnsupportedAppUsage privatevoid drawBackground(Canvas canvas){ finalDrawable background = mBackground; if(background ==null){ return; } setBackgroundBounds(); // Attempt to use a display list if requested. if(canvas.isHardwareAccelerated()&& mAttachInfo !=null && mAttachInfo.mThreadedRenderer !=null){ mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode); finalRenderNode renderNode = mBackgroundRenderNode; if(renderNode !=null&& renderNode.hasDisplayList()){ setBackgroundRenderNodeProperties(renderNode); ((RecordingCanvas) canvas).drawRenderNode(renderNode); return; } } finalint scrollX = mScrollX; finalint scrollY = mScrollY; if((scrollX | scrollY)==0){ background.draw(canvas); }else{ canvas.translate(scrollX, scrollY); background.draw(canvas); canvas.translate(-scrollX,-scrollY); } } /** * Sets the correct background bounds and rebuilds the outline, if needed. * <p/> * This is called by LayoutLib. */ void setBackgroundBounds(){ if(mBackgroundSizeChanged && mBackground !=null){ mBackground.setBounds(0,0, mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged =false; rebuildOutline(); } } privatevoid setBackgroundRenderNodeProperties(RenderNode renderNode){ renderNode.setTranslationX(mScrollX); renderNode.setTranslationY(mScrollY); } /** * Creates a new display list or updates the existing display list for the * specified Drawable. * * @param drawable Drawable for which to create a display list * @param renderNode Existing RenderNode, or {@code null} * @return A valid display list for the specified drawable */ privateRenderNode getDrawableRenderNode(Drawable drawable,RenderNode renderNode){ if(renderNode ==null){ renderNode =RenderNode.create(drawable.getClass().getName(), newViewAnimationHostBridge(this)); renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND); } finalRect bounds = drawable.getBounds(); finalint width = bounds.width(); finalint height = bounds.height(); // Hacky hack: Reset any stretch effects as those are applied during the draw pass // instead of being "stateful" like other RenderNode properties renderNode.clearStretch(); finalRecordingCanvas canvas = renderNode.beginRecording(width, height); // Reverse left/top translation done by drawable canvas, which will // instead be applied by rendernode's LTRB bounds below. This way, the // drawable's bounds match with its rendernode bounds and its content // will lie within those bounds in the rendernode tree. canvas.translate(-bounds.left,-bounds.top); try{ drawable.draw(canvas); }finally{ renderNode.endRecording(); } // Set up drawable properties that are view-independent. renderNode.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom); renderNode.setProjectBackwards(drawable.isProjected()); renderNode.setProjectionReceiver(true); renderNode.setClipToBounds(false); return renderNode; } /** * Returns the overlay for this view, creating it if it does not yet exist. * Adding drawables to the overlay will cause them to be displayed whenever * the view itself is redrawn. Objects in the overlay should be actively * managed: remove them when they should not be displayed anymore. The * overlay will always have the same size as its host view. * * <p>Note: Overlays do not currently work correctly with {@link * SurfaceView} or {@link TextureView}; contents in overlays for these * types of views may not display correctly.</p> * * @return The ViewOverlay object for this view. * @see ViewOverlay */ publicViewOverlay getOverlay(){ if(mOverlay ==null){ mOverlay =newViewOverlay(mContext,this); } return mOverlay; } /** * Override this if your view is known to always be drawn on top of a solid color background, * and needs to draw fading edges. Returning a non-zero color enables the view system to * optimize the drawing of the fading edges. If you do return a non-zero color, the alpha * should be set to 0xFF. * * @see #setVerticalFadingEdgeEnabled(boolean) * @see #setHorizontalFadingEdgeEnabled(boolean) * * @return The known solid color background for this view, or 0 if the color may vary */ @ViewDebug.ExportedProperty(category ="drawing") @InspectableProperty @ColorInt publicint getSolidColor(){ return0; } /** * Build a human readable string representation of the specified view flags. * * @param flags the view flags to convert to a string * @return a String representing the supplied flags */ privatestaticString printFlags(int flags){ String output =""; int numFlags =0; if((flags & FOCUSABLE)== FOCUSABLE){ output +="TAKES_FOCUS"; numFlags++; } switch(flags & VISIBILITY_MASK){ case INVISIBLE: if(numFlags >0){ output +=" "; } output +="INVISIBLE"; // USELESS HERE numFlags++; break; case GONE: if(numFlags >0){ output +=" "; } output +="GONE"; // USELESS HERE numFlags++; break; default: break; } return output; } /** * Build a human readable string representation of the specified private * view flags. * * @param privateFlags the private view flags to convert to a string * @return a String representing the supplied flags */ privatestaticString printPrivateFlags(int privateFlags){ String output =""; int numFlags =0; if((privateFlags & PFLAG_WANTS_FOCUS)== PFLAG_WANTS_FOCUS){ output +="WANTS_FOCUS"; numFlags++; } if((privateFlags & PFLAG_FOCUSED)== PFLAG_FOCUSED){ if(numFlags >0){ output +=" "; } output +="FOCUSED"; numFlags++; } if((privateFlags & PFLAG_SELECTED)== PFLAG_SELECTED){ if(numFlags >0){ output +=" "; } output +="SELECTED"; numFlags++; } if((privateFlags & PFLAG_IS_ROOT_NAMESPACE)== PFLAG_IS_ROOT_NAMESPACE){ if(numFlags >0){ output +=" "; } output +="IS_ROOT_NAMESPACE"; numFlags++; } if((privateFlags & PFLAG_HAS_BOUNDS)== PFLAG_HAS_BOUNDS){ if(numFlags >0){ output +=" "; } output +="HAS_BOUNDS"; numFlags++; } if((privateFlags & PFLAG_DRAWN)== PFLAG_DRAWN){ if(numFlags >0){ output +=" "; } output +="DRAWN"; // USELESS HERE numFlags++; } return output; } /** * <p>Indicates whether or not this view's layout will be requested during * the next hierarchy layout pass.</p> * * @return true if the layout will be forced during next layout pass */ publicboolean isLayoutRequested(){ return(mPrivateFlags & PFLAG_FORCE_LAYOUT)== PFLAG_FORCE_LAYOUT; } /** * Return true if o is a ViewGroup that is laying out using optical bounds. * @hide */ publicstaticboolean isLayoutModeOptical(Object o){ return o instanceofViewGroup&&((ViewGroup) o).isLayoutModeOptical(); } privateboolean setOpticalFrame(int left,int top,int right,int bottom){ Insets parentInsets = mParent instanceofView? ((View) mParent).getOpticalInsets():Insets.NONE; Insets childInsets = getOpticalInsets(); return setFrame( left + parentInsets.left - childInsets.left, top + parentInsets.top - childInsets.top, right + parentInsets.left + childInsets.right, bottom + parentInsets.top + childInsets.bottom); } /** * Assign a size and position to a view and all of its * descendants * * <p>This is the second phase of the layout mechanism. * (The first is measuring). In this phase, each parent calls * layout on all of its children to position them. * This is typically done using the child measurements * that were stored in the measure pass().</p> * * <p>Derived classes should not override this method. * Derived classes with children should override * onLayout. In that method, they should * call layout on each of their children.</p> * * @param l Left position, relative to parent * @param t Top position, relative to parent * @param r Right position, relative to parent * @param b Bottom position, relative to parent */ @SuppressWarnings({"unchecked"}) publicvoid layout(int l,int t,int r,int b){ if((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT)!=0){ onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; boolean changed = isLayoutModeOptical(mParent)? setOpticalFrame(l, t, r, b): setFrame(l, t, r, b); if(changed ||(mPrivateFlags & PFLAG_LAYOUT_REQUIRED)== PFLAG_LAYOUT_REQUIRED){ onLayout(changed, l, t, r, b); if(shouldDrawRoundScrollbar()){ if(mRoundScrollbarRenderer ==null){ mRoundScrollbarRenderer =newRoundScrollbarRenderer(this); } }else{ mRoundScrollbarRenderer =null; } mPrivateFlags &=~PFLAG_LAYOUT_REQUIRED; ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnLayoutChangeListeners !=null){ ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for(int i =0; i < numListeners;++i){ listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } finalboolean wasLayoutValid = isLayoutValid(); mPrivateFlags &=~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; if(!wasLayoutValid && isFocused()){ mPrivateFlags &=~PFLAG_WANTS_FOCUS; if(canTakeFocus()){ // We have a robust focus, so parents should no longer be wanting focus. clearParentsWantFocus(); }elseif(getViewRootImpl()==null||!getViewRootImpl().isInLayout()){ // This is a weird case. Most-likely the user, rather than ViewRootImpl, called // layout. In this case, there's no guarantee that parent layouts will be evaluated // and thus the safest action is to clear focus here. clearFocusInternal(null,/* propagate */true,/* refocus */false); clearParentsWantFocus(); }elseif(!hasParentWantsFocus()){ // original requestFocus was likely on this view directly, so just clear focus clearFocusInternal(null,/* propagate */true,/* refocus */false); } // otherwise, we let parents handle re-assigning focus during their layout passes. }elseif((mPrivateFlags & PFLAG_WANTS_FOCUS)!=0){ mPrivateFlags &=~PFLAG_WANTS_FOCUS; View focused = findFocus(); if(focused !=null){ // Try to restore focus as close as possible to our starting focus. if(!restoreDefaultFocus()&&!hasParentWantsFocus()){ // Give up and clear focus once we've reached the top-most parent which wants // focus. focused.clearFocusInternal(null,/* propagate */true,/* refocus */false); } } } if((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT)!=0){ mPrivateFlags3 &=~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; notifyEnterOrExitForAutoFillIfNeeded(true); } notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } privateboolean hasParentWantsFocus(){ ViewParent parent = mParent; while(parent instanceofViewGroup){ ViewGroup pv =(ViewGroup) parent; if((pv.mPrivateFlags & PFLAG_WANTS_FOCUS)!=0){ returntrue; } parent = pv.mParent; } returnfalse; } /** * Called from layout when this view should * assign a size and position to each of its children. * * Derived classes with children should override * this method and call layout on each of * their children. * @param changed This is a new size or position for this view * @param left Left position, relative to parent * @param top Top position, relative to parent * @param right Right position, relative to parent * @param bottom Bottom position, relative to parent */ protectedvoid onLayout(boolean changed,int left,int top,int right,int bottom){ } /** * Assign a size and position to this view. * * This is called from layout. * * @param left Left position, relative to parent * @param top Top position, relative to parent * @param right Right position, relative to parent * @param bottom Bottom position, relative to parent * @return true if the new size and position are different than the * previous ones * {@hide} */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P) protectedboolean setFrame(int left,int top,int right,int bottom){ boolean changed =false; if(DBG){ Log.d(VIEW_LOG_TAG,this+" View.setFrame("+ left +","+ top +"," + right +","+ bottom +")"); } if(mLeft != left || mRight != right || mTop != top || mBottom != bottom){ changed =true; // Remember our drawn bit int drawn = mPrivateFlags & PFLAG_DRAWN; int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; int newWidth = right - left; int newHeight = bottom - top; boolean sizeChanged =(newWidth != oldWidth)||(newHeight != oldHeight); // Invalidate our old position invalidate(sizeChanged); mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); mPrivateFlags |= PFLAG_HAS_BOUNDS; if(sizeChanged){ sizeChange(newWidth, newHeight, oldWidth, oldHeight); } if((mViewFlags & VISIBILITY_MASK)== VISIBLE || mGhostView !=null){ // If we are visible, force the DRAWN bit to on so that // this invalidate will go through (at least to our parent). // This is because someone may have invalidated this view // before this call to setFrame came in, thereby clearing // the DRAWN bit. mPrivateFlags |= PFLAG_DRAWN; invalidate(sizeChanged); // parent display list may need to be recreated based on a change in the bounds // of any child invalidateParentCaches(); } // Reset drawn bit to original value (invalidate turns it off) mPrivateFlags |= drawn; mBackgroundSizeChanged =true; mDefaultFocusHighlightSizeChanged =true; if(mForegroundInfo !=null){ mForegroundInfo.mBoundsChanged =true; } notifySubtreeAccessibilityStateChangedIfNeeded(); } return changed; } /** * Assign a size and position to this view. * * This method is meant to be used in animations only as it applies this position and size * for the view only temporary and it can be changed back at any time by the layout. * * @param left Left position, relative to parent * @param top Top position, relative to parent * @param right Right position, relative to parent * @param bottom Bottom position, relative to parent * * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int) */ publicfinalvoid setLeftTopRightBottom(int left,int top,int right,int bottom){ setFrame(left, top, right, bottom); } privatevoid sizeChange(int newWidth,int newHeight,int oldWidth,int oldHeight){ onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); if(mOverlay !=null){ mOverlay.getOverlayView().setRight(newWidth); mOverlay.getOverlayView().setBottom(newHeight); } // If this isn't laid out yet, focus assignment will be handled during the "deferment/ // backtracking" of requestFocus during layout, so don't touch focus here. if(!sCanFocusZeroSized && isLayoutValid() // Don't touch focus if animating &&!(mParent instanceofViewGroup&&((ViewGroup) mParent).isLayoutSuppressed())){ if(newWidth <=0|| newHeight <=0){ if(hasFocus()){ clearFocus(); if(mParent instanceofViewGroup){ ((ViewGroup) mParent).clearFocusedInCluster(); } } clearAccessibilityFocus(); }elseif(oldWidth <=0|| oldHeight <=0){ if(mParent !=null&& canTakeFocus()){ mParent.focusableViewAvailable(this); } } } rebuildOutline(); } /** * Finalize inflating a view from XML. This is called as the last phase * of inflation, after all child views have been added. * * <p>Even if the subclass overrides onFinishInflate, they should always be * sure to call the super method, so that we get called. */ @CallSuper protectedvoid onFinishInflate(){ } /** * Returns the resources associated with this view. * * @return Resources object. */ publicResources getResources(){ return mResources; } /** * Invalidates the specified Drawable. * * @param drawable the drawable to invalidate */ @Override publicvoid invalidateDrawable(@NonNullDrawable drawable){ if(verifyDrawable(drawable)){ finalRect dirty = drawable.getDirtyBounds(); finalint scrollX = mScrollX; finalint scrollY = mScrollY; invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); rebuildOutline(); } } /** * Schedules an action on a drawable to occur at a specified time. * * @param who the recipient of the action * @param what the action to run on the drawable * @param when the time at which the action must occur. Uses the * {@link SystemClock#uptimeMillis} timebase. */ @Override publicvoid scheduleDrawable(@NonNullDrawable who,@NonNullRunnable what,long when){ if(verifyDrawable(who)&& what !=null){ finallong delay = when -SystemClock.uptimeMillis(); if(mAttachInfo !=null){ mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed( Choreographer.CALLBACK_ANIMATION, what, who, Choreographer.subtractFrameDelay(delay)); }else{ // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().postDelayed(what, delay); } } } /** * Cancels a scheduled action on a drawable. * * @param who the recipient of the action * @param what the action to cancel */ @Override publicvoid unscheduleDrawable(@NonNullDrawable who,@NonNullRunnable what){ if(verifyDrawable(who)&& what !=null){ if(mAttachInfo !=null){ mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION, what, who); } getRunQueue().removeCallbacks(what); } } /** * Unschedule any events associated with the given Drawable. This can be * used when selecting a new Drawable into a view, so that the previous * one is completely unscheduled. * * @param who The Drawable to unschedule. * * @see #drawableStateChanged */ publicvoid unscheduleDrawable(Drawable who){ if(mAttachInfo !=null&& who !=null){ mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION,null, who); } } /** * Resolve the Drawables depending on the layout direction. This is implicitly supposing * that the View directionality can and will be resolved before its Drawables. * * Will call {@link View#onResolveDrawables} when resolution is done. * * @hide */ protectedvoid resolveDrawables(){ // Drawables resolution may need to happen before resolving the layout direction (which is // done only during the measure() call). // If the layout direction is not resolved yet, we cannot resolve the Drawables except in // one case: when the raw layout direction has not been defined as LAYOUT_DIRECTION_INHERIT. // So, if the raw layout direction is LAYOUT_DIRECTION_LTR or LAYOUT_DIRECTION_RTL or // LAYOUT_DIRECTION_LOCALE, we can "cheat" and we don't need to wait for the layout // direction to be resolved as its resolved value will be the same as its raw value. if(!isLayoutDirectionResolved()&& getRawLayoutDirection()==View.LAYOUT_DIRECTION_INHERIT){ return; } finalint layoutDirection = isLayoutDirectionResolved()? getLayoutDirection(): getRawLayoutDirection(); if(mBackground !=null){ mBackground.setLayoutDirection(layoutDirection); } if(mForegroundInfo !=null&& mForegroundInfo.mDrawable !=null){ mForegroundInfo.mDrawable.setLayoutDirection(layoutDirection); } if(mDefaultFocusHighlight !=null){ mDefaultFocusHighlight.setLayoutDirection(layoutDirection); } mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED; onResolveDrawables(layoutDirection); } boolean areDrawablesResolved(){ return(mPrivateFlags2 & PFLAG2_DRAWABLE_RESOLVED)== PFLAG2_DRAWABLE_RESOLVED; } /** * Called when layout direction has been resolved. * * The default implementation does nothing. * * @param layoutDirection The resolved layout direction. * * @see #LAYOUT_DIRECTION_LTR * @see #LAYOUT_DIRECTION_RTL * * @hide */ publicvoid onResolveDrawables(@ResolvedLayoutDirint layoutDirection){ } /** * @hide */ @TestApi protectedvoid resetResolvedDrawables(){ resetResolvedDrawablesInternal(); } void resetResolvedDrawablesInternal(){ mPrivateFlags2 &=~PFLAG2_DRAWABLE_RESOLVED; } /** * If your view subclass is displaying its own Drawable objects, it should * override this function and return true for any Drawable it is * displaying. This allows animations for those drawables to be * scheduled. * * <p>Be sure to call through to the super class when overriding this * function. * * @param who The Drawable to verify. Return true if it is one you are * displaying, else return the result of calling through to the * super class. * * @return boolean If true then the Drawable is being displayed in the * view; else false and it is not allowed to animate. * * @see #unscheduleDrawable(android.graphics.drawable.Drawable) * @see #drawableStateChanged() */ @CallSuper protectedboolean verifyDrawable(@NonNullDrawable who){ // Avoid verifying the scroll bar drawable so that we don't end up in // an invalidation loop. This effectively prevents the scroll bar // drawable from triggering invalidations and scheduling runnables. return who == mBackground ||(mForegroundInfo !=null&& mForegroundInfo.mDrawable == who) ||(mDefaultFocusHighlight == who); } /** * This function is called whenever the state of the view changes in such * a way that it impacts the state of drawables being shown. * <p> * If the View has a StateListAnimator, it will also be called to run necessary state * change animations. * <p> * Be sure to call through to the superclass when overriding this function. * * @see Drawable#setState(int[]) */ @CallSuper protectedvoid drawableStateChanged(){ finalint[] state = getDrawableState(); boolean changed =false; finalDrawable bg = mBackground; if(bg !=null&& bg.isStateful()){ changed |= bg.setState(state); } finalDrawable hl = mDefaultFocusHighlight; if(hl !=null&& hl.isStateful()){ changed |= hl.setState(state); } finalDrawable fg = mForegroundInfo !=null? mForegroundInfo.mDrawable :null; if(fg !=null&& fg.isStateful()){ changed |= fg.setState(state); } if(mScrollCache !=null){ finalDrawable scrollBar = mScrollCache.scrollBar; if(scrollBar !=null&& scrollBar.isStateful()){ changed |= scrollBar.setState(state) && mScrollCache.state !=ScrollabilityCache.OFF; } } if(mStateListAnimator !=null){ mStateListAnimator.setState(state); } if(!isAggregatedVisible()){ // If we're not visible, skip any animated changes jumpDrawablesToCurrentState(); } if(changed){ invalidate(); } } /** * This function is called whenever the view hotspot changes and needs to * be propagated to drawables or child views managed by the view. * <p> * Dispatching to child views is handled by * {@link #dispatchDrawableHotspotChanged(float, float)}. * <p> * Be sure to call through to the superclass when overriding this function. * * @param x hotspot x coordinate * @param y hotspot y coordinate */ @CallSuper publicvoid drawableHotspotChanged(float x,float y){ if(mBackground !=null){ mBackground.setHotspot(x, y); } if(mDefaultFocusHighlight !=null){ mDefaultFocusHighlight.setHotspot(x, y); } if(mForegroundInfo !=null&& mForegroundInfo.mDrawable !=null){ mForegroundInfo.mDrawable.setHotspot(x, y); } dispatchDrawableHotspotChanged(x, y); } /** * Dispatches drawableHotspotChanged to all of this View's children. * * @param x hotspot x coordinate * @param y hotspot y coordinate * @see #drawableHotspotChanged(float, float) */ publicvoid dispatchDrawableHotspotChanged(float x,float y){ } /** * Call this to force a view to update its drawable state. This will cause * drawableStateChanged to be called on this view. Views that are interested * in the new state should call getDrawableState. * * @see #drawableStateChanged * @see #getDrawableState */ publicvoid refreshDrawableState(){ mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY; drawableStateChanged(); ViewParent parent = mParent; if(parent !=null){ parent.childDrawableStateChanged(this); } } /** * Create a default focus highlight if it doesn't exist. * @return a default focus highlight. */ privateDrawable getDefaultFocusHighlightDrawable(){ if(mDefaultFocusHighlightCache ==null){ if(mContext !=null){ finalint[] attrs =newint[]{ android.R.attr.selectableItemBackground }; finalTypedArray ta = mContext.obtainStyledAttributes(attrs); mDefaultFocusHighlightCache = ta.getDrawable(0); ta.recycle(); } } return mDefaultFocusHighlightCache; } /** * Set the current default focus highlight. * @param highlight the highlight drawable, or {@code null} if it's no longer needed. */ privatevoid setDefaultFocusHighlight(Drawable highlight){ mDefaultFocusHighlight = highlight; mDefaultFocusHighlightSizeChanged =true; if(highlight !=null){ if((mPrivateFlags & PFLAG_SKIP_DRAW)!=0){ mPrivateFlags &=~PFLAG_SKIP_DRAW; } highlight.setLayoutDirection(getLayoutDirection()); if(highlight.isStateful()){ highlight.setState(getDrawableState()); } if(isAttachedToWindow()){ highlight.setVisible(getWindowVisibility()== VISIBLE && isShown(),false); } // Set callback last, since the view may still be initializing. highlight.setCallback(this); }elseif((mViewFlags & WILL_NOT_DRAW)!=0&& mBackground ==null &&(mForegroundInfo ==null|| mForegroundInfo.mDrawable ==null)){ mPrivateFlags |= PFLAG_SKIP_DRAW; } invalidate(); } /** * Check whether we need to draw a default focus highlight when this view gets focused, * which requires: * <ul> * <li>In both background and foreground, {@link android.R.attr#state_focused} * is not defined.</li> * <li>This view is not in touch mode.</li> * <li>This view doesn't opt out for a default focus highlight, via * {@link #setDefaultFocusHighlightEnabled(boolean)}.</li> * <li>This view is attached to window.</li> * </ul> * @return {@code true} if a default focus highlight is needed. * @hide */ @TestApi publicboolean isDefaultFocusHighlightNeeded(Drawable background,Drawable foreground){ finalboolean lackFocusState =(background ==null||!background.isStateful() ||!background.hasFocusStateSpecified()) &&(foreground ==null||!foreground.isStateful() ||!foreground.hasFocusStateSpecified()); return!isInTouchMode()&& getDefaultFocusHighlightEnabled()&& lackFocusState && isAttachedToWindow()&& sUseDefaultFocusHighlight; } /** * When this view is focused, switches on/off the default focused highlight. * <p> * This always happens when this view is focused, and only at this moment the default focus * highlight can be visible. */ privatevoid switchDefaultFocusHighlight(){ if(isFocused()){ finalboolean needed = isDefaultFocusHighlightNeeded(mBackground, mForegroundInfo ==null?null: mForegroundInfo.mDrawable); finalboolean active = mDefaultFocusHighlight !=null; if(needed &&!active){ setDefaultFocusHighlight(getDefaultFocusHighlightDrawable()); }elseif(!needed && active){ // The highlight is no longer needed, so tear it down. setDefaultFocusHighlight(null); } } } /** * Draw the default focus highlight onto the canvas if there is one and this view is focused. * @param canvas the canvas where we're drawing the highlight. */ privatevoid drawDefaultFocusHighlight(Canvas canvas){ if(mDefaultFocusHighlight !=null&& isFocused()){ if(mDefaultFocusHighlightSizeChanged){ mDefaultFocusHighlightSizeChanged =false; finalint l = mScrollX; finalint r = l + mRight - mLeft; finalint t = mScrollY; finalint b = t + mBottom - mTop; mDefaultFocusHighlight.setBounds(l, t, r, b); } mDefaultFocusHighlight.draw(canvas); } } /** * Return an array of resource IDs of the drawable states representing the * current state of the view. * * @return The current drawable state * * @see Drawable#setState(int[]) * @see #drawableStateChanged() * @see #onCreateDrawableState(int) */ publicfinalint[] getDrawableState(){ if((mDrawableState !=null)&&((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY)==0)){ return mDrawableState; }else{ mDrawableState = onCreateDrawableState(0); mPrivateFlags &=~PFLAG_DRAWABLE_STATE_DIRTY; return mDrawableState; } } /** * Generate the new {@link android.graphics.drawable.Drawable} state for * this view. This is called by the view * system when the cached Drawable state is determined to be invalid. To * retrieve the current state, you should use {@link #getDrawableState}. * * @param extraSpace if non-zero, this is the number of extra entries you * would like in the returned array in which you can place your own * states. * * @return Returns an array holding the current {@link Drawable} state of * the view. * * @see #mergeDrawableStates(int[], int[]) */ protectedint[] onCreateDrawableState(int extraSpace){ if((mViewFlags & DUPLICATE_PARENT_STATE)== DUPLICATE_PARENT_STATE && mParent instanceofView){ return((View) mParent).onCreateDrawableState(extraSpace); } int[] drawableState; int privateFlags = mPrivateFlags; int viewStateIndex =0; if((privateFlags & PFLAG_PRESSED)!=0) viewStateIndex |=StateSet.VIEW_STATE_PRESSED; if((mViewFlags & ENABLED_MASK)== ENABLED) viewStateIndex |=StateSet.VIEW_STATE_ENABLED; if(isFocused()) viewStateIndex |=StateSet.VIEW_STATE_FOCUSED; if((privateFlags & PFLAG_SELECTED)!=0) viewStateIndex |=StateSet.VIEW_STATE_SELECTED; if(hasWindowFocus()) viewStateIndex |=StateSet.VIEW_STATE_WINDOW_FOCUSED; if((privateFlags & PFLAG_ACTIVATED)!=0) viewStateIndex |=StateSet.VIEW_STATE_ACTIVATED; if(mAttachInfo !=null&& mAttachInfo.mHardwareAccelerationRequested){ // This is set if HW acceleration is requested, even if the current // process doesn't allow it. This is just to allow app preview // windows to better match their app. viewStateIndex |=StateSet.VIEW_STATE_ACCELERATED; } if((privateFlags & PFLAG_HOVERED)!=0) viewStateIndex |=StateSet.VIEW_STATE_HOVERED; finalint privateFlags2 = mPrivateFlags2; if((privateFlags2 & PFLAG2_DRAG_CAN_ACCEPT)!=0){ viewStateIndex |=StateSet.VIEW_STATE_DRAG_CAN_ACCEPT; } if((privateFlags2 & PFLAG2_DRAG_HOVERED)!=0){ viewStateIndex |=StateSet.VIEW_STATE_DRAG_HOVERED; } drawableState =StateSet.get(viewStateIndex); //noinspection ConstantIfStatement if(false){ Log.i("View","drawableStateIndex="+ viewStateIndex); Log.i("View", toString() +" pressed="+((privateFlags & PFLAG_PRESSED)!=0) +" en="+((mViewFlags & ENABLED_MASK)== ENABLED) +" fo="+ hasFocus() +" sl="+((privateFlags & PFLAG_SELECTED)!=0) +" wf="+ hasWindowFocus() +": "+Arrays.toString(drawableState)); } if(extraSpace ==0){ return drawableState; } finalint[] fullState; if(drawableState !=null){ fullState =newint[drawableState.length + extraSpace]; System.arraycopy(drawableState,0, fullState,0, drawableState.length); }else{ fullState =newint[extraSpace]; } return fullState; } /** * Merge your own state values in <var>additionalState</var> into the base * state values <var>baseState</var> that were returned by * {@link #onCreateDrawableState(int)}. * * @param baseState The base state values returned by * {@link #onCreateDrawableState(int)}, which will be modified to also hold your * own additional state values. * * @param additionalState The additional state values you would like * added to <var>baseState</var>; this array is not modified. * * @return As a convenience, the <var>baseState</var> array you originally * passed into the function is returned. * * @see #onCreateDrawableState(int) */ protectedstaticint[] mergeDrawableStates(int[] baseState,int[] additionalState){ finalint N = baseState.length; int i = N -1; while(i >=0&& baseState[i]==0){ i--; } System.arraycopy(additionalState,0, baseState, i +1, additionalState.length); return baseState; } /** * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()} * on all Drawable objects associated with this view. * <p> * Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator * attached to this view. */ @CallSuper publicvoid jumpDrawablesToCurrentState(){ if(mBackground !=null){ mBackground.jumpToCurrentState(); } if(mStateListAnimator !=null){ mStateListAnimator.jumpToCurrentState(); } if(mDefaultFocusHighlight !=null){ mDefaultFocusHighlight.jumpToCurrentState(); } if(mForegroundInfo !=null&& mForegroundInfo.mDrawable !=null){ mForegroundInfo.mDrawable.jumpToCurrentState(); } } /** * Sets the background color for this view. * @param color the color of the background */ @RemotableViewMethod publicvoid setBackgroundColor(@ColorIntint color){ if(mBackground instanceofColorDrawable){ ((ColorDrawable) mBackground.mutate()).setColor(color); computeOpaqueFlags(); mBackgroundResource =0; }else{ setBackground(newColorDrawable(color)); } } /** * Set the background to a given resource. The resource should refer to * a Drawable object or 0 to remove the background. * @param resid The identifier of the resource. * * @attr ref android.R.styleable#View_background */ @RemotableViewMethod publicvoid setBackgroundResource(@DrawableResint resid){ if(resid !=0&& resid == mBackgroundResource){ return; } Drawable d =null; if(resid !=0){ d = mContext.getDrawable(resid); } setBackground(d); mBackgroundResource = resid; } /** * Set the background to a given Drawable, or remove the background. If the * background has padding, this View's padding is set to the background's * padding. However, when a background is removed, this View's padding isn't * touched. If setting the padding is desired, please use * {@link #setPadding(int, int, int, int)}. * * @param background The Drawable to use as the background, or null to remove the * background */ publicvoid setBackground(Drawable background){ //noinspection deprecation setBackgroundDrawable(background); } /** * @deprecated use {@link #setBackground(Drawable)} instead */ @Deprecated publicvoid setBackgroundDrawable(Drawable background){ computeOpaqueFlags(); if(background == mBackground){ return; } boolean requestLayout =false; mBackgroundResource =0; /* * Regardless of whether we're setting a new background or not, we want * to clear the previous drawable. setVisible first while we still have the callback set. */ if(mBackground !=null){ if(isAttachedToWindow()){ mBackground.setVisible(false,false); } mBackground.setCallback(null); unscheduleDrawable(mBackground); } if(background !=null){ Rect padding = sThreadLocal.get(); if(padding ==null){ padding =newRect(); sThreadLocal.set(padding); } resetResolvedDrawablesInternal(); background.setLayoutDirection(getLayoutDirection()); if(background.getPadding(padding)){ resetResolvedPaddingInternal(); switch(background.getLayoutDirection()){ case LAYOUT_DIRECTION_RTL: mUserPaddingLeftInitial = padding.right; mUserPaddingRightInitial = padding.left; internalSetPadding(padding.right, padding.top, padding.left, padding.bottom); break; case LAYOUT_DIRECTION_LTR: default: mUserPaddingLeftInitial = padding.left; mUserPaddingRightInitial = padding.right; internalSetPadding(padding.left, padding.top, padding.right, padding.bottom); } mLeftPaddingDefined =false; mRightPaddingDefined =false; } // Compare the minimum sizes of the old Drawable and the new. If there isn't an old or // if it has a different minimum size, we should layout again if(mBackground ==null || mBackground.getMinimumHeight()!= background.getMinimumHeight() || mBackground.getMinimumWidth()!= background.getMinimumWidth()){ requestLayout =true; } // Set mBackground before we set this as the callback and start making other // background drawable state change calls. In particular, the setVisible call below // can result in drawables attempting to start animations or otherwise invalidate, // which requires the view set as the callback (us) to recognize the drawable as // belonging to it as per verifyDrawable. mBackground = background; if(background.isStateful()){ background.setState(getDrawableState()); } if(isAttachedToWindow()){ background.setVisible(getWindowVisibility()== VISIBLE && isShown(),false); } applyBackgroundTint(); // Set callback last, since the view may still be initializing. background.setCallback(this); if((mPrivateFlags & PFLAG_SKIP_DRAW)!=0){ mPrivateFlags &=~PFLAG_SKIP_DRAW; requestLayout =true; } }else{ /* Remove the background */ mBackground =null; if((mViewFlags & WILL_NOT_DRAW)!=0 &&(mDefaultFocusHighlight ==null) &&(mForegroundInfo ==null|| mForegroundInfo.mDrawable ==null)){ mPrivateFlags |= PFLAG_SKIP_DRAW; } /* * When the background is set, we try to apply its padding to this * View. When the background is removed, we don't touch this View's * padding. This is noted in the Javadocs. Hence, we don't need to * requestLayout(), the invalidate() below is sufficient. */ // The old background's minimum size could have affected this // View's layout, so let's requestLayout requestLayout =true; } computeOpaqueFlags(); if(requestLayout){ requestLayout(); } mBackgroundSizeChanged =true; invalidate(true); invalidateOutline(); } /** * Gets the background drawable * * @return The drawable used as the background for this view, if any. * * @see #setBackground(Drawable) * * @attr ref android.R.styleable#View_background */ @InspectableProperty publicDrawable getBackground(){ return mBackground; } /** * Applies a tint to the background drawable. Does not modify the current tint * mode, which is {@link BlendMode#SRC_IN} by default. * <p> * Subsequent calls to {@link #setBackground(Drawable)} will automatically * mutate the drawable and apply the specified tint and tint mode using * {@link Drawable#setTintList(ColorStateList)}. * * @param tint the tint to apply, may be {@code null} to clear tint * * @attr ref android.R.styleable#View_backgroundTint * @see #getBackgroundTintList() * @see Drawable#setTintList(ColorStateList) */ @RemotableViewMethod publicvoid setBackgroundTintList(@NullableColorStateList tint){ if(mBackgroundTint ==null){ mBackgroundTint =newTintInfo(); } mBackgroundTint.mTintList = tint; mBackgroundTint.mHasTintList =true; applyBackgroundTint(); } /** * Return the tint applied to the background drawable, if specified. * * @return the tint applied to the background drawable * @attr ref android.R.styleable#View_backgroundTint * @see #setBackgroundTintList(ColorStateList) */ @InspectableProperty(name ="backgroundTint") @Nullable publicColorStateList getBackgroundTintList(){ return mBackgroundTint !=null? mBackgroundTint.mTintList :null; } /** * Specifies the blending mode used to apply the tint specified by * {@link #setBackgroundTintList(ColorStateList)}} to the background * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. * * @param tintMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#View_backgroundTintMode * @see #getBackgroundTintMode() * @see Drawable#setTintMode(PorterDuff.Mode) */ publicvoid setBackgroundTintMode(@NullablePorterDuff.Mode tintMode){ BlendMode mode =null; if(tintMode !=null){ mode =BlendMode.fromValue(tintMode.nativeInt); } setBackgroundTintBlendMode(mode); } /** * Specifies the blending mode used to apply the tint specified by * {@link #setBackgroundTintList(ColorStateList)}} to the background * drawable. The default mode is {@link BlendMode#SRC_IN}. * * @param blendMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#View_backgroundTintMode * @see #getBackgroundTintMode() * @see Drawable#setTintBlendMode(BlendMode) */ @RemotableViewMethod publicvoid setBackgroundTintBlendMode(@NullableBlendMode blendMode){ if(mBackgroundTint ==null){ mBackgroundTint =newTintInfo(); } mBackgroundTint.mBlendMode = blendMode; mBackgroundTint.mHasTintMode =true; applyBackgroundTint(); } /** * Return the blending mode used to apply the tint to the background * drawable, if specified. * * @return the blending mode used to apply the tint to the background * drawable * @attr ref android.R.styleable#View_backgroundTintMode * @see #setBackgroundTintBlendMode(BlendMode) * */ @Nullable @InspectableProperty publicPorterDuff.Mode getBackgroundTintMode(){ PorterDuff.Mode porterDuffMode; if(mBackgroundTint !=null&& mBackgroundTint.mBlendMode !=null){ porterDuffMode =BlendMode.blendModeToPorterDuffMode(mBackgroundTint.mBlendMode); }else{ porterDuffMode =null; } return porterDuffMode; } /** * Return the blending mode used to apply the tint to the background * drawable, if specified. * * @return the blending mode used to apply the tint to the background * drawable, null if no blend has previously been configured * @attr ref android.R.styleable#View_backgroundTintMode * @see #setBackgroundTintBlendMode(BlendMode) */ public@NullableBlendMode getBackgroundTintBlendMode(){ return mBackgroundTint !=null? mBackgroundTint.mBlendMode :null; } privatevoid applyBackgroundTint(){ if(mBackground !=null&& mBackgroundTint !=null){ finalTintInfo tintInfo = mBackgroundTint; if(tintInfo.mHasTintList || tintInfo.mHasTintMode){ mBackground = mBackground.mutate(); if(tintInfo.mHasTintList){ mBackground.setTintList(tintInfo.mTintList); } if(tintInfo.mHasTintMode){ mBackground.setTintBlendMode(tintInfo.mBlendMode); } // The drawable (or one of its children) may not have been // stateful before applying the tint, so let's try again. if(mBackground.isStateful()){ mBackground.setState(getDrawableState()); } } } } /** * Returns the drawable used as the foreground of this View. The * foreground drawable, if non-null, is always drawn on top of the view's content. * * @return a Drawable or null if no foreground was set * * @see #onDrawForeground(Canvas) */ @InspectableProperty publicDrawable getForeground(){ return mForegroundInfo !=null? mForegroundInfo.mDrawable :null; } /** * Supply a Drawable that is to be rendered on top of all of the content in the view. * * @param foreground the Drawable to be drawn on top of the children * * @attr ref android.R.styleable#View_foreground */ publicvoid setForeground(Drawable foreground){ if(mForegroundInfo ==null){ if(foreground ==null){ // Nothing to do. return; } mForegroundInfo =newForegroundInfo(); } if(foreground == mForegroundInfo.mDrawable){ // Nothing to do return; } if(mForegroundInfo.mDrawable !=null){ if(isAttachedToWindow()){ mForegroundInfo.mDrawable.setVisible(false,false); } mForegroundInfo.mDrawable.setCallback(null); unscheduleDrawable(mForegroundInfo.mDrawable); } mForegroundInfo.mDrawable = foreground; mForegroundInfo.mBoundsChanged =true; if(foreground !=null){ if((mPrivateFlags & PFLAG_SKIP_DRAW)!=0){ mPrivateFlags &=~PFLAG_SKIP_DRAW; } foreground.setLayoutDirection(getLayoutDirection()); if(foreground.isStateful()){ foreground.setState(getDrawableState()); } applyForegroundTint(); if(isAttachedToWindow()){ foreground.setVisible(getWindowVisibility()== VISIBLE && isShown(),false); } // Set callback last, since the view may still be initializing. foreground.setCallback(this); }elseif((mViewFlags & WILL_NOT_DRAW)!=0&& mBackground ==null &&(mDefaultFocusHighlight ==null)){ mPrivateFlags |= PFLAG_SKIP_DRAW; } requestLayout(); invalidate(); } /** * Magic bit used to support features of framework-internal window decor implementation details. * This used to live exclusively in FrameLayout. * * @return true if the foreground should draw inside the padding region or false * if it should draw inset by the view's padding * @hide internal use only; only used by FrameLayout and internal screen layouts. */ publicboolean isForegroundInsidePadding(){ return mForegroundInfo !=null? mForegroundInfo.mInsidePadding :true; } /** * Describes how the foreground is positioned. * * @return foreground gravity. * * @see #setForegroundGravity(int) * * @attr ref android.R.styleable#View_foregroundGravity */ @InspectableProperty(valueType =InspectableProperty.ValueType.GRAVITY) publicint getForegroundGravity(){ return mForegroundInfo !=null? mForegroundInfo.mGravity :Gravity.START |Gravity.TOP; } /** * Describes how the foreground is positioned. Defaults to START and TOP. * * @param gravity see {@link android.view.Gravity} * * @see #getForegroundGravity() * * @attr ref android.R.styleable#View_foregroundGravity */ publicvoid setForegroundGravity(int gravity){ if(mForegroundInfo ==null){ mForegroundInfo =newForegroundInfo(); } if(mForegroundInfo.mGravity != gravity){ if((gravity &Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)==0){ gravity |=Gravity.START; } if((gravity &Gravity.VERTICAL_GRAVITY_MASK)==0){ gravity |=Gravity.TOP; } mForegroundInfo.mGravity = gravity; requestLayout(); } } /** * Applies a tint to the foreground drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> * Subsequent calls to {@link #setForeground(Drawable)} will automatically * mutate the drawable and apply the specified tint and tint mode using * {@link Drawable#setTintList(ColorStateList)}. * * @param tint the tint to apply, may be {@code null} to clear tint * * @attr ref android.R.styleable#View_foregroundTint * @see #getForegroundTintList() * @see Drawable#setTintList(ColorStateList) */ @RemotableViewMethod publicvoid setForegroundTintList(@NullableColorStateList tint){ if(mForegroundInfo ==null){ mForegroundInfo =newForegroundInfo(); } if(mForegroundInfo.mTintInfo ==null){ mForegroundInfo.mTintInfo =newTintInfo(); } mForegroundInfo.mTintInfo.mTintList = tint; mForegroundInfo.mTintInfo.mHasTintList =true; applyForegroundTint(); } /** * Return the tint applied to the foreground drawable, if specified. * * @return the tint applied to the foreground drawable * @attr ref android.R.styleable#View_foregroundTint * @see #setForegroundTintList(ColorStateList) */ @InspectableProperty(name ="foregroundTint") @Nullable publicColorStateList getForegroundTintList(){ return mForegroundInfo !=null&& mForegroundInfo.mTintInfo !=null ? mForegroundInfo.mTintInfo.mTintList :null; } /** * Specifies the blending mode used to apply the tint specified by * {@link #setForegroundTintList(ColorStateList)}} to the background * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. * * @param tintMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#View_foregroundTintMode * @see #getForegroundTintMode() * @see Drawable#setTintMode(PorterDuff.Mode) * */ publicvoid setForegroundTintMode(@NullablePorterDuff.Mode tintMode){ BlendMode mode =null; if(tintMode !=null){ mode =BlendMode.fromValue(tintMode.nativeInt); } setForegroundTintBlendMode(mode); } /** * Specifies the blending mode used to apply the tint specified by * {@link #setForegroundTintList(ColorStateList)}} to the background * drawable. The default mode is {@link BlendMode#SRC_IN}. * * @param blendMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#View_foregroundTintMode * @see #getForegroundTintMode() * @see Drawable#setTintBlendMode(BlendMode) */ @RemotableViewMethod publicvoid setForegroundTintBlendMode(@NullableBlendMode blendMode){ if(mForegroundInfo ==null){ mForegroundInfo =newForegroundInfo(); } if(mForegroundInfo.mTintInfo ==null){ mForegroundInfo.mTintInfo =newTintInfo(); } mForegroundInfo.mTintInfo.mBlendMode = blendMode; mForegroundInfo.mTintInfo.mHasTintMode =true; applyForegroundTint(); } /** * Return the blending mode used to apply the tint to the foreground * drawable, if specified. * * @return the blending mode used to apply the tint to the foreground * drawable * @attr ref android.R.styleable#View_foregroundTintMode * @see #setForegroundTintMode(PorterDuff.Mode) */ @InspectableProperty @Nullable publicPorterDuff.Mode getForegroundTintMode(){ BlendMode blendMode = mForegroundInfo !=null&& mForegroundInfo.mTintInfo !=null ? mForegroundInfo.mTintInfo.mBlendMode :null; if(blendMode !=null){ returnBlendMode.blendModeToPorterDuffMode(blendMode); }else{ returnnull; } } /** * Return the blending mode used to apply the tint to the foreground * drawable, if specified. * * @return the blending mode used to apply the tint to the foreground * drawable * @attr ref android.R.styleable#View_foregroundTintMode * @see #setForegroundTintBlendMode(BlendMode) * */ public@NullableBlendMode getForegroundTintBlendMode(){ return mForegroundInfo !=null&& mForegroundInfo.mTintInfo !=null ? mForegroundInfo.mTintInfo.mBlendMode :null; } privatevoid applyForegroundTint(){ if(mForegroundInfo !=null&& mForegroundInfo.mDrawable !=null && mForegroundInfo.mTintInfo !=null){ finalTintInfo tintInfo = mForegroundInfo.mTintInfo; if(tintInfo.mHasTintList || tintInfo.mHasTintMode){ mForegroundInfo.mDrawable = mForegroundInfo.mDrawable.mutate(); if(tintInfo.mHasTintList){ mForegroundInfo.mDrawable.setTintList(tintInfo.mTintList); } if(tintInfo.mHasTintMode){ mForegroundInfo.mDrawable.setTintBlendMode(tintInfo.mBlendMode); } // The drawable (or one of its children) may not have been // stateful before applying the tint, so let's try again. if(mForegroundInfo.mDrawable.isStateful()){ mForegroundInfo.mDrawable.setState(getDrawableState()); } } } } /** * Get the drawable to be overlayed when a view is autofilled * * @return The drawable * * @throws IllegalStateException if the drawable could not be found. */ @NullableprivateDrawable getAutofilledDrawable(){ if(mAttachInfo ==null){ returnnull; } // Lazily load the isAutofilled drawable. if(mAttachInfo.mAutofilledDrawable ==null){ Context rootContext = getRootView().getContext(); TypedArray a = rootContext.getTheme().obtainStyledAttributes(AUTOFILL_HIGHLIGHT_ATTR); int attributeResourceId = a.getResourceId(0,0); mAttachInfo.mAutofilledDrawable = rootContext.getDrawable(attributeResourceId); a.recycle(); } return mAttachInfo.mAutofilledDrawable; } /** * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled, unless * {@link #PFLAG4_AUTOFILL_HIDE_HIGHLIGHT} is enabled. * * @param canvas The canvas to draw on */ privatevoid drawAutofilledHighlight(@NonNullCanvas canvas){ if(isAutofilled()&&!hideAutofillHighlight()){ Drawable autofilledHighlight = getAutofilledDrawable(); if(autofilledHighlight !=null){ autofilledHighlight.setBounds(0,0, getWidth(), getHeight()); autofilledHighlight.draw(canvas); } } } /** * Draw any foreground content for this view. * * <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground} * drawable or other view-specific decorations. The foreground is drawn on top of the * primary view content.</p> * * @param canvas canvas to draw into */ publicvoid onDrawForeground(Canvas canvas){ onDrawScrollIndicators(canvas); onDrawScrollBars(canvas); finalDrawable foreground = mForegroundInfo !=null? mForegroundInfo.mDrawable :null; if(foreground !=null){ if(mForegroundInfo.mBoundsChanged){ mForegroundInfo.mBoundsChanged =false; finalRect selfBounds = mForegroundInfo.mSelfBounds; finalRect overlayBounds = mForegroundInfo.mOverlayBounds; if(mForegroundInfo.mInsidePadding){ selfBounds.set(0,0, getWidth(), getHeight()); }else{ selfBounds.set(getPaddingLeft(), getPaddingTop(), getWidth()- getPaddingRight(), getHeight()- getPaddingBottom()); } finalint ld = getLayoutDirection(); Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld); foreground.setBounds(overlayBounds); } foreground.draw(canvas); } } /** * Sets the 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 #getPaddingLeft}, {@link #getPaddingTop}, * {@link #getPaddingRight} and {@link #getPaddingBottom} may be different * from the values set in this call. * * @attr ref android.R.styleable#View_padding * @attr ref android.R.styleable#View_paddingBottom * @attr ref android.R.styleable#View_paddingLeft * @attr ref android.R.styleable#View_paddingRight * @attr ref android.R.styleable#View_paddingTop * @param left the left padding in pixels * @param top the top padding in pixels * @param right the right padding in pixels * @param bottom the bottom padding in pixels */ publicvoid setPadding(int left,int top,int right,int bottom){ resetResolvedPaddingInternal(); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; mUserPaddingLeftInitial = left; mUserPaddingRightInitial = right; mLeftPaddingDefined =true; mRightPaddingDefined =true; internalSetPadding(left, top, right, bottom); } /** * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.P, trackingBug =123768420) protectedvoid internalSetPadding(int left,int top,int right,int bottom){ mUserPaddingLeft = left; mUserPaddingRight = right; mUserPaddingBottom = bottom; finalint viewFlags = mViewFlags; boolean changed =false; // Common case is there are no scroll bars. if((viewFlags &(SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL))!=0){ if((viewFlags & SCROLLBARS_VERTICAL)!=0){ finalint offset =(viewFlags & SCROLLBARS_INSET_MASK)==0 ?0: getVerticalScrollbarWidth(); switch(mVerticalScrollbarPosition){ case SCROLLBAR_POSITION_DEFAULT: if(isLayoutRtl()){ left += offset; }else{ right += offset; } break; case SCROLLBAR_POSITION_RIGHT: right += offset; break; case SCROLLBAR_POSITION_LEFT: left += offset; break; } } if((viewFlags & SCROLLBARS_HORIZONTAL)!=0){ bottom +=(viewFlags & SCROLLBARS_INSET_MASK)==0 ?0: getHorizontalScrollbarHeight(); } } if(mPaddingLeft != left){ changed =true; mPaddingLeft = left; } if(mPaddingTop != top){ changed =true; mPaddingTop = top; } if(mPaddingRight != right){ changed =true; mPaddingRight = right; } if(mPaddingBottom != bottom){ changed =true; mPaddingBottom = bottom; } if(changed){ requestLayout(); invalidateOutline(); } } /** * 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 #getPaddingTop}, * {@link #getPaddingEnd} and {@link #getPaddingBottom} may be different * from the values set in this call. * * @attr ref android.R.styleable#View_padding * @attr ref android.R.styleable#View_paddingBottom * @attr ref android.R.styleable#View_paddingStart * @attr ref android.R.styleable#View_paddingEnd * @attr ref android.R.styleable#View_paddingTop * @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 */ publicvoid setPaddingRelative(int start,int top,int end,int bottom){ resetResolvedPaddingInternal(); mUserPaddingStart = start; mUserPaddingEnd = end; mLeftPaddingDefined =true; mRightPaddingDefined =true; switch(getLayoutDirection()){ case LAYOUT_DIRECTION_RTL: mUserPaddingLeftInitial = end; mUserPaddingRightInitial = start; internalSetPadding(end, top, start, bottom); break; case LAYOUT_DIRECTION_LTR: default: mUserPaddingLeftInitial = start; mUserPaddingRightInitial = end; internalSetPadding(start, top, end, bottom); } } /** * A {@link View} can be inflated from an XML layout. For such Views this method returns the * resource ID of the source layout. * * @return The layout resource id if this view was inflated from XML, otherwise * {@link Resources#ID_NULL}. */ @LayoutRes publicint getSourceLayoutResId(){ return mSourceLayoutId; } /** * Returns the top padding of this view. * * @return the top padding in pixels */ @InspectableProperty publicint getPaddingTop(){ return mPaddingTop; } /** * Returns the bottom padding of this view. If there are inset and enabled * scrollbars, this value may include the space required to display the * scrollbars as well. * * @return the bottom padding in pixels */ @InspectableProperty publicint getPaddingBottom(){ return mPaddingBottom; } /** * Returns the left padding of this view. If there are inset and enabled * scrollbars, this value may include the space required to display the * scrollbars as well. * * @return the left padding in pixels */ @InspectableProperty publicint getPaddingLeft(){ if(!isPaddingResolved()){ resolvePadding(); } return mPaddingLeft; } /** * Returns the start padding of this 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. * * @return the start padding in pixels */ publicint getPaddingStart(){ if(!isPaddingResolved()){ resolvePadding(); } return(getLayoutDirection()== LAYOUT_DIRECTION_RTL)? mPaddingRight : mPaddingLeft; } /** * Returns the right padding of this view. If there are inset and enabled * scrollbars, this value may include the space required to display the * scrollbars as well. * * @return the right padding in pixels */ @InspectableProperty publicint getPaddingRight(){ if(!isPaddingResolved()){ resolvePadding(); } return mPaddingRight; } /** * Returns the end padding of this 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. * * @return the end padding in pixels */ publicint getPaddingEnd(){ if(!isPaddingResolved()){ resolvePadding(); } return(getLayoutDirection()== LAYOUT_DIRECTION_RTL)? mPaddingLeft : mPaddingRight; } /** * Return if the padding has been set through relative values * {@link #setPaddingRelative(int, int, int, int)} or through * @attr ref android.R.styleable#View_paddingStart or * @attr ref android.R.styleable#View_paddingEnd * * @return true if the padding is relative or false if it is not. */ publicboolean isPaddingRelative(){ return(mUserPaddingStart != UNDEFINED_PADDING || mUserPaddingEnd != UNDEFINED_PADDING); } Insets computeOpticalInsets(){ return(mBackground ==null)?Insets.NONE : mBackground.getOpticalInsets(); } /** * @hide */ @UnsupportedAppUsage publicvoid resetPaddingToInitialValues(){ if(isRtlCompatibilityMode()){ mPaddingLeft = mUserPaddingLeftInitial; mPaddingRight = mUserPaddingRightInitial; return; } if(isLayoutRtl()){ mPaddingLeft =(mUserPaddingEnd >=0)? mUserPaddingEnd : mUserPaddingLeftInitial; mPaddingRight =(mUserPaddingStart >=0)? mUserPaddingStart : mUserPaddingRightInitial; }else{ mPaddingLeft =(mUserPaddingStart >=0)? mUserPaddingStart : mUserPaddingLeftInitial; mPaddingRight =(mUserPaddingEnd >=0)? mUserPaddingEnd : mUserPaddingRightInitial; } } /** * @hide */ publicInsets getOpticalInsets(){ if(mLayoutInsets ==null){ mLayoutInsets = computeOpticalInsets(); } return mLayoutInsets; } /** * Set this view's optical insets. * * <p>This method should be treated similarly to setMeasuredDimension and not as a general * property. Views that compute their own optical insets should call it as part of measurement. * This method does not request layout. If you are setting optical insets outside of * measure/layout itself you will want to call requestLayout() yourself. * </p> * @hide */ publicvoid setOpticalInsets(Insets insets){ mLayoutInsets = insets; } /** * Changes the selection state of this view. A view can be selected or not. * Note that selection is not the same as focus. Views are typically * selected in the context of an AdapterView like ListView or GridView; * the selected view is the view that is highlighted. * * @param selected true if the view must be selected, false otherwise */ publicvoid setSelected(boolean selected){ //noinspection DoubleNegation if(((mPrivateFlags & PFLAG_SELECTED)!=0)!= selected){ mPrivateFlags =(mPrivateFlags &~PFLAG_SELECTED)|(selected ? PFLAG_SELECTED :0); if(!selected) resetPressedState(); invalidate(true); refreshDrawableState(); dispatchSetSelected(selected); if(selected){ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); }else{ notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } /** * Dispatch setSelected to all of this View's children. * * @see #setSelected(boolean) * * @param selected The new selected state */ protectedvoid dispatchSetSelected(boolean selected){ } /** * Indicates the selection state of this view. * * @return true if the view is selected, false otherwise */ @ViewDebug.ExportedProperty @InspectableProperty(hasAttributeId =false) publicboolean isSelected(){ return(mPrivateFlags & PFLAG_SELECTED)!=0; } /** * Changes the activated state of this view. A view can be activated or not. * Note that activation is not the same as selection. Selection is * a transient property, representing the view (hierarchy) the user is * currently interacting with. Activation is a longer-term state that the * user can move views in and out of. For example, in a list view with * single or multiple selection enabled, the views in the current selection * set are activated. (Um, yeah, we are deeply sorry about the terminology * here.) The activated state is propagated down to children of the view it * is set on. * * @param activated true if the view must be activated, false otherwise */ publicvoid setActivated(boolean activated){ //noinspection DoubleNegation if(((mPrivateFlags & PFLAG_ACTIVATED)!=0)!= activated){ mPrivateFlags =(mPrivateFlags &~PFLAG_ACTIVATED)|(activated ? PFLAG_ACTIVATED :0); invalidate(true); refreshDrawableState(); dispatchSetActivated(activated); } } /** * Dispatch setActivated to all of this View's children. * * @see #setActivated(boolean) * * @param activated The new activated state */ protectedvoid dispatchSetActivated(boolean activated){ } /** * Indicates the activation state of this view. * * @return true if the view is activated, false otherwise */ @ViewDebug.ExportedProperty @InspectableProperty(hasAttributeId =false) publicboolean isActivated(){ return(mPrivateFlags & PFLAG_ACTIVATED)!=0; } /** * Returns the ViewTreeObserver for this view's hierarchy. The view tree * observer can be used to get notifications when global events, like * layout, happen. * * The returned ViewTreeObserver observer is not guaranteed to remain * valid for the lifetime of this View. If the caller of this method keeps * a long-lived reference to ViewTreeObserver, it should always check for * the return value of {@link ViewTreeObserver#isAlive()}. * * @return The ViewTreeObserver for this view's hierarchy. */ publicViewTreeObserver getViewTreeObserver(){ if(mAttachInfo !=null){ return mAttachInfo.mTreeObserver; } if(mFloatingTreeObserver ==null){ mFloatingTreeObserver =newViewTreeObserver(mContext); } return mFloatingTreeObserver; } /** * <p>Finds the topmost view in the current view hierarchy.</p> * * @return the topmost view containing this view */ publicView getRootView(){ if(mAttachInfo !=null){ finalView v = mAttachInfo.mRootView; if(v !=null){ return v; } } View parent =this; while(parent.mParent instanceofView){ parent =(View) parent.mParent; } return parent; } /** * Transforms a motion event from view-local coordinates to on-screen * coordinates. * * @param ev the view-local motion event * @return false if the transformation could not be applied * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicboolean toGlobalMotionEvent(MotionEvent ev){ finalAttachInfo info = mAttachInfo; if(info ==null){ returnfalse; } finalMatrix m = info.mTmpMatrix; m.set(Matrix.IDENTITY_MATRIX); transformMatrixToGlobal(m); ev.transform(m); returntrue; } /** * Transforms a motion event from on-screen coordinates to view-local * coordinates. * * @param ev the on-screen motion event * @return false if the transformation could not be applied * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicboolean toLocalMotionEvent(MotionEvent ev){ finalAttachInfo info = mAttachInfo; if(info ==null){ returnfalse; } finalMatrix m = info.mTmpMatrix; m.set(Matrix.IDENTITY_MATRIX); transformMatrixToLocal(m); ev.transform(m); returntrue; } /** * Modifies the input matrix such that it maps view-local coordinates to * on-screen coordinates. * * @param matrix input matrix to modify */ publicvoid transformMatrixToGlobal(@NonNullMatrix matrix){ finalViewParent parent = mParent; if(parent instanceofView){ finalView vp =(View) parent; vp.transformMatrixToGlobal(matrix); matrix.preTranslate(-vp.mScrollX,-vp.mScrollY); }elseif(parent instanceofViewRootImpl){ finalViewRootImpl vr =(ViewRootImpl) parent; vr.transformMatrixToGlobal(matrix); matrix.preTranslate(0,-vr.mCurScrollY); } matrix.preTranslate(mLeft, mTop); if(!hasIdentityMatrix()){ matrix.preConcat(getMatrix()); } } /** * Modifies the input matrix such that it maps on-screen coordinates to * view-local coordinates. * * @param matrix input matrix to modify */ publicvoid transformMatrixToLocal(@NonNullMatrix matrix){ finalViewParent parent = mParent; if(parent instanceofView){ finalView vp =(View) parent; vp.transformMatrixToLocal(matrix); matrix.postTranslate(vp.mScrollX, vp.mScrollY); }elseif(parent instanceofViewRootImpl){ finalViewRootImpl vr =(ViewRootImpl) parent; vr.transformMatrixToLocal(matrix); matrix.postTranslate(0, vr.mCurScrollY); } matrix.postTranslate(-mLeft,-mTop); if(!hasIdentityMatrix()){ matrix.postConcat(getInverseMatrix()); } } /** * @hide */ @ViewDebug.ExportedProperty(category ="layout", indexMapping ={ @ViewDebug.IntToString(from =0, to ="x"), @ViewDebug.IntToString(from =1, to ="y") }) @UnsupportedAppUsage publicint[] getLocationOnScreen(){ int[] location =newint[2]; getLocationOnScreen(location); return location; } /** * Gets the coordinates of this view in the coordinate space of the device * screen, irrespective of system decorations and whether the system is in * multi-window mode. * * <p>In multi-window mode, the coordinate space encompasses the entire * device screen, ignoring the bounds of the app window. For example, if the * view is in the bottom portion of a horizontal split screen, the top edge * of the screen&mdash;not the top edge of the window&mdash;is the origin * from which the y-coordinate is calculated. * * <p>In multiple-screen scenarios, the coordinate space can span screens. * For example, if the app is spanning both screens of a dual-screen device * and the view is located on the right-hand screen, the x-coordinate is * calculated from the left edge of the left-hand screen to the left edge of * the view. When the app is restricted to a single screen in a * multiple-screen environment, the coordinate space includes only the * screen on which the app is running. * * <p>After the method returns, the argument array contains the x and y * coordinates of the view relative to the view's left and top edges, * respectively. * * @param outLocation A two-element integer array in which the view * coordinates are stored. The x-coordinate is at index 0; the * y-coordinate, at index 1. */ publicvoid getLocationOnScreen(@Size(2)int[] outLocation){ getLocationInWindow(outLocation); finalAttachInfo info = mAttachInfo; if(info !=null){ outLocation[0]+= info.mWindowLeft; outLocation[1]+= info.mWindowTop; } } /** * Gets the coordinates of this view in the coordinate space of the window * that contains the view, irrespective of system decorations. * * <p>In multi-window mode, the origin of the coordinate space is the * top left corner of the window that contains the view. In full screen * mode, the origin is the top left corner of the device screen. * * <p>In multiple-screen scenarios, if the app spans multiple screens, the * coordinate space also spans multiple screens. But if the app is * restricted to a single screen, the coordinate space includes only the * screen on which the app is running. * * <p>After the method returns, the argument array contains the x and y * coordinates of the view relative to the view's left and top edges, * respectively. * * @param outLocation A two-element integer array in which the view * coordinates are stored. The x-coordinate is at index 0; the * y-coordinate, at index 1. */ publicvoid getLocationInWindow(@Size(2)int[] outLocation){ if(outLocation ==null|| outLocation.length <2){ thrownewIllegalArgumentException("outLocation must be an array of two integers"); } outLocation[0]=0; outLocation[1]=0; transformFromViewToWindowSpace(outLocation); } /** @hide */ publicvoid transformFromViewToWindowSpace(@Size(2)int[] inOutLocation){ if(inOutLocation ==null|| inOutLocation.length <2){ thrownewIllegalArgumentException("inOutLocation must be an array of two integers"); } if(mAttachInfo ==null){ // When the view is not attached to a window, this method does not make sense inOutLocation[0]= inOutLocation[1]=0; return; } float position[]= mAttachInfo.mTmpTransformLocation; position[0]= inOutLocation[0]; position[1]= inOutLocation[1]; if(!hasIdentityMatrix()){ getMatrix().mapPoints(position); } position[0]+= mLeft; position[1]+= mTop; ViewParent viewParent = mParent; while(viewParent instanceofView){ finalView view =(View) viewParent; position[0]-= view.mScrollX; position[1]-= view.mScrollY; if(!view.hasIdentityMatrix()){ view.getMatrix().mapPoints(position); } position[0]+= view.mLeft; position[1]+= view.mTop; viewParent = view.mParent; } if(viewParent instanceofViewRootImpl){ // *cough* finalViewRootImpl vr =(ViewRootImpl) viewParent; position[1]-= vr.mCurScrollY; } inOutLocation[0]=Math.round(position[0]); inOutLocation[1]=Math.round(position[1]); } /** * @param id the id of the view to be found * @return the view of the specified id, null if cannot be found * @hide */ protected<T extendsView> T findViewTraversal(@IdResint id){ if(id == mID){ return(T)this; } returnnull; } /** * @param tag the tag of the view to be found * @return the view of specified tag, null if cannot be found * @hide */ protected<T extendsView> T findViewWithTagTraversal(Object tag){ if(tag !=null&& tag.equals(mTag)){ return(T)this; } returnnull; } /** * @param predicate The predicate to evaluate. * @param childToSkip If not null, ignores this child during the recursive traversal. * @return The first view that matches the predicate or null. * @hide */ protected<T extendsView> T findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip){ if(predicate.test(this)){ return(T)this; } returnnull; } /** * Finds the first descendant view with the given ID, the view itself if * the ID matches {@link #getId()}, or {@code null} if the ID is invalid * (< 0) or there is no matching view in the hierarchy. * <p> * <strong>Note:</strong> 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 if found, or {@code null} otherwise * @see View#requireViewById(int) */ @Nullable publicfinal<T extendsView> T findViewById(@IdResint id){ if(id == NO_ID){ returnnull; } return findViewTraversal(id); } /** * Finds the first descendant view with the given ID, the view itself if the ID matches * {@link #getId()}, or throws an IllegalArgumentException if the ID is invalid or there is no * matching view in the hierarchy. * <p> * <strong>Note:</strong> 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) */ @NonNull publicfinal<T extendsView> T requireViewById(@IdResint id){ T view = findViewById(id); if(view ==null){ thrownewIllegalArgumentException("ID does not reference a View inside this View"); } return view; } /** * Performs the traversal to find a view by its unique and stable accessibility id. * * <strong>Note:</strong>This method does not stop at the root namespace * boundary since the user can touch the screen at an arbitrary location * potentially crossing the root namespace boundary which will send an * accessibility event to accessibility services and they should be able * to obtain the event source. Also accessibility ids are guaranteed to be * unique in the window. * * @param accessibilityId The accessibility id. * @return The found view. * @hide */ public<T extendsView> T findViewByAccessibilityIdTraversal(int accessibilityId){ if(getAccessibilityViewId()== accessibilityId){ return(T)this; } returnnull; } /** * Performs the traversal to find a view by its autofill id. * * <strong>Note:</strong>This method does not stop at the root namespace * boundary. * * @param autofillId The autofill id. * @return The found view. * @hide */ public<T extendsView> T findViewByAutofillIdTraversal(int autofillId){ if(getAutofillViewId()== autofillId){ return(T)this; } returnnull; } /** * Look for a child view with the given tag. If this view has the given * tag, return this view. * * @param tag The tag to search for, using "tag.equals(getTag())". * @return The View that has the given tag in the hierarchy or null */ publicfinal<T extendsView> T findViewWithTag(Object tag){ if(tag ==null){ returnnull; } return findViewWithTagTraversal(tag); } /** * Look for a child view that matches the specified predicate. * If this view matches the predicate, return this view. * * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. * @hide */ publicfinal<T extendsView> T findViewByPredicate(Predicate<View> predicate){ return findViewByPredicateTraversal(predicate,null); } /** * Look for a child view that matches the specified predicate, * starting with the specified view and its descendents and then * recusively searching the ancestors and siblings of that view * until this view is reached. * * This method is useful in cases where the predicate does not match * a single unique view (perhaps multiple views use the same id) * and we are trying to find the view that is "closest" in scope to the * starting view. * * @param start The view to start from. * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. * @hide */ publicfinal<T extendsView> T findViewByPredicateInsideOut( View start,Predicate<View> predicate){ View childToSkip =null; for(;;){ T view = start.findViewByPredicateTraversal(predicate, childToSkip); if(view !=null|| start ==this){ return view; } ViewParent parent = start.getParent(); if(parent ==null||!(parent instanceofView)){ returnnull; } childToSkip = start; start =(View) parent; } } /** * Sets the identifier for this view. The identifier does not have to be * unique in this view's hierarchy. The identifier should be a positive * number. * * @see #NO_ID * @see #getId() * @see #findViewById(int) * * @param id a number used to identify the view * * @attr ref android.R.styleable#View_id */ publicvoid setId(@IdResint id){ mID = id; if(mID ==View.NO_ID && mLabelForId !=View.NO_ID){ mID = generateViewId(); } } /** * {@hide} * * @param isRoot true if the view belongs to the root namespace, false * otherwise */ @UnsupportedAppUsage @TestApi publicvoid setIsRootNamespace(boolean isRoot){ if(isRoot){ mPrivateFlags |= PFLAG_IS_ROOT_NAMESPACE; }else{ mPrivateFlags &=~PFLAG_IS_ROOT_NAMESPACE; } } /** * {@hide} * * @return true if the view belongs to the root namespace, false otherwise */ @UnsupportedAppUsage publicboolean isRootNamespace(){ return(mPrivateFlags&PFLAG_IS_ROOT_NAMESPACE)!=0; } /** * Returns this view's identifier. * * @return a positive integer used to identify the view or {@link #NO_ID} * if the view has no ID * * @see #setId(int) * @see #findViewById(int) * @attr ref android.R.styleable#View_id */ @IdRes @ViewDebug.CapturedViewProperty @InspectableProperty publicint getId(){ return mID; } /** * Get the identifier used for this view by the drawing system. * * @see RenderNode#getUniqueId() * @return A long that uniquely identifies this view's drawing component */ publiclong getUniqueDrawingId(){ return mRenderNode.getUniqueId(); } /** * Returns this view's tag. * * @return the Object stored in this view as a tag, or {@code null} if not * set * * @see #setTag(Object) * @see #getTag(int) */ @ViewDebug.ExportedProperty @InspectableProperty publicObject getTag(){ return mTag; } /** * Sets the tag associated with this view. A tag can be used to mark * a view in its hierarchy and does not have to be unique within the * hierarchy. Tags can also be used to store data within a view without * resorting to another data structure. * * @param tag an Object to tag the view with * * @see #getTag() * @see #setTag(int, Object) */ publicvoid setTag(finalObject tag){ mTag = tag; } /** * Returns the tag associated with this view and the specified key. * * @param key The key identifying the tag * * @return the Object stored in this view as a tag, or {@code null} if not * set * * @see #setTag(int, Object) * @see #getTag() */ publicObject getTag(int key){ if(mKeyedTags !=null)return mKeyedTags.get(key); returnnull; } /** * Sets a tag associated with this view and a key. A tag can be used * to mark a view in its hierarchy and does not have to be unique within * the hierarchy. Tags can also be used to store data within a view * without resorting to another data structure. * * The specified key should be an id declared in the resources of the * application to ensure it is unique (see the <a * href="{@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>). * Keys identified as belonging to * the Android framework or not associated with any package will cause * an {@link IllegalArgumentException} to be thrown. * * @param key The key identifying the tag * @param tag An Object to tag the view with * * @throws IllegalArgumentException If they specified key is not valid * * @see #setTag(Object) * @see #getTag(int) */ publicvoid setTag(int key,finalObject tag){ // If the package id is 0x00 or 0x01, it's either an undefined package // or a framework id if((key >>>24)<2){ thrownewIllegalArgumentException("The key must be an application-specific " +"resource id."); } setKeyedTag(key, tag); } /** * Variation of {@link #setTag(int, Object)} that enforces the key to be a * framework id. * * @hide */ @UnsupportedAppUsage publicvoid setTagInternal(int key,Object tag){ if((key >>>24)!=0x1){ thrownewIllegalArgumentException("The key must be a framework-specific " +"resource id."); } setKeyedTag(key, tag); } privatevoid setKeyedTag(int key,Object tag){ if(mKeyedTags ==null){ mKeyedTags =newSparseArray<Object>(2); } mKeyedTags.put(key, tag); } /** * Prints information about this view in the log output, with the tag * {@link #VIEW_LOG_TAG}. * * @hide */ @UnsupportedAppUsage publicvoid debug(){ debug(0); } /** * Prints information about this view in the log output, with the tag * {@link #VIEW_LOG_TAG}. Each line in the output is preceded with an * indentation defined by the <code>depth</code>. * * @param depth the indentation level * * @hide */ @UnsupportedAppUsage protectedvoid debug(int depth){ String output = debugIndent(depth -1); output +="+ "+this; int id = getId(); if(id !=-1){ output +=" (id="+ id +")"; } Object tag = getTag(); if(tag !=null){ output +=" (tag="+ tag +")"; } Log.d(VIEW_LOG_TAG, output); if((mPrivateFlags & PFLAG_FOCUSED)!=0){ output = debugIndent(depth)+" FOCUSED"; Log.d(VIEW_LOG_TAG, output); } output = debugIndent(depth); output +="frame={"+ mLeft +", "+ mTop +", "+ mRight +", "+ mBottom +"} scroll={"+ mScrollX +", "+ mScrollY +"} "; Log.d(VIEW_LOG_TAG, output); if(mPaddingLeft !=0|| mPaddingTop !=0|| mPaddingRight !=0 || mPaddingBottom !=0){ output = debugIndent(depth); output +="padding={"+ mPaddingLeft +", "+ mPaddingTop +", "+ mPaddingRight +", "+ mPaddingBottom +"}"; Log.d(VIEW_LOG_TAG, output); } output = debugIndent(depth); output +="mMeasureWidth="+ mMeasuredWidth + " mMeasureHeight="+ mMeasuredHeight; Log.d(VIEW_LOG_TAG, output); output = debugIndent(depth); if(mLayoutParams ==null){ output +="BAD! no layout params"; }else{ output = mLayoutParams.debug(output); } Log.d(VIEW_LOG_TAG, output); output = debugIndent(depth); output +="flags={"; output +=View.printFlags(mViewFlags); output +="}"; Log.d(VIEW_LOG_TAG, output); output = debugIndent(depth); output +="privateFlags={"; output +=View.printPrivateFlags(mPrivateFlags); output +="}"; Log.d(VIEW_LOG_TAG, output); } /** * Creates a string of whitespaces used for indentation. * * @param depth the indentation level * @return a String containing (depth * 2 + 3) * 2 white spaces * * @hide */ protectedstaticString debugIndent(int depth){ StringBuilder spaces =newStringBuilder((depth *2+3)*2); for(int i =0; i <(depth *2)+3; i++){ spaces.append(' ').append(' '); } return spaces.toString(); } /** * <p>Return the offset of the widget's text baseline from the widget's top * boundary. If this widget does not support baseline alignment, this * method returns -1. </p> * * @return the offset of the baseline within the widget's bounds or -1 * if baseline alignment is not supported */ @ViewDebug.ExportedProperty(category ="layout") @InspectableProperty publicint getBaseline(){ return-1; } /** * Returns whether the view hierarchy is currently undergoing a layout pass. This * information is useful to avoid situations such as calling {@link #requestLayout()} during * a layout pass. * * @return whether the view hierarchy is currently undergoing a layout pass */ publicboolean isInLayout(){ ViewRootImpl viewRoot = getViewRootImpl(); return(viewRoot !=null&& viewRoot.isInLayout()); } /** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view * tree. This should not be called while the view hierarchy is currently in a layout * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the * end of the current layout pass (and then layout will run again) or after the current * frame is drawn and the next layout occurs. * * <p>Subclasses which override this method should call the superclass method to * handle possible request-during-layout errors correctly.</p> */ @CallSuper publicvoid requestLayout(){ if(mMeasureCache !=null) mMeasureCache.clear(); if(mAttachInfo !=null&& mAttachInfo.mViewRequestingLayout ==null){ // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if(viewRoot !=null&& viewRoot.isInLayout()){ if(!viewRoot.requestLayoutDuringLayout(this)){ return; } } mAttachInfo.mViewRequestingLayout =this; } mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if(mParent !=null&&!mParent.isLayoutRequested()){ mParent.requestLayout(); } if(mAttachInfo !=null&& mAttachInfo.mViewRequestingLayout ==this){ mAttachInfo.mViewRequestingLayout =null; } } /** * Forces this view to be laid out during the next layout pass. * This method does not call requestLayout() or forceLayout() * on the parent. */ publicvoid forceLayout(){ if(mMeasureCache !=null) mMeasureCache.clear(); mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; } /** * <p> * This is called to find out how big a view should be. The parent * supplies constraint information in the width and height parameters. * </p> * * <p> * The actual measurement work of a view is performed in * {@link #onMeasure(int, int)}, called by this method. Therefore, only * {@link #onMeasure(int, int)} can and must be overridden by subclasses. * </p> * * * @param widthMeasureSpec Horizontal space requirements as imposed by the * parent * @param heightMeasureSpec Vertical space requirements as imposed by the * parent * * @see #onMeasure(int, int) */ publicfinalvoid measure(int widthMeasureSpec,int heightMeasureSpec){ boolean optical = isLayoutModeOptical(this); if(optical != isLayoutModeOptical(mParent)){ Insets insets = getOpticalInsets(); int oWidth = insets.left + insets.right; int oHeight = insets.top + insets.bottom; widthMeasureSpec =MeasureSpec.adjust(widthMeasureSpec, optical ?-oWidth : oWidth); heightMeasureSpec =MeasureSpec.adjust(heightMeasureSpec, optical ?-oHeight : oHeight); } // Suppress sign extension for the low bytes long key =(long) widthMeasureSpec <<32|(long) heightMeasureSpec &0xffffffffL; if(mMeasureCache ==null) mMeasureCache =newLongSparseLongArray(2); finalboolean forceLayout =(mPrivateFlags & PFLAG_FORCE_LAYOUT)== PFLAG_FORCE_LAYOUT; // Optimize layout by avoiding an extra EXACTLY pass when the view is // already measured as the correct size. In API 23 and below, this // extra pass is required to make LinearLayout re-distribute weight. finalboolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec; finalboolean isSpecExactly =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.EXACTLY &&MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.EXACTLY; finalboolean matchesSpecSize = getMeasuredWidth()==MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight()==MeasureSpec.getSize(heightMeasureSpec); finalboolean needsLayout = specChanged &&(sAlwaysRemeasureExactly ||!isSpecExactly ||!matchesSpecSize); if(forceLayout || needsLayout){ // first clears the measured dimension flag mPrivateFlags &=~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); int cacheIndex = forceLayout ?-1: mMeasureCache.indexOfKey(key); if(cacheIndex <0|| sIgnoreMeasureCache){ // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }else{ long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int)(value >>32),(int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET)!= PFLAG_MEASURED_DIMENSION_SET){ thrownewIllegalStateException("View with id "+ getId()+": " + getClass().getName()+"#onMeasure() did not set the" +" measured dimension by calling" +" setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key,((long) mMeasuredWidth)<<32| (long) mMeasuredHeight &0xffffffffL);// suppress sign extension } /** * <p> * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overridden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * * <p> * <strong>CONTRACT:</strong> When overriding this method, you * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the * measured width and height of this view. Failure to do so will trigger an * <code>IllegalStateException</code>, thrown by * {@link #measure(int, int)}. Calling the superclass' * {@link #onMeasure(int, int)} is a valid use. * </p> * * <p> * The base class implementation of measure defaults to the background size, * unless a larger size is allowed by the MeasureSpec. Subclasses should * override {@link #onMeasure(int, int)} to provide better measurements of * their content. * </p> * * <p> * If this method is overridden, it is the subclass's responsibility to make * sure the measured height and width are at least the view's minimum height * and width ({@link #getSuggestedMinimumHeight()} and * {@link #getSuggestedMinimumWidth()}). * </p> * * @param widthMeasureSpec horizontal space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * @param heightMeasureSpec vertical space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * * @see #getMeasuredWidth() * @see #getMeasuredHeight() * @see #setMeasuredDimension(int, int) * @see #getSuggestedMinimumHeight() * @see #getSuggestedMinimumWidth() * @see android.view.View.MeasureSpec#getMode(int) * @see android.view.View.MeasureSpec#getSize(int) */ protectedvoid onMeasure(int widthMeasureSpec,int heightMeasureSpec){ setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } /** * <p>This method must be called by {@link #onMeasure(int, int)} to store the * measured width and measured height. Failing to do so will trigger an * exception at measurement time.</p> * * @param measuredWidth The measured width of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. * @param measuredHeight The measured height of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. */ protectedfinalvoid setMeasuredDimension(int measuredWidth,int measuredHeight){ boolean optical = isLayoutModeOptical(this); if(optical != isLayoutModeOptical(mParent)){ Insets insets = getOpticalInsets(); int opticalWidth = insets.left + insets.right; int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth :-opticalWidth; measuredHeight += optical ? opticalHeight :-opticalHeight; } setMeasuredDimensionRaw(measuredWidth, measuredHeight); } /** * Sets the measured dimension without extra processing for things like optical bounds. * Useful for reapplying consistent values that have already been cooked with adjustments * for optical bounds, etc. such as those from the measurement cache. * * @param measuredWidth The measured width of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. * @param measuredHeight The measured height of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. */ privatevoid setMeasuredDimensionRaw(int measuredWidth,int measuredHeight){ mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; } /** * Merge two states as returned by {@link #getMeasuredState()}. * @param curState The current state as returned from a view or the result * of combining multiple views. * @param newState The new view state to combine. * @return Returns a new integer reflecting the combination of the two * states. */ publicstaticint combineMeasuredStates(int curState,int newState){ return curState | newState; } /** * Version of {@link #resolveSizeAndState(int, int, int)} * returning only the {@link #MEASURED_SIZE_MASK} bits of the result. */ publicstaticint resolveSize(int size,int measureSpec){ return resolveSizeAndState(size, measureSpec,0)& MEASURED_SIZE_MASK; } /** * Utility to reconcile a desired size and state, with constraints imposed * by a MeasureSpec. Will take the desired size, unless a different size * is imposed by the constraints. The returned value is a compound integer, * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the * resulting size is smaller than the size the view wants to be. * * @param size How big the view wants to be. * @param measureSpec Constraints imposed by the parent. * @param childMeasuredState Size information bit mask for the view's * children. * @return Size information bit mask as defined by * {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. */ publicstaticint resolveSizeAndState(int size,int measureSpec,int childMeasuredState){ finalint specMode =MeasureSpec.getMode(measureSpec); finalint specSize =MeasureSpec.getSize(measureSpec); finalint result; switch(specMode){ caseMeasureSpec.AT_MOST: if(specSize < size){ result = specSize | MEASURED_STATE_TOO_SMALL; }else{ result = size; } break; caseMeasureSpec.EXACTLY: result = specSize; break; caseMeasureSpec.UNSPECIFIED: default: result = size; } return result |(childMeasuredState & MEASURED_STATE_MASK); } /** * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no constraints. Will get larger if allowed * by the MeasureSpec. * * @param size Default size for this view * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ publicstaticint getDefaultSize(int size,int measureSpec){ int result = size; int specMode =MeasureSpec.getMode(measureSpec); int specSize =MeasureSpec.getSize(measureSpec); switch(specMode){ caseMeasureSpec.UNSPECIFIED: result = size; break; caseMeasureSpec.AT_MOST: caseMeasureSpec.EXACTLY: result = specSize; break; } return result; } /** * Returns the suggested minimum height that the view should use. This * returns the maximum of the view's minimum height * and the background's minimum height * ({@link android.graphics.drawable.Drawable#getMinimumHeight()}). * <p> * When being used in {@link #onMeasure(int, int)}, the caller should still * ensure the returned height is within the requirements of the parent. * * @return The suggested minimum height of the view. */ protectedint getSuggestedMinimumHeight(){ return(mBackground ==null)? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight()); } /** * Returns the suggested minimum width that the view should use. This * returns the maximum of the view's minimum width * and the background's minimum width * ({@link android.graphics.drawable.Drawable#getMinimumWidth()}). * <p> * When being used in {@link #onMeasure(int, int)}, the caller should still * ensure the returned width is within the requirements of the parent. * * @return The suggested minimum width of the view. */ protectedint getSuggestedMinimumWidth(){ return(mBackground ==null)? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); } /** * Returns the minimum height of the view. * * @return the minimum height the view will try to be, in pixels * * @see #setMinimumHeight(int) * * @attr ref android.R.styleable#View_minHeight */ @InspectableProperty(name ="minHeight") publicint getMinimumHeight(){ return mMinHeight; } /** * Sets the minimum height of the view. It is not guaranteed the view will * be able to achieve this minimum height (for example, if its parent layout * constrains it with less available height). * * @param minHeight The minimum height the view will try to be, in pixels * * @see #getMinimumHeight() * * @attr ref android.R.styleable#View_minHeight */ @RemotableViewMethod publicvoid setMinimumHeight(int minHeight){ mMinHeight = minHeight; requestLayout(); } /** * Returns the minimum width of the view. * * @return the minimum width the view will try to be, in pixels * * @see #setMinimumWidth(int) * * @attr ref android.R.styleable#View_minWidth */ @InspectableProperty(name ="minWidth") publicint getMinimumWidth(){ return mMinWidth; } /** * Sets the minimum width of the view. It is not guaranteed the view will * be able to achieve this minimum width (for example, if its parent layout * constrains it with less available width). * * @param minWidth The minimum width the view will try to be, in pixels * * @see #getMinimumWidth() * * @attr ref android.R.styleable#View_minWidth */ @RemotableViewMethod publicvoid setMinimumWidth(int minWidth){ mMinWidth = minWidth; requestLayout(); } /** * Get the animation currently associated with this view. * * @return The animation that is currently playing or * scheduled to play for this view. */ publicAnimation getAnimation(){ return mCurrentAnimation; } /** * Start the specified animation now. * * @param animation the animation to start now */ publicvoid startAnimation(Animation animation){ animation.setStartTime(Animation.START_ON_FIRST_FRAME); setAnimation(animation); invalidateParentCaches(); invalidate(true); } /** * Cancels any animations for this view. */ publicvoid clearAnimation(){ if(mCurrentAnimation !=null){ mCurrentAnimation.detach(); } mCurrentAnimation =null; invalidateParentIfNeeded(); } /** * Sets the next animation to play for this view. * If you want the animation to play immediately, use * {@link #startAnimation(android.view.animation.Animation)} instead. * This method provides allows fine-grained * control over the start time and invalidation, but you * must make sure that 1) the animation has a start time set, and * 2) the view's parent (which controls animations on its children) * will be invalidated when the animation is supposed to * start. * * @param animation The next animation, or null. */ publicvoid setAnimation(Animation animation){ mCurrentAnimation = animation; if(animation !=null){ // If the screen is off assume the animation start time is now instead of // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time // would cause the animation to start when the screen turns back on if(mAttachInfo !=null&& mAttachInfo.mDisplayState ==Display.STATE_OFF && animation.getStartTime()==Animation.START_ON_FIRST_FRAME){ animation.setStartTime(AnimationUtils.currentAnimationTimeMillis()); } animation.reset(); } } /** * Invoked by a parent ViewGroup to notify the start of the animation * currently associated with this view. If you override this method, * always call super.onAnimationStart(); * * @see #setAnimation(android.view.animation.Animation) * @see #getAnimation() */ @CallSuper protectedvoid onAnimationStart(){ mPrivateFlags |= PFLAG_ANIMATION_STARTED; } /** * Invoked by a parent ViewGroup to notify the end of the animation * currently associated with this view. If you override this method, * always call super.onAnimationEnd(); * * @see #setAnimation(android.view.animation.Animation) * @see #getAnimation() */ @CallSuper protectedvoid onAnimationEnd(){ mPrivateFlags &=~PFLAG_ANIMATION_STARTED; } /** * Invoked if there is a Transform that involves alpha. Subclass that can * draw themselves with the specified alpha should return true, and then * respect that alpha when their onDraw() is called. If this returns false * then the view may be redirected to draw into an offscreen buffer to * fulfill the request, which will look fine, but may be slower than if the * subclass handles it internally. The default implementation returns false. * * @param alpha The alpha (0..255) to apply to the view's drawing * @return true if the view can draw with the specified alpha. */ protectedboolean onSetAlpha(int alpha){ returnfalse; } /** * This is used by the ViewRoot to perform an optimization when * the view hierarchy contains one or several SurfaceView. * SurfaceView is always considered transparent, but its children are not, * therefore all View objects remove themselves from the global transparent * region (passed as a parameter to this function). * * @param region The transparent region for this ViewAncestor (window). * * @return Returns true if the effective visibility of the view at this * point is opaque, regardless of the transparent region; returns false * if it is possible for underlying windows to be seen behind the view. * */ publicboolean gatherTransparentRegion(@NullableRegion region){ finalAttachInfo attachInfo = mAttachInfo; if(region !=null&& attachInfo !=null){ finalint pflags = mPrivateFlags; if((pflags & PFLAG_SKIP_DRAW)==0){ // The SKIP_DRAW flag IS NOT set, so this view draws. We need to // remove it from the transparent region. finalint[] location = attachInfo.mTransparentLocation; getLocationInWindow(location); // When a view has Z value, then it will be better to leave some area below the view // for drawing shadow. The shadow outset is proportional to the Z value. Note that // the bottom part needs more offset than the left, top and right parts due to the // spot light effects. int shadowOffset = getZ()>0?(int) getZ():0; region.op(location[0]- shadowOffset, location[1]- shadowOffset, location[0]+ mRight - mLeft + shadowOffset, location[1]+ mBottom - mTop +(shadowOffset *3),Region.Op.DIFFERENCE); }else{ if(mBackground !=null&& mBackground.getOpacity()!=PixelFormat.TRANSPARENT){ // The SKIP_DRAW flag IS set and the background drawable exists, we remove // the background drawable's non-transparent parts from this transparent region. applyDrawableToTransparentRegion(mBackground, region); } if(mForegroundInfo !=null&& mForegroundInfo.mDrawable !=null && mForegroundInfo.mDrawable.getOpacity()!=PixelFormat.TRANSPARENT){ // Similarly, we remove the foreground drawable's non-transparent parts. applyDrawableToTransparentRegion(mForegroundInfo.mDrawable, region); } if(mDefaultFocusHighlight !=null && mDefaultFocusHighlight.getOpacity()!=PixelFormat.TRANSPARENT){ // Similarly, we remove the default focus highlight's non-transparent parts. applyDrawableToTransparentRegion(mDefaultFocusHighlight, region); } } } returntrue; } /** * Play a sound effect for this view. * * <p>The framework will play sound effects for some built in actions, such as * clicking, but you may wish to play these effects in your widget, * for instance, for internal navigation. * * <p>The sound effect will only be played if sound effects are enabled by the user, and * {@link #isSoundEffectsEnabled()} is true. * * @param soundConstant One of the constants defined in {@link SoundEffectConstants}. */ publicvoid playSoundEffect(@SoundEffectConstants.SoundEffectint soundConstant){ if(mAttachInfo ==null|| mAttachInfo.mRootCallbacks ==null||!isSoundEffectsEnabled()){ return; } mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant); } /** * BZZZTT!!1! * * <p>Provide haptic feedback to the user for this view. * * <p>The framework will provide haptic feedback for some built in actions, * such as long presses, but you may wish to provide feedback for your * own widget. * * <p>The feedback will only be performed if * {@link #isHapticFeedbackEnabled()} is true. * * @param feedbackConstant One of the constants defined in * {@link HapticFeedbackConstants} */ publicboolean performHapticFeedback(int feedbackConstant){ return performHapticFeedback(feedbackConstant,0); } /** * BZZZTT!!1! * * <p>Like {@link #performHapticFeedback(int)}, with additional options. * * @param feedbackConstant One of the constants defined in * {@link HapticFeedbackConstants} * @param flags Additional flags as per {@link HapticFeedbackConstants}. */ publicboolean performHapticFeedback(int feedbackConstant,int flags){ if(mAttachInfo ==null){ returnfalse; } //noinspection SimplifiableIfStatement if((flags &HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING)==0 &&!isHapticFeedbackEnabled()){ returnfalse; } return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant, (flags &HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)!=0); } /** * Request that the visibility of the status bar or other screen/window * decorations be changed. * * <p>This method is used to put the over device UI into temporary modes * where the user's attention is focused more on the application content, * by dimming or hiding surrounding system affordances. This is typically * used in conjunction with {@link Window#FEATURE_ACTION_BAR_OVERLAY * Window.FEATURE_ACTION_BAR_OVERLAY}, allowing the applications content * to be placed behind the action bar (and with these flags other system * affordances) so that smooth transitions between hiding and showing them * can be done. * * <p>Two representative examples of the use of system UI visibility is * implementing a content browsing application (like a magazine reader) * and a video playing application. * * <p>The first code shows a typical implementation of a View in a content * browsing application. In this implementation, the application goes * into a content-oriented mode by hiding the status bar and action bar, * and putting the navigation elements into lights out mode. The user can * then interact with content while in this mode. Such an application should * provide an easy way for the user to toggle out of the mode (such as to * check information in the status bar or access notifications). In the * implementation here, this is done simply by tapping on the content. * * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/ContentBrowserActivity.java * content} * * <p>This second code sample shows a typical implementation of a View * in a video playing application. In this situation, while the video is * playing the application would like to go into a complete full-screen mode, * to use as much of the display as possible for the video. When in this state * the user can not interact with the application; the system intercepts * touching on the screen to pop the UI out of full screen mode. See * {@link #fitSystemWindows(Rect)} for a sample layout that goes with this code. * * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java * content} * * @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE}, * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN}, * {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}, * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE}, * and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}. * * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController} * instead. */ @Deprecated publicvoid setSystemUiVisibility(int visibility){ if(visibility != mSystemUiVisibility){ mSystemUiVisibility = visibility; if(mParent !=null&& mAttachInfo !=null&&!mAttachInfo.mRecomputeGlobalAttributes){ mParent.recomputeViewAttributes(this); } } } /** * Returns the last {@link #setSystemUiVisibility(int)} that this view has requested. * @return Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE}, * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN}, * {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}, * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE}, * and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}. * * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController} * instead. */ @Deprecated publicint getSystemUiVisibility(){ return mSystemUiVisibility; } /** * Returns the current system UI visibility that is currently set for * the entire window. This is the combination of the * {@link #setSystemUiVisibility(int)} values supplied by all of the * views in the window. * * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController} * instead. */ @Deprecated publicint getWindowSystemUiVisibility(){ return mAttachInfo !=null? mAttachInfo.mSystemUiVisibility :0; } /** * Override to find out when the window's requested system UI visibility * has changed, that is the value returned by {@link #getWindowSystemUiVisibility()}. * This is different from the callbacks received through * {@link #setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener)} * in that this is only telling you about the local request of the window, * not the actual values applied by the system. * * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController} * instead. */ @Deprecated publicvoid onWindowSystemUiVisibilityChanged(int visible){ } /** * Dispatch callbacks to {@link #onWindowSystemUiVisibilityChanged(int)} down * the view hierarchy. * * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController} * instead. */ @Deprecated publicvoid dispatchWindowSystemUiVisiblityChanged(int visible){ onWindowSystemUiVisibilityChanged(visible); } /** * Set a listener to receive callbacks when the visibility of the system bar changes. * @param l The {@link OnSystemUiVisibilityChangeListener} to receive callbacks. * * @deprecated Use {@link WindowInsets#isVisible(int)} to find out about system bar visibilities * by setting a {@link OnApplyWindowInsetsListener} on this view. */ @Deprecated publicvoid setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener l){ getListenerInfo().mOnSystemUiVisibilityChangeListener = l; if(mParent !=null&& mAttachInfo !=null&&!mAttachInfo.mRecomputeGlobalAttributes){ mParent.recomputeViewAttributes(this); } } /** * Dispatch callbacks to {@link #setOnSystemUiVisibilityChangeListener} down * the view hierarchy. * * @deprecated Use {@link WindowInsets#isVisible(int)} to find out about system bar visibilities * by setting a {@link OnApplyWindowInsetsListener} on this view. */ @Deprecated publicvoid dispatchSystemUiVisibilityChanged(int visibility){ ListenerInfo li = mListenerInfo; if(li !=null&& li.mOnSystemUiVisibilityChangeListener !=null){ li.mOnSystemUiVisibilityChangeListener.onSystemUiVisibilityChange( visibility & PUBLIC_STATUS_BAR_VISIBILITY_MASK); } } boolean updateLocalSystemUiVisibility(int localValue,int localChanges){ int val =(mSystemUiVisibility&~localChanges)|(localValue&localChanges); if(val != mSystemUiVisibility){ setSystemUiVisibility(val); returntrue; } returnfalse; } /** @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicvoid setDisabledSystemUiVisibility(int flags){ if(mAttachInfo !=null){ if(mAttachInfo.mDisabledSystemUiVisibility != flags){ mAttachInfo.mDisabledSystemUiVisibility = flags; if(mParent !=null){ mParent.recomputeViewAttributes(this); } } } } /** * This needs to be a better API before it is exposed. For now, only the root view will get * notified. * @hide */ publicvoid onSystemBarAppearanceChanged(@WindowInsetsController.Appearanceint appearance){ } /** * Creates an image that the system displays during the drag and drop * operation. This is called a &quot;drag shadow&quot;. The default implementation * for a DragShadowBuilder based on a View returns an image that has exactly the same * appearance as the given View. The default also positions the center of the drag shadow * directly under the touch point. If no View is provided (the constructor with no parameters * is used), and {@link #onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} and * {@link #onDrawShadow(Canvas) onDrawShadow()} are not overridden, then the * default is an invisible drag shadow. * <p> * You are not required to use the View you provide to the constructor as the basis of the * drag shadow. The {@link #onDrawShadow(Canvas) onDrawShadow()} method allows you to draw * anything you want as the drag shadow. * </p> * <p> * You pass a DragShadowBuilder object to the system when you start the drag. The system * calls {@link #onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} to get the * size and position of the drag shadow. It uses this data to construct a * {@link android.graphics.Canvas} object, then it calls {@link #onDrawShadow(Canvas) onDrawShadow()} * so that your application can draw the shadow image in the Canvas. * </p> * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For a guide to implementing drag and drop features, read the * <a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and Drop</a> developer guide.</p> * </div> */ publicstaticclassDragShadowBuilder{ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) privatefinalWeakReference<View> mView; /** * Constructs a shadow image builder based on a View. By default, the resulting drag * shadow will have the same appearance and dimensions as the View, with the touch point * over the center of the View. * @param view A View. Any View in scope can be used. */ publicDragShadowBuilder(View view){ mView =newWeakReference<View>(view); } /** * Construct a shadow builder object with no associated View. This * constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)} * and {@link #onDrawShadow(Canvas)} methods are also overridden in order * to supply the drag shadow's dimensions and appearance without * reference to any View object. */ publicDragShadowBuilder(){ mView =newWeakReference<View>(null); } /** * Returns the View object that had been passed to the * {@link #DragShadowBuilder(View)} * constructor. If that View parameter was {@code null} or if the * {@link #DragShadowBuilder()} * constructor was used to instantiate the builder object, this method will return * null. * * @return The View object associate with this builder object. */ @SuppressWarnings({"JavadocReference"}) finalpublicView getView(){ return mView.get(); } /** * Provides the metrics for the shadow image. These include the dimensions of * the shadow image, and the point within that shadow that should * be centered under the touch location while dragging. * <p> * The default implementation sets the dimensions of the shadow to be the * same as the dimensions of the View itself and centers the shadow under * the touch point. * </p> * * @param outShadowSize A {@link android.graphics.Point} containing the width and height * of the shadow image. Your application must set {@link android.graphics.Point#x} to the * desired width and must set {@link android.graphics.Point#y} to the desired height of the * image. Since Android P, the width and height must be positive values. * * @param outShadowTouchPoint A {@link android.graphics.Point} for the position within the * shadow image that should be underneath the touch point during the drag and drop * operation. Your application must set {@link android.graphics.Point#x} to the * X coordinate and {@link android.graphics.Point#y} to the Y coordinate of this position. */ publicvoid onProvideShadowMetrics(Point outShadowSize,Point outShadowTouchPoint){ finalView view = mView.get(); if(view !=null){ outShadowSize.set(view.getWidth(), view.getHeight()); outShadowTouchPoint.set(outShadowSize.x /2, outShadowSize.y /2); }else{ Log.e(View.VIEW_LOG_TAG,"Asked for drag thumb metrics but no view"); } } /** * Draws the shadow image. The system creates the {@link android.graphics.Canvas} object * based on the dimensions it received from the * {@link #onProvideShadowMetrics(Point, Point)} callback. * * @param canvas A {@link android.graphics.Canvas} object in which to draw the shadow image. */ publicvoid onDrawShadow(Canvas canvas){ finalView view = mView.get(); if(view !=null){ view.draw(canvas); }else{ Log.e(View.VIEW_LOG_TAG,"Asked to draw drag shadow but no view"); } } } /** * @deprecated Use {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int) * startDragAndDrop()} for newer platform versions. */ @Deprecated publicfinalboolean startDrag(ClipData data,DragShadowBuilder shadowBuilder, Object myLocalState,int flags){ return startDragAndDrop(data, shadowBuilder, myLocalState, flags); } /** * Starts a drag and drop operation. When your application calls this method, it passes a * {@link android.view.View.DragShadowBuilder} object to the system. The * system calls this object's {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)} * to get metrics for the drag shadow, and then calls the object's * {@link DragShadowBuilder#onDrawShadow(Canvas)} to draw the drag shadow itself. * <p> * Once the system has the drag shadow, it begins the drag and drop operation by sending * drag events to all the View objects in your application that are currently visible. It does * this either by calling the View object's drag listener (an implementation of * {@link android.view.View.OnDragListener#onDrag(View,DragEvent) onDrag()} or by calling the * View object's {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} method. * Both are passed a {@link android.view.DragEvent} object that has a * {@link android.view.DragEvent#getAction()} value of * {@link android.view.DragEvent#ACTION_DRAG_STARTED}. * </p> * <p> * Your application can invoke {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, * int) startDragAndDrop()} on any attached View object. The View object does not need to be * the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to be related * to the View the user selected for dragging. * </p> * @param data A {@link android.content.ClipData} object pointing to the data to be * transferred by the drag and drop operation. * @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the * drag shadow. * @param myLocalState An {@link java.lang.Object} containing local data about the drag and * drop operation. When dispatching drag events to views in the same activity this object * will be available through {@link android.view.DragEvent#getLocalState()}. Views in other * activities will not have access to this data ({@link android.view.DragEvent#getLocalState()} * will return null). * <p> * myLocalState is a lightweight mechanism for the sending information from the dragged View * to the target Views. For example, it can contain flags that differentiate between a * a copy operation and a move operation. * </p> * @param flags Flags that control the drag and drop operation. This can be set to 0 for no * flags, or any combination of the following: * <ul> * <li>{@link #DRAG_FLAG_GLOBAL}</li> * <li>{@link #DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION}</li> * <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li> * <li>{@link #DRAG_FLAG_GLOBAL_URI_READ}</li> * <li>{@link #DRAG_FLAG_GLOBAL_URI_WRITE}</li> * <li>{@link #DRAG_FLAG_OPAQUE}</li> * <li>{@link #DRAG_FLAG_ACCESSIBILITY_ACTION}</li> * </ul> * @return {@code true} if the method completes successfully, or * {@code false} if it fails anywhere. Returning {@code false} means the system was unable to * do a drag because of another ongoing operation or some other reasons. */ publicfinalboolean startDragAndDrop(ClipData data,DragShadowBuilder shadowBuilder, Object myLocalState,int flags){ if(ViewDebug.DEBUG_DRAG){ Log.d(VIEW_LOG_TAG,"startDragAndDrop: data="+ data +" flags="+ flags); } if(mAttachInfo ==null){ Log.w(VIEW_LOG_TAG,"startDragAndDrop called on a detached view."); returnfalse; } if(!mAttachInfo.mViewRootImpl.mSurface.isValid()){ Log.w(VIEW_LOG_TAG,"startDragAndDrop called with an invalid surface."); returnfalse; } if(data !=null){ data.prepareToLeaveProcess((flags &View.DRAG_FLAG_GLOBAL)!=0); } Rect bounds =newRect(); getBoundsOnScreen(bounds,true); Point lastTouchPoint =newPoint(); mAttachInfo.mViewRootImpl.getLastTouchPoint(lastTouchPoint); finalViewRootImpl root = mAttachInfo.mViewRootImpl; // Skip surface logic since shadows and animation are not required during the a11y drag finalboolean a11yEnabled =AccessibilityManager.getInstance(mContext).isEnabled(); if(a11yEnabled &&(flags &View.DRAG_FLAG_ACCESSIBILITY_ACTION)!=0){ try{ IBinder token = mAttachInfo.mSession.performDrag( mAttachInfo.mWindow, flags,null, mAttachInfo.mViewRootImpl.getLastTouchSource(), 0f,0f,0f,0f, data); if(ViewDebug.DEBUG_DRAG){ Log.d(VIEW_LOG_TAG,"startDragAndDrop via a11y action returned "+ token); } if(token !=null){ root.setLocalDragState(myLocalState); mAttachInfo.mDragToken = token; mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this); setAccessibilityDragStarted(true); } return token !=null; }catch(Exception e){ Log.e(VIEW_LOG_TAG,"Unable to initiate a11y drag", e); returnfalse; } } Point shadowSize =newPoint(); Point shadowTouchPoint =newPoint(); shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint); if((shadowSize.x <0)||(shadowSize.y <0) ||(shadowTouchPoint.x <0)||(shadowTouchPoint.y <0)){ thrownewIllegalStateException("Drag shadow dimensions must not be negative"); } // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder // does not accept zero size surface. if(shadowSize.x ==0|| shadowSize.y ==0){ if(!sAcceptZeroSizeDragShadow){ thrownewIllegalStateException("Drag shadow dimensions must be positive"); } shadowSize.x =1; shadowSize.y =1; } if(ViewDebug.DEBUG_DRAG){ Log.d(VIEW_LOG_TAG,"drag shadow: width="+ shadowSize.x +" height="+ shadowSize.y +" shadowX="+ shadowTouchPoint.x +" shadowY="+ shadowTouchPoint.y); } finalSurfaceSession session =newSurfaceSession(); finalSurfaceControl surfaceControl =newSurfaceControl.Builder(session) .setName("drag surface") .setParent(root.getSurfaceControl()) .setBufferSize(shadowSize.x, shadowSize.y) .setFormat(PixelFormat.TRANSLUCENT) .setCallsite("View.startDragAndDrop") .build(); finalSurface surface =newSurface(); surface.copyFrom(surfaceControl); IBinder token =null; try{ finalCanvas canvas = isHardwareAccelerated() ? surface.lockHardwareCanvas() : surface.lockCanvas(null); try{ canvas.drawColor(0,PorterDuff.Mode.CLEAR); shadowBuilder.onDrawShadow(canvas); }finally{ surface.unlockCanvasAndPost(canvas); } token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl, root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y, shadowTouchPoint.x, shadowTouchPoint.y, data); if(ViewDebug.DEBUG_DRAG){ Log.d(VIEW_LOG_TAG,"performDrag returned "+ token); } if(token !=null){ if(mAttachInfo.mDragSurface !=null){ mAttachInfo.mDragSurface.release(); } mAttachInfo.mDragSurface = surface; mAttachInfo.mDragToken = token; // Cache the local state object for delivery with DragEvents root.setLocalDragState(myLocalState); if(a11yEnabled){ // Set for AccessibilityEvents mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this); } } return token !=null; }catch(Exception e){ Log.e(VIEW_LOG_TAG,"Unable to initiate drag", e); returnfalse; }finally{ if(token ==null){ surface.destroy(); } session.kill(); } } void setAccessibilityDragStarted(boolean started){ int pflags4 = mPrivateFlags4; if(started){ pflags4 |= PFLAG4_DRAG_A11Y_STARTED; }else{ pflags4 &=~PFLAG4_DRAG_A11Y_STARTED; } if(pflags4 != mPrivateFlags4){ mPrivateFlags4 = pflags4; sendWindowContentChangedAccessibilityEvent(CONTENT_CHANGE_TYPE_UNDEFINED); } } privateboolean startedSystemDragForAccessibility(){ return(mPrivateFlags4 & PFLAG4_DRAG_A11Y_STARTED)!=0; } /** * Cancels an ongoing drag and drop operation. * <p> * A {@link android.view.DragEvent} object with * {@link android.view.DragEvent#getAction()} value of * {@link android.view.DragEvent#ACTION_DRAG_ENDED} and * {@link android.view.DragEvent#getResult()} value of {@code false} * will be sent to every * View that received {@link android.view.DragEvent#ACTION_DRAG_STARTED} * even if they are not currently visible. * </p> * <p> * This method can be called on any View in the same window as the View on which * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int) startDragAndDrop} * was called. * </p> */ publicfinalvoid cancelDragAndDrop(){ if(ViewDebug.DEBUG_DRAG){ Log.d(VIEW_LOG_TAG,"cancelDragAndDrop"); } if(mAttachInfo ==null){ Log.w(VIEW_LOG_TAG,"cancelDragAndDrop called on a detached view."); return; } if(mAttachInfo.mDragToken !=null){ try{ mAttachInfo.mSession.cancelDragAndDrop(mAttachInfo.mDragToken,false); }catch(Exception e){ Log.e(VIEW_LOG_TAG,"Unable to cancel drag", e); } mAttachInfo.mDragToken =null; }else{ Log.e(VIEW_LOG_TAG,"No active drag to cancel"); } } /** * Updates the drag shadow for the ongoing drag and drop operation. * * @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the * new drag shadow. */ publicfinalvoid updateDragShadow(DragShadowBuilder shadowBuilder){ if(ViewDebug.DEBUG_DRAG){ Log.d(VIEW_LOG_TAG,"updateDragShadow"); } if(mAttachInfo ==null){ Log.w(VIEW_LOG_TAG,"updateDragShadow called on a detached view."); return; } if(mAttachInfo.mDragToken !=null){ try{ Canvas canvas = isHardwareAccelerated() ? mAttachInfo.mDragSurface.lockHardwareCanvas() : mAttachInfo.mDragSurface.lockCanvas(null); try{ canvas.drawColor(0,PorterDuff.Mode.CLEAR); shadowBuilder.onDrawShadow(canvas); }finally{ mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas); } }catch(Exception e){ Log.e(VIEW_LOG_TAG,"Unable to update drag shadow", e); } }else{ Log.e(VIEW_LOG_TAG,"No active drag"); } } /** * Starts a move from {startX, startY}, the amount of the movement will be the offset * between {startX, startY} and the new cursor positon. * @param startX horizontal coordinate where the move started. * @param startY vertical coordinate where the move started. * @return whether moving was started successfully. * @hide */ publicfinalboolean startMovingTask(float startX,float startY){ if(ViewDebug.DEBUG_POSITIONING){ Log.d(VIEW_LOG_TAG,"startMovingTask: {"+ startX +","+ startY +"}"); } try{ return mAttachInfo.mSession.startMovingTask(mAttachInfo.mWindow, startX, startY); }catch(RemoteException e){ Log.e(VIEW_LOG_TAG,"Unable to start moving", e); } returnfalse; } /** * Finish a window move task. * @hide */ publicvoid finishMovingTask(){ if(ViewDebug.DEBUG_POSITIONING){ Log.d(VIEW_LOG_TAG,"finishMovingTask"); } try{ mAttachInfo.mSession.finishMovingTask(mAttachInfo.mWindow); }catch(RemoteException e){ Log.e(VIEW_LOG_TAG,"Unable to finish moving", e); } } /** * Handles drag events sent by the system following a call to * {@link android.view.View#startDragAndDrop(ClipData,DragShadowBuilder,Object,int) * startDragAndDrop()}. * <p> * The system calls this method and passes a {@link DragEvent} object in response to drag and * drop events. This method can then call {@link DragEvent#getAction()} to determine the state * of the drag and drop operation. * <p> * The default implementation returns {@code false} unless an {@link OnReceiveContentListener} * has been set for this view (see {@link #setOnReceiveContentListener}), in which case * the default implementation does the following: * <ul> * <li>Returns {@code true} for an * {@link DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} event * <li>Calls {@link #performReceiveContent} for an * {@link DragEvent#ACTION_DROP ACTION_DROP} event * <li>Returns {@code true} for an {@link DragEvent#ACTION_DROP ACTION_DROP} event if the * {@code OnReceiveContentListener} consumed some or all of the content * </ul> * * @param event The {@link DragEvent} object sent by the system. The * {@link DragEvent#getAction()} method returns an action type constant that indicates the * type of drag event represented by this object. * @return {@code true} if the method successfully handled the drag event, otherwise * {@code false}. * <p> * The method must return {@code true} in response to an * {@link DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} action type to continue to * receive drag events for the current drag and drop operation. * <p> * The method should return {@code true} in response to an * {@link DragEvent#ACTION_DROP ACTION_DROP} action type if the dropped data was consumed * (at least partially); {@code false}, if none of the data was consumed. * <p> * For all other events, the return value is {@code false}. */ publicboolean onDragEvent(DragEvent event){ if(mListenerInfo ==null|| mListenerInfo.mOnReceiveContentListener ==null){ returnfalse; } // Accept drag events by default if there's an OnReceiveContentListener set. if(event.getAction()==DragEvent.ACTION_DRAG_STARTED){ returntrue; } if(event.getAction()==DragEvent.ACTION_DROP){ finalDragAndDropPermissions permissions =DragAndDropPermissions.obtain(event); if(permissions !=null){ permissions.takeTransient(); } finalContentInfo payload = newContentInfo.Builder(event.getClipData(), SOURCE_DRAG_AND_DROP) .setDragAndDropPermissions(permissions) .build(); ContentInfo remainingPayload = performReceiveContent(payload); // Return true unless none of the payload was consumed. return remainingPayload != payload; } returnfalse; } // Dispatches ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED events for pre-Nougat apps. boolean dispatchDragEnterExitInPreN(DragEvent event){ return callDragEventHandler(event); } /** * Detects if this View is enabled and has a drag event listener. * If both are true, then it calls the drag event listener with the * {@link android.view.DragEvent} it received. If the drag event listener returns * {@code true}, then dispatchDragEvent() returns {@code true}. * <p> * For all other cases, the method calls the * {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} drag event handler * method and returns its result. * </p> * <p> * This ensures that a drag event is always consumed, even if the View does not have a drag * event listener. However, if the View has a listener and the listener returns true, then * onDragEvent() is not called. * </p> */ publicboolean dispatchDragEvent(DragEvent event){ event.mEventHandlerWasCalled =true; if(event.mAction ==DragEvent.ACTION_DRAG_LOCATION || event.mAction ==DragEvent.ACTION_DROP){ // About to deliver an event with coordinates to this view. Notify that now this view // has drag focus. This will send exit/enter events as needed. getViewRootImpl().setDragFocus(this, event); } return callDragEventHandler(event); } finalboolean callDragEventHandler(DragEvent event){ finalboolean result; ListenerInfo li = mListenerInfo; //noinspection SimplifiableIfStatement if(li !=null&& li.mOnDragListener !=null&&(mViewFlags & ENABLED_MASK)== ENABLED && li.mOnDragListener.onDrag(this, event)){ result =true; }else{ result = onDragEvent(event); } switch(event.mAction){ caseDragEvent.ACTION_DRAG_STARTED:{ if(result && li !=null&& li.mOnDragListener !=null){ sendWindowContentChangedAccessibilityEvent( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } }break; caseDragEvent.ACTION_DRAG_ENTERED:{ mPrivateFlags2 |=View.PFLAG2_DRAG_HOVERED; refreshDrawableState(); }break; caseDragEvent.ACTION_DRAG_EXITED:{ mPrivateFlags2 &=~View.PFLAG2_DRAG_HOVERED; refreshDrawableState(); }break; caseDragEvent.ACTION_DROP:{ if(result && li !=null&&(li.mOnDragListener !=null || li.mOnReceiveContentListener !=null)){ sendWindowContentChangedAccessibilityEvent( AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_DROPPED); } }break; caseDragEvent.ACTION_DRAG_ENDED:{ sendWindowContentChangedAccessibilityEvent( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); mPrivateFlags2 &=~View.DRAG_MASK; refreshDrawableState(); }break; } return result; } boolean canAcceptDrag(){ return(mPrivateFlags2 & PFLAG2_DRAG_CAN_ACCEPT)!=0; } void sendWindowContentChangedAccessibilityEvent(int changeType){ if(AccessibilityManager.getInstance(mContext).isEnabled()){ AccessibilityEvent event =AccessibilityEvent.obtain(); event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); event.setContentChangeTypes(changeType); sendAccessibilityEventUnchecked(event); } } /** * This needs to be a better API (NOT ON VIEW) before it is exposed. If * it is ever exposed at all. * @hide */ @UnsupportedAppUsage publicvoid onCloseSystemDialogs(String reason){ } /** * Given a Drawable whose bounds have been set to draw into this view, * update a Region being computed for * {@link #gatherTransparentRegion(android.graphics.Region)} so * that any non-transparent parts of the Drawable are removed from the * given transparent region. * * @param dr The Drawable whose transparency is to be applied to the region. * @param region A Region holding the current transparency information, * where any parts of the region that are set are considered to be * transparent. On return, this region will be modified to have the * transparency information reduced by the corresponding parts of the * Drawable that are not transparent. * {@hide} */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicvoid applyDrawableToTransparentRegion(Drawable dr,Region region){ if(DBG){ Log.i("View","Getting transparent region for: "+this); } finalRegion r = dr.getTransparentRegion(); finalRect db = dr.getBounds(); finalAttachInfo attachInfo = mAttachInfo; if(r !=null&& attachInfo !=null){ finalint w = getRight()-getLeft(); finalint h = getBottom()-getTop(); if(db.left >0){ //Log.i("VIEW", "Drawable left " + db.left + " > view 0"); r.op(0,0, db.left, h,Region.Op.UNION); } if(db.right < w){ //Log.i("VIEW", "Drawable right " + db.right + " < view " + w); r.op(db.right,0, w, h,Region.Op.UNION); } if(db.top >0){ //Log.i("VIEW", "Drawable top " + db.top + " > view 0"); r.op(0,0, w, db.top,Region.Op.UNION); } if(db.bottom < h){ //Log.i("VIEW", "Drawable bottom " + db.bottom + " < view " + h); r.op(0, db.bottom, w, h,Region.Op.UNION); } finalint[] location = attachInfo.mTransparentLocation; getLocationInWindow(location); r.translate(location[0], location[1]); region.op(r,Region.Op.INTERSECT); }else{ region.op(db,Region.Op.DIFFERENCE); } } privatevoid checkForLongClick(long delay,float x,float y,int classification){ if((mViewFlags & LONG_CLICKABLE)== LONG_CLICKABLE ||(mViewFlags & TOOLTIP)== TOOLTIP){ mHasPerformedLongPress =false; if(mPendingCheckForLongPress ==null){ mPendingCheckForLongPress =newCheckForLongPress(); } mPendingCheckForLongPress.setAnchor(x, y); mPendingCheckForLongPress.rememberWindowAttachCount(); mPendingCheckForLongPress.rememberPressedState(); mPendingCheckForLongPress.setClassification(classification); postDelayed(mPendingCheckForLongPress, delay); } } /** * Inflate a view from an XML resource. This convenience method wraps the {@link * LayoutInflater} class, which provides a full range of options for view inflation. * * @param context The Context object for your activity or application. * @param resource The resource ID to inflate * @param root A view group that will be the parent. Used to properly inflate the * layout_* parameters. * @see LayoutInflater */ publicstaticView inflate(Context context,@LayoutResint resource,ViewGroup root){ LayoutInflater factory =LayoutInflater.from(context); return factory.inflate(resource, root); } /** * Scroll the view with standard behavior for scrolling beyond the normal * content boundaries. Views that call this method should override * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the * results of an over-scroll operation. * * Views can use this method to handle any touch or fling-based scrolling. * * @param deltaX Change in X in pixels * @param deltaY Change in Y in pixels * @param scrollX Current X scroll value in pixels before applying deltaX * @param scrollY Current Y scroll value in pixels before applying deltaY * @param scrollRangeX Maximum content scroll range along the X axis * @param scrollRangeY Maximum content scroll range along the Y axis * @param maxOverScrollX Number of pixels to overscroll by in either direction * along the X axis. * @param maxOverScrollY Number of pixels to overscroll by in either direction * along the Y axis. * @param isTouchEvent true if this scroll operation is the result of a touch event. * @return true if scrolling was clamped to an over-scroll boundary along either * axis, false otherwise. */ @SuppressWarnings({"UnusedParameters"}) protectedboolean overScrollBy(int deltaX,int deltaY, int scrollX,int scrollY, int scrollRangeX,int scrollRangeY, int maxOverScrollX,int maxOverScrollY, boolean isTouchEvent){ finalint overScrollMode = mOverScrollMode; finalboolean canScrollHorizontal = computeHorizontalScrollRange()> computeHorizontalScrollExtent(); finalboolean canScrollVertical = computeVerticalScrollRange()> computeVerticalScrollExtent(); finalboolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS || (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal); finalboolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS || (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical); int newScrollX = scrollX + deltaX; if(!overScrollHorizontal){ maxOverScrollX =0; } int newScrollY = scrollY + deltaY; if(!overScrollVertical){ maxOverScrollY =0; } // Clamp values if at the limits and record finalint left =-maxOverScrollX; finalint right = maxOverScrollX + scrollRangeX; finalint top =-maxOverScrollY; finalint bottom = maxOverScrollY + scrollRangeY; boolean clampedX =false; if(newScrollX > right){ newScrollX = right; clampedX =true; }elseif(newScrollX < left){ newScrollX = left; clampedX =true; } boolean clampedY =false; if(newScrollY > bottom){ newScrollY = bottom; clampedY =true; }elseif(newScrollY < top){ newScrollY = top; clampedY =true; } onOverScrolled(newScrollX, newScrollY, clampedX, clampedY); return clampedX || clampedY; } /** * Called by {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)} to * respond to the results of an over-scroll operation. * * @param scrollX New X scroll value in pixels * @param scrollY New Y scroll value in pixels * @param clampedX True if scrollX was clamped to an over-scroll boundary * @param clampedY True if scrollY was clamped to an over-scroll boundary */ protectedvoid onOverScrolled(int scrollX,int scrollY, boolean clampedX,boolean clampedY){ // Intentionally empty. } /** * Returns the over-scroll mode for this view. The result will be * one of {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} * (allow over-scrolling only if the view content is larger than the container), * or {@link #OVER_SCROLL_NEVER}. * * @return This view's over-scroll mode. */ @InspectableProperty(enumMapping ={ @EnumEntry(value = OVER_SCROLL_ALWAYS, name ="always"), @EnumEntry(value = OVER_SCROLL_IF_CONTENT_SCROLLS, name ="ifContentScrolls"), @EnumEntry(value = OVER_SCROLL_NEVER, name ="never") }) publicint getOverScrollMode(){ return mOverScrollMode; } /** * Set the over-scroll mode for this view. Valid over-scroll modes are * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} * (allow over-scrolling only if the view content is larger than the container), * or {@link #OVER_SCROLL_NEVER}. * * Setting the over-scroll mode of a view will have an effect only if the * view is capable of scrolling. * * @param overScrollMode The new over-scroll mode for this view. */ publicvoid setOverScrollMode(int overScrollMode){ if(overScrollMode != OVER_SCROLL_ALWAYS && overScrollMode != OVER_SCROLL_IF_CONTENT_SCROLLS && overScrollMode != OVER_SCROLL_NEVER){ thrownewIllegalArgumentException("Invalid overscroll mode "+ overScrollMode); } mOverScrollMode = overScrollMode; } /** * Enable or disable nested scrolling for this view. * * <p>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() stopping} * the nested scroll.</p> * * @param enabled true to enable nested scrolling, false to disable * * @see #isNestedScrollingEnabled() */ publicvoid setNestedScrollingEnabled(boolean enabled){ if(enabled){ mPrivateFlags3 |= PFLAG3_NESTED_SCROLLING_ENABLED; }else{ stopNestedScroll(); mPrivateFlags3 &=~PFLAG3_NESTED_SCROLLING_ENABLED; } } /** * Returns true if nested scrolling is enabled for this view. * * <p>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.</p> * * @return true if nested scrolling is enabled * * @see #setNestedScrollingEnabled(boolean) */ @InspectableProperty publicboolean isNestedScrollingEnabled(){ return(mPrivateFlags3 & PFLAG3_NESTED_SCROLLING_ENABLED)== PFLAG3_NESTED_SCROLLING_ENABLED; } /** * Begin a nestable scroll operation along the given axes. * * <p>A view starting a nested scroll promises to abide by the following contract:</p> * * <p>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()} to indicate the end of the nested scroll.</p> * * <p>If <code>startNestedScroll</code> 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.</p> * * <p>At each incremental step of the scroll the caller should invoke * {@link #dispatchNestedPreScroll(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.</p> * * <p>After applying the remainder of the scroll delta the caller should invoke * {@link #dispatchNestedScroll(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 ViewParent#onNestedScroll(View, int, int, int, int)}. * </p> * * @param axes Flags consisting of a combination of {@link #SCROLL_AXIS_HORIZONTAL} and/or * {@link #SCROLL_AXIS_VERTICAL}. * @return true if a cooperative parent was found and nested scrolling has been enabled for * the current gesture. * * @see #stopNestedScroll() * @see #dispatchNestedPreScroll(int, int, int[], int[]) * @see #dispatchNestedScroll(int, int, int, int, int[]) */ publicboolean startNestedScroll(int axes){ if(hasNestedScrollingParent()){ // Already in progress returntrue; } if(isNestedScrollingEnabled()){ ViewParent p = getParent(); View child =this; while(p !=null){ try{ if(p.onStartNestedScroll(child,this, axes)){ mNestedScrollingParent = p; p.onNestedScrollAccepted(child,this, axes); returntrue; } }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG,"ViewParent "+ p +" does not implement interface "+ "method onStartNestedScroll", e); // Allow the search upward to continue } if(p instanceofView){ child =(View) p; } p = p.getParent(); } } returnfalse; } /** * Stop a nested scroll in progress. * * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p> * * @see #startNestedScroll(int) */ publicvoid stopNestedScroll(){ if(mNestedScrollingParent !=null){ mNestedScrollingParent.onStopNestedScroll(this); mNestedScrollingParent =null; } } /** * Returns true if this view has a nested scrolling parent. * * <p>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.</p> * * @return whether this view has a nested scrolling parent */ publicboolean hasNestedScrollingParent(){ return mNestedScrollingParent !=null; } /** * Dispatch one step of a nested scroll in progress. * * <p>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() enabled} for this view this method does nothing.</p> * * <p>Compatible View implementations should also call * {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll} before * consuming a component of the scroll event themselves.</p> * * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view * @param offsetInWindow Optional. If not null, on return this will contain the offset * in local view coordinates of this view from before this operation * to after it completes. View implementations may use this to adjust * expected input coordinate tracking. * @return true if the event was dispatched, false if it could not be dispatched. * @see #dispatchNestedPreScroll(int, int, int[], int[]) */ publicboolean dispatchNestedScroll(int dxConsumed,int dyConsumed, int dxUnconsumed,int dyUnconsumed,@Nullable@Size(2)int[] offsetInWindow){ if(isNestedScrollingEnabled()&& mNestedScrollingParent !=null){ if(dxConsumed !=0|| dyConsumed !=0|| dxUnconsumed !=0|| dyUnconsumed !=0){ int startX =0; int startY =0; if(offsetInWindow !=null){ getLocationInWindow(offsetInWindow); startX = offsetInWindow[0]; startY = offsetInWindow[1]; } mNestedScrollingParent.onNestedScroll(this, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); if(offsetInWindow !=null){ getLocationInWindow(offsetInWindow); offsetInWindow[0]-= startX; offsetInWindow[1]-= startY; } returntrue; }elseif(offsetInWindow !=null){ // No motion, no dispatch. Keep offsetInWindow up to date. offsetInWindow[0]=0; offsetInWindow[1]=0; } } returnfalse; } /** * Dispatch one step of a nested scroll in progress before this view consumes any portion of it. * * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch. * <code>dispatchNestedPreScroll</code> 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.</p> * * @param dx Horizontal scroll distance in pixels * @param dy Vertical scroll distance in pixels * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx * and consumed[1] the consumed dy. * @param offsetInWindow Optional. If not null, on return this will contain the offset * in local view coordinates of this view from before this operation * to after it completes. View implementations may use this to adjust * expected input coordinate tracking. * @return true if the parent consumed some or all of the scroll delta * @see #dispatchNestedScroll(int, int, int, int, int[]) */ publicboolean dispatchNestedPreScroll(int dx,int dy, @Nullable@Size(2)int[] consumed,@Nullable@Size(2)int[] offsetInWindow){ if(isNestedScrollingEnabled()&& mNestedScrollingParent !=null){ if(dx !=0|| dy !=0){ int startX =0; int startY =0; if(offsetInWindow !=null){ getLocationInWindow(offsetInWindow); startX = offsetInWindow[0]; startY = offsetInWindow[1]; } if(consumed ==null){ if(mTempNestedScrollConsumed ==null){ mTempNestedScrollConsumed =newint[2]; } consumed = mTempNestedScrollConsumed; } consumed[0]=0; consumed[1]=0; mNestedScrollingParent.onNestedPreScroll(this, dx, dy, consumed); if(offsetInWindow !=null){ getLocationInWindow(offsetInWindow); offsetInWindow[0]-= startX; offsetInWindow[1]-= startY; } return consumed[0]!=0|| consumed[1]!=0; }elseif(offsetInWindow !=null){ offsetInWindow[0]=0; offsetInWindow[1]=0; } } returnfalse; } /** * Dispatch a fling to a nested scrolling parent. * * <p>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.</p> * * <p>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.</p> * * @param velocityX Horizontal fling velocity in pixels per second * @param velocityY Vertical fling velocity in pixels per second * @param consumed true if the child consumed the fling, false otherwise * @return true if the nested scrolling parent consumed or otherwise reacted to the fling */ publicboolean dispatchNestedFling(float velocityX,float velocityY,boolean consumed){ if(isNestedScrollingEnabled()&& mNestedScrollingParent !=null){ return mNestedScrollingParent.onNestedFling(this, velocityX, velocityY, consumed); } returnfalse; } /** * Dispatch a fling to a nested scrolling parent before it is processed by this view. * * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code> * 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 <code>true</code>, a nested * parent view consumed the fling and this view should not scroll as a result.</p> * * <p>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:</p> * * <ul> * <li>If a custom view is paged and needs to settle to a fixed page-point, do not * call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid * position regardless.</li> * <li>If a nested parent does consume the fling, this view should not scroll at all, * even to settle back to a valid idle position.</li> * </ul> * * <p>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.</p> * * @param velocityX Horizontal fling velocity in pixels per second * @param velocityY Vertical fling velocity in pixels per second * @return true if a nested scrolling parent consumed the fling */ publicboolean dispatchNestedPreFling(float velocityX,float velocityY){ if(isNestedScrollingEnabled()&& mNestedScrollingParent !=null){ return mNestedScrollingParent.onNestedPreFling(this, velocityX, velocityY); } returnfalse; } /** * Gets a scale factor that determines the distance the view should scroll * vertically in response to {@link MotionEvent#ACTION_SCROLL}. * @return The vertical scroll scale factor. * @hide */ @UnsupportedAppUsage protectedfloat getVerticalScrollFactor(){ if(mVerticalScrollFactor ==0){ TypedValue outValue =newTypedValue(); if(!mContext.getTheme().resolveAttribute( com.android.internal.R.attr.listPreferredItemHeight, outValue,true)){ thrownewIllegalStateException( "Expected theme to define listPreferredItemHeight."); } mVerticalScrollFactor = outValue.getDimension( mContext.getResources().getDisplayMetrics()); } return mVerticalScrollFactor; } /** * Gets a scale factor that determines the distance the view should scroll * horizontally in response to {@link MotionEvent#ACTION_SCROLL}. * @return The horizontal scroll scale factor. * @hide */ @UnsupportedAppUsage protectedfloat getHorizontalScrollFactor(){ // TODO: Should use something else. return getVerticalScrollFactor(); } /** * Return the value specifying the text direction or policy that was set with * {@link #setTextDirection(int)}. * * @return the defined text direction. It can be one of: * * {@link #TEXT_DIRECTION_INHERIT}, * {@link #TEXT_DIRECTION_FIRST_STRONG}, * {@link #TEXT_DIRECTION_ANY_RTL}, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE}, * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR}, * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL} * * @attr ref android.R.styleable#View_textDirection * * @hide */ @ViewDebug.ExportedProperty(category ="text", mapping ={ @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to ="INHERIT"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to ="FIRST_STRONG"), @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to ="ANY_RTL"), @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to ="LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to ="RTL"), @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to ="LOCALE"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to ="FIRST_STRONG_LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to ="FIRST_STRONG_RTL") }) @InspectableProperty(hasAttributeId =false, enumMapping ={ @EnumEntry(value = TEXT_DIRECTION_INHERIT, name ="inherit"), @EnumEntry(value = TEXT_DIRECTION_LOCALE, name ="locale"), @EnumEntry(value = TEXT_DIRECTION_ANY_RTL, name ="anyRtl"), @EnumEntry(value = TEXT_DIRECTION_LTR, name ="ltr"), @EnumEntry(value = TEXT_DIRECTION_RTL, name ="rtl"), @EnumEntry(value = TEXT_DIRECTION_FIRST_STRONG, name ="firstStrong"), @EnumEntry(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name ="firstStrongLtr"), @EnumEntry(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name ="firstStrongRtl"), }) @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicint getRawTextDirection(){ return(mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_MASK)>> PFLAG2_TEXT_DIRECTION_MASK_SHIFT; } /** * Set the text direction. * * @param textDirection the direction to set. Should be one of: * * {@link #TEXT_DIRECTION_INHERIT}, * {@link #TEXT_DIRECTION_FIRST_STRONG}, * {@link #TEXT_DIRECTION_ANY_RTL}, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR}, * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL}, * * Resolution will be done if the value is set to TEXT_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 #TEXT_DIRECTION_FIRST_STRONG}. * * @attr ref android.R.styleable#View_textDirection */ publicvoid setTextDirection(int textDirection){ if(getRawTextDirection()!= textDirection){ // Reset the current text direction and the resolved one mPrivateFlags2 &=~PFLAG2_TEXT_DIRECTION_MASK; resetResolvedTextDirection(); // Set the new text direction mPrivateFlags2 |=((textDirection << PFLAG2_TEXT_DIRECTION_MASK_SHIFT)& PFLAG2_TEXT_DIRECTION_MASK); // Do resolution resolveTextDirection(); // Notify change onRtlPropertiesChanged(getLayoutDirection()); // Refresh requestLayout(); invalidate(true); } } /** * Return the resolved text direction. * * @return the resolved text direction. Returns one of: * * {@link #TEXT_DIRECTION_FIRST_STRONG}, * {@link #TEXT_DIRECTION_ANY_RTL}, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE}, * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR}, * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL} * * @attr ref android.R.styleable#View_textDirection */ @ViewDebug.ExportedProperty(category ="text", mapping ={ @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to ="INHERIT"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to ="FIRST_STRONG"), @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to ="ANY_RTL"), @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to ="LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to ="RTL"), @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to ="LOCALE"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to ="FIRST_STRONG_LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to ="FIRST_STRONG_RTL") }) @InspectableProperty(hasAttributeId =false, enumMapping ={ @EnumEntry(value = TEXT_DIRECTION_LOCALE, name ="locale"), @EnumEntry(value = TEXT_DIRECTION_ANY_RTL, name ="anyRtl"), @EnumEntry(value = TEXT_DIRECTION_LTR, name ="ltr"), @EnumEntry(value = TEXT_DIRECTION_RTL, name ="rtl"), @EnumEntry(value = TEXT_DIRECTION_FIRST_STRONG, name ="firstStrong"), @EnumEntry(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name ="firstStrongLtr"), @EnumEntry(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name ="firstStrongRtl"), }) publicint getTextDirection(){ return(mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK)>> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; } /** * Resolve the text direction. * * @return true if resolution has been done, false otherwise. * * @hide */ publicboolean resolveTextDirection(){ // Reset any previous text direction resolution mPrivateFlags2 &=~(PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_DIRECTION_RESOLVED_MASK); if(hasRtlSupport()){ // Set resolved text direction flag depending on text direction flag finalint textDirection = getRawTextDirection(); switch(textDirection){ case TEXT_DIRECTION_INHERIT: if(!canResolveTextDirection()){ // We cannot do the resolution if there is no parent, so use the default one mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; // Resolution will need to happen again later returnfalse; } // Parent has not yet resolved, so we still return the default try{ if(!mParent.isTextDirectionResolved()){ mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; // Resolution will need to happen again later returnfalse; } }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; returntrue; } // Set current resolved direction to the same value as the parent's one int parentResolvedDirection; try{ parentResolvedDirection = mParent.getTextDirection(); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); parentResolvedDirection = TEXT_DIRECTION_LTR; } switch(parentResolvedDirection){ case TEXT_DIRECTION_FIRST_STRONG: case TEXT_DIRECTION_ANY_RTL: case TEXT_DIRECTION_LTR: case TEXT_DIRECTION_RTL: case TEXT_DIRECTION_LOCALE: case TEXT_DIRECTION_FIRST_STRONG_LTR: case TEXT_DIRECTION_FIRST_STRONG_RTL: mPrivateFlags2 |= (parentResolvedDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT); break; default: // Default resolved direction is "first strong" heuristic mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; } break; case TEXT_DIRECTION_FIRST_STRONG: case TEXT_DIRECTION_ANY_RTL: case TEXT_DIRECTION_LTR: case TEXT_DIRECTION_RTL: case TEXT_DIRECTION_LOCALE: case TEXT_DIRECTION_FIRST_STRONG_LTR: case TEXT_DIRECTION_FIRST_STRONG_RTL: // Resolved direction is the same as text direction mPrivateFlags2 |=(textDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT); break; default: // Default resolved direction is "first strong" heuristic mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; } }else{ // Default resolved direction is "first strong" heuristic mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; } // Set to resolved mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED; returntrue; } /** * Check if text direction resolution can be done. * * @return true if text direction resolution can be done otherwise return false. */ publicboolean canResolveTextDirection(){ switch(getRawTextDirection()){ case TEXT_DIRECTION_INHERIT: if(mParent !=null){ try{ return mParent.canResolveTextDirection(); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } } returnfalse; default: returntrue; } } /** * Reset resolved text direction. Text direction will be resolved during a call to * {@link #onMeasure(int, int)}. * * @hide */ @TestApi publicvoid resetResolvedTextDirection(){ // Reset any previous text direction resolution mPrivateFlags2 &=~(PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_DIRECTION_RESOLVED_MASK); // Set to default value mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; } /** * @return true if text direction is inherited. * * @hide */ publicboolean isTextDirectionInherited(){ return(getRawTextDirection()== TEXT_DIRECTION_INHERIT); } /** * @return true if text direction is resolved. */ publicboolean isTextDirectionResolved(){ return(mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED)== PFLAG2_TEXT_DIRECTION_RESOLVED; } /** * Return the value specifying the text alignment or policy that was set with * {@link #setTextAlignment(int)}. * * @return the defined text alignment. It can be one of: * * {@link #TEXT_ALIGNMENT_INHERIT}, * {@link #TEXT_ALIGNMENT_GRAVITY}, * {@link #TEXT_ALIGNMENT_CENTER}, * {@link #TEXT_ALIGNMENT_TEXT_START}, * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} * * @attr ref android.R.styleable#View_textAlignment * * @hide */ @ViewDebug.ExportedProperty(category ="text", mapping ={ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to ="INHERIT"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to ="GRAVITY"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to ="TEXT_START"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to ="TEXT_END"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to ="CENTER"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to ="VIEW_START"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to ="VIEW_END") }) @InspectableProperty(hasAttributeId =false, enumMapping ={ @EnumEntry(value = TEXT_ALIGNMENT_INHERIT, name ="inherit"), @EnumEntry(value = TEXT_ALIGNMENT_GRAVITY, name ="gravity"), @EnumEntry(value = TEXT_ALIGNMENT_TEXT_START, name ="textStart"), @EnumEntry(value = TEXT_ALIGNMENT_TEXT_END, name ="textEnd"), @EnumEntry(value = TEXT_ALIGNMENT_CENTER, name ="center"), @EnumEntry(value = TEXT_ALIGNMENT_VIEW_START, name ="viewStart"), @EnumEntry(value = TEXT_ALIGNMENT_VIEW_END, name ="viewEnd") }) @TextAlignment @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicint getRawTextAlignment(){ return(mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_MASK)>> PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT; } /** * Set the text alignment. * * @param textAlignment The text alignment to set. Should be one of * * {@link #TEXT_ALIGNMENT_INHERIT}, * {@link #TEXT_ALIGNMENT_GRAVITY}, * {@link #TEXT_ALIGNMENT_CENTER}, * {@link #TEXT_ALIGNMENT_TEXT_START}, * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} * * Resolution will be done if the value is set to TEXT_ALIGNMENT_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 #TEXT_ALIGNMENT_GRAVITY}. * * @attr ref android.R.styleable#View_textAlignment */ publicvoid setTextAlignment(@TextAlignmentint textAlignment){ if(textAlignment != getRawTextAlignment()){ // Reset the current and resolved text alignment mPrivateFlags2 &=~PFLAG2_TEXT_ALIGNMENT_MASK; resetResolvedTextAlignment(); // Set the new text alignment mPrivateFlags2 |= ((textAlignment << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT)& PFLAG2_TEXT_ALIGNMENT_MASK); // Do resolution resolveTextAlignment(); // Notify change onRtlPropertiesChanged(getLayoutDirection()); // Refresh requestLayout(); invalidate(true); } } /** * Return the resolved text alignment. * * @return the resolved text alignment. Returns one of: * * {@link #TEXT_ALIGNMENT_GRAVITY}, * {@link #TEXT_ALIGNMENT_CENTER}, * {@link #TEXT_ALIGNMENT_TEXT_START}, * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} * * @attr ref android.R.styleable#View_textAlignment */ @ViewDebug.ExportedProperty(category ="text", mapping ={ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to ="INHERIT"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to ="GRAVITY"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to ="TEXT_START"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to ="TEXT_END"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to ="CENTER"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to ="VIEW_START"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to ="VIEW_END") }) @InspectableProperty(enumMapping ={ @EnumEntry(value = TEXT_ALIGNMENT_GRAVITY, name ="gravity"), @EnumEntry(value = TEXT_ALIGNMENT_TEXT_START, name ="textStart"), @EnumEntry(value = TEXT_ALIGNMENT_TEXT_END, name ="textEnd"), @EnumEntry(value = TEXT_ALIGNMENT_CENTER, name ="center"), @EnumEntry(value = TEXT_ALIGNMENT_VIEW_START, name ="viewStart"), @EnumEntry(value = TEXT_ALIGNMENT_VIEW_END, name ="viewEnd") }) @TextAlignment publicint getTextAlignment(){ return(mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK)>> PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; } /** * Resolve the text alignment. * * @return true if resolution has been done, false otherwise. * * @hide */ publicboolean resolveTextAlignment(){ // Reset any previous text alignment resolution mPrivateFlags2 &=~(PFLAG2_TEXT_ALIGNMENT_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK); if(hasRtlSupport()){ // Set resolved text alignment flag depending on text alignment flag finalint textAlignment = getRawTextAlignment(); switch(textAlignment){ case TEXT_ALIGNMENT_INHERIT: // Check if we can resolve the text alignment if(!canResolveTextAlignment()){ // We cannot do the resolution if there is no parent so use the default mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; // Resolution will need to happen again later returnfalse; } // Parent has not yet resolved, so we still return the default try{ if(!mParent.isTextAlignmentResolved()){ mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; // Resolution will need to happen again later returnfalse; } }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; returntrue; } int parentResolvedTextAlignment; try{ parentResolvedTextAlignment = mParent.getTextAlignment(); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); parentResolvedTextAlignment = TEXT_ALIGNMENT_GRAVITY; } switch(parentResolvedTextAlignment){ case TEXT_ALIGNMENT_GRAVITY: case TEXT_ALIGNMENT_TEXT_START: case TEXT_ALIGNMENT_TEXT_END: case TEXT_ALIGNMENT_CENTER: case TEXT_ALIGNMENT_VIEW_START: case TEXT_ALIGNMENT_VIEW_END: // Resolved text alignment is the same as the parent resolved // text alignment mPrivateFlags2 |= (parentResolvedTextAlignment << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT); break; default: // Use default resolved text alignment mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; } break; case TEXT_ALIGNMENT_GRAVITY: case TEXT_ALIGNMENT_TEXT_START: case TEXT_ALIGNMENT_TEXT_END: case TEXT_ALIGNMENT_CENTER: case TEXT_ALIGNMENT_VIEW_START: case TEXT_ALIGNMENT_VIEW_END: // Resolved text alignment is the same as text alignment mPrivateFlags2 |=(textAlignment << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT); break; default: // Use default resolved text alignment mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; } }else{ // Use default resolved text alignment mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; } // Set the resolved mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED; returntrue; } /** * Check if text alignment resolution can be done. * * @return true if text alignment resolution can be done otherwise return false. */ publicboolean canResolveTextAlignment(){ switch(getRawTextAlignment()){ case TEXT_DIRECTION_INHERIT: if(mParent !=null){ try{ return mParent.canResolveTextAlignment(); }catch(AbstractMethodError e){ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()+ " does not fully implement ViewParent", e); } } returnfalse; default: returntrue; } } /** * Reset resolved text alignment. Text alignment will be resolved during a call to * {@link #onMeasure(int, int)}. * * @hide */ @TestApi publicvoid resetResolvedTextAlignment(){ // Reset any previous text alignment resolution mPrivateFlags2 &=~(PFLAG2_TEXT_ALIGNMENT_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK); // Set to default mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; } /** * @return true if text alignment is inherited. * * @hide */ publicboolean isTextAlignmentInherited(){ return(getRawTextAlignment()== TEXT_ALIGNMENT_INHERIT); } /** * @return true if text alignment is resolved. */ publicboolean isTextAlignmentResolved(){ return(mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED)== PFLAG2_TEXT_ALIGNMENT_RESOLVED; } /** * Generate a value suitable for use in {@link #setId(int)}. * This value will not collide with ID values generated at build time by aapt for R.id. * * @return a generated ID value */ publicstaticint generateViewId(){ for(;;){ finalint result = sNextGeneratedId.get(); // aapt-generated IDs have the high byte nonzero; clamp to the range under that. int newValue = result +1; if(newValue >0x00FFFFFF) newValue =1;// Roll over to 1, not 0. if(sNextGeneratedId.compareAndSet(result, newValue)){ return result; } } } privatestaticboolean isViewIdGenerated(int id){ return(id &0xFF000000)==0&&(id &0x00FFFFFF)!=0; } /** * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions. * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and * a normal View or a ViewGroup with * {@link android.view.ViewGroup#isTransitionGroup()} true. * @hide */ publicvoid captureTransitioningViews(List<View> transitioningViews){ if(getVisibility()==View.VISIBLE){ transitioningViews.add(this); } } /** * Adds all Views that have {@link #getTransitionName()} non-null to namedElements. * @param namedElements Will contain all Views in the hierarchy having a transitionName. * @hide */ publicvoid findNamedViews(Map<String,View> namedElements){ if(getVisibility()== VISIBLE || mGhostView !=null){ String transitionName = getTransitionName(); if(transitionName !=null){ namedElements.put(transitionName,this); } } } /** * Returns the pointer icon for the motion event, or null if it doesn't specify the icon. * The default implementation does not care the location or event types, but some subclasses * may use it (such as WebViews). * @param event The MotionEvent from a mouse * @param pointerIndex The index of the pointer for which to retrieve the {@link PointerIcon}. * This will be between 0 and {@link MotionEvent#getPointerCount()}. * @see PointerIcon */ publicPointerIcon onResolvePointerIcon(MotionEvent event,int pointerIndex){ finalfloat x = event.getX(pointerIndex); finalfloat y = event.getY(pointerIndex); if(isDraggingScrollBar()|| isOnScrollbarThumb(x, y)){ returnPointerIcon.getSystemIcon(mContext,PointerIcon.TYPE_ARROW); } return mPointerIcon; } /** * Set the pointer icon for the current view. * Passing {@code null} will restore the pointer icon to its default value. * @param pointerIcon A PointerIcon instance which will be shown when the mouse hovers. */ publicvoid setPointerIcon(PointerIcon pointerIcon){ mPointerIcon = pointerIcon; if(mAttachInfo ==null|| mAttachInfo.mHandlingPointerEvent){ return; } try{ mAttachInfo.mSession.updatePointerIcon(mAttachInfo.mWindow); }catch(RemoteException e){ } } /** * Gets the pointer icon for the current view. */ @InspectableProperty publicPointerIcon getPointerIcon(){ return mPointerIcon; } /** * Checks pointer capture status. * * @return true if the view has pointer capture. * @see #requestPointerCapture() * @see #hasPointerCapture() */ publicboolean hasPointerCapture(){ finalViewRootImpl viewRootImpl = getViewRootImpl(); if(viewRootImpl ==null){ returnfalse; } return viewRootImpl.hasPointerCapture(); } /** * Requests pointer capture mode. * <p> * When the window has pointer capture, the mouse pointer icon will disappear and will not * change its position. Enabling pointer capture will change the behavior of input devices in * the following ways: * <ul> * <li>Events from a mouse will be delivered with the source * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be * available through {@link MotionEvent#getX} and {@link MotionEvent#getY}.</li> * * <li>Events from a touchpad or trackpad will be delivered with the source * {@link InputDevice#SOURCE_TOUCHPAD}, where the absolute position of each of the pointers * on the touchpad will be available through {@link MotionEvent#getX(int)} and * {@link MotionEvent#getY(int)}, and their relative movements are stored in * {@link MotionEvent#AXIS_RELATIVE_X} and {@link MotionEvent#AXIS_RELATIVE_Y}.</li> * * <li>Events from other types of devices, such as touchscreens, will not be affected.</li> * </ul> * <p> * When pointer capture changes, connected mouse and trackpad devices may be reconfigured, * and their properties (such as their sources or motion ranges) may change. Use an * {@link android.hardware.input.InputManager.InputDeviceListener} to be notified when a device * changes (which may happen after enabling or disabling pointer capture), and use * {@link InputDevice#getDevice(int)} to get the updated {@link InputDevice}. * <p> * Events captured through pointer capture will be dispatched to * {@link OnCapturedPointerListener#onCapturedPointer(View, MotionEvent)} if an * {@link OnCapturedPointerListener} is set, and otherwise to * {@link #onCapturedPointerEvent(MotionEvent)}. * <p> * If the window already has pointer capture, this call does nothing. * <p> * The capture may be released through {@link #releasePointerCapture()}, or will be lost * automatically when the window loses focus. * * @see #releasePointerCapture() * @see #hasPointerCapture() * @see #onPointerCaptureChange(boolean) */ publicvoid requestPointerCapture(){ finalViewRootImpl viewRootImpl = getViewRootImpl(); if(viewRootImpl !=null){ viewRootImpl.requestPointerCapture(true); } } /** * Releases the pointer capture. * <p> * If the window does not have pointer capture, this call will do nothing. * @see #requestPointerCapture() * @see #hasPointerCapture() * @see #onPointerCaptureChange(boolean) */ publicvoid releasePointerCapture(){ finalViewRootImpl viewRootImpl = getViewRootImpl(); if(viewRootImpl !=null){ viewRootImpl.requestPointerCapture(false); } } /** * Called when the window has just acquired or lost pointer capture. * * @param hasCapture True if the view now has pointerCapture, false otherwise. */ @CallSuper publicvoid onPointerCaptureChange(boolean hasCapture){ } /** * @see #onPointerCaptureChange */ publicvoid dispatchPointerCaptureChanged(boolean hasCapture){ onPointerCaptureChange(hasCapture); } /** * Implement this method to handle captured pointer events * * @param event The captured pointer event. * @return True if the event was handled, false otherwise. * @see #requestPointerCapture() */ publicboolean onCapturedPointerEvent(MotionEvent event){ returnfalse; } /** * Interface definition for a callback to be invoked when a captured pointer event * is being dispatched this view. The callback will be invoked before the event is * given to the view. */ publicinterfaceOnCapturedPointerListener{ /** * Called when a captured pointer event is dispatched to a view. * @param view The view this event has been dispatched to. * @param event The captured event. * @return True if the listener has consumed the event, false otherwise. */ boolean onCapturedPointer(View view,MotionEvent event); } /** * Set a listener to receive callbacks when the pointer capture state of a view changes. * @param l The {@link OnCapturedPointerListener} to receive callbacks. */ publicvoid setOnCapturedPointerListener(OnCapturedPointerListener l){ getListenerInfo().mOnCapturedPointerListener = l; } // Properties // /** * A Property wrapper around the <code>alpha</code> functionality handled by the * {@link View#setAlpha(float)} and {@link View#getAlpha()} methods. */ publicstaticfinalProperty<View,Float> ALPHA =newFloatProperty<View>("alpha"){ @Override publicvoid setValue(View object,float value){ object.setAlpha(value); } @Override publicFloat get(View object){ return object.getAlpha(); } }; /** * A Property wrapper around the <code>translationX</code> functionality handled by the * {@link View#setTranslationX(float)} and {@link View#getTranslationX()} methods. */ publicstaticfinalProperty<View,Float> TRANSLATION_X =newFloatProperty<View>("translationX"){ @Override publicvoid setValue(View object,float value){ object.setTranslationX(value); } @Override publicFloat get(View object){ return object.getTranslationX(); } }; /** * A Property wrapper around the <code>translationY</code> functionality handled by the * {@link View#setTranslationY(float)} and {@link View#getTranslationY()} methods. */ publicstaticfinalProperty<View,Float> TRANSLATION_Y =newFloatProperty<View>("translationY"){ @Override publicvoid setValue(View object,float value){ object.setTranslationY(value); } @Override publicFloat get(View object){ return object.getTranslationY(); } }; /** * A Property wrapper around the <code>translationZ</code> functionality handled by the * {@link View#setTranslationZ(float)} and {@link View#getTranslationZ()} methods. */ publicstaticfinalProperty<View,Float> TRANSLATION_Z =newFloatProperty<View>("translationZ"){ @Override publicvoid setValue(View object,float value){ object.setTranslationZ(value); } @Override publicFloat get(View object){ return object.getTranslationZ(); } }; /** * A Property wrapper around the <code>x</code> functionality handled by the * {@link View#setX(float)} and {@link View#getX()} methods. */ publicstaticfinalProperty<View,Float> X =newFloatProperty<View>("x"){ @Override publicvoid setValue(View object,float value){ object.setX(value); } @Override publicFloat get(View object){ return object.getX(); } }; /** * A Property wrapper around the <code>y</code> functionality handled by the * {@link View#setY(float)} and {@link View#getY()} methods. */ publicstaticfinalProperty<View,Float> Y =newFloatProperty<View>("y"){ @Override publicvoid setValue(View object,float value){ object.setY(value); } @Override publicFloat get(View object){ return object.getY(); } }; /** * A Property wrapper around the <code>z</code> functionality handled by the * {@link View#setZ(float)} and {@link View#getZ()} methods. */ publicstaticfinalProperty<View,Float> Z =newFloatProperty<View>("z"){ @Override publicvoid setValue(View object,float value){ object.setZ(value); } @Override publicFloat get(View object){ return object.getZ(); } }; /** * A Property wrapper around the <code>rotation</code> functionality handled by the * {@link View#setRotation(float)} and {@link View#getRotation()} methods. */ publicstaticfinalProperty<View,Float> ROTATION =newFloatProperty<View>("rotation"){ @Override publicvoid setValue(View object,float value){ object.setRotation(value); } @Override publicFloat get(View object){ return object.getRotation(); } }; /** * A Property wrapper around the <code>rotationX</code> functionality handled by the * {@link View#setRotationX(float)} and {@link View#getRotationX()} methods. */ publicstaticfinalProperty<View,Float> ROTATION_X =newFloatProperty<View>("rotationX"){ @Override publicvoid setValue(View object,float value){ object.setRotationX(value); } @Override publicFloat get(View object){ return object.getRotationX(); } }; /** * A Property wrapper around the <code>rotationY</code> functionality handled by the * {@link View#setRotationY(float)} and {@link View#getRotationY()} methods. */ publicstaticfinalProperty<View,Float> ROTATION_Y =newFloatProperty<View>("rotationY"){ @Override publicvoid setValue(View object,float value){ object.setRotationY(value); } @Override publicFloat get(View object){ return object.getRotationY(); } }; /** * A Property wrapper around the <code>scaleX</code> functionality handled by the * {@link View#setScaleX(float)} and {@link View#getScaleX()} methods. */ publicstaticfinalProperty<View,Float> SCALE_X =newFloatProperty<View>("scaleX"){ @Override publicvoid setValue(View object,float value){ object.setScaleX(value); } @Override publicFloat get(View object){ return object.getScaleX(); } }; /** * A Property wrapper around the <code>scaleY</code> functionality handled by the * {@link View#setScaleY(float)} and {@link View#getScaleY()} methods. */ publicstaticfinalProperty<View,Float> SCALE_Y =newFloatProperty<View>("scaleY"){ @Override publicvoid setValue(View object,float value){ object.setScaleY(value); } @Override publicFloat get(View object){ return object.getScaleY(); } }; /** * A MeasureSpec encapsulates the layout requirements passed from parent to child. * Each MeasureSpec represents a requirement for either the width or the height. * A MeasureSpec is comprised of a size and a mode. There are three possible * modes: * <dl> * <dt>UNSPECIFIED</dt> * <dd> * The parent has not imposed any constraint on the child. It can be whatever size * it wants. * </dd> * * <dt>EXACTLY</dt> * <dd> * The parent has determined an exact size for the child. The child is going to be * given those bounds regardless of how big it wants to be. * </dd> * * <dt>AT_MOST</dt> * <dd> * The child can be as large as it wants up to the specified size. * </dd> * </dl> * * MeasureSpecs are implemented as ints to reduce object allocation. This class * is provided to pack and unpack the &lt;size, mode&gt; tuple into the int. */ publicstaticclassMeasureSpec{ privatestaticfinalint MODE_SHIFT =30; privatestaticfinalint MODE_MASK =0x3<< MODE_SHIFT; /** @hide */ @IntDef({UNSPECIFIED, EXACTLY, AT_MOST}) @Retention(RetentionPolicy.SOURCE) public@interfaceMeasureSpecMode{} /** * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ publicstaticfinalint UNSPECIFIED =0<< MODE_SHIFT; /** * Measure specification mode: The parent has determined an exact size * for the child. The child is going to be given those bounds regardless * of how big it wants to be. */ publicstaticfinalint EXACTLY =1<< MODE_SHIFT; /** * Measure specification mode: The child can be as large as it wants up * to the specified size. */ publicstaticfinalint AT_MOST =2<< MODE_SHIFT; /** * Creates a measure specification based on the supplied size and mode. * * The mode must always be one of the following: * <ul> * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> * </ul> * * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's * implementation was such that the order of arguments did not matter * and overflow in either value could impact the resulting MeasureSpec. * {@link android.widget.RelativeLayout} was affected by this bug. * Apps targeting API levels greater than 17 will get the fixed, more strict * behavior.</p> * * @param size the size of the measure specification * @param mode the mode of the measure specification * @return the measure specification based on size and mode */ publicstaticint makeMeasureSpec(@IntRange(from =0, to =(1<<MeasureSpec.MODE_SHIFT)- 1)int size, @MeasureSpecModeint mode){ if(sUseBrokenMakeMeasureSpec){ return size + mode; }else{ return(size &~MODE_MASK)|(mode & MODE_MASK); } } /** * Like {@link #makeMeasureSpec(int, int)}, but any spec with a mode of UNSPECIFIED * will automatically get a size of 0. Older apps expect this. * * @hide internal use only for compatibility with system widgets and older apps */ @UnsupportedAppUsage publicstaticint makeSafeMeasureSpec(int size,int mode){ if(sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED){ return0; } return makeMeasureSpec(size, mode); } /** * Extracts the mode from the supplied measure specification. * * @param measureSpec the measure specification to extract the mode from * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, * {@link android.view.View.MeasureSpec#AT_MOST} or * {@link android.view.View.MeasureSpec#EXACTLY} */ @MeasureSpecMode publicstaticint getMode(int measureSpec){ //noinspection ResourceType return(measureSpec & MODE_MASK); } /** * Extracts the size from the supplied measure specification. * * @param measureSpec the measure specification to extract the size from * @return the size in pixels defined in the supplied measure specification */ publicstaticint getSize(int measureSpec){ return(measureSpec &~MODE_MASK); } staticint adjust(int measureSpec,int delta){ finalint mode = getMode(measureSpec); int size = getSize(measureSpec); if(mode == UNSPECIFIED){ // No need to adjust size for UNSPECIFIED mode. return makeMeasureSpec(size, UNSPECIFIED); } size += delta; if(size <0){ Log.e(VIEW_LOG_TAG,"MeasureSpec.adjust: new size would be negative! ("+ size + ") spec: "+ toString(measureSpec)+" delta: "+ delta); size =0; } return makeMeasureSpec(size, mode); } /** * Returns a String representation of the specified measure * specification. * * @param measureSpec the measure specification to convert to a String * @return a String with the following format: "MeasureSpec: MODE SIZE" */ publicstaticString toString(int measureSpec){ int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb =newStringBuilder("MeasureSpec: "); if(mode == UNSPECIFIED) sb.append("UNSPECIFIED "); elseif(mode == EXACTLY) sb.append("EXACTLY "); elseif(mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } } privatefinalclassCheckForLongPressimplementsRunnable{ privateint mOriginalWindowAttachCount; privatefloat mX; privatefloat mY; privateboolean mOriginalPressedState; /** * The classification of the long click being checked: one of the * FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants. */ privateint mClassification; @UnsupportedAppUsage privateCheckForLongPress(){ } @Override publicvoid run(){ if((mOriginalPressedState == isPressed())&&(mParent !=null) && mOriginalWindowAttachCount == mWindowAttachCount){ recordGestureClassification(mClassification); if(performLongClick(mX, mY)){ mHasPerformedLongPress =true; } } } publicvoid setAnchor(float x,float y){ mX = x; mY = y; } publicvoid rememberWindowAttachCount(){ mOriginalWindowAttachCount = mWindowAttachCount; } publicvoid rememberPressedState(){ mOriginalPressedState = isPressed(); } publicvoid setClassification(int classification){ mClassification = classification; } } privatefinalclassCheckForTapimplementsRunnable{ publicfloat x; publicfloat y; @Override publicvoid run(){ mPrivateFlags &=~PFLAG_PREPRESSED; setPressed(true, x, y); finallong delay = ViewConfiguration.getLongPressTimeout()-ViewConfiguration.getTapTimeout(); checkForLongClick(delay, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } } privatefinalclassPerformClickimplementsRunnable{ @Override publicvoid run(){ recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); performClickInternal(); } } /** Records a classification for the current event stream. */ privatevoid recordGestureClassification(int classification){ if(classification == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION){ return; } // To avoid negatively impacting View performance, the latency and displacement metrics // are omitted. FrameworkStatsLog.write(FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(), classification); } /** * This method returns a ViewPropertyAnimator object, which can be used to animate * specific properties on this View. * * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View. */ publicViewPropertyAnimator animate(){ if(mAnimator ==null){ mAnimator =newViewPropertyAnimator(this); } return mAnimator; } /** * Sets the name of the View to be used to identify Views in Transitions. * Names should be unique in the View hierarchy. * * @param transitionName The name of the View to uniquely identify it for Transitions. */ publicfinalvoid setTransitionName(String transitionName){ mTransitionName = transitionName; } /** * Returns the name of the View to be used to identify Views in Transitions. * Names should be unique in the View hierarchy. * * <p>This returns null if the View has not been given a name.</p> * * @return The name used of the View to be used to identify Views in Transitions or null * if no name has been given. */ @ViewDebug.ExportedProperty @InspectableProperty publicString getTransitionName(){ return mTransitionName; } /** * @hide */ publicvoid requestKeyboardShortcuts(List<KeyboardShortcutGroup> data,int deviceId){ // Do nothing. } /** * Interface definition for a callback to be invoked when a hardware key event is * dispatched to this view. The callback will be invoked before the key event is * given to the view. This is only useful for hardware keyboards; a software input * method has no obligation to trigger this listener. */ publicinterfaceOnKeyListener{ /** * Called when a hardware key is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * <p>Key presses in software keyboards will generally NOT trigger this method, * although some may elect to do so in some situations. Do not assume a * software input method has to be key-based; even if it is, it may use key presses * in a different way than you expect, so there is no way to reliably catch soft * input key presses. * * @param v The view the key has been dispatched to. * @param keyCode The code for the physical key that was pressed * @param event The KeyEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onKey(View v,int keyCode,KeyEvent event); } /** * Interface definition for a callback to be invoked when a hardware key event hasn't * been handled by the view hierarchy. */ publicinterfaceOnUnhandledKeyEventListener{ /** * Called when a hardware key is dispatched to a view after being unhandled during normal * {@link KeyEvent} dispatch. * * @param v The view the key has been dispatched to. * @param event The KeyEvent object containing information about the event. * @return {@code true} if the listener has consumed the event, {@code false} otherwise. */ boolean onUnhandledKeyEvent(View v,KeyEvent event); } /** * Interface definition for a callback to be invoked when a touch event is * dispatched to this view. The callback will be invoked before the touch * event is given to the view. */ publicinterfaceOnTouchListener{ /** * Called when a touch event is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @param v The view the touch event has been dispatched to. * @param event The MotionEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onTouch(View v,MotionEvent event); } /** * Interface definition for a callback to be invoked when a hover event is * dispatched to this view. The callback will be invoked before the hover * event is given to the view. */ publicinterfaceOnHoverListener{ /** * Called when a hover event is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @param v The view the hover event has been dispatched to. * @param event The MotionEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onHover(View v,MotionEvent event); } /** * Interface definition for a callback to be invoked when a generic motion event is * dispatched to this view. The callback will be invoked before the generic motion * event is given to the view. */ publicinterfaceOnGenericMotionListener{ /** * Called when a generic motion event is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @param v The view the generic motion event has been dispatched to. * @param event The MotionEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onGenericMotion(View v,MotionEvent event); } /** * Interface definition for a callback to be invoked when a view has been clicked and held. */ publicinterfaceOnLongClickListener{ /** * Called when a view has been clicked and held. * * @param v The view that was clicked and held. * * @return true if the callback consumed the long click, false otherwise. */ boolean onLongClick(View v); } /** * Interface definition for a listener that's invoked when a drag event is dispatched to this * view. The listener is invoked before the view's own * {@link #onDragEvent(DragEvent)} method. To fall back to the view's * {@code onDragEvent(DragEvent)} behavior, return {@code false} from the listener method. * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For a guide to implementing drag and drop features, see the * <a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and drop</a> developer guide.</p> * </div> */ publicinterfaceOnDragListener{ /** * Called when a drag event is dispatched to a view. Enables listeners to override the * base behavior provided by {@link #onDragEvent(DragEvent)}. * * @param v The {@code View} that received the drag event. * @param event The event object for the drag event. * @return {@code true} if the drag event was handled successfully; {@code false}, if the * drag event was not handled. <b>Note:</b> A {@code false} return value triggers the * view's {@link #onDragEvent(DragEvent)} handler. */ boolean onDrag(View v,DragEvent event); } /** * Interface definition for a callback to be invoked when the focus state of * a view changed. */ publicinterfaceOnFocusChangeListener{ /** * Called when the focus state of a view has changed. * * @param v The view whose state has changed. * @param hasFocus The new focus state of v. */ void onFocusChange(View v,boolean hasFocus); } /** * Interface definition for a callback to be invoked when a view is clicked. */ publicinterfaceOnClickListener{ /** * Called when a view has been clicked. * * @param v The view that was clicked. */ void onClick(View v); } /** * Interface definition for a callback to be invoked when a view is context clicked. */ publicinterfaceOnContextClickListener{ /** * Called when a view is context clicked. * * @param v The view that has been context clicked. * @return true if the callback consumed the context click, false otherwise. */ boolean onContextClick(View v); } /** * Interface definition for a callback to be invoked when the context menu * for this view is being built. */ publicinterfaceOnCreateContextMenuListener{ /** * Called when the context menu for this view is being built. It is not * safe to hold onto the menu after this method returns. * * @param menu The context menu that is being built * @param v The view for which the context menu is being built * @param menuInfo Extra information about the item for which the * context menu should be shown. This information will vary * depending on the class of v. */ void onCreateContextMenu(ContextMenu menu,View v,ContextMenuInfo menuInfo); } /** * Interface definition for a callback to be invoked when the status bar changes * visibility. This reports <strong>global</strong> changes to the system UI * state, not what the application is requesting. * * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener) * * @deprecated Use {@link WindowInsets#isVisible(int)} to find out about system bar visibilities * by setting a {@link OnApplyWindowInsetsListener} on this view. */ @Deprecated publicinterfaceOnSystemUiVisibilityChangeListener{ /** * Called when the status bar changes visibility because of a call to * {@link View#setSystemUiVisibility(int)}. * * @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE}, * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, and {@link #SYSTEM_UI_FLAG_FULLSCREEN}. * This tells you the <strong>global</strong> state of these UI visibility * flags, not what your app is currently applying. */ publicvoid onSystemUiVisibilityChange(int visibility); } /** * Interface definition for a callback to be invoked when this view is attached * or detached from its window. */ publicinterfaceOnAttachStateChangeListener{ /** * Called when the view is attached to a window. * @param v The view that was attached */ publicvoid onViewAttachedToWindow(@NonNullView v); /** * Called when the view is detached from a window. * @param v The view that was detached */ publicvoid onViewDetachedFromWindow(@NonNullView v); } /** * Listener for applying window insets on a view in a custom way. * * <p>Apps may choose to implement this interface if they want to apply custom policy * to the way that window insets are treated for a view. If an OnApplyWindowInsetsListener * is set, its * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsets) onApplyWindowInsets} * method will be called instead of the View's own * {@link #onApplyWindowInsets(WindowInsets) onApplyWindowInsets} method. The listener * may optionally call the parameter View's <code>onApplyWindowInsets</code> method to apply * the View's normal behavior as part of its own.</p> */ publicinterfaceOnApplyWindowInsetsListener{ /** * When {@link View#setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener) set} * on a View, this listener method will be called instead of the view's own * {@link View#onApplyWindowInsets(WindowInsets) onApplyWindowInsets} method. * * @param v The view applying window insets * @param insets The insets to apply * @return The insets supplied, minus any insets that were consumed */ public@NonNullWindowInsets onApplyWindowInsets(@NonNullView v, @NonNullWindowInsets insets); } privatefinalclassUnsetPressedStateimplementsRunnable{ @Override publicvoid run(){ setPressed(false); } } /** * When a view becomes invisible checks if autofill considers the view invisible too. This * happens after the regular removal operation to make sure the operation is finished by the * time this is called. */ privatestaticclassVisibilityChangeForAutofillHandlerextendsHandler{ privatefinalAutofillManager mAfm; privatefinalView mView; privateVisibilityChangeForAutofillHandler(@NonNullAutofillManager afm, @NonNullView view){ mAfm = afm; mView = view; } @Override publicvoid handleMessage(Message msg){ mAfm.notifyViewVisibilityChanged(mView, mView.isShown()); } } /** * Base class for derived classes that want to save and restore their own * state in {@link android.view.View#onSaveInstanceState()}. */ publicstaticclassBaseSavedStateextendsAbsSavedState{ staticfinalint START_ACTIVITY_REQUESTED_WHO_SAVED =0b1; staticfinalint IS_AUTOFILLED =0b10; staticfinalint AUTOFILL_ID =0b100; // Flags that describe what data in this state is valid int mSavedData; String mStartActivityRequestWhoSaved; boolean mIsAutofilled; boolean mHideHighlight; int mAutofillViewId; /** * Constructor used when reading from a parcel. Reads the state of the superclass. * * @param source parcel to read from */ publicBaseSavedState(Parcel source){ this(source,null); } /** * Constructor used when reading from a parcel using a given class loader. * Reads the state of the superclass. * * @param source parcel to read from * @param loader ClassLoader to use for reading */ publicBaseSavedState(Parcel source,ClassLoader loader){ super(source, loader); mSavedData = source.readInt(); mStartActivityRequestWhoSaved = source.readString(); mIsAutofilled = source.readBoolean(); mHideHighlight = source.readBoolean(); mAutofillViewId = source.readInt(); } /** * Constructor called by derived classes when creating their SavedState objects * * @param superState The state of the superclass of this view */ publicBaseSavedState(Parcelable superState){ super(superState); } @Override publicvoid writeToParcel(Parcel out,int flags){ super.writeToParcel(out, flags); out.writeInt(mSavedData); out.writeString(mStartActivityRequestWhoSaved); out.writeBoolean(mIsAutofilled); out.writeBoolean(mHideHighlight); out.writeInt(mAutofillViewId); } publicstaticfinal@android.annotation.NonNullParcelable.Creator<BaseSavedState> CREATOR =newParcelable.ClassLoaderCreator<BaseSavedState>(){ @Override publicBaseSavedState createFromParcel(Parcel in){ returnnewBaseSavedState(in); } @Override publicBaseSavedState createFromParcel(Parcel in,ClassLoader loader){ returnnewBaseSavedState(in, loader); } @Override publicBaseSavedState[] newArray(int size){ returnnewBaseSavedState[size]; } }; } /** * A set of information given to a view when it is attached to its parent * window. */ finalstaticclassAttachInfo{ interfaceCallbacks{ void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId,boolean always); } /** * InvalidateInfo is used to post invalidate(int, int, int, int) messages * to a Handler. This class contains the target (View) to invalidate and * the coordinates of the dirty rectangle. * * For performance purposes, this class also implements a pool of up to * POOL_LIMIT objects that get reused. This reduces memory allocations * whenever possible. */ staticclassInvalidateInfo{ @UnsupportedAppUsage InvalidateInfo(){ } privatestaticfinalint POOL_LIMIT =10; privatestaticfinalSynchronizedPool<InvalidateInfo> sPool = newSynchronizedPool<InvalidateInfo>(POOL_LIMIT); @UnsupportedAppUsage View target; @UnsupportedAppUsage int left; @UnsupportedAppUsage int top; @UnsupportedAppUsage int right; @UnsupportedAppUsage int bottom; publicstaticInvalidateInfo obtain(){ InvalidateInfo instance = sPool.acquire(); return(instance !=null)? instance :newInvalidateInfo(); } publicvoid recycle(){ target =null; sPool.release(this); } } @UnsupportedAppUsage finalIWindowSession mSession; @UnsupportedAppUsage finalIWindow mWindow; finalIBinder mWindowToken; Display mDisplay; finalCallbacks mRootCallbacks; IWindowId mIWindowId; WindowId mWindowId; /** * The top view of the hierarchy. */ View mRootView; IBinder mPanelParentWindowToken; boolean mHardwareAccelerated; boolean mHardwareAccelerationRequested; ThreadedRenderer mThreadedRenderer; List<RenderNode> mPendingAnimatingRenderNodes; /** * The state of the display to which the window is attached, as reported * by {@link Display#getState()}. Note that the display state constants * declared by {@link Display} do not exactly line up with the screen state * constants declared by {@link View} (there are more display states than * screen states). */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) int mDisplayState =Display.STATE_UNKNOWN; /** * Scale factor used by the compatibility mode */ @UnsupportedAppUsage float mApplicationScale; /** * Indicates whether the application is in compatibility mode */ @UnsupportedAppUsage boolean mScalingRequired; /** * Left position of this view's window */ int mWindowLeft; /** * Top position of this view's window */ int mWindowTop; /** * Indicates whether views need to use 32-bit drawing caches */ boolean mUse32BitDrawingCache; /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * content of the window. */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.Q, publicAlternatives ="Use {@link WindowInsets#getInsets(int)}") finalRect mContentInsets =newRect(); /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * actual visible parts of the window. */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.Q, publicAlternatives ="Use {@link WindowInsets#getInsets(int)}") finalRect mVisibleInsets =newRect(); /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * stable system windows. */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.Q, publicAlternatives ="Use {@link WindowInsets#getInsets(int)}") finalRect mStableInsets =newRect(); /** * Current caption insets to the display coordinate. */ finalRect mCaptionInsets =newRect(); /** * In multi-window we force show the system bars. Because we don't want that the surface * size changes in this mode, we instead have a flag whether the system bars sizes should * always be consumed, so the app is treated like there are no virtual system bars at all. */ boolean mAlwaysConsumeSystemBars; /** * The internal insets given by this window. This value is * supplied by the client (through * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will * be given to the window manager when changed to be used in laying * out windows behind it. */ @UnsupportedAppUsage finalViewTreeObserver.InternalInsetsInfo mGivenInternalInsets =newViewTreeObserver.InternalInsetsInfo(); /** * Set to true when mGivenInternalInsets is non-empty. */ boolean mHasNonEmptyGivenInternalInsets; /** * All views in the window's hierarchy that serve as scroll containers, * used to determine if the window can be resized or must be panned * to adjust for a soft input area. */ @UnsupportedAppUsage finalArrayList<View> mScrollContainers =newArrayList<View>(); @UnsupportedAppUsage finalKeyEvent.DispatcherState mKeyDispatchState =newKeyEvent.DispatcherState(); /** * Indicates whether the view's window currently has the focus. */ @UnsupportedAppUsage boolean mHasWindowFocus; /** * The current visibility of the window. */ int mWindowVisibility; /** * Indicates the time at which drawing started to occur. */ @UnsupportedAppUsage long mDrawingTime; /** * Indicates whether the view's window is currently in touch mode. */ @UnsupportedAppUsage boolean mInTouchMode; /** * Indicates whether the view has requested unbuffered input dispatching for the current * event stream. */ boolean mUnbufferedDispatchRequested; /** * Indicates that ViewAncestor should trigger a global layout change * the next time it performs a traversal */ @UnsupportedAppUsage boolean mRecomputeGlobalAttributes; /** * Always report new attributes at next traversal. */ boolean mForceReportNewAttributes; /** * Set during a traveral if any views want to keep the screen on. */ @UnsupportedAppUsage boolean mKeepScreenOn; /** * Set during a traveral if the light center needs to be updated. */ boolean mNeedsUpdateLightCenter; /** * Bitwise-or of all of the values that views have passed to setSystemUiVisibility(). */ int mSystemUiVisibility; /** * Hack to force certain system UI visibility flags to be cleared. */ int mDisabledSystemUiVisibility; /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener * attached. */ boolean mHasSystemUiListeners; /** * Set if the visibility of any views has changed. */ @UnsupportedAppUsage boolean mViewVisibilityChanged; /** * Set to true if a view has been scrolled. */ @UnsupportedAppUsage boolean mViewScrollChanged; /** * Set to true if a pointer event is currently being handled. */ boolean mHandlingPointerEvent; /** * The window matrix of this view when it's on a {@link SurfaceControlViewHost} that is * embedded within a SurfaceView. */ Matrix mWindowMatrixInEmbeddedHierarchy; /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the transparent region computations. */ finalint[] mTransparentLocation =newint[2]; /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the ViewGroup.invalidateChild implementation. */ finalint[] mInvalidateChildLocation =newint[2]; /** * Global to the view hierarchy used as a temporary for dealing with * computing absolute on-screen location. */ finalint[] mTmpLocation =newint[2]; /** * Global to the view hierarchy used as a temporary for dealing with * x/y location when view is transformed. */ finalfloat[] mTmpTransformLocation =newfloat[2]; /** * The view tree observer used to dispatch global events like * layout, pre-draw, touch mode change, etc. */ @UnsupportedAppUsage finalViewTreeObserver mTreeObserver; /** * A Canvas used by the view hierarchy to perform bitmap caching. */ Canvas mCanvas; /** * The view root impl. */ finalViewRootImpl mViewRootImpl; /** * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This * handler can be used to pump events in the UI events queue. */ @UnsupportedAppUsage finalHandler mHandler; /** * Temporary for use in computing invalidate rectangles while * calling up the hierarchy. */ finalRect mTmpInvalRect =newRect(); /** * Temporary for use in computing hit areas with transformed views */ finalRectF mTmpTransformRect =newRectF(); /** * Temporary for use in computing hit areas with transformed views */ finalRectF mTmpTransformRect1 =newRectF(); /** * Temporary list of rectanges. */ finalList<RectF> mTmpRectList =newArrayList<>(); /** * Temporary for use in transforming invalidation rect */ finalMatrix mTmpMatrix =newMatrix(); /** * Temporary for use in transforming invalidation rect */ finalTransformation mTmpTransformation =newTransformation(); /** * Temporary for use in querying outlines from OutlineProviders */ finalOutline mTmpOutline =newOutline(); /** * Temporary list for use in collecting focusable descendents of a view. */ finalArrayList<View> mTempArrayList =newArrayList<View>(24); /** * The id of the window for accessibility purposes. */ int mAccessibilityWindowId =AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; /** * Flags related to accessibility processing. * * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS */ int mAccessibilityFetchFlags; /** * The drawable for highlighting accessibility focus. */ Drawable mAccessibilityFocusDrawable; /** * The drawable for highlighting autofilled views. * * @see #isAutofilled() */ Drawable mAutofilledDrawable; /** * Show where the margins, bounds and layout bounds are for each view. */ boolean mDebugLayout =DisplayProperties.debug_layout().orElse(false); /** * Point used to compute visible regions. */ finalPoint mPoint =newPoint(); /** * Used to track which View originated a requestLayout() call, used when * requestLayout() is called during layout. */ View mViewRequestingLayout; /** * Used to track the identity of the current drag operation. */ IBinder mDragToken; /** * The drag shadow surface for the current drag operation. */ publicSurface mDragSurface; /** * The view that currently has a tooltip displayed. */ View mTooltipHost; /** * The initial structure has been reported so the view is ready to report updates. */ boolean mReadyForContentCaptureUpdates; /** * Map(keyed by session) of content capture events that need to be notified after the view * hierarchy is traversed: value is either the view itself for appearead events, or its * autofill id for disappeared. */ SparseArray<ArrayList<Object>> mContentCaptureEvents; /** * Cached reference to the {@link ContentCaptureManager}. */ ContentCaptureManager mContentCaptureManager; /** * Listener used to fit content on window level. */ OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener; /** * The leash token of this view's parent when it's in an embedded hierarchy that is * re-parented to another window. */ IBinder mLeashedParentToken; /** * The accessibility view id of this view's parent when it's in an embedded * hierarchy that is re-parented to another window. */ int mLeashedParentAccessibilityViewId; /** * */ ScrollCaptureInternal mScrollCaptureInternal; /** * Creates a new set of attachment information with the specified * events handler and thread. * * @param handler the events handler the view must use */ AttachInfo(IWindowSession session,IWindow window,Display display, ViewRootImpl viewRootImpl,Handler handler,Callbacks effectPlayer, Context context){ mSession = session; mWindow = window; mWindowToken = window.asBinder(); mDisplay = display; mViewRootImpl = viewRootImpl; mHandler = handler; mRootCallbacks = effectPlayer; mTreeObserver =newViewTreeObserver(context); } @Nullable ContentCaptureManager getContentCaptureManager(@NonNullContext context){ if(mContentCaptureManager !=null){ return mContentCaptureManager; } mContentCaptureManager = context.getSystemService(ContentCaptureManager.class); return mContentCaptureManager; } void delayNotifyContentCaptureInsetsEvent(@NonNullInsets insets){ if(mContentCaptureManager ==null){ return; } ArrayList<Object> events = ensureEvents( mContentCaptureManager.getMainContentCaptureSession()); events.add(insets); } privatevoid delayNotifyContentCaptureEvent(@NonNullContentCaptureSession session, @NonNullView view,boolean appeared){ ArrayList<Object> events = ensureEvents(session); events.add(appeared ? view : view.getAutofillId()); } @NonNull privateArrayList<Object> ensureEvents(@NonNullContentCaptureSession session){ if(mContentCaptureEvents ==null){ // Most of the time there will be just one session, so intial capacity is 1 mContentCaptureEvents =newSparseArray<>(1); } int sessionId = session.getId(); // TODO: life would be much easier if we provided a MultiMap implementation somwhere... ArrayList<Object> events = mContentCaptureEvents.get(sessionId); if(events ==null){ events =newArrayList<>(); mContentCaptureEvents.put(sessionId, events); } return events; } @Nullable ScrollCaptureInternal getScrollCaptureInternal(){ if(mScrollCaptureInternal !=null){ mScrollCaptureInternal =newScrollCaptureInternal(); } return mScrollCaptureInternal; } AttachedSurfaceControl getRootSurfaceControl(){ return mViewRootImpl; } publicvoid dump(String prefix,PrintWriter writer){ String innerPrefix = prefix +" "; writer.println(prefix +"AttachInfo:"); writer.println(innerPrefix +"mHasWindowFocus="+ mHasWindowFocus); writer.println(innerPrefix +"mWindowVisibility="+ mWindowVisibility); writer.println(innerPrefix +"mInTouchMode="+ mInTouchMode); writer.println(innerPrefix +"mUnbufferedDispatchRequested=" + mUnbufferedDispatchRequested); } } /** * <p>ScrollabilityCache holds various fields used by a View when scrolling * is supported. This avoids keeping too many unused fields in most * instances of View.</p> */ privatestaticclassScrollabilityCacheimplementsRunnable{ /** * Scrollbars are not visible */ publicstaticfinalint OFF =0; /** * Scrollbars are visible */ publicstaticfinalint ON =1; /** * Scrollbars are fading away */ publicstaticfinalint FADING =2; publicboolean fadeScrollBars; publicint fadingEdgeLength; publicint scrollBarDefaultDelayBeforeFade; publicint scrollBarFadeDuration; publicint scrollBarSize; publicint scrollBarMinTouchTarget; @UnsupportedAppUsage publicScrollBarDrawable scrollBar; publicfloat[] interpolatorValues; @UnsupportedAppUsage publicView host; publicfinalPaint paint; publicfinalMatrix matrix; publicShader shader; publicfinalInterpolator scrollBarInterpolator =newInterpolator(1,2); privatestaticfinalfloat[] OPAQUE ={255}; privatestaticfinalfloat[] TRANSPARENT ={0.0f}; /** * When fading should start. This time moves into the future every time * a new scroll happens. Measured based on SystemClock.uptimeMillis() */ publiclong fadeStartTime; /** * The current state of the scrollbars: ON, OFF, or FADING */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicint state = OFF; privateint mLastColor; publicfinalRect mScrollBarBounds =newRect(); publicfinalRect mScrollBarTouchBounds =newRect(); publicstaticfinalint NOT_DRAGGING =0; publicstaticfinalint DRAGGING_VERTICAL_SCROLL_BAR =1; publicstaticfinalint DRAGGING_HORIZONTAL_SCROLL_BAR =2; publicint mScrollBarDraggingState = NOT_DRAGGING; publicfloat mScrollBarDraggingPos =0; publicScrollabilityCache(ViewConfiguration configuration,View host){ fadingEdgeLength = configuration.getScaledFadingEdgeLength(); scrollBarSize = configuration.getScaledScrollBarSize(); scrollBarMinTouchTarget = configuration.getScaledMinScrollbarTouchTarget(); scrollBarDefaultDelayBeforeFade =ViewConfiguration.getScrollDefaultDelay(); scrollBarFadeDuration =ViewConfiguration.getScrollBarFadeDuration(); paint =newPaint(); matrix =newMatrix(); // use use a height of 1, and then wack the matrix each time we // actually use it. shader =newLinearGradient(0,0,0,1,0xFF000000,0,Shader.TileMode.CLAMP); paint.setShader(shader); paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.DST_OUT)); this.host = host; } publicvoid setFadeColor(int color){ if(color != mLastColor){ mLastColor = color; if(color !=0){ shader =newLinearGradient(0,0,0,1, color |0xFF000000, color &0x00FFFFFF,Shader.TileMode.CLAMP); paint.setShader(shader); // Restore the default transfer mode (src_over) paint.setXfermode(null); }else{ shader =newLinearGradient(0,0,0,1,0xFF000000,0,Shader.TileMode.CLAMP); paint.setShader(shader); paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } } } publicvoid run(){ long now =AnimationUtils.currentAnimationTimeMillis(); if(now >= fadeStartTime){ // the animation fades the scrollbars out by changing // the opacity (alpha) from fully opaque to fully // transparent int nextFrame =(int) now; int framesCount =0; Interpolator interpolator = scrollBarInterpolator; // Start opaque interpolator.setKeyFrame(framesCount++, nextFrame, OPAQUE); // End transparent nextFrame += scrollBarFadeDuration; interpolator.setKeyFrame(framesCount, nextFrame, TRANSPARENT); state = FADING; // Kick off the fade animation host.invalidate(true); } } } privateclassSendAccessibilityEventThrottleimplementsRunnable{ publicvolatileboolean mIsPending; privateAccessibilityEvent mAccessibilityEvent; publicvoid post(AccessibilityEvent accessibilityEvent){ updateWithAccessibilityEvent(accessibilityEvent); if(!mIsPending){ mIsPending =true; postDelayed(this, ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); } } @Override publicvoid run(){ if(AccessibilityManager.getInstance(mContext).isEnabled()&& isShown()){ requestParentSendAccessibilityEvent(mAccessibilityEvent); } reset(); } publicvoid updateWithAccessibilityEvent(AccessibilityEvent accessibilityEvent){ mAccessibilityEvent = accessibilityEvent; } publicvoid reset(){ mIsPending =false; mAccessibilityEvent =null; } } /** * Resuable callback for sending * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event. */ privateclassSendViewScrolledAccessibilityEventextendsSendAccessibilityEventThrottle{ publicint mDeltaX; publicint mDeltaY; @Override publicvoid updateWithAccessibilityEvent(AccessibilityEvent accessibilityEvent){ super.updateWithAccessibilityEvent(accessibilityEvent); mDeltaX += accessibilityEvent.getScrollDeltaX(); mDeltaY += accessibilityEvent.getScrollDeltaY(); accessibilityEvent.setScrollDeltaX(mDeltaX); accessibilityEvent.setScrollDeltaY(mDeltaY); } @Override publicvoid reset(){ super.reset(); mDeltaX =0; mDeltaY =0; } } /** * Remove the pending callback for sending a throttled accessibility event. */ @UnsupportedAppUsage privatevoid cancel(@NullableSendAccessibilityEventThrottle callback){ if(callback ==null||!callback.mIsPending)return; removeCallbacks(callback); callback.reset(); } /** * <p> * This class represents a delegate that can be registered in a {@link View} * to enhance accessibility support via composition rather via inheritance. * It is specifically targeted to widget developers that extend basic View * classes i.e. classes in package android.view, that would like their * applications to be backwards compatible. * </p> * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For more information about making applications accessible, read the * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> * developer guide.</p> * </div> * <p> * A scenario in which a developer would like to use an accessibility delegate * is overriding a method introduced in a later API version than the minimal API * version supported by the application. For example, the method * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} is not available * in API version 4 when the accessibility APIs were first introduced. If a * developer would like their application to run on API version 4 devices (assuming * all other APIs used by the application are version 4 or lower) and take advantage * of this method, instead of overriding the method which would break the application's * backwards compatibility, they can override the corresponding method in this * delegate and register the delegate in the target View if the API version of * the system is high enough, i.e. the API version is the same as or higher than the API * version that introduced * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)}. * </p> * <p> * Here is an example implementation: * </p> * <code><pre><p> * if (Build.VERSION.SDK_INT >= 14) { * // If the API version is equal of higher than the version in * // which onInitializeAccessibilityNodeInfo was introduced we * // register a delegate with a customized implementation. * View view = findViewById(R.id.view_id); * view.setAccessibilityDelegate(new AccessibilityDelegate() { * public void onInitializeAccessibilityNodeInfo(View host, * AccessibilityNodeInfo info) { * // Let the default implementation populate the info. * super.onInitializeAccessibilityNodeInfo(host, info); * // Set some other information. * info.setEnabled(host.isEnabled()); * } * }); * } * </code></pre></p> * <p> * This delegate contains methods that correspond to the accessibility methods * in View. If a delegate has been specified the implementation in View hands * off handling to the corresponding method in this delegate. The default * implementation the delegate methods behaves exactly as the corresponding * method in View for the case of no accessibility delegate been set. Hence, * to customize the behavior of a View method, clients can override only the * corresponding delegate method without altering the behavior of the rest * accessibility related methods of the host view. * </p> * <p> * <strong>Note:</strong> 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 <i>before</i> * host methods. This prevents certain properties such as class name from * being modified by overriding * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)}, * as any changes will be overwritten by the host class. * <p> * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate * methods are called <i>after</i> host methods, which all properties to be * modified without being overwritten by the host class. */ publicstaticclassAccessibilityDelegate{ /** * Sends an accessibility event of the given type. If accessibility is not * enabled this method has no effect. * <p> * The default implementation behaves as {@link View#sendAccessibilityEvent(int) * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate * been set. * </p> * * @param host The View hosting the delegate. * @param eventType The type of the event to send. * * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int) */ publicvoid sendAccessibilityEvent(@NonNullView host,int eventType){ host.sendAccessibilityEventInternal(eventType); } /** * Performs the specified accessibility action on the view. For * possible accessibility actions look at {@link AccessibilityNodeInfo}. * <p> * The default implementation behaves as * {@link View#performAccessibilityAction(int, Bundle) * View#performAccessibilityAction(int, Bundle)} for the case of * no accessibility delegate been set. * </p> * * @param action The action to perform. * @return Whether the action was performed. * * @see View#performAccessibilityAction(int, Bundle) * View#performAccessibilityAction(int, Bundle) */ publicboolean performAccessibilityAction(@NonNullView host,int action, @NullableBundle args){ return host.performAccessibilityActionInternal(action, args); } /** * Sends an accessibility event. This method behaves exactly as * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an * empty {@link AccessibilityEvent} and does not perform a check whether * accessibility is enabled. * <p> * The default implementation behaves as * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent) * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for * the case of no accessibility delegate been set. * </p> * * @param host The View hosting the delegate. * @param event The event to send. * * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent) * View#sendAccessibilityEventUnchecked(AccessibilityEvent) */ publicvoid sendAccessibilityEventUnchecked(@NonNullView host, @NonNullAccessibilityEvent event){ host.sendAccessibilityEventUncheckedInternal(event); } /** * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then * to its children for adding their text content to the event. * <p> * The default implementation behaves as * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for * the case of no accessibility delegate been set. * </p> * * @param host The View hosting the delegate. * @param event The event. * @return True if the event population was completed. * * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ publicboolean dispatchPopulateAccessibilityEvent(@NonNullView host, @NonNullAccessibilityEvent event){ return host.dispatchPopulateAccessibilityEventInternal(event); } /** * Gives a chance to the host View to populate the accessibility event with its * text content. * <p> * The default implementation behaves as * {@link View#onPopulateAccessibilityEvent(AccessibilityEvent) * View#onPopulateAccessibilityEvent(AccessibilityEvent)} for * the case of no accessibility delegate been set. * </p> * * @param host The View hosting the delegate. * @param event The accessibility event which to populate. * * @see View#onPopulateAccessibilityEvent(AccessibilityEvent) * View#onPopulateAccessibilityEvent(AccessibilityEvent) */ publicvoid onPopulateAccessibilityEvent(@NonNullView host, @NonNullAccessibilityEvent event){ host.onPopulateAccessibilityEventInternal(event); } /** * Initializes an {@link AccessibilityEvent} with information about the * the host View which is the event source. * <p> * The default implementation behaves as * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent) * View#onInitializeAccessibilityEvent(AccessibilityEvent)} for * the case of no accessibility delegate been set. * </p> * * @param host The View hosting the delegate. * @param event The event to initialize. * * @see View#onInitializeAccessibilityEvent(AccessibilityEvent) * View#onInitializeAccessibilityEvent(AccessibilityEvent) */ publicvoid onInitializeAccessibilityEvent(@NonNullView host, @NonNullAccessibilityEvent event){ host.onInitializeAccessibilityEventInternal(event); } /** * Initializes an {@link AccessibilityNodeInfo} with information about the host view. * <p> * The default implementation behaves as * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} for * the case of no accessibility delegate been set. * </p> * * @param host The View hosting the delegate. * @param info The instance to initialize. * * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) */ publicvoid onInitializeAccessibilityNodeInfo(@NonNullView host, @NonNullAccessibilityNodeInfo info){ host.onInitializeAccessibilityNodeInfoInternal(info); } /** * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the * additional data. * <p> * This method only needs to be implemented if the View offers to provide additional data. * </p> * <p> * The default implementation behaves as * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle) * for the case where no accessibility delegate is set. * </p> * * @param host The View hosting the delegate. Never {@code null}. * @param info The info to which to add the extra data. Never {@code null}. * @param extraDataKey A key specifying the type of extra data to add to the info. The * extra data should be added to the {@link Bundle} returned by * the info's {@link AccessibilityNodeInfo#getExtras} method. Never * {@code null}. * @param arguments A {@link Bundle} holding any arguments relevant for this request. * May be {@code null} if the if the service provided no arguments. * * @see AccessibilityNodeInfo#setAvailableExtraData(List) */ publicvoid addExtraDataToAccessibilityNodeInfo(@NonNullView host, @NonNullAccessibilityNodeInfo info,@NonNullString extraDataKey, @NullableBundle arguments){ host.addExtraDataToAccessibilityNodeInfo(info, extraDataKey, arguments); } /** * Called when a child of the host View has requested sending an * {@link AccessibilityEvent} and gives an opportunity to the parent (the host) * to augment the event. * <p> * The default implementation behaves as * {@link ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) * ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent)} for * the case of no accessibility delegate been set. * </p> * * @param host The View hosting the delegate. * @param child The child which requests sending the event. * @param event The event to be sent. * @return True if the event should be sent * * @see ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) * ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) */ publicboolean onRequestSendAccessibilityEvent(@NonNullViewGroup host,@NonNullView child, @NonNullAccessibilityEvent event){ return host.onRequestSendAccessibilityEventInternal(child, event); } /** * 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. * <p> * The default implementation behaves as * {@link View#getAccessibilityNodeProvider() View#getAccessibilityNodeProvider()} for * the case of no accessibility delegate been set. * </p> * * @return The provider. * * @see AccessibilityNodeProvider */ public@NullableAccessibilityNodeProvider getAccessibilityNodeProvider( @NonNullView host){ returnnull; } /** * Returns an {@link AccessibilityNodeInfo} representing the host view from the * point of view of an {@link android.accessibilityservice.AccessibilityService}. * This method is responsible for obtaining an accessibility node info from a * pool of reusable instances and calling * {@link #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} on the host * view to initialize the former. * <p> * <strong>Note:</strong> The client is responsible for recycling the obtained * instance by calling {@link AccessibilityNodeInfo#recycle()} to minimize object * creation. * </p> * <p> * The default implementation behaves as * {@link View#createAccessibilityNodeInfo() View#createAccessibilityNodeInfo()} for * the case of no accessibility delegate been set. * </p> * @return A populated {@link AccessibilityNodeInfo}. * * @see AccessibilityNodeInfo * * @hide */ @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) publicAccessibilityNodeInfo createAccessibilityNodeInfo(@NonNullView host){ return host.createAccessibilityNodeInfoInternal(); } } privatestaticclassMatchIdPredicateimplementsPredicate<View>{ publicint mId; @Override publicboolean test(View view){ return(view.mID == mId); } } privatestaticclassMatchLabelForPredicateimplementsPredicate<View>{ privateint mLabeledId; @Override publicboolean test(View view){ return(view.mLabelForId == mLabeledId); } } /** * Returns the current scroll capture hint for this view. * * @return the current scroll capture hint */ @ScrollCaptureHint publicint getScrollCaptureHint(){ return(mPrivateFlags4 & PFLAG4_SCROLL_CAPTURE_HINT_MASK) >> PFLAG4_SCROLL_CAPTURE_HINT_SHIFT; } /** * Sets the scroll capture hint for this View. These flags affect the search for a potential * scroll capture targets. * * @param hint the scrollCaptureHint flags value to set */ publicvoid setScrollCaptureHint(@ScrollCaptureHintint hint){ mPrivateFlags4 &=~PFLAG4_SCROLL_CAPTURE_HINT_MASK; // Since include/exclude are mutually exclusive, exclude takes precedence. if((hint & SCROLL_CAPTURE_HINT_EXCLUDE)!=0){ hint &=~SCROLL_CAPTURE_HINT_INCLUDE; } mPrivateFlags4 |=((hint << PFLAG4_SCROLL_CAPTURE_HINT_SHIFT) & PFLAG4_SCROLL_CAPTURE_HINT_MASK); } /** * Sets the callback to receive scroll capture requests. This component is the adapter between * the scroll capture API and application UI code. If no callback is set, the system may provide * an implementation. Any value provided here will take precedence over a system version. * <p> * This view will be ignored when {@link #SCROLL_CAPTURE_HINT_EXCLUDE} is set in its {@link * #setScrollCaptureHint(int) scrollCaptureHint}, regardless whether a callback has been set. * <p> * It is recommended to set the scroll capture hint {@link #SCROLL_CAPTURE_HINT_INCLUDE} when * setting a custom callback to help ensure it is selected as the target. * * @param callback the new callback to assign */ publicfinalvoid setScrollCaptureCallback(@NullableScrollCaptureCallback callback){ getListenerInfo().mScrollCaptureCallback = callback; } /** {@hide} */ @Nullable publicScrollCaptureCallback createScrollCaptureCallbackInternal(@NonNullRect localVisibleRect, @NonNullPoint windowOffset){ if(mAttachInfo ==null){ returnnull; } if(mAttachInfo.mScrollCaptureInternal ==null){ mAttachInfo.mScrollCaptureInternal =newScrollCaptureInternal(); } return mAttachInfo.mScrollCaptureInternal.requestCallback(this, localVisibleRect, windowOffset); } /** * Dispatch a scroll capture search request down the view hierarchy. * * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to * the parent * @param windowOffset the offset of this view within the window * @param targets accepts potential scroll capture targets; {@link Consumer#accept * results.accept} may be called zero or more times on the calling * thread before onScrollCaptureSearch returns */ publicvoid dispatchScrollCaptureSearch( @NonNullRect localVisibleRect,@NonNullPoint windowOffset, @NonNullConsumer<ScrollCaptureTarget> targets){ onScrollCaptureSearch(localVisibleRect, windowOffset, targets); } /** * Called when scroll capture is requested, to search for appropriate content to scroll. If * applicable, this view adds itself to the provided list for consideration, subject to the * flags set by {@link #setScrollCaptureHint}. * * @param localVisibleRect the local visible rect of this view * @param windowOffset the offset of localVisibleRect within the window * @param targets accepts potential scroll capture targets; {@link Consumer#accept * results.accept} may be called zero or more times on the calling * thread before onScrollCaptureSearch returns * @throws IllegalStateException if this view is not attached to a window */ publicvoid onScrollCaptureSearch(@NonNullRect localVisibleRect, @NonNullPoint windowOffset,@NonNullConsumer<ScrollCaptureTarget> targets){ int hint = getScrollCaptureHint(); if((hint & SCROLL_CAPTURE_HINT_EXCLUDE)!=0){ return; } boolean rectIsVisible =true; // Apply clipBounds if present. if(mClipBounds !=null){ rectIsVisible = localVisibleRect.intersect(mClipBounds); } if(!rectIsVisible){ return; } // Get a callback provided by the framework, library or application. ScrollCaptureCallback callback = (mListenerInfo ==null)?null: mListenerInfo.mScrollCaptureCallback; // Try framework support for standard scrolling containers. if(callback ==null){ callback = createScrollCaptureCallbackInternal(localVisibleRect, windowOffset); } // If found, then add it to the list. if(callback !=null){ // Add to the list for consideration Point offset =newPoint(windowOffset.x, windowOffset.y); Rect rect =newRect(localVisibleRect); targets.accept(newScrollCaptureTarget(this, rect, offset, callback)); } } /** * Dump all private flags in readable format, useful for documentation and * consistency checking. */ privatestaticvoid dumpFlags(){ finalHashMap<String,String> found =Maps.newHashMap(); try{ for(Field field :View.class.getDeclaredFields()){ finalint modifiers = field.getModifiers(); if(Modifier.isStatic(modifiers)&&Modifier.isFinal(modifiers)){ if(field.getType().equals(int.class)){ finalint value = field.getInt(null); dumpFlag(found, field.getName(), value); }elseif(field.getType().equals(int[].class)){ finalint[] values =(int[]) field.get(null); for(int i =0; i < values.length; i++){ dumpFlag(found, field.getName()+"["+ i +"]", values[i]); } } } } }catch(IllegalAccessException e){ thrownewRuntimeException(e); } finalArrayList<String> keys =Lists.newArrayList(); keys.addAll(found.keySet()); Collections.sort(keys); for(String key : keys){ Log.d(VIEW_LOG_TAG, found.get(key)); } } privatestaticvoid dumpFlag(HashMap<String,String> found,String name,int value){ // Sort flags by prefix, then by bits, always keeping unique keys finalString bits =String.format("%32s",Integer.toBinaryString(value)).replace('0',' '); finalint prefix = name.indexOf('_'); finalString key =(prefix >0? name.substring(0, prefix): name)+ bits + name; finalString output = bits +" "+ name; found.put(key, output); } /** {@hide} */ publicvoid encode(@NonNullViewHierarchyEncoder stream){ stream.beginObject(this); encodeProperties(stream); stream.endObject(); } /** {@hide} */ @CallSuper protectedvoid encodeProperties(@NonNullViewHierarchyEncoder stream){ Object resolveId =ViewDebug.resolveId(getContext(), mID); if(resolveId instanceofString){ stream.addProperty("id",(String) resolveId); }else{ stream.addProperty("id", mID); } stream.addProperty("misc:transformation.alpha", mTransformationInfo !=null? mTransformationInfo.mAlpha :0); stream.addProperty("misc:transitionName", getTransitionName()); // layout stream.addProperty("layout:left", mLeft); stream.addProperty("layout:right", mRight); stream.addProperty("layout:top", mTop); stream.addProperty("layout:bottom", mBottom); stream.addProperty("layout:width", getWidth()); stream.addProperty("layout:height", getHeight()); stream.addProperty("layout:layoutDirection", getLayoutDirection()); stream.addProperty("layout:layoutRtl", isLayoutRtl()); stream.addProperty("layout:hasTransientState", hasTransientState()); stream.addProperty("layout:baseline", getBaseline()); // layout params ViewGroup.LayoutParams layoutParams = getLayoutParams(); if(layoutParams !=null){ stream.addPropertyKey("layoutParams"); layoutParams.encode(stream); } // scrolling stream.addProperty("scrolling:scrollX", mScrollX); stream.addProperty("scrolling:scrollY", mScrollY); // padding stream.addProperty("padding:paddingLeft", mPaddingLeft); stream.addProperty("padding:paddingRight", mPaddingRight); stream.addProperty("padding:paddingTop", mPaddingTop); stream.addProperty("padding:paddingBottom", mPaddingBottom); stream.addProperty("padding:userPaddingRight", mUserPaddingRight); stream.addProperty("padding:userPaddingLeft", mUserPaddingLeft); stream.addProperty("padding:userPaddingBottom", mUserPaddingBottom); stream.addProperty("padding:userPaddingStart", mUserPaddingStart); stream.addProperty("padding:userPaddingEnd", mUserPaddingEnd); // measurement stream.addProperty("measurement:minHeight", mMinHeight); stream.addProperty("measurement:minWidth", mMinWidth); stream.addProperty("measurement:measuredWidth", mMeasuredWidth); stream.addProperty("measurement:measuredHeight", mMeasuredHeight); // drawing stream.addProperty("drawing:elevation", getElevation()); stream.addProperty("drawing:translationX", getTranslationX()); stream.addProperty("drawing:translationY", getTranslationY()); stream.addProperty("drawing:translationZ", getTranslationZ()); stream.addProperty("drawing:rotation", getRotation()); stream.addProperty("drawing:rotationX", getRotationX()); stream.addProperty("drawing:rotationY", getRotationY()); stream.addProperty("drawing:scaleX", getScaleX()); stream.addProperty("drawing:scaleY", getScaleY()); stream.addProperty("drawing:pivotX", getPivotX()); stream.addProperty("drawing:pivotY", getPivotY()); stream.addProperty("drawing:clipBounds", mClipBounds ==null?null: mClipBounds.toString()); stream.addProperty("drawing:opaque", isOpaque()); stream.addProperty("drawing:alpha", getAlpha()); stream.addProperty("drawing:transitionAlpha", getTransitionAlpha()); stream.addProperty("drawing:shadow", hasShadow()); stream.addProperty("drawing:solidColor", getSolidColor()); stream.addProperty("drawing:layerType", mLayerType); stream.addProperty("drawing:willNotDraw", willNotDraw()); stream.addProperty("drawing:hardwareAccelerated", isHardwareAccelerated()); stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing()); stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled()); stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering()); stream.addProperty("drawing:outlineAmbientShadowColor", getOutlineAmbientShadowColor()); stream.addProperty("drawing:outlineSpotShadowColor", getOutlineSpotShadowColor()); // focus stream.addProperty("focus:hasFocus", hasFocus()); stream.addProperty("focus:isFocused", isFocused()); stream.addProperty("focus:focusable", getFocusable()); stream.addProperty("focus:isFocusable", isFocusable()); stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode()); stream.addProperty("misc:clickable", isClickable()); stream.addProperty("misc:pressed", isPressed()); stream.addProperty("misc:selected", isSelected()); stream.addProperty("misc:touchMode", isInTouchMode()); stream.addProperty("misc:hovered", isHovered()); stream.addProperty("misc:activated", isActivated()); stream.addProperty("misc:visibility", getVisibility()); stream.addProperty("misc:fitsSystemWindows", getFitsSystemWindows()); stream.addProperty("misc:filterTouchesWhenObscured", getFilterTouchesWhenObscured()); stream.addProperty("misc:enabled", isEnabled()); stream.addProperty("misc:soundEffectsEnabled", isSoundEffectsEnabled()); stream.addProperty("misc:hapticFeedbackEnabled", isHapticFeedbackEnabled()); // theme attributes Resources.Theme theme = getContext().getTheme(); if(theme !=null){ stream.addPropertyKey("theme"); theme.encode(stream); } // view attribute information int n = mAttributes !=null? mAttributes.length :0; stream.addProperty("meta:__attrCount__", n/2); for(int i =0; i < n; i +=2){ stream.addProperty("meta:__attr__"+ mAttributes[i], mAttributes[i+1]); } stream.addProperty("misc:scrollBarStyle", getScrollBarStyle()); // text stream.addProperty("text:textDirection", getTextDirection()); stream.addProperty("text:textAlignment", getTextAlignment()); // accessibility CharSequence contentDescription = getContentDescription(); stream.addUserProperty("accessibility:contentDescription", contentDescription ==null?"": contentDescription.toString()); stream.addProperty("accessibility:labelFor", getLabelFor()); stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility()); } /** * Determine if this view is rendered on a round wearable device and is the main view * on the screen. */ boolean shouldDrawRoundScrollbar(){ if(!mResources.getConfiguration().isScreenRound()|| mAttachInfo ==null){ returnfalse; } finalView rootView = getRootView(); finalWindowInsets insets = getRootWindowInsets(); int height = getHeight(); int width = getWidth(); int displayHeight = rootView.getHeight(); int displayWidth = rootView.getWidth(); if(height != displayHeight || width != displayWidth){ returnfalse; } getLocationInWindow(mAttachInfo.mTmpLocation); return mAttachInfo.mTmpLocation[0]== insets.getStableInsetLeft() && mAttachInfo.mTmpLocation[1]== insets.getStableInsetTop(); } /** * Sets the tooltip text which will be displayed in a small popup next to the view. * <p> * The tooltip will be displayed: * <ul> * <li>On long click, unless it is handled otherwise (by OnLongClickListener or a context * menu). </li> * <li>On hover, after a brief delay since the pointer has stopped moving </li> * </ul> * <p> * <strong>Note:</strong> Do not override this method, as it will have no * effect on the text displayed in the tooltip. * * @param tooltipText the tooltip text, or null if no tooltip is required * @see #getTooltipText() * @attr ref android.R.styleable#View_tooltipText */ publicvoid setTooltipText(@NullableCharSequence tooltipText){ if(TextUtils.isEmpty(tooltipText)){ setFlags(0, TOOLTIP); hideTooltip(); mTooltipInfo =null; }else{ setFlags(TOOLTIP, TOOLTIP); if(mTooltipInfo ==null){ mTooltipInfo =newTooltipInfo(); mTooltipInfo.mShowTooltipRunnable =this::showHoverTooltip; mTooltipInfo.mHideTooltipRunnable =this::hideTooltip; mTooltipInfo.mHoverSlop =ViewConfiguration.get(mContext).getScaledHoverSlop(); mTooltipInfo.clearAnchorPos(); } mTooltipInfo.mTooltipText = tooltipText; } } /** * @hide Binary compatibility stub. To be removed when we finalize O APIs. */ @UnsupportedAppUsage publicvoid setTooltip(@NullableCharSequence tooltipText){ setTooltipText(tooltipText); } /** * Returns the view's tooltip text. * * <strong>Note:</strong> Do not override this method, as it will have no * effect on the text displayed in the tooltip. You must call * {@link #setTooltipText(CharSequence)} to modify the tooltip text. * * @return the tooltip text * @see #setTooltipText(CharSequence) * @attr ref android.R.styleable#View_tooltipText */ @InspectableProperty @Nullable publicCharSequence getTooltipText(){ return mTooltipInfo !=null? mTooltipInfo.mTooltipText :null; } /** * @hide Binary compatibility stub. To be removed when we finalize O APIs. */ @Nullable publicCharSequence getTooltip(){ return getTooltipText(); } privateboolean showTooltip(int x,int y,boolean fromLongClick){ if(mAttachInfo ==null|| mTooltipInfo ==null){ returnfalse; } if(fromLongClick &&(mViewFlags & ENABLED_MASK)!= ENABLED){ returnfalse; } if(TextUtils.isEmpty(mTooltipInfo.mTooltipText)){ returnfalse; } hideTooltip(); mTooltipInfo.mTooltipFromLongClick = fromLongClick; mTooltipInfo.mTooltipPopup =newTooltipPopup(getContext()); finalboolean fromTouch =(mPrivateFlags3 & PFLAG3_FINGER_DOWN)== PFLAG3_FINGER_DOWN; mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText); mAttachInfo.mTooltipHost =this; // The available accessibility actions have changed notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED); returntrue; } @UnsupportedAppUsage(maxTargetSdk =Build.VERSION_CODES.R, trackingBug =170729553) void hideTooltip(){ if(mTooltipInfo ==null){ return; } removeCallbacks(mTooltipInfo.mShowTooltipRunnable); if(mTooltipInfo.mTooltipPopup ==null){ return; } mTooltipInfo.mTooltipPopup.hide(); mTooltipInfo.mTooltipPopup =null; mTooltipInfo.mTooltipFromLongClick =false; mTooltipInfo.clearAnchorPos(); if(mAttachInfo !=null){ mAttachInfo.mTooltipHost =null; } // The available accessibility actions have changed notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED); } privateboolean showLongClickTooltip(int x,int y){ removeCallbacks(mTooltipInfo.mShowTooltipRunnable); removeCallbacks(mTooltipInfo.mHideTooltipRunnable); return showTooltip(x, y,true); } privateboolean showHoverTooltip(){ return showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY,false); } boolean dispatchTooltipHoverEvent(MotionEvent event){ if(mTooltipInfo ==null){ returnfalse; } switch(event.getAction()){ caseMotionEvent.ACTION_HOVER_MOVE: if((mViewFlags & TOOLTIP)!= TOOLTIP){ break; } if(!mTooltipInfo.mTooltipFromLongClick && mTooltipInfo.updateAnchorPos(event)){ if(mTooltipInfo.mTooltipPopup ==null){ // Schedule showing the tooltip after a timeout. removeCallbacks(mTooltipInfo.mShowTooltipRunnable); postDelayed(mTooltipInfo.mShowTooltipRunnable, ViewConfiguration.getHoverTooltipShowTimeout()); } // Hide hover-triggered tooltip after a period of inactivity. // Match the timeout used by NativeInputManager to hide the mouse pointer // (depends on SYSTEM_UI_FLAG_LOW_PROFILE being set). finalint timeout; if((getWindowSystemUiVisibility()& SYSTEM_UI_FLAG_LOW_PROFILE) == SYSTEM_UI_FLAG_LOW_PROFILE){ timeout =ViewConfiguration.getHoverTooltipHideShortTimeout(); }else{ timeout =ViewConfiguration.getHoverTooltipHideTimeout(); } removeCallbacks(mTooltipInfo.mHideTooltipRunnable); postDelayed(mTooltipInfo.mHideTooltipRunnable, timeout); } returntrue; caseMotionEvent.ACTION_HOVER_EXIT: mTooltipInfo.clearAnchorPos(); if(!mTooltipInfo.mTooltipFromLongClick){ hideTooltip(); } break; } returnfalse; } void handleTooltipKey(KeyEvent event){ switch(event.getAction()){ caseKeyEvent.ACTION_DOWN: if(event.getRepeatCount()==0){ hideTooltip(); } break; caseKeyEvent.ACTION_UP: handleTooltipUp(); break; } } privatevoid handleTooltipUp(){ if(mTooltipInfo ==null|| mTooltipInfo.mTooltipPopup ==null){ return; } removeCallbacks(mTooltipInfo.mHideTooltipRunnable); postDelayed(mTooltipInfo.mHideTooltipRunnable, ViewConfiguration.getLongPressTooltipHideTimeout()); } privateint getFocusableAttribute(TypedArray attributes){ TypedValue val =newTypedValue(); if(attributes.getValue(com.android.internal.R.styleable.View_focusable, val)){ if(val.type ==TypedValue.TYPE_INT_BOOLEAN){ return(val.data ==0? NOT_FOCUSABLE : FOCUSABLE); }else{ return val.data; } }else{ return FOCUSABLE_AUTO; } } /** * @return The content view of the tooltip popup currently being shown, or null if the tooltip * is not showing. * @hide */ @TestApi publicView getTooltipView(){ if(mTooltipInfo ==null|| mTooltipInfo.mTooltipPopup ==null){ returnnull; } return mTooltipInfo.mTooltipPopup.getContentView(); } /** * @return {@code true} if the default focus highlight is enabled, {@code false} otherwies. * @hide */ @TestApi publicstaticboolean isDefaultFocusHighlightEnabled(){ return sUseDefaultFocusHighlight; } /** * Dispatch a previously unhandled {@link KeyEvent} to this view. Unlike normal key dispatch, * this dispatches to ALL child views until it is consumed. The dispatch order is z-order * (visually on-top views first). * * @param evt the previously unhandled {@link KeyEvent}. * @return the {@link View} which consumed the event or {@code null} if not consumed. */ View dispatchUnhandledKeyEvent(KeyEvent evt){ if(onUnhandledKeyEvent(evt)){ returnthis; } returnnull; } /** * Allows this view to handle {@link KeyEvent}s which weren't handled by normal dispatch. This * occurs after the normal view hierarchy dispatch, but before the window callback. By default, * this will dispatch into all the listeners registered via * {@link #addOnUnhandledKeyEventListener(OnUnhandledKeyEventListener)} in last-in-first-out * order (most recently added will receive events first). * * @param event An unhandled event. * @return {@code true} if the event was handled, {@code false} otherwise. * @see #addOnUnhandledKeyEventListener */ boolean onUnhandledKeyEvent(@NonNullKeyEvent event){ if(mListenerInfo !=null&& mListenerInfo.mUnhandledKeyListeners !=null){ for(int i = mListenerInfo.mUnhandledKeyListeners.size()-1; i >=0;--i){ if(mListenerInfo.mUnhandledKeyListeners.get(i).onUnhandledKeyEvent(this, event)){ returntrue; } } } returnfalse; } boolean hasUnhandledKeyListener(){ return(mListenerInfo !=null&& mListenerInfo.mUnhandledKeyListeners !=null &&!mListenerInfo.mUnhandledKeyListeners.isEmpty()); } /** * Adds a listener which will receive unhandled {@link KeyEvent}s. This must be called on the * UI thread. * * @param listener a receiver of unhandled {@link KeyEvent}s. * @see #removeOnUnhandledKeyEventListener */ publicvoid addOnUnhandledKeyEventListener(OnUnhandledKeyEventListener listener){ ArrayList<OnUnhandledKeyEventListener> listeners = getListenerInfo().mUnhandledKeyListeners; if(listeners ==null){ listeners =newArrayList<>(); getListenerInfo().mUnhandledKeyListeners = listeners; } listeners.add(listener); if(listeners.size()==1&& mParent instanceofViewGroup){ ((ViewGroup) mParent).incrementChildUnhandledKeyListeners(); } } /** * Removes a listener which will receive unhandled {@link KeyEvent}s. This must be called on the * UI thread. * * @param listener a receiver of unhandled {@link KeyEvent}s. * @see #addOnUnhandledKeyEventListener */ publicvoid removeOnUnhandledKeyEventListener(OnUnhandledKeyEventListener listener){ if(mListenerInfo !=null){ if(mListenerInfo.mUnhandledKeyListeners !=null &&!mListenerInfo.mUnhandledKeyListeners.isEmpty()){ mListenerInfo.mUnhandledKeyListeners.remove(listener); if(mListenerInfo.mUnhandledKeyListeners.isEmpty()){ mListenerInfo.mUnhandledKeyListeners =null; if(mParent instanceofViewGroup){ ((ViewGroup) mParent).decrementChildUnhandledKeyListeners(); } } } } } /** * Set the view to be detached or not detached. * * @param detached Whether the view is detached. * * @hide */ protectedvoid setDetached(boolean detached){ if(detached){ mPrivateFlags4 |= PFLAG4_DETACHED; }else{ mPrivateFlags4 &=~PFLAG4_DETACHED; } } /** * Set whether this view enables automatic handwriting initiation. * * For a view with an active {@link InputConnection}, if auto handwriting is enabled then * stylus movement within its view boundary will automatically trigger the handwriting mode. * Check {@link android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)} for * more details about handwriting mode. * * If the View wants to initiate handwriting mode by itself, it can set this field to * {@code false} and call * {@link android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)} when there * is stylus movement detected. * * @see #onCreateInputConnection(EditorInfo) * @see android.view.inputmethod.InputMethodManager#startStylusHandwriting(View) * @param enabled whether auto handwriting initiation is enabled for this view. * @attr ref android.R.styleable#View_autoHandwritingEnabled */ publicvoid setAutoHandwritingEnabled(boolean enabled){ if(enabled){ mPrivateFlags4 |= PFLAG4_AUTO_HANDWRITING_ENABLED; }else{ mPrivateFlags4 &=~PFLAG4_AUTO_HANDWRITING_ENABLED; } updatePositionUpdateListener(); postUpdate(this::updateHandwritingArea); } /** * Return whether the View allows automatic handwriting initiation. Returns true if automatic * handwriting initiation is enabled, and verse visa. * @see #setAutoHandwritingEnabled(boolean) */ publicboolean isAutoHandwritingEnabled(){ return(mPrivateFlags4 & PFLAG4_AUTO_HANDWRITING_ENABLED) == PFLAG4_AUTO_HANDWRITING_ENABLED; } /** * Collects a {@link ViewTranslationRequest} which represents the content to be translated in * the view. * * <p>The default implementation does nothing.</p> * * @param supportedFormats the supported translation formats. For now, the only possible value * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}. * @param requestsCollector a {@link ViewTranslationRequest} collector that can be used to * collect the information to be translated in the view. The {@code requestsCollector} only * accepts one request; an IllegalStateException is thrown if more than one * {@link ViewTranslationRequest} is submitted to it. The {@link AutofillId} must be set on the * {@link ViewTranslationRequest}. */ publicvoid onCreateViewTranslationRequest(@NonNull@DataFormatint[] supportedFormats, @NonNullConsumer<ViewTranslationRequest> requestsCollector){ } /** * Collects {@link ViewTranslationRequest}s which represents the content to be translated * for the virtual views in the host view. This is called if this view returned a virtual * view structure from {@link #onProvideContentCaptureStructure} and the system determined that * those virtual views were relevant for translation. * * <p>The default implementation does nothing.</p> * * @param virtualIds the virtual view ids which represents the virtual views in the host * view. * @param supportedFormats the supported translation formats. For now, the only possible value * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}. * @param requestsCollector a {@link ViewTranslationRequest} collector that can be called * multiple times to collect the information to be translated in the host view. One * {@link ViewTranslationRequest} per virtual child. The {@link ViewTranslationRequest} must * contains the {@link AutofillId} corresponding to the virtualChildIds. Do not keep this * Consumer after the method returns. */ @SuppressLint("NullableCollection") publicvoid onCreateVirtualViewTranslationRequests(@NonNulllong[] virtualIds, @NonNull@DataFormatint[] supportedFormats, @NonNullConsumer<ViewTranslationRequest> requestsCollector){ // no-op } /** * Returns a {@link ViewTranslationCallback} that is used to display the translated information * or {@code null} if this View doesn't support translation. * * @hide */ @Nullable publicViewTranslationCallback getViewTranslationCallback(){ return mViewTranslationCallback; } /** * Sets a {@link ViewTranslationCallback} that is used to display/hide the translated * information. Developers can provide the customized implementation for show/hide translated * information. * * @param callback a {@link ViewTranslationCallback} that is used to control how to display the * translated information */ publicvoid setViewTranslationCallback(@NonNullViewTranslationCallback callback){ mViewTranslationCallback = callback; } /** * Clear the {@link ViewTranslationCallback} from this view. */ publicvoid clearViewTranslationCallback(){ mViewTranslationCallback =null; } /** * Returns the {@link ViewTranslationResponse} associated with this view. The response will be * set when the translation is done then {@link #onViewTranslationResponse} is called. The * {@link ViewTranslationCallback} can use to get {@link ViewTranslationResponse} to display the * translated information. * * @return a {@link ViewTranslationResponse} that contains the translated information associated * with this view or {@code null} if this View doesn't have the translation. */ @Nullable publicViewTranslationResponse getViewTranslationResponse(){ return mViewTranslationResponse; } /** * Called when the content from {@link View#onCreateViewTranslationRequest} had been translated * by the TranslationService. The {@link ViewTranslationResponse} should be saved here so that * the {@link ViewTranslationResponse} can be used to display the translation when the system * calls {@link ViewTranslationCallback#onShowTranslation}. * * <p> The default implementation will set the ViewTranslationResponse that can be get from * {@link View#getViewTranslationResponse}. </p> * * @param response a {@link ViewTranslationResponse} that contains the translated information * which can be shown in the view. */ publicvoid onViewTranslationResponse(@NonNullViewTranslationResponse response){ mViewTranslationResponse = response; } /** * Clears the ViewTranslationResponse stored by the default implementation of {@link * #onViewTranslationResponse}. * * @hide */ publicvoid clearViewTranslationResponse(){ mViewTranslationResponse =null; } /** * Called when the content from {@link View#onCreateVirtualViewTranslationRequests} had been * translated by the TranslationService. * * <p> The default implementation does nothing.</p> * * @param response a {@link ViewTranslationResponse} SparseArray for the request that send by * {@link View#onCreateVirtualViewTranslationRequests} that contains the translated information * which can be shown in the view. The key of SparseArray is the virtual child ids. */ publicvoid onVirtualViewTranslationResponses( @NonNullLongSparseArray<ViewTranslationResponse> response){ // no-op } /** * Dispatch to collect the {@link ViewTranslationRequest}s for translation purpose by traversing * the hierarchy when the app requests ui translation. Typically, this method should only be * overridden by subclasses that provide a view hierarchy (such as {@link ViewGroup}). Other * classes should override {@link View#onCreateViewTranslationRequest} for normal view or * override {@link View#onVirtualViewTranslationResponses} for view contains virtual children. * When requested to start the ui translation, the system will call this method to traverse the * view hierarchy to collect {@link ViewTranslationRequest}s and create a * {@link android.view.translation.Translator} to translate the requests. All the * {@link ViewTranslationRequest}s must be added when the traversal is done. * * <p> The default implementation calls {@link View#onCreateViewTranslationRequest} for normal * view or calls {@link View#onVirtualViewTranslationResponses} for view contains virtual * children to build {@link ViewTranslationRequest} if the view should be translated. * The view is marked as having {@link #setHasTransientState(boolean) transient state} so that * recycling of views doesn't prevent the system from attaching the response to it. Therefore, * if overriding this method, you should set or reset the transient state. </p> * * @param viewIds a map for the view's {@link AutofillId} and its virtual child ids or * {@code null} if the view doesn't have virtual child that should be translated. The virtual * child ids are the same virtual ids provided by ContentCapture. * @param supportedFormats the supported translation formats. For now, the only possible value * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}. * @param capability a {@link TranslationCapability} that holds translation capability. * information, e.g. source spec, target spec. * @param requests fill in with {@link ViewTranslationRequest}s for translation purpose. */ publicvoid dispatchCreateViewTranslationRequest(@NonNullMap<AutofillId,long[]> viewIds, @NonNull@DataFormatint[] supportedFormats, @NonNullTranslationCapability capability, @NonNullList<ViewTranslationRequest> requests){ AutofillId autofillId = getAutofillId(); if(viewIds.containsKey(autofillId)){ if(viewIds.get(autofillId)==null){ // TODO: avoiding the allocation per view onCreateViewTranslationRequest(supportedFormats, newViewTranslationRequestConsumer(requests)); }else{ onCreateVirtualViewTranslationRequests(viewIds.get(autofillId), supportedFormats, request ->{ requests.add(request); }); } } } privateclassViewTranslationRequestConsumerimplementsConsumer<ViewTranslationRequest>{ privatefinalList<ViewTranslationRequest> mRequests; privateboolean mCalled; ViewTranslationRequestConsumer(List<ViewTranslationRequest> requests){ mRequests = requests; } @Override publicvoid accept(ViewTranslationRequest request){ if(mCalled){ thrownewIllegalStateException("The translation Consumer is not reusable."); } mCalled =true; if(request !=null&& request.getKeys().size()>0){ mRequests.add(request); if(Log.isLoggable(CONTENT_CAPTURE_LOG_TAG,Log.VERBOSE)){ Log.v(CONTENT_CAPTURE_LOG_TAG,"Calling setHasTransientState(true) for " + getAutofillId()); } setHasTransientState(true); setHasTranslationTransientState(true); } } } /** * Called to generate a {@link DisplayHash} for this view. * * @param hashAlgorithm The hash algorithm to use when hashing the display. Must be one of * the values returned from * {@link DisplayHashManager#getSupportedHashAlgorithms()} * @param bounds The bounds for the content within the View to generate the hash for. If * bounds are null, the entire View's bounds will be used. If empty, it will * invoke the callback * {@link DisplayHashResultCallback#onDisplayHashError} with error * {@link DisplayHashResultCallback#DISPLAY_HASH_ERROR_INVALID_BOUNDS} * @param executor The executor that the callback should be invoked on. * @param callback The callback to handle the results of generating the display hash */ publicvoid generateDisplayHash(@NonNullString hashAlgorithm, @NullableRect bounds,@NonNullExecutor executor, @NonNullDisplayHashResultCallback callback){ IWindowSession session = getWindowSession(); if(session ==null){ callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW); return; } IWindow window = getWindow(); if(window ==null){ callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW); return; } Rect visibleBounds =newRect(); getGlobalVisibleRect(visibleBounds); if(bounds !=null&& bounds.isEmpty()){ callback.onDisplayHashError(DISPLAY_HASH_ERROR_INVALID_BOUNDS); return; } if(bounds !=null){ bounds.offset(visibleBounds.left, visibleBounds.top); visibleBounds.intersectUnchecked(bounds); } if(visibleBounds.isEmpty()){ callback.onDisplayHashError(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN); return; } RemoteCallback remoteCallback =newRemoteCallback(result -> executor.execute(()->{ DisplayHash displayHash = result.getParcelable(EXTRA_DISPLAY_HASH); int errorCode = result.getInt(EXTRA_DISPLAY_HASH_ERROR_CODE, DISPLAY_HASH_ERROR_UNKNOWN); if(displayHash !=null){ callback.onDisplayHashResult(displayHash); }else{ callback.onDisplayHashError(errorCode); } })); try{ session.generateDisplayHash(window, visibleBounds, hashAlgorithm, remoteCallback); }catch(RemoteException e){ Log.e(VIEW_LOG_TAG,"Failed to call generateDisplayHash"); callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); } } /** * The AttachedSurfaceControl itself is not a View, it is just the interface to the * windowing-system object that contains the entire view hierarchy. * For the root View of a given hierarchy see {@link #getRootView}. * @return The {@link android.view.AttachedSurfaceControl} interface for this View. * This will only return a non-null value when called between {@link #onAttachedToWindow} * and {@link #onDetachedFromWindow}. */ public@NullableAttachedSurfaceControl getRootSurfaceControl(){ if(mAttachInfo !=null){ return mAttachInfo.getRootSurfaceControl(); } returnnull; } }