/* * 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 com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; 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.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Shader; 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.Config; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Pool; import android.util.Poolable; import android.util.PoolableManager; import android.util.Pools; import android.util.SparseArray; import android.view.ContextMenu.ContextMenuInfo; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.ScrollBarDrawable; import java.lang.ref.SoftReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.WeakHashMap; /** *
* 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 widgets, which are * used to create interactive UI components (buttons, text fields, etc.). The * {@link android.view.ViewGroup} subclass is the base class for layouts, which * are invisible containers that hold other Views (or other ViewGroups) and define * their layout properties. *
* *For an introduction to using this class to develop your
* application's user interface, read the Developer Guide documentation on
* User Interface. Special topics
* include:
*
Declaring Layout
*
Creating Menus
*
Common Layout Objects
*
Binding to Data with AdapterView
*
Handling UI Events
*
Applying Styles and Themes
*
Building Custom Components
*
How Android Draws Views.
*
* 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. *
** Once you have created a tree of views, there are typically a few types of * common operations you may wish to perform: *
* 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}. *
* * ** 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)}. *
Category | Methods | Description |
---|---|---|
Creation | *Constructors | *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. * | *
{@link #onFinishInflate()} |
* Called after a view and all of its children has been inflated * from XML. | *|
Layout | *{@link #onMeasure} |
* Called to determine the size requirements for this view and all * of its children. * | *
{@link #onLayout} |
* Called when this view should assign a size and position to all * of its children. * | *|
{@link #onSizeChanged} |
* Called when the size of this view has changed. * | *|
Drawing | *{@link #onDraw} |
* Called when the view should render its content. * | *
Event processing | *{@link #onKeyDown} |
* Called when a new key event occurs. * | *
{@link #onKeyUp} |
* Called when a key up event occurs. * | *|
{@link #onTrackballEvent} |
* Called when a trackball motion event occurs. * | *|
{@link #onTouchEvent} |
* Called when a touch screen motion event occurs. * | *|
Focus | *{@link #onFocusChanged} |
* Called when the view gains or loses focus. * | *
{@link #onWindowFocusChanged} |
* Called when the window containing the view gains or loses focus. * | *|
Attaching | *{@link #onAttachedToWindow()} |
* Called when the view is attached to a window. * | *
{@link #onDetachedFromWindow} |
* Called when the view is detached from its window. * | *|
{@link #onWindowVisibilityChanged} |
* Called when the visibility of the window containing the view * has changed. * | *
* <Button id="@+id/my_button" * android:layout_width="wrap_content" * android:layout_height="wrap_content" * android:text="@string/my_button_text"/> *
* Button myButton = (Button) findViewById(R.id.my_button); *
* 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. *
* * ** The geometry of a view is that of a rectangle. A view has a location, * expressed as a pair of left and top coordinates, and * two dimensions, expressed as a width and a height. The unit for location * and dimensions is the pixel. *
* ** 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. *
* *
* 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: getLeft() + getWidth()
* (see Size for more information about the width.)
*
* The size of a view is expressed with a width and a height. A view actually * possess two pairs of width and height values. *
* ** The first pair is known as measured width and * measured height. These dimensions define how big a view wants to be * within its parent (see Layout for more details.) The * measured dimensions can be obtained by calling {@link #getMeasuredWidth()} * and {@link #getMeasuredHeight()}. *
* ** The second pair is simply known as width and height, or * sometimes drawing width and drawing height. 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()}. *
* ** 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()}. *
* ** 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. *
* * ** 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. *
* ** 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. *
* ** 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: *
* MeasureSpecs are used to push requirements down the tree from parent to * child. A MeasureSpec can be in one of three modes: *
* 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. *
* * *
* 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 onDraw()
method.
*
* Note that the framework will not draw views that are not in the invalid region. *
* ** To force a view to draw, call {@link #invalidate()}. *
* * ** The basic cycle of a view is as follows: *
Note: The entire view tree is single threaded. You must always be on * the UI thread when calling any method on any view. * 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}. *
* * ** 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)}. *
** 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: *
* nextFocusDown * nextFocusLeft * nextFocusRight * nextFocusUp ** * * *
* To get a particular view to take focus, call {@link #requestFocus()}. *
* * ** 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'. *
** 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. *
** 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. *
** 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. *
* * ** 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. *
* * ** 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. *
* * ** 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. *
* * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable * @attr ref android.R.styleable#View_contentDescription * @attr ref android.R.styleable#View_drawingCacheQuality * @attr ref android.R.styleable#View_duplicateParentState * @attr ref android.R.styleable#View_id * @attr ref android.R.styleable#View_fadingEdge * @attr ref android.R.styleable#View_fadingEdgeLength * @attr ref android.R.styleable#View_fitsSystemWindows * @attr ref android.R.styleable#View_isScrollContainer * @attr ref android.R.styleable#View_focusable * @attr ref android.R.styleable#View_focusableInTouchMode * @attr ref android.R.styleable#View_hapticFeedbackEnabled * @attr ref android.R.styleable#View_keepScreenOn * @attr ref android.R.styleable#View_longClickable * @attr ref android.R.styleable#View_minHeight * @attr ref android.R.styleable#View_minWidth * @attr ref android.R.styleable#View_nextFocusDown * @attr ref android.R.styleable#View_nextFocusLeft * @attr ref android.R.styleable#View_nextFocusRight * @attr ref android.R.styleable#View_nextFocusUp * @attr ref android.R.styleable#View_onClick * @attr ref android.R.styleable#View_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 * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_scrollX * @attr ref android.R.styleable#View_scrollY * @attr ref android.R.styleable#View_scrollbarSize * @attr ref android.R.styleable#View_scrollbarStyle * @attr ref android.R.styleable#View_scrollbars * @attr ref android.R.styleable#View_scrollbarTrackHorizontal * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarThumbVertical * @attr ref android.R.styleable#View_scrollbarTrackVertical * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag * @attr ref android.R.styleable#View_visibility * * @see android.view.ViewGroup */ public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { 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; /** *This view doesn't show scrollbars.
* {@hide} */ static final int SCROLLBARS_NONE = 0x00000000; /** *This view shows horizontal scrollbars.
* {@hide} */ static final int SCROLLBARS_HORIZONTAL = 0x00000100; /** *This view shows vertical scrollbars.
* {@hide} */ static final int SCROLLBARS_VERTICAL = 0x00000200; /** *Mask for use with setFlags indicating bits used for indicating which * scrollbars are enabled.
* {@hide} */ static final int SCROLLBARS_MASK = 0x00000300; // note 0x00000400 and 0x00000800 are now available for next flags... /** *This view doesn't show fading edges.
* {@hide} */ static final int FADING_EDGE_NONE = 0x00000000; /** *This view shows horizontal fading edges.
* {@hide} */ static final int FADING_EDGE_HORIZONTAL = 0x00001000; /** *This view shows vertical fading edges.
* {@hide} */ static final int FADING_EDGE_VERTICAL = 0x00002000; /** *Mask for use with setFlags indicating bits used for indicating which * fading edges are enabled.
* {@hide} */ static final int FADING_EDGE_MASK = 0x00003000; /** *Indicates this view can be clicked. When clickable, a View reacts * to clicks by notifying the OnClickListener.
* {@hide} */ static final int CLICKABLE = 0x00004000; /** *
Indicates this view is caching its drawing into a bitmap.
* {@hide} */ static final int DRAWING_CACHE_ENABLED = 0x00008000; /** *Indicates that no icicle should be saved for this view.
* {@hide} */ static final int SAVE_DISABLED = 0x000010000; /** *
Mask for use with setFlags indicating bits used for the saveEnabled * property.
* {@hide} */ static final int SAVE_DISABLED_MASK = 0x000010000; /** *Indicates that no drawing cache should ever be created for this view.
* {@hide} */ static final int WILL_NOT_CACHE_DRAWING = 0x000020000; /** *
Indicates this view can take / keep focus when int touch mode.
* {@hide} */ static final int FOCUSABLE_IN_TOUCH_MODE = 0x00040000; /** *Enables low quality mode for the drawing cache.
*/ public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000; /** *Enables high quality mode for the drawing cache.
*/ public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000; /** *Enables automatic quality mode for the drawing cache.
*/ 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 }; /** *Mask for use with setFlags indicating bits used for the cache * quality property.
* {@hide} */ static final int DRAWING_CACHE_QUALITY_MASK = 0x00180000; /** ** 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. *
* {@hide} */ static final int LONG_CLICKABLE = 0x00200000; /** *Indicates that this view gets its drawable states from its direct parent * and ignores its original internal states.
* * @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; /** * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} * should add all focusable Views regardless if they are focusable in touch mode. */ public static final int FOCUSABLES_ALL = 0x00000000; /** * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} * should add only Views focusable in touch mode. */ public static final int FOCUSABLES_TOUCH_MODE = 0x00000001; /** * 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
* 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
* R.attr.buttonStyle
for defStyle; 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_contentDescription:
mContentDescription = a.getString(attr);
break;
case com.android.internal.R.styleable.View_soundEffectsEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
viewFlagMasks |= SOUND_EFFECTS_ENABLED;
}
break;
case com.android.internal.R.styleable.View_hapticFeedbackEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED;
viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED;
}
break;
case R.styleable.View_scrollbars:
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;
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new OnClickListener() {
private Method mHandler;
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity", e);
}
}
try {
mHandler.invoke(getContext(), View.this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
}
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);
}
computeOpaqueFlags();
a.recycle();
}
/**
* Non-public constructor for use in testing
*/
View() {
}
@Override
protected void finalize() throws Throwable {
super.finalize();
--sInstanceCount;
}
/**
*
* 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. *
* * @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; } /** ** 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. *
* * @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(); } /** ** Initalizes the scrollability cache if necessary. *
*/ 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() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 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() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 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. * *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. * *
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. * *
When 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.
*
* 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 disable 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 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.
*
* The default implementation always returns false. This does
* not 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.
*
* 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
* relative 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 parent 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 parent 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);
}
}
}
/**
* Indicates whether this View is opaque. An opaque View guarantees that it will
* draw all the pixels overlapping its bounds using a fully opaque color.
*
* Subclasses of View should override this method whenever possible to indicate
* whether an instance is opaque. Opaque Views are treated in a special way by
* the View hierarchy, possibly allowing it to perform optimizations during
* invalidate/draw passes.
*
* @return True if this View is guaranteed to be fully opaque, false otherwise.
*
* @hide Pending API council approval
*/
@ViewDebug.ExportedProperty
public boolean isOpaque() {
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
}
private void computeOpaqueFlags() {
// Opaque if:
// - Has a background
// - Background is opaque
// - Doesn't have scrollbars or scrollbars are inside overlay
if (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE) {
mPrivateFlags |= OPAQUE_BACKGROUND;
} else {
mPrivateFlags &= ~OPAQUE_BACKGROUND;
}
final int flags = mViewFlags;
if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY) {
mPrivateFlags |= OPAQUE_SCROLLBARS;
} else {
mPrivateFlags &= ~OPAQUE_SCROLLBARS;
}
}
/**
* @hide
*/
protected boolean hasOpaqueScrollbars() {
return (mPrivateFlags & OPAQUE_SCROLLBARS) == OPAQUE_SCROLLBARS;
}
/**
* @return A handler associated with the thread running the View. This
* handler can be used to pump events in the UI events queue.
*/
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() {
}
/**
* Indicate whether the horizontal edges are faded when the view is
* scrolled horizontally. Define whether the horizontal edges should be faded when this view
* is scrolled horizontally. Indicate whether the vertical edges are faded when the view is
* scrolled horizontally. Define whether the vertical edges should be faded when this view
* is scrolled vertically. Indicate whether the horizontal scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Define whether the horizontal scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Indicate whether the vertical scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Define whether the vertical scrollbar should be drawn or not. The
* scrollbar is not drawn by default. 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. Returns the current scrollbar style. Compute the horizontal range that the horizontal scrollbar
* represents. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollExtent()} and
* {@link #computeHorizontalScrollOffset()}. The default range is the drawing width of this view. 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. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeHorizontalScrollExtent()}. The default offset is the scroll offset of this view. 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. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeHorizontalScrollOffset()}. The default extent is the drawing width of this view. Compute the vertical range that the vertical scrollbar represents. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollExtent()} and
* {@link #computeVerticalScrollOffset()}. The default range is the drawing height of this view. 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. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollExtent()}. The default offset is the scroll offset of this view. 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. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeVerticalScrollOffset()}. The default extent is the drawing height of this view. Request the drawing of the horizontal and the vertical scrollbar. The
* scrollbars are painted only if they have been awakened first. Draw the horizontal scrollbar if
* {@link #isHorizontalScrollBarEnabled()} returns true. 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. Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()}
* returns true. 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.
* 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 Return the time at which the drawing of the view hierarchy started. 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. 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. Note: if this view's parent addStateFromChildren property is enabled and this
* property is enabled, an exception will be thrown. Indicates whether this duplicates its drawable state from its parent. 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. Indicates whether the drawing cache is enabled for this view. Calling this method is equivalent to calling 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. Note about auto scaling in compatibility mode: When auto scaling is not enabled,
* this method will create a bitmap of the same size as this view. Because this bitmap
* will be drawn scaled by the parent ViewGroup, the result on screen might show
* scaling artifacts. To avoid such artifacts, you should call this method by setting
* the auto scaling to true. Doing so, however, will generate a bitmap of a different
* size than the view. This implies that your application must be able to handle this
* size. 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. Calling this method is equivalent to calling Forces the drawing cache to be built if the drawing cache is invalid. If you call {@link #buildDrawingCache()} manually without calling
* {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
* should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards. Note about auto scaling in compatibility mode: When auto scaling is not enabled,
* this method will create a bitmap of the same size as this view. Because this bitmap
* will be drawn scaled by the parent ViewGroup, the result on screen might show
* scaling artifacts. To avoid such artifacts, you should call this method by setting
* the auto scaling to true. Doing so, however, will generate a bitmap of a different
* size than the view. This implies that your application must be able to handle this
* size. Indicates whether or not this view's layout will be requested during
* the next hierarchy layout pass. 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) {
Log.d("View", 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.
*
* 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.
*
* 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.
*
* 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 additionalState into the base
* state values baseState 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 baseState; this array is not modified.
*
* @return As a convenience, the baseState 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;
}
computeOpaqueFlags();
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);
if (!selected) resetPressedState();
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;
}
/**
* Finds the topmost view in the current view hierarchy. 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. 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.immediate
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 null
otherwise.
*/
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
InputMethodManager imm = InputMethodManager.peekInstance();
if (!gainFocus) {
if (isPressed()) {
setPressed(false);
}
if (imm != null && mAttachInfo != null
&& mAttachInfo.mHasWindowFocus) {
imm.focusOut(this);
}
onFocusLost();
} else if (imm != null && mAttachInfo != null
&& mAttachInfo.mHasWindowFocus) {
imm.focusIn(this);
}
invalidate();
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(this, gainFocus);
}
}
/**
* {@inheritDoc}
*/
public void sendAccessibilityEvent(int eventType) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
}
}
/**
* {@inheritDoc}
*/
public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
event.setClassName(getClass().getName());
event.setPackageName(getContext().getPackageName());
event.setEnabled(isEnabled());
event.setContentDescription(mContentDescription);
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
ArrayListnull
.
*
* @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
* null
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);
}
onFocusLost();
} 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 not 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.
*
* getDrawingCache(false)
.buildDrawingCache(false)
.