/* * 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; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Shader; import android.graphics.Point; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.util.SparseArray; import android.view.ContextMenu.ContextMenuInfo; import android.view.animation.Animation; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; import android.widget.ScrollBarDrawable; import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; import java.util.ArrayList; import java.util.Arrays; import java.lang.ref.SoftReference; /** * <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"> * <p>For an introduction to using this class to develop your * application's user interface, read the Developer Guide documentation on * <strong><a href="{@docRoot}guide/topics/ui/index.html">User Interface</a></strong>. Special topics * include: * <br/><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> * <br/><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a> * <br/><a href="{@docRoot}guide/topics/ui/layout-objects.html">Common Layout Objects</a> * <br/><a href="{@docRoot}guide/topics/ui/binding.html">Binding to Data with AdapterView</a> * <br/><a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a> * <br/><a href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a> * <br/><a href="{@docRoot}guide/topics/ui/custom-components.html">Building Custom Components</a> * <br/><a href="{@docRoot}guide/topics/ui/how-android-draws.html">How Android Draws Views</a>. * </p> * </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 handled 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}. 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}.</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}</code></td> * <td>Called to determine the size requirements for this view and all * of its children. * </td> * </tr> * <tr> * <td><code>{@link #onLayout}</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}</code></td> * <td>Called when the size of this view has changed. * </td> * </tr> * * <tr> * <td>Drawing</td> * <td><code>{@link #onDraw}</code></td> * <td>Called when the view should render its content. * </td> * </tr> * * <tr> * <td rowspan="4">Event processing</td> * <td><code>{@link #onKeyDown}</code></td> * <td>Called when a new key event occurs. * </td> * </tr> * <tr> * <td><code>{@link #onKeyUp}</code></td> * <td>Called when a key up event occurs. * </td> * </tr> * <tr> * <td><code>{@link #onTrackballEvent}</code></td> * <td>Called when a trackball motion event occurs. * </td> * </tr> * <tr> * <td><code>{@link #onTouchEvent}</code></td> * <td>Called when a touch screen motion event occurs. * </td> * </tr> * * <tr> * <td rowspan="2">Focus</td> * <td><code>{@link #onFocusChanged}</code></td> * <td>Called when the view gains or loses focus. * </td> * </tr> * * <tr> * <td><code>{@link #onWindowFocusChanged}</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}</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> * <Button id="@+id/my_button" * android:layout_width="wrap_content" * android:layout_height="wrap_content" * android:text="@string/my_button_text"/> * </pre></li> * <li>From the onCreate method of an Activity, find the Button * <pre class="prettyprint"> * Button myButton = (Button) 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)} method and queried by calling * {@link #getPaddingLeft()}, {@link #getPaddingTop()}, * {@link #getPaddingRight()} and {@link #getPaddingBottom()}. * </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>FILL_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 gurantee that it and all of its descendants will fit * within this size. * </ul> * </p> * * <p> * To intiate a layout, call {@link #requestLayout}. This method is typically * called by a view on itself when it believes that is 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 rendering each view that * intersects the the invalid region. Because the tree is traversed in-order, * this means that parents will draw 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 for you * before calling back to its <code>onDraw()</code> method. * </p> * * <p> * Note that the framework will not draw views that are not in the invalid region. * </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)} 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> * * <a name="Animation"></a> * <h3>Animation</h3> * <p> * 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> * * @attr ref android.R.styleable#View_fitsSystemWindows * @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_scrollX * @attr ref android.R.styleable#View_scrollY * @attr ref android.R.styleable#View_scrollbarTrackHorizontal * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarSize * @attr ref android.R.styleable#View_scrollbars * @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 * * @see android.view.ViewGroup */ public class View implements Drawable.Callback, KeyEvent.Callback { private static final boolean DBG = false; /** * The logging tag used by this class with android.util.Log. */ protected static final String VIEW_LOG_TAG = "View"; /** * Used to mark a View that has no ID. */ public static final int NO_ID = -1; /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. */ private static final int NOT_FOCUSABLE = 0x00000000; /** * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling * setFlags. */ private static final int FOCUSABLE = 0x00000001; /** * Mask for use with setFlags indicating bits used for focus. */ private static final int FOCUSABLE_MASK = 0x00000001; /** * This view will adjust its padding to fit sytem windows (e.g. status bar) */ private static final int FITS_SYSTEM_WINDOWS = 0x00000002; /** * This view is visible. Use with {@link #setVisibility}. */ public static final int VISIBLE = 0x00000000; /** * This view is invisible, but it still takes up space for layout purposes. * Use with {@link #setVisibility}. */ public static final int INVISIBLE = 0x00000004; /** * This view is invisible, and it doesn't take any space for layout * purposes. Use with {@link #setVisibility}. */ public static final int GONE = 0x00000008; /** * Mask for use with setFlags indicating bits used for visibility. * {@hide} */ static final int VISIBILITY_MASK = 0x0000000C; private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE}; /** * This view is enabled. Intrepretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. * {@hide} */ static final int ENABLED = 0x00000000; /** * This view is disabled. Intrepretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. * {@hide} */ static final int DISABLED = 0x00000020; /** * Mask for use with setFlags indicating bits used for indicating whether * this view is enabled * {@hide} */ static final int ENABLED_MASK = 0x00000020; /** * This view won't draw. {@link #onDraw} 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} */ static final int WILL_NOT_DRAW = 0x00000080; /** * Mask for use with setFlags indicating bits used for indicating whether * this view is will draw * {@hide} */ static final int DRAW_MASK = 0x00000080; /** * <p>This view doesn't show scrollbars.</p> * {@hide} */ static final int SCROLLBARS_NONE = 0x00000000; /** * <p>This view shows horizontal scrollbars.</p> * {@hide} */ static final int SCROLLBARS_HORIZONTAL = 0x00000100; /** * <p>This view shows vertical scrollbars.</p> * {@hide} */ static final int SCROLLBARS_VERTICAL = 0x00000200; /** * <p>Mask for use with setFlags indicating bits used for indicating which * scrollbars are enabled.</p> * {@hide} */ static final int SCROLLBARS_MASK = 0x00000300; // note 0x00000400 and 0x00000800 are now available for next flags... /** * <p>This view doesn't show fading edges.</p> * {@hide} */ static final int FADING_EDGE_NONE = 0x00000000; /** * <p>This view shows horizontal fading edges.</p> * {@hide} */ static final int FADING_EDGE_HORIZONTAL = 0x00001000; /** * <p>This view shows vertical fading edges.</p> * {@hide} */ static final int FADING_EDGE_VERTICAL = 0x00002000; /** * <p>Mask for use with setFlags indicating bits used for indicating which * fading edges are enabled.</p> * {@hide} */ static final int FADING_EDGE_MASK = 0x00003000; /** * <p>Indicates this view can be clicked. When clickable, a View reacts * to clicks by notifying the OnClickListener.<p> * {@hide} */ static final int CLICKABLE = 0x00004000; /** * <p>Indicates this view is caching its drawing into a bitmap.</p> * {@hide} */ static final int DRAWING_CACHE_ENABLED = 0x00008000; /** * <p>Indicates that no icicle should be saved for this view.<p> * {@hide} */ static final int SAVE_DISABLED = 0x000010000; /** * <p>Mask for use with setFlags indicating bits used for the saveEnabled * property.</p> * {@hide} */ static final int SAVE_DISABLED_MASK = 0x000010000; /** * <p>Indicates that no drawing cache should ever be created for this view.<p> * {@hide} */ static final int WILL_NOT_CACHE_DRAWING = 0x000020000; /** * <p>Indicates this view can take / keep focus when int touch mode.</p> * {@hide} */ static final int FOCUSABLE_IN_TOUCH_MODE = 0x00040000; /** * <p>Enables low quality mode for the drawing cache.</p> */ public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000; /** * <p>Enables high quality mode for the drawing cache.</p> */ public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000; /** * <p>Enables automatic quality mode for the drawing cache.</p> */ public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000; private static final int[] 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} */ static final int 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} */ static final int LONG_CLICKABLE = 0x00200000; /** * <p>Indicates that this view gets its drawable states from its direct parent * and ignores its original internal states.</p> * * @hide */ static final int DUPLICATE_PARENT_STATE = 0x00400000; /** * 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. */ public static final int 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. */ public static final int 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. */ public static final int 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. */ public static final int SCROLLBARS_OUTSIDE_INSET = 0x03000000; /** * Mask to check if the scrollbar style is overlay or inset. * {@hide} */ static final int SCROLLBARS_INSET_MASK = 0x01000000; /** * Mask to check if the scrollbar style is inside or outside. * {@hide} */ static final int SCROLLBARS_OUTSIDE_MASK = 0x02000000; /** * Mask for scrollbar style. * {@hide} */ static final int 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}. */ public static final int KEEP_SCREEN_ON = 0x04000000; /** * View flag indicating whether this view should have sound effects enabled * for events such as clicking and touching. */ public static final int SOUND_EFFECTS_ENABLED = 0x08000000; /** * View flag indicating whether this view should have haptic feedback * enabled for events such as long presses. */ public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000; /** * Use with {@link #focusSearch}. Move focus to the previous selectable * item. */ public static final int FOCUS_BACKWARD = 0x00000001; /** * Use with {@link #focusSearch}. Move focus to the next selectable * item. */ public static final int FOCUS_FORWARD = 0x00000002; /** * Use with {@link #focusSearch}. Move focus to the left. */ public static final int FOCUS_LEFT = 0x00000011; /** * Use with {@link #focusSearch}. Move focus up. */ public static final int FOCUS_UP = 0x00000021; /** * Use with {@link #focusSearch}. Move focus to the right. */ public static final int FOCUS_RIGHT = 0x00000042; /** * Use with {@link #focusSearch}. Move focus down. */ public static final int FOCUS_DOWN = 0x00000082; /** * 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() */ protected static final int[] 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() */ protected static final int[] ENABLED_STATE_SET = {R.attr.state_enabled}; /** * 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() */ protected static final int[] FOCUSED_STATE_SET = {R.attr.state_focused}; /** * 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() */ protected static final int[] SELECTED_STATE_SET = {R.attr.state_selected}; /** * 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() * @hide */ protected static final int[] PRESSED_STATE_SET = {R.attr.state_pressed}; /** * 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() */ protected static final int[] WINDOW_FOCUSED_STATE_SET = {R.attr.state_window_focused}; // Doubles /** * Indicates the view is enabled and has the focus. * * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET */ protected static final int[] ENABLED_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, FOCUSED_STATE_SET); /** * Indicates the view is enabled and selected. * * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET */ protected static final int[] ENABLED_SELECTED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, SELECTED_STATE_SET); /** * Indicates the view is enabled and that its window has focus. * * @see #ENABLED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is focused and selected. * * @see #FOCUSED_STATE_SET * @see #SELECTED_STATE_SET */ protected static final int[] FOCUSED_SELECTED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, 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 */ protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, 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 */ protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(SELECTED_STATE_SET, 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 */ protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, 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 */ protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, 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 */ protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_SELECTED_STATE_SET, 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 */ protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_SELECTED_STATE_SET, 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 */ protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed and its window has the focus. * * @see #PRESSED_STATE_SET * @see #WINDOW_FOCUSED_STATE_SET */ protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed and selected. * * @see #PRESSED_STATE_SET * @see #SELECTED_STATE_SET */ protected static final int[] PRESSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, 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 */ protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed and focused. * * @see #PRESSED_STATE_SET * @see #FOCUSED_STATE_SET */ protected static final int[] PRESSED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, 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 */ protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed, focused and selected. * * @see #PRESSED_STATE_SET * @see #SELECTED_STATE_SET * @see #FOCUSED_STATE_SET */ protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, 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 */ protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed and enabled. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET */ protected static final int[] PRESSED_ENABLED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, 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 */ protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed, enabled and selected. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #SELECTED_STATE_SET */ protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, 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 */ protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * Indicates the view is pressed, enabled and focused. * * @see #PRESSED_STATE_SET * @see #ENABLED_STATE_SET * @see #FOCUSED_STATE_SET */ protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, 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 */ protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, 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 */ protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, 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 */ protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); /** * The order here is very important to {@link #getDrawableState()} */ private static final int[][] VIEW_STATE_SETS = { EMPTY_STATE_SET, // 0 0 0 0 0 WINDOW_FOCUSED_STATE_SET, // 0 0 0 0 1 SELECTED_STATE_SET, // 0 0 0 1 0 SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 0 0 1 1 FOCUSED_STATE_SET, // 0 0 1 0 0 FOCUSED_WINDOW_FOCUSED_STATE_SET, // 0 0 1 0 1 FOCUSED_SELECTED_STATE_SET, // 0 0 1 1 0 FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 0 1 1 1 ENABLED_STATE_SET, // 0 1 0 0 0 ENABLED_WINDOW_FOCUSED_STATE_SET, // 0 1 0 0 1 ENABLED_SELECTED_STATE_SET, // 0 1 0 1 0 ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 1 0 1 1 ENABLED_FOCUSED_STATE_SET, // 0 1 1 0 0 ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, // 0 1 1 0 1 ENABLED_FOCUSED_SELECTED_STATE_SET, // 0 1 1 1 0 ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 1 1 1 1 PRESSED_STATE_SET, // 1 0 0 0 0 PRESSED_WINDOW_FOCUSED_STATE_SET, // 1 0 0 0 1 PRESSED_SELECTED_STATE_SET, // 1 0 0 1 0 PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 0 0 1 1 PRESSED_FOCUSED_STATE_SET, // 1 0 1 0 0 PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET, // 1 0 1 0 1 PRESSED_FOCUSED_SELECTED_STATE_SET, // 1 0 1 1 0 PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 0 1 1 1 PRESSED_ENABLED_STATE_SET, // 1 1 0 0 0 PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET, // 1 1 0 0 1 PRESSED_ENABLED_SELECTED_STATE_SET, // 1 1 0 1 0 PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 0 1 1 PRESSED_ENABLED_FOCUSED_STATE_SET, // 1 1 1 0 0 PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 0 1 PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, // 1 1 1 1 0 PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 1 1 }; /** * Used by views that contain lists of items. This state indicates that * the view is showing the last item. * @hide */ protected static final int[] LAST_STATE_SET = {R.attr.state_last}; /** * Used by views that contain lists of items. This state indicates that * the view is showing the first item. * @hide */ protected static final int[] FIRST_STATE_SET = {R.attr.state_first}; /** * Used by views that contain lists of items. This state indicates that * the view is showing the middle item. * @hide */ protected static final int[] MIDDLE_STATE_SET = {R.attr.state_middle}; /** * Used by views that contain lists of items. This state indicates that * the view is showing only one item. * @hide */ protected static final int[] SINGLE_STATE_SET = {R.attr.state_single}; /** * Used by views that contain lists of items. This state indicates that * the view is pressed and showing the last item. * @hide */ protected static final int[] PRESSED_LAST_STATE_SET = {R.attr.state_last, R.attr.state_pressed}; /** * Used by views that contain lists of items. This state indicates that * the view is pressed and showing the first item. * @hide */ protected static final int[] PRESSED_FIRST_STATE_SET = {R.attr.state_first, R.attr.state_pressed}; /** * Used by views that contain lists of items. This state indicates that * the view is pressed and showing the middle item. * @hide */ protected static final int[] PRESSED_MIDDLE_STATE_SET = {R.attr.state_middle, R.attr.state_pressed}; /** * Used by views that contain lists of items. This state indicates that * the view is pressed and showing only one item. * @hide */ protected static final int[] PRESSED_SINGLE_STATE_SET = {R.attr.state_single, R.attr.state_pressed}; /** * 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. :) */ static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>(); /** * The animation currently associated with this view. * @hide */ protected Animation mCurrentAnimation = null; /** * Width as measured during measure pass. * {@hide} */ @ViewDebug.ExportedProperty protected int mMeasuredWidth; /** * Height as measured during measure pass. * {@hide} */ @ViewDebug.ExportedProperty protected int mMeasuredHeight; /** * The view's identifier. * {@hide} * * @see #setId(int) * @see #getId() */ @ViewDebug.ExportedProperty(resolveId = true) int mID = NO_ID; /** * The view's tag. * {@hide} * * @see #setTag(Object) * @see #getTag() */ protected Object mTag; // for mPrivateFlags: /** {@hide} */ static final int WANTS_FOCUS = 0x00000001; /** {@hide} */ static final int FOCUSED = 0x00000002; /** {@hide} */ static final int SELECTED = 0x00000004; /** {@hide} */ static final int IS_ROOT_NAMESPACE = 0x00000008; /** {@hide} */ static final int HAS_BOUNDS = 0x00000010; /** {@hide} */ static final int 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} */ static final int DRAW_ANIMATION = 0x00000040; /** {@hide} */ static final int SKIP_DRAW = 0x00000080; /** {@hide} */ static final int ONLY_DRAWS_BACKGROUND = 0x00000100; /** {@hide} */ static final int REQUEST_TRANSPARENT_REGIONS = 0x00000200; /** {@hide} */ static final int DRAWABLE_STATE_DIRTY = 0x00000400; /** {@hide} */ static final int MEASURED_DIMENSION_SET = 0x00000800; /** {@hide} */ static final int FORCE_LAYOUT = 0x00001000; private static final int LAYOUT_REQUIRED = 0x00002000; private static final int PRESSED = 0x00004000; /** {@hide} */ static final int 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} */ static final int ANIMATION_STARTED = 0x00010000; private static final int SAVE_STATE_CALLED = 0x00020000; /** * Indicates that the View returned true when onSetAlpha() was called and that * the alpha must be restored. * {@hide} */ static final int ALPHA_SET = 0x00040000; /** * Set by {@link #setScrollContainer(boolean)}. */ static final int SCROLL_CONTAINER = 0x00080000; /** * Set by {@link #setScrollContainer(boolean)}. */ static final int SCROLL_CONTAINER_ADDED = 0x00100000; /** * The parent this view is attached to. * {@hide} * * @see #getParent() */ protected ViewParent mParent; /** * {@hide} */ AttachInfo mAttachInfo; /** * {@hide} */ @ViewDebug.ExportedProperty int mPrivateFlags; /** * 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. * {@hide} */ protected ViewGroup.LayoutParams mLayoutParams; /** * The view flags hold various views states. * {@hide} */ @ViewDebug.ExportedProperty int mViewFlags; /** * The distance in pixels from the left edge of this view's parent * to the left edge of this view. * {@hide} */ @ViewDebug.ExportedProperty protected int mLeft; /** * The distance in pixels from the left edge of this view's parent * to the right edge of this view. * {@hide} */ @ViewDebug.ExportedProperty protected int mRight; /** * The distance in pixels from the top edge of this view's parent * to the top edge of this view. * {@hide} */ @ViewDebug.ExportedProperty protected int mTop; /** * The distance in pixels from the top edge of this view's parent * to the bottom edge of this view. * {@hide} */ @ViewDebug.ExportedProperty protected int mBottom; /** * The offset, in pixels, by which the content of this view is scrolled * horizontally. * {@hide} */ @ViewDebug.ExportedProperty protected int mScrollX; /** * The offset, in pixels, by which the content of this view is scrolled * vertically. * {@hide} */ @ViewDebug.ExportedProperty protected int mScrollY; /** * The left padding in pixels, that is the distance in pixels between the * left edge of this view and the left edge of its content. * {@hide} */ @ViewDebug.ExportedProperty protected int mPaddingLeft; /** * The right padding in pixels, that is the distance in pixels between the * right edge of this view and the right edge of its content. * {@hide} */ @ViewDebug.ExportedProperty protected int mPaddingRight; /** * The top padding in pixels, that is the distance in pixels between the * top edge of this view and the top edge of its content. * {@hide} */ @ViewDebug.ExportedProperty protected int mPaddingTop; /** * The bottom padding in pixels, that is the distance in pixels between the * bottom edge of this view and the bottom edge of its content. * {@hide} */ @ViewDebug.ExportedProperty protected int mPaddingBottom; /** * Cache the paddingRight set by the user to append to the scrollbar's size. */ @ViewDebug.ExportedProperty int mUserPaddingRight; /** * Cache the paddingBottom set by the user to append to the scrollbar's size. */ @ViewDebug.ExportedProperty int mUserPaddingBottom; /** * @hide */ int mOldWidthMeasureSpec = Integer.MIN_VALUE; /** * @hide */ int mOldHeightMeasureSpec = Integer.MIN_VALUE; private Resources mResources = null; private Drawable mBGDrawable; private int mBackgroundResource; private boolean mBackgroundSizeChanged; /** * Listener used to dispatch focus change events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected OnFocusChangeListener mOnFocusChangeListener; /** * Listener used to dispatch click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected OnClickListener mOnClickListener; /** * Listener used to dispatch long click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected OnLongClickListener mOnLongClickListener; /** * Listener used to build the context menu. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected OnCreateContextMenuListener mOnCreateContextMenuListener; private OnKeyListener mOnKeyListener; private OnTouchListener mOnTouchListener; /** * The application environment this view lives in. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected Context mContext; private ScrollabilityCache mScrollCache; private int[] mDrawableState = null; private SoftReference<Bitmap> mDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, * the user may specify which view to go to next. */ private int 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. */ private int 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. */ private int 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. */ private int mNextFocusDownId = View.NO_ID; private CheckForLongPress mPendingCheckForLongPress; private UnsetPressedState 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. */ private boolean mHasPerformedLongPress; /** * 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 private int 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 private int mMinWidth; /** * The delegate to handle touch events that are physically in this view * but should be handled by another view. */ private TouchDelegate mTouchDelegate = null; /** * Solid color to use as a background when creating the drawing cache. Enables * the cache to use 16 bit bitmaps instead of 32 bit. */ private int mDrawingCacheBackgroundColor = 0; /** * Special tree observer used when mAttachInfo is null. */ private ViewTreeObserver mFloatingTreeObserver; // Used for debug only static long sInstanceCount = 0; /** * 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. */ public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; mViewFlags = SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED; ++sInstanceCount; } /** * 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) */ public View(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * Perform inflation from XML and apply a class-specific base style. 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>defStyle</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 defStyle The default style to apply to this view. If 0, no style * will be applied (beyond what is included in the theme). This may * either be an attribute resource, whose value will be retrieved * from the current theme, or an explicit style resource. * @see #View(Context, AttributeSet) */ public View(Context context, AttributeSet attrs, int defStyle) { this(context); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0); Drawable background = null; int leftPadding = -1; int topPadding = -1; int rightPadding = -1; int bottomPadding = -1; int padding = -1; int viewFlagValues = 0; int viewFlagMasks = 0; boolean setScrollContainer = false; int x = 0; int y = 0; int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY; final int 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); break; case com.android.internal.R.styleable.View_paddingLeft: leftPadding = a.getDimensionPixelSize(attr, -1); 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); break; case com.android.internal.R.styleable.View_paddingBottom: bottomPadding = a.getDimensionPixelSize(attr, -1); 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_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: if (a.getBoolean(attr, false)) { viewFlagValues |= FOCUSABLE; viewFlagMasks |= FOCUSABLE_MASK; } break; case com.android.internal.R.styleable.View_focusableInTouchMode: if (a.getBoolean(attr, false)) { 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_longClickable: if (a.getBoolean(attr, false)) { viewFlagValues |= LONG_CLICKABLE; viewFlagMasks |= LONG_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: final int visibility = a.getInt(attr, 0); if (visibility != 0) { viewFlagValues |= VISIBILITY_FLAGS[visibility]; viewFlagMasks |= VISIBILITY_MASK; } break; case com.android.internal.R.styleable.View_drawingCacheQuality: final int 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_soundEffectsEnabled: if (!a.getBoolean(attr, true)) { viewFlagValues &= ~SOUND_EFFECTS_ENABLED; viewFlagMasks |= SOUND_EFFECTS_ENABLED; } case com.android.internal.R.styleable.View_hapticFeedbackEnabled: if (!a.getBoolean(attr, true)) { viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED; viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED; } case R.styleable.View_scrollbars: final int scrollbars = a.getInt(attr, SCROLLBARS_NONE); if (scrollbars != SCROLLBARS_NONE) { viewFlagValues |= scrollbars; viewFlagMasks |= SCROLLBARS_MASK; initializeScrollbars(a); } break; case R.styleable.View_fadingEdge: final int fadingEdge = a.getInt(attr, FADING_EDGE_NONE); if (fadingEdge != FADING_EDGE_NONE) { viewFlagValues |= fadingEdge; viewFlagMasks |= FADING_EDGE_MASK; initializeFadingEdge(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_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_minWidth: mMinWidth = a.getDimensionPixelSize(attr, 0); break; case R.styleable.View_minHeight: mMinHeight = a.getDimensionPixelSize(attr, 0); break; } } if (background != null) { setBackgroundDrawable(background); } if (padding >= 0) { leftPadding = padding; topPadding = padding; rightPadding = padding; bottomPadding = padding; } // If the user specified the padding (either with android:padding or // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise // use the default padding or the padding from the background drawable // (stored at this point in mPadding*) setPadding(leftPadding >= 0 ? leftPadding : mPaddingLeft, topPadding >= 0 ? topPadding : mPaddingTop, rightPadding >= 0 ? rightPadding : mPaddingRight, bottomPadding >= 0 ? bottomPadding : mPaddingBottom); if (viewFlagMasks != 0) { setFlags(viewFlagValues, viewFlagMasks); } // Needs to be called after mViewFlags is set if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) { recomputePadding(); } if (x != 0 || y != 0) { scrollTo(x, y); } if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) { setScrollContainer(true); } a.recycle(); } /** * Non-public constructor for use in testing */ View() { } @Override protected void finalize() throws Throwable { super.finalize(); --sInstanceCount; } /** * <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 */ protected void initializeFadingEdge(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 */ public int getVerticalFadingEdgeLength() { if (isVerticalFadingEdgeEnabled()) { ScrollabilityCache cache = mScrollCache; if (cache != null) { return cache.fadingEdgeLength; } } return 0; } /** * 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} or {@link #setHorizontalFadingEdgeEnabled} * 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. */ public void 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 */ public int getHorizontalFadingEdgeLength() { if (isHorizontalFadingEdgeEnabled()) { ScrollabilityCache cache = mScrollCache; if (cache != null) { return cache.fadingEdgeLength; } } return 0; } /** * Returns the width of the vertical scrollbar. * * @return The width in pixels of the vertical scrollbar or 0 if there * is no vertical scrollbar. */ public int 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; } return 0; } return 0; } /** * Returns the height of the horizontal scrollbar. * * @return The height in pixels of the horizontal scrollbar or 0 if * there is no horizontal scrollbar. */ protected int 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; } return 0; } return 0; } /** * <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 */ protected void initializeScrollbars(TypedArray a) { initScrollCache(); if (mScrollCache.scrollBar == null) { mScrollCache.scrollBar = new ScrollBarDrawable(); } final ScrollabilityCache scrollabilityCache = mScrollCache; 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); } // Re-apply user/background padding so that scrollbar(s) get added recomputePadding(); } /** * <p> * Initalizes the scrollability cache if necessary. * </p> */ private void initScrollCache() { if (mScrollCache == null) { mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext)); } } /** * Register a callback to be invoked when focus of this view changed. * * @param l The callback that will run. */ public void setOnFocusChangeListener(OnFocusChangeListener l) { mOnFocusChangeListener = l; } /** * Returns the focus-change callback registered for this view. * * @return The callback, or null if one is not registered. */ public OnFocusChangeListener getOnFocusChangeListener() { return mOnFocusChangeListener; } /** * 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) */ public void setOnClickListener(OnClickListener l) { if (!isClickable()) { setClickable(true); } mOnClickListener = l; } /** * 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) */ public void setOnLongClickListener(OnLongClickListener l) { if (!isLongClickable()) { setLongClickable(true); } mOnLongClickListener = 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 * */ public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) { if (!isLongClickable()) { setLongClickable(true); } mOnCreateContextMenuListener = l; } /** * Call this view's OnClickListener, if it is defined. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ public boolean performClick() { if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; } return false; } /** * Call this view's OnLongClickListener, if it is defined. Invokes the context menu * if the OnLongClickListener did not consume the event. * * @return True there was an assigned OnLongClickListener that was called, false * otherwise is returned. */ public boolean performLongClick() { boolean handled = false; if (mOnLongClickListener != null) { handled = mOnLongClickListener.onLongClick(View.this); } if (!handled) { handled = showContextMenu(); } if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } return handled; } /** * Bring up the context menu for this view. * * @return Whether a context menu was displayed. */ public boolean showContextMenu() { return getParent().showContextMenuForChild(this); } /** * Register a callback to be invoked when a key is pressed in this view. * @param l the key listener to attach to this view */ public void setOnKeyListener(OnKeyListener l) { 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 */ public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; } /** * Give this view focus. This will cause {@link #onFocusChanged} 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 View.FOCUS_UP, View.FOCUS_DOWN, * View.FOCUS_LEFT or 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(int direction, Rect previouslyFocusedRect) { if (DBG) { System.out.println(this + " requestFocus()"); } if ((mPrivateFlags & FOCUSED) == 0) { mPrivateFlags |= FOCUSED; if (mParent != null) { mParent.requestChildFocus(this, this); } onFocusChanged(true, direction, previouslyFocusedRect); refreshDrawableState(); } } /** * 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. * * @param rectangle The rectangle. * @return Whether any parent scrolled. */ public boolean 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>When <code>immediate</code> is set to true, scrolling will not be * animated. * * @param rectangle The rectangle. * @param immediate True to forbid animated scrolling, false otherwise * @return Whether any parent scrolled. */ public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) { View child = this; ViewParent parent = mParent; boolean scrolled = false; while (parent != null) { scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate); // offset rect so next call has the rectangle in the // coordinate system of its direct child. rectangle.offset(child.getLeft(), child.getTop()); rectangle.offset(-child.getScrollX(), -child.getScrollY()); if (!(parent instanceof View)) { break; } child = (View) parent; parent = child.getParent(); } return scrolled; } /** * Called when this view wants to give up focus. This will cause * {@link #onFocusChanged} to be called. */ public void clearFocus() { if (DBG) { System.out.println(this + " clearFocus()"); } if ((mPrivateFlags & FOCUSED) != 0) { mPrivateFlags &= ~FOCUSED; if (mParent != null) { mParent.clearChildFocus(this); } onFocusChanged(false, 0, null); refreshDrawableState(); } } /** * Called to clear the focus of a view that is about to be removed. * Doesn't call clearChildFocus, which prevents this view from taking * focus again before it has been removed from the parent */ void clearFocusForRemoval() { if ((mPrivateFlags & FOCUSED) != 0) { mPrivateFlags &= ~FOCUSED; onFocusChanged(false, 0, null); refreshDrawableState(); } } /** * Called internally by the view system when a new view is getting focus. * This is what clears the old focus. */ void unFocus() { if (DBG) { System.out.println(this + " unFocus()"); } if ((mPrivateFlags & FOCUSED) != 0) { mPrivateFlags &= ~FOCUSED; onFocusChanged(false, 0, null); refreshDrawableState(); } } /** * Returns true if this view has focus iteself, or is the ancestor of the * view that has focus. * * @return True if this view has or contains focus, false otherwise. */ @ViewDebug.ExportedProperty public boolean hasFocus() { return (mPrivateFlags & FOCUSED) != 0; } /** * Returns true if this view is focusable or if it contains a reachable View * for which {@link #hasFocusable()} returns true. A "reachable hasFocusable()" * is a View whose parents do not block descendants focus. * * Only {@link #VISIBLE} views are considered focusable. * * @return True if the view is focusable or if the view contains a focusable * View, false otherwise. * * @see ViewGroup#FOCUS_BLOCK_DESCENDANTS */ public boolean hasFocusable() { return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable(); } /** * 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 * View.FOCUS_UP, View.FOCUS_DOWN, View.FOCUS_LEFT or * View.FOCUS_RIGHT. 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. */ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { InputMethodManager imm = InputMethodManager.peekInstance(); if (!gainFocus) { if (isPressed()) { setPressed(false); } if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) { imm.focusOut(this); } } else if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) { imm.focusIn(this); } invalidate(); if (mOnFocusChangeListener != null) { mOnFocusChangeListener.onFocusChange(this, gainFocus); } } /** * Returns true if this view has focus * * @return True if this view has focus, false otherwise. */ @ViewDebug.ExportedProperty public boolean isFocused() { return (mPrivateFlags & 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. */ public View findFocus() { return (mPrivateFlags & FOCUSED) != 0 ? this : null; } /** * 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. */ public void setScrollContainer(boolean isScrollContainer) { if (isScrollContainer) { if (mAttachInfo != null && (mPrivateFlags&SCROLL_CONTAINER_ADDED) == 0) { mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= SCROLL_CONTAINER_ADDED; } mPrivateFlags |= SCROLL_CONTAINER; } else { if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) { mAttachInfo.mScrollContainers.remove(this); } mPrivateFlags &= ~(SCROLL_CONTAINER|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 */ public int 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 */ public void setDrawingCacheQuality(int 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 */ public boolean 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 */ public void setKeepScreenOn(boolean keepScreenOn) { setFlags(keepScreenOn ? KEEP_SCREEN_ON : 0, KEEP_SCREEN_ON); } /** * @return The user specified next focus ID. * * @attr ref android.R.styleable#View_nextFocusLeft */ public int getNextFocusLeftId() { return mNextFocusLeftId; } /** * Set the id of the view to use for the next focus * * @param nextFocusLeftId * * @attr ref android.R.styleable#View_nextFocusLeft */ public void setNextFocusLeftId(int nextFocusLeftId) { mNextFocusLeftId = nextFocusLeftId; } /** * @return The user specified next focus ID. * * @attr ref android.R.styleable#View_nextFocusRight */ public int getNextFocusRightId() { return mNextFocusRightId; } /** * Set the id of the view to use for the next focus * * @param nextFocusRightId * * @attr ref android.R.styleable#View_nextFocusRight */ public void setNextFocusRightId(int nextFocusRightId) { mNextFocusRightId = nextFocusRightId; } /** * @return The user specified next focus ID. * * @attr ref android.R.styleable#View_nextFocusUp */ public int getNextFocusUpId() { return mNextFocusUpId; } /** * Set the id of the view to use for the next focus * * @param nextFocusUpId * * @attr ref android.R.styleable#View_nextFocusUp */ public void setNextFocusUpId(int nextFocusUpId) { mNextFocusUpId = nextFocusUpId; } /** * @return The user specified next focus ID. * * @attr ref android.R.styleable#View_nextFocusDown */ public int getNextFocusDownId() { return mNextFocusDownId; } /** * Set the id of the view to use for the next focus * * @param nextFocusDownId * * @attr ref android.R.styleable#View_nextFocusDown */ public void setNextFocusDownId(int nextFocusDownId) { mNextFocusDownId = nextFocusDownId; } /** * Returns the visibility of this view and all of its ancestors * * @return True if this view and all of its ancestors are {@link #VISIBLE} */ public boolean isShown() { View current = this; //noinspection ConstantConditions do { if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) { return false; } ViewParent parent = current.mParent; if (parent == null) { return false; // We are not attached to the view root } if (!(parent instanceof View)) { return true; } current = (View) parent; } while (current != null); return false; } /** * Apply the insets for system windows to this view, if the FITS_SYSTEM_WINDOWS flag * is set * * @param insets Insets for system windows * * @return True if this view applied the insets, false otherwise */ protected boolean fitSystemWindows(Rect insets) { if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) { mPaddingLeft = insets.left; mPaddingTop = insets.top; mPaddingRight = insets.right; mPaddingBottom = insets.bottom; requestLayout(); return true; } return false; } /** * 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") }) public int getVisibility() { return mViewFlags & VISIBILITY_MASK; } /** * Set the enabled state of this view. * * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. * @attr ref android.R.styleable#View_visibility */ @RemotableViewMethod public void setVisibility(int visibility) { setFlags(visibility, VISIBILITY_MASK); if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false); } /** * 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 public boolean 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. */ public void setEnabled(boolean enabled) { 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(); } /** * Set whether this view can receive the focus. * * 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) * @attr ref android.R.styleable#View_focusable */ public void setFocusable(boolean focusable) { if (!focusable) { setFlags(0, FOCUSABLE_IN_TOUCH_MODE); } setFlags(focusable ? FOCUSABLE : NOT_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 */ public void 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); if (focusableInTouchMode) { setFlags(FOCUSABLE, FOCUSABLE_MASK); } } /** * 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 */ public void 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 public boolean 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 */ public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) { setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED); } /** * @return whether this view should have haptic feedback enabled for events * long presses. * * @see #setHapticFeedbackEnabled(boolean) * @see #performHapticFeedback(int) * @attr ref android.R.styleable#View_hapticFeedbackEnabled */ @ViewDebug.ExportedProperty public boolean isHapticFeedbackEnabled() { return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED); } /** * 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} you should clear this flag. * * @param willNotDraw whether or not this View draw on its own */ public void 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 public boolean 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 */ public void 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 */ @ViewDebug.ExportedProperty public boolean 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 public boolean 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 */ public void setClickable(boolean clickable) { setFlags(clickable ? CLICKABLE : 0, CLICKABLE); } /** * 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 */ public boolean 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 */ public void setLongClickable(boolean longClickable) { setFlags(longClickable ? LONG_CLICKABLE : 0, LONG_CLICKABLE); } /** * Sets the pressed that 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. */ public void setPressed(boolean pressed) { if (pressed) { mPrivateFlags |= PRESSED; } else { mPrivateFlags &= ~PRESSED; } refreshDrawableState(); dispatchSetPressed(pressed); } /** * Dispatch setPressed to all of this View's children. * * @see #setPressed(boolean) * * @param pressed The new pressed state */ protected void 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 * @see #isClickable() * @see #setClickable(boolean) * * @return true if the view is currently pressed, false otherwise */ public boolean isPressed() { return (mPrivateFlags & PRESSED) == PRESSED; } /** * 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 */ public boolean 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 setId()}) * 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 */ public void setSaveEnabled(boolean enabled) { setFlags(enabled ? 0 : SAVE_DISABLED, SAVE_DISABLED_MASK); } /** * Returns whether this View is able to take focus. * * @return True if this view can take focus, or false otherwise. * @attr ref android.R.styleable#View_focusable */ @ViewDebug.ExportedProperty public final boolean isFocusable() { return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK); } /** * 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 public final boolean isFocusableInTouchMode() { return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE); } /** * 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. */ public View focusSearch(int direction) { if (mParent != null) { return mParent.focusSearch(this, direction); } else { return null; } } /** * 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. */ public boolean dispatchUnhandledMove(View focused, int direction) { return false; } /** * If a user manually specified the next view id for a particular direction, * use the root to look up the view. Once a view is found, it is cached * for future lookups. * @param root The root view of the hierarchy containing this view. * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * @return The user specified next view, or null if there is none. */ View findUserSetNextFocus(View root, int direction) { switch (direction) { case FOCUS_LEFT: if (mNextFocusLeftId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusLeftId); case FOCUS_RIGHT: if (mNextFocusRightId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusRightId); case FOCUS_UP: if (mNextFocusUpId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusUpId); case FOCUS_DOWN: if (mNextFocusDownId == View.NO_ID) return null; return findViewShouldExist(root, mNextFocusDownId); } return null; } private static View findViewShouldExist(View root, int childViewId) { View result = root.findViewById(childViewId); if (result == null) { Log.w(VIEW_LOG_TAG, "couldn't find next focus view specified " + "by user for id " + childViewId); } 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 */ public ArrayList<View> getFocusables(int direction) { ArrayList<View> result = new ArrayList<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 */ public void addFocusables(ArrayList<View> views, int direction) { if (!isFocusable()) return; if (isInTouchMode() && !isFocusableInTouchMode()) return; views.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 */ public ArrayList<View> getTouchables() { ArrayList<View> result = new ArrayList<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 */ public void addTouchables(ArrayList<View> views) { final int viewFlags = mViewFlags; if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && (viewFlags & ENABLED_MASK) == ENABLED) { views.add(this); } } /** * 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 is focusable and it is not focusable in touch mode ({@link #isFocusableInTouchMode}) * while the device is in touch mode. * * See also {@link #focusSearch}, 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. */ public final boolean requestFocus() { 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}, 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. */ public final boolean 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}, 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. */ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { // need to be focusable if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE || (mViewFlags & VISIBILITY_MASK) != VISIBLE) { return false; } // need to be focusable in touch mode if in touch mode if (isInTouchMode() && (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) { return false; } // need to not have any parents blocking us if (hasAncestorThatBlocksDescendantFocus()) { return false; } handleFocusGainInternal(direction, previouslyFocusedRect); return true; } /** * 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 focuable in * touch mode to request focus when they are touched. * * @return Whether this view or one of its descendants actually took focus. * * @see #isInTouchMode() * */ public final boolean requestFocusFromTouch() { // Leave touch mode if we need to if (isInTouchMode()) { View root = getRootView(); if (root != null) { ViewRoot viewRoot = (ViewRoot)root.getParent(); if (viewRoot != null) { viewRoot.ensureTouchMode(false); } } } return requestFocus(View.FOCUS_DOWN); } /** * @return Whether any ancestor of this view blocks descendant focus. */ private boolean hasAncestorThatBlocksDescendantFocus() { ViewParent ancestor = mParent; while (ancestor instanceof ViewGroup) { final ViewGroup vgAncestor = (ViewGroup) ancestor; if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS) { return true; } else { ancestor = vgAncestor.getParent(); } } return false; } /** * This is called when a container is going to temporarily detach a child * that currently has focus, with * {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}. * It will either be followed by {@link #onFinishTemporaryDetach()} or * {@link #onDetachedFromWindow()} when the container is done. Generally * this is currently only done ListView for a view with focus. */ public void onStartTemporaryDetach() { } /** * Called after {@link #onStartTemporaryDetach} when the container is done * changing the view. */ public void onFinishTemporaryDetach() { } /** * capture information of this view for later analysis: developement only * check dynamic switch to make sure we only dump view * when ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW) is set */ private static void captureViewInfo(String subTag, View v) { if (v == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW, 0) == 0) { return; } ViewDebug.dumpCapturedView(subTag, v); } /** * 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. */ public boolean 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. */ public boolean dispatchKeyEvent(KeyEvent event) { // If any attached key listener a first crack at the event. //noinspection SimplifiableIfStatement if (android.util.Config.LOGV) { captureViewInfo("captureViewKeyEvent", this); } if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnKeyListener.onKey(this, event.getKeyCode(), event)) { return true; } return event.dispatch(this); } /** * 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. */ public boolean 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. */ public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); } /** * 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. */ public boolean dispatchTrackballEvent(MotionEvent event) { //Log.i("view", "view=" + this + ", " + event.toString()); return onTrackballEvent(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. */ public void 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. */ public void onWindowFocusChanged(boolean hasWindowFocus) { InputMethodManager imm = InputMethodManager.peekInstance(); if (!hasWindowFocus) { if (isPressed()) { setPressed(false); } if (imm != null && (mPrivateFlags & FOCUSED) != 0) { imm.focusOut(this); } if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } } else if (imm != null && (mPrivateFlags & FOCUSED) != 0) { imm.focusIn(this); } 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. */ public boolean hasWindowFocus() { return mAttachInfo != null && mAttachInfo.mHasWindowFocus; } /** * 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 */ public void dispatchWindowVisibilityChanged(int 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. */ protected void onWindowVisibilityChanged(int visibility) { } /** * 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. */ public int 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. * * <p>This function requires an IPC back to the window manager to retrieve * the requested information, so should not be used in performance critical * code like drawing. * * @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. */ public void getWindowVisibleDisplayFrame(Rect outRect) { if (mAttachInfo != null) { try { mAttachInfo.mSession.getDisplayFrame(mAttachInfo.mWindow, outRect); } catch (RemoteException e) { return; } // XXX This is really broken, and probably all needs to be done // in the window manager, and we need to know more about whether // we want the area behind or in front of the IME. final Rect insets = mAttachInfo.mVisibleInsets; outRect.left += insets.left; outRect.top += insets.top; outRect.right -= insets.right; outRect.bottom -= insets.bottom; return; } Display d = WindowManagerImpl.getDefault().getDefaultDisplay(); outRect.set(0, 0, d.getWidth(), d.getHeight()); } /** * Private function to aggregate all per-view attributes in to the view * root. */ void dispatchCollectViewAttributes(int visibility) { performCollectViewAttributes(visibility); } void performCollectViewAttributes(int visibility) { //noinspection PointlessBitwiseExpression if (((visibility | mViewFlags) & (VISIBILITY_MASK | KEEP_SCREEN_ON)) == (VISIBLE | KEEP_SCREEN_ON)) { mAttachInfo.mKeepScreenOn = true; } } void needGlobalAttributesUpdate(boolean force) { AttachInfo ai = mAttachInfo; if (ai != null) { if (ai.mKeepScreenOn || force) { 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 public boolean isInTouchMode() { if (mAttachInfo != null) { return mAttachInfo.mInTouchMode; } else { return ViewRoot.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 public final Context 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. */ public boolean onKeyPreIme(int keyCode, KeyEvent event) { return false; } /** * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) * KeyEvent.Callback.onKeyMultiple()}: 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. * * @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. */ public boolean onKeyDown(int keyCode, KeyEvent event) { boolean result = false; switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } // Long clickable items don't necessarily have to be clickable if (((mViewFlags & CLICKABLE) == CLICKABLE || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && (event.getRepeatCount() == 0)) { setPressed(true); if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { postCheckForLongClick(); } return true; } break; } } return result; } /** * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) * KeyEvent.Callback.onKeyMultiple()}: perform clicking of the view * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or * {@link KeyEvent#KEYCODE_ENTER} is released. * * @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. */ public boolean onKeyUp(int keyCode, KeyEvent event) { boolean result = false; switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) { setPressed(false); if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } result = performClick(); } } break; } } return result; } /** * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle * the event). * * @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. */ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { return false; } /** * Called when an unhandled key shortcut event occurs. * * @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. */ public boolean onKeyShortcut(int keyCode, KeyEvent event) { return false; } /** * 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. */ public boolean onCheckIsTextEditor() { return false; } /** * 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. * * @param outAttrs Fill in with attribute information about the connection. */ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { return null; } /** * 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. */ public boolean checkInputConnectionProxy(View view) { return false; } /** * Show the context menu for this view. It is not safe to hold on to the * menu after returning from this method. * * @param menu The context menu to populate */ public void 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); if (mOnCreateContextMenuListener != null) { 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. */ protected ContextMenuInfo getContextMenuInfo() { return null; } /** * Views should implement this if the view itself is going to add items to * the context menu. * * @param menu the context menu to populate */ protected void 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. */ public boolean onTrackballEvent(MotionEvent event) { return false; } /** * Implement this method to handle touch screen motion events. * * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: if ((mPrivateFlags & PRESSED) != 0) { // 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 (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } // Only perform take click actions if we were in the pressed state if (!focusTaken) { performClick(); } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } } break; case MotionEvent.ACTION_DOWN: mPrivateFlags |= PRESSED; refreshDrawableState(); if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { postCheckForLongClick(); } break; case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = ViewConfiguration.get(mContext).getScaledTouchSlop(); if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button if ((mPrivateFlags & PRESSED) != 0) { // Remove any future long press checks if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } } else { // Inside button if ((mPrivateFlags & PRESSED) == 0) { // Need to switch from not pressed to pressed mPrivateFlags |= PRESSED; refreshDrawableState(); } } break; } return true; } return false; } /** * 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. */ public void cancelLongPress() { if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } } /** * Sets the TouchDelegate for this View. */ public void setTouchDelegate(TouchDelegate delegate) { mTouchDelegate = delegate; } /** * Gets the TouchDelegate for this View. */ public TouchDelegate getTouchDelegate() { return mTouchDelegate; } /** * 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 */ void setFlags(int flags, int mask) { int old = mViewFlags; mViewFlags = (mViewFlags & ~mask) | (flags & mask); int changed = mViewFlags ^ old; if (changed == 0) { return; } int privateFlags = mPrivateFlags; /* Check if the FOCUSABLE bit has changed */ if (((changed & FOCUSABLE_MASK) != 0) && ((privateFlags & HAS_BOUNDS) !=0)) { if (((old & FOCUSABLE_MASK) == FOCUSABLE) && ((privateFlags & FOCUSED) != 0)) { /* Give up focus if we are no longer focusable */ clearFocus(); } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE) && ((privateFlags & FOCUSED) == 0)) { /* * Tell the view system that we are now available to take focus * if no one else already has it. */ if (mParent != null) mParent.focusableViewAvailable(this); } } if ((flags & VISIBILITY_MASK) == VISIBLE) { if ((changed & VISIBILITY_MASK) != 0) { /* * If this view is becoming visible, set the DRAWN flag so that * the next invalidate() will not be skipped. */ mPrivateFlags |= DRAWN; 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. if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) { mParent.focusableViewAvailable(this); } } } /* Check if the GONE bit has changed */ if ((changed & GONE) != 0) { needGlobalAttributesUpdate(false); requestLayout(); invalidate(); if (((mViewFlags & VISIBILITY_MASK) == GONE) && hasFocus()) { clearFocus(); } if (mAttachInfo != null) { mAttachInfo.mViewVisibilityChanged = true; } } /* Check if the VISIBLE bit has changed */ if ((changed & INVISIBLE) != 0) { needGlobalAttributesUpdate(false); invalidate(); if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) { // root view becoming invisible shouldn't clear focus if (getRootView() != this) { clearFocus(); } } if (mAttachInfo != null) { mAttachInfo.mViewVisibilityChanged = true; } } if ((changed & WILL_NOT_CACHE_DRAWING) != 0) { destroyDrawingCache(); } if ((changed & DRAWING_CACHE_ENABLED) != 0) { destroyDrawingCache(); mPrivateFlags &= ~DRAWING_CACHE_VALID; } if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) { destroyDrawingCache(); mPrivateFlags &= ~DRAWING_CACHE_VALID; } if ((changed & DRAW_MASK) != 0) { if ((mViewFlags & WILL_NOT_DRAW) != 0) { if (mBGDrawable != null) { mPrivateFlags &= ~SKIP_DRAW; mPrivateFlags |= ONLY_DRAWS_BACKGROUND; } else { mPrivateFlags |= SKIP_DRAW; } } else { mPrivateFlags &= ~SKIP_DRAW; } requestLayout(); invalidate(); } if ((changed & KEEP_SCREEN_ON) != 0) { if (mParent != null) { mParent.recomputeViewAttributes(this); } } } /** * Change the view's z order in the tree, so it's on top of other sibling * views */ public void 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. */ protected void onScrollChanged(int l, int t, int oldl, int oldt) { mBackgroundSizeChanged = true; final AttachInfo ai = mAttachInfo; if (ai != null) { ai.mViewScrollChanged = true; } } /** * 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. */ protected void 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 */ protected void 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. */ public final ViewParent getParent() { return mParent; } /** * 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. */ public final int 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. */ public final int getScrollY() { return mScrollY; } /** * Return the width of the your view. * * @return The width of your view, in pixels. */ @ViewDebug.ExportedProperty public final int getWidth() { return mRight - mLeft; } /** * Return the height of your view. * * @return The height of your view, in pixels. */ @ViewDebug.ExportedProperty public final int 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(). * * @param outRect The (scrolled) drawing bounds of the view. */ public void getDrawingRect(Rect outRect) { outRect.left = mScrollX; outRect.top = mScrollY; outRect.right = mScrollX + (mRight - mLeft); outRect.bottom = mScrollY + (mBottom - mTop); } /** * The width of this view as measured in the most recent call to measure(). * 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. */ public final int getMeasuredWidth() { return mMeasuredWidth; } /** * The height of this view as measured in the most recent call to measure(). * This should be used during measurement and layout calculations only. Use * {@link #getHeight()} to see how tall a view is after layout. * * @return The measured height of this view. */ public final int getMeasuredHeight() { return mMeasuredHeight; } /** * Top position of this view relative to its parent. * * @return The top of this view, in pixels. */ @ViewDebug.CapturedViewProperty public final int getTop() { return mTop; } /** * Bottom position of this view relative to its parent. * * @return The bottom of this view, in pixels. */ @ViewDebug.CapturedViewProperty public final int getBottom() { return mBottom; } /** * Left position of this view relative to its parent. * * @return The left edge of this view, in pixels. */ @ViewDebug.CapturedViewProperty public final int getLeft() { return mLeft; } /** * Right position of this view relative to its parent. * * @return The right edge of this view, in pixels. */ @ViewDebug.CapturedViewProperty public final int getRight() { return mRight; } /** * Hit rectangle in parent's coordinates * * @param outRect The hit rectangle of the view. */ public void getHitRect(Rect outRect) { outRect.set(mLeft, mTop, mRight, mBottom); } /** * 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 rectange is the {@link #getDrawingRect})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. */ public void getFocusedRect(Rect r) { getDrawingRect(r); } /** * If some part of this view is not clipped by any of its parents, then * return that area in r in global (root) coordinates. To convert r to local * coordinates, offset it by -globalOffset (e.g. r.offset(-globalOffset.x, * -globalOffset.y)) If the view is completely clipped or translated out, * return false. * * @param r If true is returned, r holds the global coordinates of the * visible portion of this view. * @param globalOffset If true is returned, globalOffset holds the dx,dy * between this view and its root. globalOffet may be null. * @return true if r is non-empty (i.e. part of the view is visible at the * root level. */ public boolean 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); } return false; } public final boolean getGlobalVisibleRect(Rect r) { return getGlobalVisibleRect(r, null); } public final boolean getLocalVisibleRect(Rect r) { Point offset = new Point(); if (getGlobalVisibleRect(r, offset)) { r.offset(-offset.x, -offset.y); // make r local return true; } return false; } /** * Offset this view's vertical location by the specified number of pixels. * * @param offset the number of pixels to offset the view by */ public void offsetTopAndBottom(int offset) { mTop += offset; mBottom += offset; } /** * Offset this view's horizontal location by the specified amount of pixels. * * @param offset the numer of pixels to offset the view by */ public void offsetLeftAndRight(int offset) { mLeft += offset; mRight += offset; } /** * 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. * @return The LayoutParams associated with this view */ @ViewDebug.ExportedProperty(deepExport = true, prefix = "layout_") public ViewGroup.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 */ public void setLayoutParams(ViewGroup.LayoutParams params) { if (params == null) { throw new NullPointerException("params == null"); } mLayoutParams = params; requestLayout(); } /** * 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 */ public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; onScrollChanged(mScrollX, mScrollY, oldX, oldY); invalidate(); } } /** * 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 */ public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); } /** * Mark the the area defined by dirty as needing to be drawn. If the view is * visible, {@link #onDraw} will be called at some point in the future. * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. * * WARNING: This method is destructive to dirty. * @param dirty the rectangle representing the bounds of the dirty region */ public void invalidate(Rect dirty) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) { mPrivateFlags &= ~DRAWING_CACHE_VALID; final ViewParent p = mParent; final AttachInfo ai = mAttachInfo; if (p != null && ai != null) { final int scrollX = mScrollX; final int scrollY = mScrollY; final Rect r = ai.mTmpInvalRect; r.set(dirty.left - scrollX, dirty.top - scrollY, dirty.right - scrollX, dirty.bottom - scrollY); mParent.invalidateChild(this, r); } } } /** * Mark the 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} will be called at some point * in the future. 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 */ public void invalidate(int l, int t, int r, int b) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) { mPrivateFlags &= ~DRAWING_CACHE_VALID; final ViewParent p = mParent; final AttachInfo ai = mAttachInfo; if (p != null && ai != null && l < r && t < b) { final int scrollX = mScrollX; final int scrollY = mScrollY; final Rect tmpr = ai.mTmpInvalRect; tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY); p.invalidateChild(this, tmpr); } } } /** * Invalidate the whole view. If the view is visible, {@link #onDraw} will * be called at some point in the future. This must be called from a * UI thread. To call from a non-UI thread, call {@link #postInvalidate()}. */ public void invalidate() { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) { mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID; final ViewParent p = mParent; final AttachInfo ai = mAttachInfo; if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); // Don't call invalidate -- we don't want to internally scroll // our own bounds p.invalidateChild(this, r); } } } /** * @return A handler associated with the thread running the View. This * handler can be used to pump events in the UI events queue. */ public Handler getHandler() { if (mAttachInfo != null) { return mAttachInfo.mHandler; } return null; } /** * Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread. * * @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. */ public boolean post(Runnable action) { Handler handler; if (mAttachInfo != null) { handler = mAttachInfo.mHandler; } else { // Assume that post will succeed later ViewRoot.getRunQueue().post(action); return true; } return handler.post(action); } /** * 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. * * @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. */ public boolean postDelayed(Runnable action, long delayMillis) { Handler handler; if (mAttachInfo != null) { handler = mAttachInfo.mHandler; } else { // Assume that post will succeed later ViewRoot.getRunQueue().postDelayed(action, delayMillis); return true; } return handler.postDelayed(action, delayMillis); } /** * Removes the specified Runnable from the message queue. * * @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.) */ public boolean removeCallbacks(Runnable action) { Handler handler; if (mAttachInfo != null) { handler = mAttachInfo.mHandler; } else { // Assume that post will succeed later ViewRoot.getRunQueue().removeCallbacks(action); return true; } handler.removeCallbacks(action); return true; } /** * Cause an invalidate to happen on a subsequent cycle through the event loop. * Use this to invalidate the View from a non-UI thread. * * @see #invalidate() */ public void postInvalidate() { postInvalidateDelayed(0); } /** * 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. * * @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) */ public void postInvalidate(int left, int top, int right, int bottom) { postInvalidateDelayed(0, left, top, right, bottom); } /** * Cause an invalidate to happen on a subsequent cycle through the event * loop. Waits for the specified amount of time. * * @param delayMilliseconds the duration in milliseconds to delay the * invalidation by */ public void postInvalidateDelayed(long delayMilliseconds) { // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window if (mAttachInfo != null) { Message msg = Message.obtain(); msg.what = AttachInfo.INVALIDATE_MSG; msg.obj = this; mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); } } /** * Cause an invalidate of the specified area to happen on a subsequent cycle * through the event loop. Waits for the specified amount of time. * * @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. */ public void 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 if (mAttachInfo != null) { final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire(); info.target = this; info.left = left; info.top = top; info.right = right; info.bottom = bottom; final Message msg = Message.obtain(); msg.what = AttachInfo.INVALIDATE_RECT_MSG; msg.obj = info; mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); } } /** * 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. */ public void 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_fadingEdge */ public boolean 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_fadingEdge */ public void 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_fadingEdge */ public boolean 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_fadingEdge */ public void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) { if (isVerticalFadingEdgeEnabled() != verticalFadingEdgeEnabled) { if (verticalFadingEdgeEnabled) { initScrollCache(); } mViewFlags ^= FADING_EDGE_VERTICAL; } } /** * 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 */ protected float 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 */ protected float 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 */ protected float 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 */ protected float 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) */ public boolean 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() */ public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) { if (isHorizontalScrollBarEnabled() != horizontalScrollBarEnabled) { mViewFlags ^= SCROLLBARS_HORIZONTAL; recomputePadding(); } } /** * <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) */ public boolean 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() */ public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { if (isVerticalScrollBarEnabled() != verticalScrollBarEnabled) { mViewFlags ^= SCROLLBARS_VERTICAL; recomputePadding(); } } private void recomputePadding() { setPadding(mPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom); } /** * <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 */ public void setScrollBarStyle(int style) { if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) { mViewFlags = (mViewFlags & ~SCROLLBARS_STYLE_MASK) | (style & SCROLLBARS_STYLE_MASK); recomputePadding(); } } /** * <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 */ public int 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() * @see android.widget.ScrollBarDrawable */ protected int 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() * @see android.widget.ScrollBarDrawable */ protected int 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() * @see android.widget.ScrollBarDrawable */ protected int 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() * @see android.widget.ScrollBarDrawable */ protected int 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() * @see android.widget.ScrollBarDrawable */ protected int computeVerticalScrollOffset() { return mScrollY; } /** * <p>Compute the vertical extent of the horizontal 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 #computeHorizontalScrollRange()} 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() * @see android.widget.ScrollBarDrawable */ protected int computeVerticalScrollExtent() { return getHeight(); } /** * <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 */ private void onDrawScrollBars(Canvas canvas) { // scrollbars are drawn only when the animation is running final ScrollabilityCache cache = mScrollCache; if (cache != null) { final int viewFlags = mViewFlags; final boolean drawHorizontalScrollBar = (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL; final boolean drawVerticalScrollBar = (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL && !isVerticalScrollBarHidden(); if (drawVerticalScrollBar || drawHorizontalScrollBar) { final int width = mRight - mLeft; final int height = mBottom - mTop; final ScrollBarDrawable scrollBar = cache.scrollBar; int size = scrollBar.getSize(false); if (size <= 0) { size = cache.scrollBarSize; } if (drawHorizontalScrollBar) { onDrawHorizontalScrollBar(canvas, scrollBar, width, height, size); } if (drawVerticalScrollBar) { onDrawVerticalScrollBar(canvas, scrollBar, width, height, size); } } } } /** * 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 */ protected boolean isVerticalScrollBarHidden() { return false; } /** * <p>Draw the horizontal scrollbar if * {@link #isHorizontalScrollBarEnabled()} returns true.</p> * * <p>The length of the scrollbar and its thumb is computed according to the * values returned by {@link #computeHorizontalScrollRange()}, * {@link #computeHorizontalScrollExtent()} and * {@link #computeHorizontalScrollOffset()}. Refer to * {@link android.widget.ScrollBarDrawable} for more information about how * these values relate to each other.</p> * * @param canvas the canvas on which to draw the scrollbar * @param scrollBar the scrollbar's drawable * @param width the width of the drawing surface * @param height the height of the drawing surface * @param size the size of the scrollbar * * @see #isHorizontalScrollBarEnabled() * @see #computeHorizontalScrollRange() * @see #computeHorizontalScrollExtent() * @see #computeHorizontalScrollOffset() * @see android.widget.ScrollBarDrawable */ private void onDrawHorizontalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width, int height, int size) { final int viewFlags = mViewFlags; final int scrollX = mScrollX; final int scrollY = mScrollY; final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; final int top = scrollY + height - size - (mUserPaddingBottom & inside); final int verticalScrollBarGap = (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL ? getVerticalScrollbarWidth() : 0; scrollBar.setBounds(scrollX + (mPaddingLeft & inside), top, scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap, top + size); scrollBar.setParameters( computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(), false); scrollBar.draw(canvas); } /** * <p>Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()} * returns true.</p> * * <p>The length of the scrollbar and its thumb is computed according to the * values returned by {@link #computeVerticalScrollRange()}, * {@link #computeVerticalScrollExtent()} and * {@link #computeVerticalScrollOffset()}. Refer to * {@link android.widget.ScrollBarDrawable} for more information about how * these values relate to each other.</p> * * @param canvas the canvas on which to draw the scrollbar * @param scrollBar the scrollbar's drawable * @param width the width of the drawing surface * @param height the height of the drawing surface * @param size the size of the scrollbar * * @see #isVerticalScrollBarEnabled() * @see #computeVerticalScrollRange() * @see #computeVerticalScrollExtent() * @see #computeVerticalScrollOffset() * @see android.widget.ScrollBarDrawable */ private void onDrawVerticalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width, int height, int size) { final int scrollX = mScrollX; final int scrollY = mScrollY; final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; // TODO: Deal with RTL languages to position scrollbar on left final int left = scrollX + width - size - (mUserPaddingRight & inside); scrollBar.setBounds(left, scrollY + (mPaddingTop & inside), left + size, scrollY + height - (mUserPaddingBottom & inside)); scrollBar.setParameters( computeVerticalScrollRange(), computeVerticalScrollOffset(), computeVerticalScrollExtent(), true); scrollBar.draw(canvas); } /** * Implement this to do your drawing. * * @param canvas the canvas on which the background will be drawn */ protected void onDraw(Canvas canvas) { } /* * Caller is responsible for calling requestLayout if necessary. * (This allows addViewInLayout to not request a new layout.) */ void assignParent(ViewParent parent) { if (mParent == null) { mParent = parent; } else if (parent == null) { mParent = null; } else { throw new RuntimeException("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}, however it may be called * any time before the first onDraw -- including before or after * {@link #onMeasure}. * * @see #onDetachedFromWindow() */ protected void onAttachedToWindow() { if ((mPrivateFlags & REQUEST_TRANSPARENT_REGIONS) != 0) { mParent.requestTransparentRegion(this); } } /** * This is called when the view is detached from a window. At this point it * no longer has a surface for drawing. * * @see #onAttachedToWindow() */ protected void onDetachedFromWindow() { if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } destroyDrawingCache(); } /** * @return The number of times this view has been attached to a window */ protected int 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}. */ public IBinder getWindowToken() { return mAttachInfo != null ? mAttachInfo.mWindowToken : null; } /** * 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. */ public IBinder getApplicationWindowToken() { AttachInfo ai = mAttachInfo; if (ai != null) { IBinder appWindowToken = ai.mPanelParentWindowToken; if (appWindowToken == null) { appWindowToken = ai.mWindowToken; } return appWindowToken; } return 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 */ /*package*/ IWindowSession getWindowSession() { return mAttachInfo != null ? mAttachInfo.mSession : null; } /** * @param info the {@link android.view.View.AttachInfo} to associated with * this view */ void dispatchAttachedToWindow(AttachInfo info, int visibility) { //System.out.println("Attached! " + this); mAttachInfo = info; mWindowAttachCount++; if (mFloatingTreeObserver != null) { info.mTreeObserver.merge(mFloatingTreeObserver); mFloatingTreeObserver = null; } if ((mPrivateFlags&SCROLL_CONTAINER) != 0) { mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= SCROLL_CONTAINER_ADDED; } performCollectViewAttributes(visibility); onAttachedToWindow(); int vis = info.mWindowVisibility; if (vis != GONE) { onWindowVisibilityChanged(vis); } } void dispatchDetachedFromWindow() { //System.out.println("Detached! " + this); AttachInfo info = mAttachInfo; if (info != null) { int vis = info.mWindowVisibility; if (vis != GONE) { onWindowVisibilityChanged(GONE); } } onDetachedFromWindow(); if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) { mAttachInfo.mScrollContainers.remove(this); mPrivateFlags &= ~SCROLL_CONTAINER_ADDED; } mAttachInfo = null; } /** * 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 * @see #dispatchSaveInstanceState * @see #onSaveInstanceState */ public void saveHierarchyState(SparseArray<Parcelable> container) { dispatchSaveInstanceState(container); } /** * Called by {@link #saveHierarchyState} 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 * @see #saveHierarchyState * @see #onSaveInstanceState */ protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) { mPrivateFlags &= ~SAVE_STATE_CALLED; Parcelable state = onSaveInstanceState(); if ((mPrivateFlags & SAVE_STATE_CALLED) == 0) { throw new IllegalStateException( "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. The * default implementation returns null. * @see #onRestoreInstanceState * @see #saveHierarchyState * @see #dispatchSaveInstanceState * @see #setSaveEnabled(boolean) */ protected Parcelable onSaveInstanceState() { mPrivateFlags |= SAVE_STATE_CALLED; return BaseSavedState.EMPTY_STATE; } /** * Restore this view hierarchy's frozen state from the given container. * * @param container The SparseArray which holds previously frozen states. * * @see #saveHierarchyState * @see #dispatchRestoreInstanceState * @see #onRestoreInstanceState */ public void restoreHierarchyState(SparseArray<Parcelable> container) { dispatchRestoreInstanceState(container); } /** * Called by {@link #restoreHierarchyState} to retrieve the state for this view and its * children. May be overridden to modify how restoreing 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 * @see #restoreHierarchyState * @see #onRestoreInstanceState */ protected void 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 &= ~SAVE_STATE_CALLED; onRestoreInstanceState(state); if ((mPrivateFlags & SAVE_STATE_CALLED) == 0) { throw new IllegalStateException( "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 * @see #dispatchRestoreInstanceState */ protected void onRestoreInstanceState(Parcelable state) { mPrivateFlags |= SAVE_STATE_CALLED; if (state != BaseSavedState.EMPTY_STATE && state != null) { throw new IllegalArgumentException("Wrong state class -- expecting View State"); } } /** * <p>Return the time at which the drawing of the view hierarchy started.</p> * * @return the drawing start time in milliseconds */ public long 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> * * @param enabled True to enable duplication of the parent's drawable state, false * to disable it. * * @see #getDrawableState() * @see #isDuplicateParentStateEnabled() */ public void 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) */ public boolean isDuplicateParentStateEnabled() { return (mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE; } /** * <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> * * @param enabled true to enable the drawing cache, false otherwise * * @see #isDrawingCacheEnabled() * @see #getDrawingCache() * @see #buildDrawingCache() */ public void setDrawingCacheEnabled(boolean enabled) { 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() */ @ViewDebug.ExportedProperty public boolean isDrawingCacheEnabled() { return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED; } /** * <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> * * @return a bitmap representing this view or null if cache is disabled * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() * @see #buildDrawingCache() * @see #destroyDrawingCache() */ public Bitmap getDrawingCache() { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { buildDrawingCache(); } return mDrawingCache == null ? null : mDrawingCache.get(); } /** * <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() */ public void destroyDrawingCache() { if (mDrawingCache != null) { final Bitmap bitmap = mDrawingCache.get(); if (bitmap != null) bitmap.recycle(); mDrawingCache = null; } } /** * Setting a solid background color for the drawing cache's bitmaps will improve * perfromance 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() */ public void setDrawingCacheBackgroundColor(int color) { mDrawingCacheBackgroundColor = color; } /** * @see #setDrawingCacheBackgroundColor(int) * * @return The background color to used for the drawing cache's bitmap */ public int getDrawingCacheBackgroundColor() { return mDrawingCacheBackgroundColor; } /** * <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> * * @see #getDrawingCache() * @see #destroyDrawingCache() */ public void buildDrawingCache() { if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null || mDrawingCache.get() == null) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); } if (ViewRoot.PROFILE_DRAWING) { EventLog.writeEvent(60002, hashCode()); } final int width = mRight - mLeft; final int height = mBottom - mTop; final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; final boolean opaque = drawingCacheBackgroundColor != 0 || (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE); if (width <= 0 || height <= 0 || (width * height * (opaque ? 2 : 4) > // Projected bitmap size in bytes ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); return; } boolean clear = true; Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get(); if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { Bitmap.Config quality; if (!opaque) { switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) { case DRAWING_CACHE_QUALITY_AUTO: quality = Bitmap.Config.ARGB_8888; break; case DRAWING_CACHE_QUALITY_LOW: quality = Bitmap.Config.ARGB_4444; break; case DRAWING_CACHE_QUALITY_HIGH: quality = Bitmap.Config.ARGB_8888; break; default: quality = Bitmap.Config.ARGB_8888; break; } } else { quality = Bitmap.Config.RGB_565; } // Try to cleanup memory if (bitmap != null) bitmap.recycle(); try { bitmap = Bitmap.createBitmap(width, height, quality); mDrawingCache = new SoftReference<Bitmap>(bitmap); } 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 mDrawingCache = null; return; } clear = drawingCacheBackgroundColor != 0; } Canvas canvas; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { canvas = new Canvas(); } 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 = new Canvas(bitmap); } if (clear) { bitmap.eraseColor(drawingCacheBackgroundColor); } computeScroll(); final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN; // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } dispatchDraw(canvas); } else { draw(canvas); } canvas.restoreToCount(restoreCount); if (attachInfo != null) { // Restore the cached Canvas for our siblings attachInfo.mCanvas = canvas; } mPrivateFlags |= DRAWING_CACHE_VALID; } } /** * Create a snapshot of the view into a bitmap. We should probably make * some form of this public, but should think about the API. */ /*package*/ Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor) { final int width = mRight - mLeft; final int height = mBottom - mTop; Bitmap bitmap = Bitmap.createBitmap(width, height, quality); if (bitmap == null) { throw new OutOfMemoryError(); } Canvas canvas; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { canvas = new Canvas(); } 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 // things would happen (invisible views, corrupted drawings, etc.) attachInfo.mCanvas = null; } else { // This case should hopefully never or seldom happen canvas = new Canvas(bitmap); } if ((backgroundColor&0xff000000) != 0) { bitmap.eraseColor(backgroundColor); } computeScroll(); final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { dispatchDraw(canvas); } else { draw(canvas); } canvas.restoreToCount(restoreCount); if (attachInfo != null) { // Restore the cached Canvas for our siblings attachInfo.mCanvas = canvas; } return bitmap; } /** * 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. */ public boolean isInEditMode() { return false; } /** * 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 */ protected boolean isPaddingOffsetRequired() { return false; } /** * 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 */ protected int getLeftPaddingOffset() { return 0; } /** * 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 */ protected int getRightPaddingOffset() { return 0; } /** * 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 */ protected int getTopPaddingOffset() { return 0; } /** * 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 */ protected int getBottomPaddingOffset() { return 0; } /** * 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, do not override this method; instead, * you should implement {@link #onDraw}. * * @param canvas The Canvas to which the View is rendered. */ public void draw(Canvas canvas) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } mPrivateFlags |= 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) */ // Step 1, draw the background, if needed int saveCount; final Drawable background = mBGDrawable; if (background != null) { final int scrollX = mScrollX; final int scrollY = mScrollY; if (mBackgroundSizeChanged) { background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged = false; } if ((scrollX | scrollY) == 0) { background.draw(canvas); } else { canvas.translate(scrollX, scrollY); background.draw(canvas); canvas.translate(-scrollX, -scrollY); } } // skip step 2 & 5 if possible (common case) final int 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); // Step 6, draw decorations (scrollbars) onDrawScrollBars(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; int paddingTop = mPaddingTop; final boolean offsetRequired = isPaddingOffsetRequired(); if (offsetRequired) { paddingLeft += getLeftPaddingOffset(); paddingTop += getTopPaddingOffset(); } int left = mScrollX + paddingLeft; int right = left + mRight - mLeft - mPaddingRight - paddingLeft; int top = mScrollY + paddingTop; int bottom = top + mBottom - mTop - mPaddingBottom - paddingTop; if (offsetRequired) { right += getRightPaddingOffset(); bottom += getBottomPaddingOffset(); } final ScrollabilityCache scrollabilityCache = mScrollCache; int length = scrollabilityCache.fadingEdgeLength; // 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 >= 0.0f; bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength())); drawBottom = bottomFadeStrength >= 0.0f; } if (horizontalEdges) { leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength())); drawLeft = leftFadeStrength >= 0.0f; rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength())); drawRight = rightFadeStrength >= 0.0f; } saveCount = canvas.getSaveCount(); int solidColor = getSolidColor(); if (solidColor == 0) { final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); } if (drawBottom) { canvas.saveLayer(left, bottom - length, right, bottom, null, flags); } if (drawLeft) { canvas.saveLayer(left, top, left + length, bottom, null, flags); } if (drawRight) { canvas.saveLayer(right - length, top, right, bottom, null, flags); } } 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 final Paint p = scrollabilityCache.paint; final Matrix matrix = scrollabilityCache.matrix; final Shader fade = scrollabilityCache.shader; final float fadeHeight = scrollabilityCache.fadingEdgeLength; if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); canvas.drawRect(left, top, right, top + length, p); } if (drawBottom) { matrix.setScale(1, fadeHeight * bottomFadeStrength); matrix.postRotate(180); matrix.postTranslate(left, bottom); fade.setLocalMatrix(matrix); canvas.drawRect(left, bottom - length, right, bottom, p); } if (drawLeft) { matrix.setScale(1, fadeHeight * leftFadeStrength); matrix.postRotate(-90); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); canvas.drawRect(left, top, left + length, bottom, p); } if (drawRight) { matrix.setScale(1, fadeHeight * rightFadeStrength); matrix.postRotate(90); matrix.postTranslate(right, top); fade.setLocalMatrix(matrix); canvas.drawRect(right - length, top, right, bottom, p); } canvas.restoreToCount(saveCount); // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); } /** * 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 * @see #setHorizontalFadingEdgeEnabled * * @return The known solid color background for this view, or 0 if the color may vary */ public int getSolidColor() { return 0; } /** * 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 */ private static String printFlags(int flags) { String output = ""; int numFlags = 0; if ((flags & FOCUSABLE_MASK) == 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 */ private static String printPrivateFlags(int privateFlags) { String output = ""; int numFlags = 0; if ((privateFlags & WANTS_FOCUS) == WANTS_FOCUS) { output += "WANTS_FOCUS"; numFlags++; } if ((privateFlags & FOCUSED) == FOCUSED) { if (numFlags > 0) { output += " "; } output += "FOCUSED"; numFlags++; } if ((privateFlags & SELECTED) == SELECTED) { if (numFlags > 0) { output += " "; } output += "SELECTED"; numFlags++; } if ((privateFlags & IS_ROOT_NAMESPACE) == IS_ROOT_NAMESPACE) { if (numFlags > 0) { output += " "; } output += "IS_ROOT_NAMESPACE"; numFlags++; } if ((privateFlags & HAS_BOUNDS) == HAS_BOUNDS) { if (numFlags > 0) { output += " "; } output += "HAS_BOUNDS"; numFlags++; } if ((privateFlags & DRAWN) == 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 */ public boolean isLayoutRequested() { return (mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT; } /** * 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(). * * Derived classes with children should override * onLayout. In that method, they should * call layout on each of their their children. * * @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 */ public final void layout(int l, int t, int r, int b) { boolean changed = setFrame(l, t, r, b); if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT); } onLayout(changed, l, t, r, b); mPrivateFlags &= ~LAYOUT_REQUIRED; } mPrivateFlags &= ~FORCE_LAYOUT; } /** * 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 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 */ protected void 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} */ protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; if (DBG) { System.out.println(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 & DRAWN; // Invalidate our old position invalidate(); int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; mLeft = left; mTop = top; mRight = right; mBottom = bottom; mPrivateFlags |= HAS_BOUNDS; int newWidth = right - left; int newHeight = bottom - top; if (newWidth != oldWidth || newHeight != oldHeight) { onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); } if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) { // 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, therby clearing // the DRAWN bit. mPrivateFlags |= DRAWN; invalidate(); } // Reset drawn bit to original value (invalidate turns it off) mPrivateFlags |= drawn; mBackgroundSizeChanged = true; } return changed; } /** * 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. */ protected void onFinishInflate() { } /** * Returns the resources associated with this view. * * @return Resources object. */ public Resources getResources() { return mResources; } /** * Invalidates the specified Drawable. * * @param drawable the drawable to invalidate */ public void invalidateDrawable(Drawable drawable) { if (verifyDrawable(drawable)) { final Rect dirty = drawable.getBounds(); final int scrollX = mScrollX; final int scrollY = mScrollY; invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); } } /** * 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. */ public void scheduleDrawable(Drawable who, Runnable what, long when) { if (verifyDrawable(who) && what != null && mAttachInfo != null) { mAttachInfo.mHandler.postAtTime(what, who, when); } } /** * Cancels a scheduled action on a drawable. * * @param who the recipient of the action * @param what the action to cancel */ public void unscheduleDrawable(Drawable who, Runnable what) { if (verifyDrawable(who) && what != null && mAttachInfo != null) { mAttachInfo.mHandler.removeCallbacks(what, who); } } /** * 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 */ public void unscheduleDrawable(Drawable who) { if (mAttachInfo != null) { mAttachInfo.mHandler.removeCallbacksAndMessages(who); } } /** * 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 than the Drawable is being displayed in the * view; else false and it is not allowed to animate. * * @see #unscheduleDrawable * @see #drawableStateChanged */ protected boolean verifyDrawable(Drawable who) { return who == mBGDrawable; } /** * 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>Be sure to call through to the superclass when overriding this * function. * * @see Drawable#setState */ protected void drawableStateChanged() { Drawable d = mBGDrawable; if (d != null && d.isStateful()) { d.setState(getDrawableState()); } } /** * 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 */ public void refreshDrawableState() { mPrivateFlags |= DRAWABLE_STATE_DIRTY; drawableStateChanged(); ViewParent parent = mParent; if (parent != null) { parent.childDrawableStateChanged(this); } } /** * 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 * @see #drawableStateChanged * @see #onCreateDrawableState */ public final int[] getDrawableState() { if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) { return mDrawableState; } else { mDrawableState = onCreateDrawableState(0); mPrivateFlags &= ~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 */ protected int[] onCreateDrawableState(int extraSpace) { if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE && mParent instanceof View) { return ((View) mParent).onCreateDrawableState(extraSpace); } int[] drawableState; int privateFlags = mPrivateFlags; int viewStateIndex = (((privateFlags & PRESSED) != 0) ? 1 : 0); viewStateIndex = (viewStateIndex << 1) + (((mViewFlags & ENABLED_MASK) == ENABLED) ? 1 : 0); viewStateIndex = (viewStateIndex << 1) + (isFocused() ? 1 : 0); viewStateIndex = (viewStateIndex << 1) + (((privateFlags & SELECTED) != 0) ? 1 : 0); final boolean hasWindowFocus = hasWindowFocus(); viewStateIndex = (viewStateIndex << 1) + (hasWindowFocus ? 1 : 0); drawableState = VIEW_STATE_SETS[viewStateIndex]; //noinspection ConstantIfStatement if (false) { Log.i("View", "drawableStateIndex=" + viewStateIndex); Log.i("View", toString() + " pressed=" + ((privateFlags & PRESSED) != 0) + " en=" + ((mViewFlags & ENABLED_MASK) == ENABLED) + " fo=" + hasFocus() + " sl=" + ((privateFlags & SELECTED) != 0) + " wf=" + hasWindowFocus + ": " + Arrays.toString(drawableState)); } if (extraSpace == 0) { return drawableState; } final int[] fullState; if (drawableState != null) { fullState = new int[drawableState.length + extraSpace]; System.arraycopy(drawableState, 0, fullState, 0, drawableState.length); } else { fullState = new int[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}. * * @param baseState The base state values returned by * {@link #onCreateDrawableState}, 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 */ protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) { final int 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; } /** * Sets the background color for this view. * @param color the color of the background */ public void setBackgroundColor(int color) { setBackgroundDrawable(new ColorDrawable(color)); } /** * Set the background to a given resource. The resource should refer to * a Drawable object. * @param resid The identifier of the resource. * @attr ref android.R.styleable#View_background */ public void setBackgroundResource(int resid) { if (resid != 0 && resid == mBackgroundResource) { return; } Drawable d= null; if (resid != 0) { d = mResources.getDrawable(resid); } setBackgroundDrawable(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 d The Drawable to use as the background, or null to remove the * background */ public void setBackgroundDrawable(Drawable d) { boolean requestLayout = false; mBackgroundResource = 0; /* * Regardless of whether we're setting a new background or not, we want * to clear the previous drawable. */ if (mBGDrawable != null) { mBGDrawable.setCallback(null); unscheduleDrawable(mBGDrawable); } if (d != null) { Rect padding = sThreadLocal.get(); if (padding == null) { padding = new Rect(); sThreadLocal.set(padding); } if (d.getPadding(padding)) { setPadding(padding.left, padding.top, padding.right, padding.bottom); } // 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 (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() || mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) { requestLayout = true; } d.setCallback(this); if (d.isStateful()) { d.setState(getDrawableState()); } d.setVisible(getVisibility() == VISIBLE, false); mBGDrawable = d; if ((mPrivateFlags & SKIP_DRAW) != 0) { mPrivateFlags &= ~SKIP_DRAW; mPrivateFlags |= ONLY_DRAWS_BACKGROUND; requestLayout = true; } } else { /* Remove the background */ mBGDrawable = null; if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) { /* * This view ONLY drew the background before and we're removing * the background, so now it won't draw anything * (hence we SKIP_DRAW) */ mPrivateFlags &= ~ONLY_DRAWS_BACKGROUND; mPrivateFlags |= 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; } if (requestLayout) { requestLayout(); } mBackgroundSizeChanged = true; invalidate(); } /** * Gets the background drawable * @return The drawable used as the background for this view, if any. */ public Drawable getBackground() { return mBGDrawable; } /** * 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 */ public void setPadding(int left, int top, int right, int bottom) { boolean changed = false; mUserPaddingRight = right; mUserPaddingBottom = bottom; final int viewFlags = mViewFlags; // Common case is there are no scroll bars. if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) { // TODO: Deal with RTL languages to adjust left padding instead of right. if ((viewFlags & SCROLLBARS_VERTICAL) != 0) { right += (viewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getVerticalScrollbarWidth(); } 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(); } } /** * Returns the top padding of this view. * * @return the top padding in pixels */ public int 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 */ public int 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 */ public int getPaddingLeft() { return 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 */ public int getPaddingRight() { return mPaddingRight; } /** * 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 */ public void setSelected(boolean selected) { if (((mPrivateFlags & SELECTED) != 0) != selected) { mPrivateFlags = (mPrivateFlags & ~SELECTED) | (selected ? SELECTED : 0); invalidate(); refreshDrawableState(); dispatchSetSelected(selected); } } /** * Dispatch setSelected to all of this View's children. * * @see #setSelected(boolean) * * @param selected The new selected state */ protected void dispatchSetSelected(boolean selected) { } /** * Indicates the selection state of this view. * * @return true if the view is selected, false otherwise */ @ViewDebug.ExportedProperty public boolean isSelected() { return (mPrivateFlags & SELECTED) != 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. */ public ViewTreeObserver getViewTreeObserver() { if (mAttachInfo != null) { return mAttachInfo.mTreeObserver; } if (mFloatingTreeObserver == null) { mFloatingTreeObserver = new ViewTreeObserver(); } return mFloatingTreeObserver; } /** * <p>Finds the topmost view in the current view hierarchy.</p> * * @return the topmost view containing this view */ public View getRootView() { if (mAttachInfo != null) { final View v = mAttachInfo.mRootView; if (v != null) { return v; } } View parent = this; while (parent.mParent != null && parent.mParent instanceof View) { parent = (View) parent.mParent; } return parent; } /** * <p>Computes the coordinates of this view on the screen. The argument * must be an array of two integers. After the method returns, the array * contains the x and y location in that order.</p> * * @param location an array of two integers in which to hold the coordinates */ public void getLocationOnScreen(int[] location) { getLocationInWindow(location); final AttachInfo info = mAttachInfo; location[0] += info.mWindowLeft; location[1] += info.mWindowTop; } /** * <p>Computes the coordinates of this view in its window. The argument * must be an array of two integers. After the method returns, the array * contains the x and y location in that order.</p> * * @param location an array of two integers in which to hold the coordinates */ public void getLocationInWindow(int[] location) { if (location == null || location.length < 2) { throw new IllegalArgumentException("location must be an array of " + "two integers"); } location[0] = mLeft; location[1] = mTop; ViewParent viewParent = mParent; while (viewParent instanceof View) { final View view = (View)viewParent; location[0] += view.mLeft - view.mScrollX; location[1] += view.mTop - view.mScrollY; viewParent = view.mParent; } if (viewParent instanceof ViewRoot) { // *cough* final ViewRoot vr = (ViewRoot)viewParent; location[1] -= vr.mCurScrollY; } } /** * {@hide} * @param id the id of the view to be found * @return the view of the specified id, null if cannot be found */ protected View findViewTraversal(int id) { if (id == mID) { return this; } return null; } /** * {@hide} * @param tag the tag of the view to be found * @return the view of specified tag, null if cannot be found */ protected View findViewWithTagTraversal(Object tag) { if (tag != null && tag.equals(mTag)) { return this; } return null; } /** * Look for a child view with the given id. If this view has the given * id, return this view. * * @param id The id to search for. * @return The view that has the given id in the hierarchy or null */ public final View findViewById(int id) { if (id < 0) { return null; } return findViewTraversal(id); } /** * 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 */ public final View findViewWithTag(Object tag) { if (tag == null) { return null; } return findViewWithTagTraversal(tag); } /** * 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 * * @param id a number used to identify the view * * @attr ref android.R.styleable#View_id */ public void setId(int id) { mID = id; } /** * {@hide} * * @param isRoot true if the view belongs to the root namespace, false * otherwise */ public void setIsRootNamespace(boolean isRoot) { if (isRoot) { mPrivateFlags |= IS_ROOT_NAMESPACE; } else { mPrivateFlags &= ~IS_ROOT_NAMESPACE; } } /** * {@hide} * * @return true if the view belongs to the root namespace, false otherwise */ public boolean isRootNamespace() { return (mPrivateFlags&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 * @see #findViewById * @attr ref android.R.styleable#View_id */ @ViewDebug.CapturedViewProperty public int getId() { return mID; } /** * Returns this view's tag. * * @return the Object stored in this view as a tag */ @ViewDebug.ExportedProperty public Object 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 */ public void setTag(final Object tag) { mTag = tag; } /** * Prints information about this view in the log output, with the tag * {@link #VIEW_LOG_TAG}. * * @hide */ public void 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 */ protected void 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 & 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 an string of whitespaces used for indentation. * * @param depth the indentation level * @return a String containing (depth * 2 + 3) * 2 white spaces * * @hide */ protected static String debugIndent(int depth) { StringBuilder spaces = new StringBuilder((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 public int getBaseline() { return -1; } /** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view * tree. */ public void requestLayout() { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT); } mPrivateFlags |= FORCE_LAYOUT; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } } /** * Forces this view to be laid out during the next layout pass. * This method does not call requestLayout() or forceLayout() * on the parent. */ public void forceLayout() { mPrivateFlags |= FORCE_LAYOUT; } /** * <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 mesurement 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 overriden 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) */ public final void measure(int widthMeasureSpec, int heightMeasureSpec) { if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~MEASURED_DIMENSION_SET; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE); } // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) { throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; } /** * <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 overriden 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) */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } /** * <p>This mehod 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 * @param measuredHeight the measured height of this view */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= MEASURED_DIMENSION_SET; } /** * Utility to reconcile a desired size with constraints imposed by a MeasureSpec. * Will take the desired size, unless a different size is imposed by the constraints. * * @param size How big the view wants to be * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ public static int resolveSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: result = Math.min(size, specSize); break; case MeasureSpec.EXACTLY: result = specSize; break; } return result; } /** * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no contraints. 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. */ public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.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. */ protected int getSuggestedMinimumHeight() { int suggestedMinHeight = mMinHeight; if (mBGDrawable != null) { final int bgMinHeight = mBGDrawable.getMinimumHeight(); if (suggestedMinHeight < bgMinHeight) { suggestedMinHeight = bgMinHeight; } } return suggestedMinHeight; } /** * 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. */ protected int getSuggestedMinimumWidth() { int suggestedMinWidth = mMinWidth; if (mBGDrawable != null) { final int bgMinWidth = mBGDrawable.getMinimumWidth(); if (suggestedMinWidth < bgMinWidth) { suggestedMinWidth = bgMinWidth; } } return suggestedMinWidth; } /** * 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. */ public void setMinimumHeight(int minHeight) { mMinHeight = minHeight; } /** * 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. */ public void setMinimumWidth(int minWidth) { mMinWidth = minWidth; } /** * Get the animation currently associated with this view. * * @return The animation that is currently playing or * scheduled to play for this view. */ public Animation getAnimation() { return mCurrentAnimation; } /** * Start the specified animation now. * * @param animation the animation to start now */ public void startAnimation(Animation animation) { animation.setStartTime(Animation.START_ON_FIRST_FRAME); setAnimation(animation); invalidate(); } /** * Cancels any animations for this view. */ public void clearAnimation() { mCurrentAnimation = null; } /** * Sets the next animation to play for this view. * If you want the animation to play immediately, use * startAnimation. 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 will be invalidated when the animation is supposed to * start. * * @param animation The next animation, or null. */ public void setAnimation(Animation animation) { mCurrentAnimation = animation; if (animation != null) { 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() */ protected void onAnimationStart() { mPrivateFlags |= 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() */ protected void onAnimationEnd() { mPrivateFlags &= ~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. */ protected boolean onSetAlpha(int alpha) { return false; } /** * This is used by the RootView 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 ViewRoot (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. * * {@hide} */ public boolean gatherTransparentRegion(Region region) { final AttachInfo attachInfo = mAttachInfo; if (region != null && attachInfo != null) { final int pflags = mPrivateFlags; if ((pflags & SKIP_DRAW) == 0) { // The SKIP_DRAW flag IS NOT set, so this view draws. We need to // remove it from the transparent region. final int[] location = attachInfo.mTransparentLocation; getLocationInWindow(location); region.op(location[0], location[1], location[0] + mRight - mLeft, location[1] + mBottom - mTop, Region.Op.DIFFERENCE); } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBGDrawable != null) { // The ONLY_DRAWS_BACKGROUND flag IS set and the background drawable // exists, so we remove the background drawable's non-transparent // parts from this transparent region. applyDrawableToTransparentRegion(mBGDrawable, region); } } return true; } /** * 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} */ public void playSoundEffect(int 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} */ public boolean 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}. */ public boolean performHapticFeedback(int feedbackConstant, int flags) { if (mAttachInfo == null) { return false; } if ((flags&HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0 && !isHapticFeedbackEnabled()) { return false; } return mAttachInfo.mRootCallbacks.performHapticFeedback( feedbackConstant, (flags&HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0); } /** * Given a Drawable whose bounds have been set to draw into this view, * update a Region being computed for {@link #gatherTransparentRegion} 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} */ public void applyDrawableToTransparentRegion(Drawable dr, Region region) { if (DBG) { Log.i("View", "Getting transparent region for: " + this); } final Region r = dr.getTransparentRegion(); final Rect db = dr.getBounds(); final AttachInfo attachInfo = mAttachInfo; if (r != null && attachInfo != null) { final int w = getRight()-getLeft(); final int 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); } final int[] location = attachInfo.mTransparentLocation; getLocationInWindow(location); r.translate(location[0], location[1]); region.op(r, Region.Op.INTERSECT); } else { region.op(db, Region.Op.DIFFERENCE); } } private void postCheckForLongClick() { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.rememberWindowAttachCount(); postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout()); } private static int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2) { final int stateSet1Length = stateSet1.length; final int stateSet2Length = stateSet2.length; final int[] newSet = new int[stateSet1Length + stateSet2Length]; int k = 0; int i = 0; int j = 0; // This is a merge of the two input state sets and assumes that the // input sets are sorted by the order imposed by ViewDrawableStates. for (int viewState : R.styleable.ViewDrawableStates) { if (i < stateSet1Length && stateSet1[i] == viewState) { newSet[k++] = viewState; i++; } else if (j < stateSet2Length && stateSet2[j] == viewState) { newSet[k++] = viewState; j++; } if (k > 1) { assert(newSet[k - 1] > newSet[k - 2]); } } return newSet; } /** * 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 */ public static View inflate(Context context, int resource, ViewGroup root) { LayoutInflater factory = LayoutInflater.from(context); return factory.inflate(resource, root); } /** * 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 <size, mode> tuple into the int. */ public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /** * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ public static final int 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. */ public static final int EXACTLY = 1 << MODE_SHIFT; /** * Measure specification mode: The child can be as large as it wants up * to the specified size. */ public static final int 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> * * @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 */ public static int makeMeasureSpec(int size, int mode) { return 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} */ public static int getMode(int measureSpec) { 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 */ public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } /** * 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" */ public static String toString(int measureSpec) { int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb = new StringBuilder("MeasureSpec: "); if (mode == UNSPECIFIED) sb.append("UNSPECIFIED "); else if (mode == EXACTLY) sb.append("EXACTLY "); else if (mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } } class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; public void run() { if (isPressed() && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { if (performLongClick()) { mHasPerformedLongPress = true; } } } public void rememberWindowAttachCount() { mOriginalWindowAttachCount = mWindowAttachCount; } } /** * Interface definition for a callback to be invoked when a key event is * dispatched to this view. The callback will be invoked before the key * event is given to the view. */ public interface OnKeyListener { /** * Called when a key is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @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 touch event is * dispatched to this view. The callback will be invoked before the touch * event is given to the view. */ public interface OnTouchListener { /** * 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 view has been clicked and held. */ public interface OnLongClickListener { /** * 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 callback to be invoked when the focus state of * a view changed. */ public interface OnFocusChangeListener { /** * 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. */ public interface OnClickListener { /** * 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 the context menu * for this view is being built. */ public interface OnCreateContextMenuListener { /** * 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); } private final class UnsetPressedState implements Runnable { public void run() { setPressed(false); } } /** * Base class for derived classes that want to save and restore their own * state in {@link android.view.View#onSaveInstanceState()}. */ public static class BaseSavedState extends AbsSavedState { /** * Constructor used when reading from a parcel. Reads the state of the superclass. * * @param source */ public BaseSavedState(Parcel source) { super(source); } /** * Constructor called by derived classes when creating their SavedState objects * * @param superState The state of the superclass of this view */ public BaseSavedState(Parcelable superState) { super(superState); } public static final Parcelable.Creator<BaseSavedState> CREATOR = new Parcelable.Creator<BaseSavedState>() { public BaseSavedState createFromParcel(Parcel in) { return new BaseSavedState(in); } public BaseSavedState[] newArray(int size) { return new BaseSavedState[size]; } }; } /** * A set of information given to a view when it is attached to its parent * window. */ static class AttachInfo { interface Callbacks { 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. * * The pool is implemented as a linked list of InvalidateInfo object with * the root pointing to the next available InvalidateInfo. If the root * is null (i.e. when all instances from the pool have been acquired), * then a new InvalidateInfo is created and returned to the caller. * * An InvalidateInfo is sent back to the pool by calling its release() * method. If the pool is full the object is simply discarded. * * This implementation follows the object pool pattern used in the * MotionEvent class. */ static class InvalidateInfo { private static final int POOL_LIMIT = 10; private static final Object sLock = new Object(); private static int sAcquiredCount = 0; private static InvalidateInfo sRoot; private InvalidateInfo next; View target; int left; int top; int right; int bottom; static InvalidateInfo acquire() { synchronized (sLock) { if (sRoot == null) { return new InvalidateInfo(); } InvalidateInfo info = sRoot; sRoot = info.next; sAcquiredCount--; return info; } } void release() { synchronized (sLock) { if (sAcquiredCount < POOL_LIMIT) { sAcquiredCount++; next = sRoot; sRoot = this; } } } } final IWindowSession mSession; final IWindow mWindow; final IBinder mWindowToken; final Callbacks mRootCallbacks; /** * The top view of the hierarchy. */ View mRootView; IBinder mPanelParentWindowToken; Surface mSurface; /** * Left position of this view's window */ int mWindowLeft; /** * Top position of this view's window */ int mWindowTop; /** * 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. */ final Rect mContentInsets = new Rect(); /** * 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. */ final Rect mVisibleInsets = new Rect(); /** * 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. */ final ViewTreeObserver.InternalInsetsInfo mGivenInternalInsets = new ViewTreeObserver.InternalInsetsInfo(); /** * 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. */ final ArrayList<View> mScrollContainers = new ArrayList<View>(); /** * Indicates whether the view's window currently has the focus. */ boolean mHasWindowFocus; /** * The current visibility of the window. */ int mWindowVisibility; /** * Indicates the time at which drawing started to occur. */ long mDrawingTime; /** * Indicates whether the view's window is currently in touch mode. */ boolean mInTouchMode; /** * Indicates that ViewRoot should trigger a global layout change * the next time it performs a traversal */ boolean mRecomputeGlobalAttributes; /** * Set to true when attributes (like mKeepScreenOn) need to be * recomputed. */ boolean mAttributesChanged; /** * Set during a traveral if any views want to keep the screen on. */ boolean mKeepScreenOn; /** * Set if the visibility of any views has changed. */ boolean mViewVisibilityChanged; /** * Set to true if a view has been scrolled. */ boolean mViewScrollChanged; /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the transparent region computations. */ final int[] mTransparentLocation = new int[2]; /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the ViewGroup.invalidateChild implementation. */ final int[] mInvalidateChildLocation = new int[2]; /** * The view tree observer used to dispatch global events like * layout, pre-draw, touch mode change, etc. */ final ViewTreeObserver mTreeObserver = new ViewTreeObserver(); /** * A Canvas used by the view hierarchy to perform bitmap caching. */ Canvas mCanvas; /** * A Handler supplied by a view's {@link android.view.ViewRoot}. This * handler can be used to pump events in the UI events queue. */ final Handler mHandler; /** * Identifier for messages requesting the view to be invalidated. * Such messages should be sent to {@link #mHandler}. */ static final int INVALIDATE_MSG = 0x1; /** * Identifier for messages requesting the view to invalidate a region. * Such messages should be sent to {@link #mHandler}. */ static final int INVALIDATE_RECT_MSG = 0x2; /** * Temporary for use in computing invalidate rectangles while * calling up the hierarchy. */ final Rect mTmpInvalRect = new Rect(); /** * 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, Handler handler, Callbacks effectPlayer) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); mHandler = handler; mRootCallbacks = effectPlayer; } } /** * <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> */ private static class ScrollabilityCache { public int fadingEdgeLength; public int scrollBarSize; public ScrollBarDrawable scrollBar; public final Paint paint; public final Matrix matrix; public Shader shader; private int mLastColor; public ScrollabilityCache(ViewConfiguration configuration) { fadingEdgeLength = configuration.getScaledFadingEdgeLength(); scrollBarSize = configuration.getScaledScrollBarSize(); paint = new Paint(); matrix = new Matrix(); // use use a height of 1, and then wack the matrix each time we // actually use it. shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP); paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } public void setFadeColor(int color) { if (color != 0 && color != mLastColor) { mLastColor = color; color |= 0xFF000000; shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP); paint.setShader(shader); // Restore the default transfer mode (src_over) paint.setXfermode(null); } } } }