/*
* Copyright (C) 2010 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 com.glview.view;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static java.lang.Math.max;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Interpolator;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.VelocityTracker;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnDragListener;
import android.view.View.OnGenericMotionListener;
import android.view.View.OnHoverListener;
import android.view.ViewDebug;
import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import com.glview.animation.AnimatorInflater;
import com.glview.animation.FloatProperty;
import com.glview.animation.StateListAnimator;
import com.glview.content.GLContext;
import com.glview.content.res.GLResources;
import com.glview.graphics.Bitmap;
import com.glview.graphics.Point;
import com.glview.graphics.Rect;
import com.glview.graphics.drawable.ColorDrawable;
import com.glview.graphics.drawable.Drawable;
import com.glview.graphics.shader.BaseShader;
import com.glview.graphics.shader.LinearGradient;
import com.glview.graphics.shader.TileMode;
import com.glview.hwui.GLCanvas;
import com.glview.hwui.GLPaint;
import com.glview.hwui.RenderNode;
import com.glview.thread.Handler;
import com.glview.util.LayoutDirection;
import com.glview.util.Predicate;
import com.glview.util.Property;
import com.glview.view.animation.Animation;
import com.glview.view.animation.AnimationUtils;
import com.glview.widget.ScrollBarDrawable;
// GLView is a UI component. It can render to a GLCanvas and accept touch
// events. A GLView may have zero or more child GLView and they form a tree
// structure. The rendering and event handling will pass through the tree
// structure.
//
// A GLView tree should be attached to a GLRoot before event dispatching and
// rendering happens. GLView asks GLRoot to re-render or re-layout the
// GLView hierarchy using requestRender() and requestLayoutContentPane().
//
// The render() method is called in a separate thread. Before calling
// dispatchTouchEvent() and layout(), GLRoot acquires a lock to avoid the
// rendering thread running at the same time. If there are other entry points
// from main thread (like a Handler) in your GLView, you need to call
// lockRendering() if the rendering thread should not run at the same time.
//
/**
* @attr ref android.R.styleable#View_alpha
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
* @attr ref android.R.styleable#View_contentDescription
* @attr ref android.R.styleable#View_drawingCacheQuality
* @attr ref android.R.styleable#View_duplicateParentState
* @attr ref android.R.styleable#View_id
* @attr ref android.R.styleable#View_requiresFadingEdge
* @attr ref android.R.styleable#View_fadeScrollbars
* @attr ref android.R.styleable#View_fadingEdgeLength
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
* @attr ref android.R.styleable#View_fitsSystemWindows
* @attr ref android.R.styleable#View_isScrollContainer
* @attr ref android.R.styleable#View_focusable
* @attr ref android.R.styleable#View_focusableInTouchMode
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
* @attr ref android.R.styleable#View_keepScreenOn
* @attr ref android.R.styleable#View_layerType
* @attr ref android.R.styleable#View_layoutDirection
* @attr ref android.R.styleable#View_longClickable
* @attr ref android.R.styleable#View_minHeight
* @attr ref android.R.styleable#View_minWidth
* @attr ref android.R.styleable#View_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_paddingStart
* @attr ref android.R.styleable#View_paddingEnd
* @attr ref android.R.styleable#View_saveEnabled
* @attr ref android.R.styleable#View_rotation
* @attr ref android.R.styleable#View_rotationX
* @attr ref android.R.styleable#View_rotationY
* @attr ref android.R.styleable#View_scaleX
* @attr ref android.R.styleable#View_scaleY
* @attr ref android.R.styleable#View_scrollX
* @attr ref android.R.styleable#View_scrollY
* @attr ref android.R.styleable#View_scrollbarSize
* @attr ref android.R.styleable#View_scrollbarStyle
* @attr ref android.R.styleable#View_scrollbars
* @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
* @attr ref android.R.styleable#View_scrollbarFadeDuration
* @attr ref android.R.styleable#View_scrollbarTrackHorizontal
* @attr ref android.R.styleable#View_scrollbarThumbHorizontal
* @attr ref android.R.styleable#View_scrollbarThumbVertical
* @attr ref android.R.styleable#View_scrollbarTrackVertical
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
* @attr ref android.R.styleable#View_soundEffectsEnabled
* @attr ref android.R.styleable#View_tag
* @attr ref android.R.styleable#View_textAlignment
* @attr ref android.R.styleable#View_textDirection
* @attr ref android.R.styleable#View_transformPivotX
* @attr ref android.R.styleable#View_transformPivotY
* @attr ref android.R.styleable#View_translationX
* @attr ref android.R.styleable#View_translationY
* @attr ref android.R.styleable#View_visibility
*
* @see com.ViewGroup.image.view.GLViewGroup
*/
public class View implements KeyEvent.Callback, Drawable.Callback{
private static final boolean DBG = false;
/**
* The logging tag used by this class with android.util.Log.
*/
protected static final String VIEW_LOG_TAG = "View";
// static final int FLAG_SET_MEASURED_SIZE = 2;
protected Context mContext;
protected ViewGroup mParent;
/**
* Indicates no axis of view scrolling.
*/
public static final int SCROLL_AXIS_NONE = 0;
/**
* Indicates scrolling along the horizontal axis.
*/
public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
/**
* Indicates scrolling along the vertical axis.
*/
public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
/**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
* and {@link #OVER_SCROLL_NEVER}.
*/
private int mOverScrollMode;
/**
* {@hide}
*/
AttachInfo mAttachInfo;
/**
* When this view has focus and the next focus is {@link #FOCUS_LEFT},
* the user may specify which view to go to next.
*/
private int mNextFocusLeftId = View.NO_ID;
/**
* When this view has focus and the next focus is {@link #FOCUS_RIGHT},
* the user may specify which view to go to next.
*/
private int mNextFocusRightId = View.NO_ID;
/**
* When this view has focus and the next focus is {@link #FOCUS_UP},
* the user may specify which view to go to next.
*/
private int mNextFocusUpId = View.NO_ID;
/**
* When this view has focus and the next focus is {@link #FOCUS_DOWN},
* the user may specify which view to go to next.
*/
private int mNextFocusDownId = View.NO_ID;
/**
* When this view has focus and the next focus is {@link #FOCUS_FORWARD},
* the user may specify which view to go to next.
*/
int mNextFocusForwardId = View.NO_ID;
private PerformClick mPerformClick;
private CheckForLongPress mPendingCheckForLongPress;
private CheckForTap mPendingCheckForTap = null;
private UnsetPressedState mUnsetPressedState;
/**
* Whether the long press's action has been invoked. The tap's action is invoked on the
* up event while a long press is invoked as soon as the long press duration is reached, so
* a long press could be performed before the tap is checked, in which case the tap's action
* should not be invoked.
*/
private boolean mHasPerformedLongPress;
/**
* The minimum height of the view. We'll try our best to have the height
* of this view to at least this amount.
*/
private int mMinHeight;
/**
* The minimum width of the view. We'll try our best to have the width
* of this view to at least this amount.
*/
private int mMinWidth;
/**
* The delegate to handle touch events that are physically in this view
* but should be handled by another view.
*/
private TouchDelegate mTouchDelegate = null;
/**
* Solid color to use as a background when creating the drawing cache. Enables
* the cache to use 16 bit bitmaps instead of 32 bit.
*/
private int mDrawingCacheBackgroundColor = 0;
/**
* Special tree observer used when mAttachInfo is null.
*/
private ViewTreeObserver mFloatingTreeObserver;
/**
* Vertical scroll factor cached by {@link #getVerticalScrollFactor}.
*/
private float mVerticalScrollFactor;
/**
* Position of the vertical scroll bar.
*/
private int mVerticalScrollbarPosition;
/**
* Position the scroll bar at the default position as determined by the system.
*/
public static final int SCROLLBAR_POSITION_DEFAULT = 0;
/**
* Position the scroll bar along the left edge.
*/
public static final int SCROLLBAR_POSITION_LEFT = 1;
/**
* Position the scroll bar along the right edge.
*/
public static final int SCROLLBAR_POSITION_RIGHT = 2;
/**
* The view's tag.
*
* @see #setTag(Object)
* @see #getTag()
*/
protected Object mTag;
/**
* The view's identifier.
* {@hide}
*
* @see #setId(int)
* @see #getId()
*/
public int mID = NO_ID;
/**
* The view flags hold various views states.
* {@hide}
*/
int mViewFlags = 0x0;
/**
* The currently active parent view for receiving delegated nested scrolling events.
* This is set by {@link #startNestedScroll(int)} during a touch interaction and cleared
* by {@link #stopNestedScroll()} at the same point where we clear
* requestDisallowInterceptTouchEvent.
*/
private ViewGroup mNestedScrollingParent;
private int[] mTempNestedScrollConsumed;
/**
* 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} and <a href="#attr_android:visibility">{@code
* android:visibility}.
*/
public static final int VISIBLE = 0x00000000;
/**
* This view is invisible, but it still takes up space for layout purposes.
* Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code
* android:visibility}.
*/
public static final int INVISIBLE = 0x00000004;
/**
* This view is invisible, and it doesn't take any space for layout
* purposes. Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code
* android:visibility}.
*/
public static final int GONE = 0x00000008;
/**
* Mask for use with setFlags indicating bits used for visibility.
* {@hide}
*/
static final int VISIBILITY_MASK = 0x0000000C;
/**
* <p>Indicates this view can be clicked. When clickable, a View reacts
* to clicks by notifying the OnClickListener.<p>
* {@hide}
*/
static final int CLICKABLE = 0x00004000;
/**
* <p>Indicates this view is caching its drawing into a bitmap.</p>
* {@hide}
*/
static final int DRAWING_CACHE_ENABLED = 0x00008000;
/**
* <p>Indicates that no icicle should be saved for this view.<p>
* {@hide}
*/
static final int SAVE_DISABLED = 0x000010000;
/**
* <p>Mask for use with setFlags indicating bits used for the saveEnabled
* property.</p>
* {@hide}
*/
static final int SAVE_DISABLED_MASK = 0x000010000;
/**
* <p>Indicates that no drawing cache should ever be created for this view.<p>
* {@hide}
*/
static final int WILL_NOT_CACHE_DRAWING = 0x000020000;
/**
* <p>Indicates this view can take / keep focus when int touch mode.</p>
* {@hide}
*/
static final int FOCUSABLE_IN_TOUCH_MODE = 0x00040000;
/**
* <p>
* Indicates this view can be long clicked. When long clickable, a View
* reacts to long clicks by notifying the OnLongClickListener or showing a
* context menu.
* </p>
* {@hide}
*/
static final int LONG_CLICKABLE = 0x00200000;
/**
* 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;
/**
* This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
* {@hide}
*/
static final int ENABLED = 0x00000000;
/**
* This view is disabled. Interpretation 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(android.graphics.Canvas)} won't be
* called and further optimizations will be performed. It is okay to have
* this flag set and a background. Use with DRAW_MASK when calling setFlags.
* {@hide}
*/
static final int WILL_NOT_DRAW = 0x00000080;
/**
* Mask for use with setFlags indicating bits used for indicating whether
* this view is will draw
* {@hide}
*/
static final int DRAW_MASK = 0x00000080;
/**
* <p>This view doesn't show scrollbars.</p>
* {@hide}
*/
static final int SCROLLBARS_NONE = 0x00000000;
/**
* <p>This view shows horizontal scrollbars.</p>
* {@hide}
*/
static final int SCROLLBARS_HORIZONTAL = 0x00000100;
/**
* <p>This view shows vertical scrollbars.</p>
* {@hide}
*/
static final int SCROLLBARS_VERTICAL = 0x00000200;
/**
* <p>Mask for use with setFlags indicating bits used for indicating which
* scrollbars are enabled.</p>
* {@hide}
*/
static final int SCROLLBARS_MASK = 0x00000300;
/**
* Bits of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that provide the actual measured size.
*/
public static final int MEASURED_SIZE_MASK = 0x00ffffff;
/**
* Bits of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that provide the additional state bits.
*/
public static final int MEASURED_STATE_MASK = 0xff000000;
private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE};
protected int mMeasuredWidth = 0;
protected int mMeasuredHeight = 0;
int mOldWidthMeasureSpec = -1;
int mOldHeightMeasureSpec = -1;
static final int FLAG_INVISIBLE = VISIBILITY_MASK;
/**
* The offset, in pixels, by which the content of this view is scrolled horizontally.
*/
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled vertically.
*/
protected int mScrollY;
protected int mScrollHeight = 0;
protected int mScrollWidth = 0;
private int mBackgroundResource = 0;
private boolean mBackgroundSizeChanged;
private Drawable mBackground;
private String mTransitionName;
protected Animation mCurrentAnimation;
// protected boolean mBisViewEnable = true;
/**
* 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(int)}. Move focus to the previous selectable
* item.
*/
public static final int FOCUS_BACKWARD = 0x00000001;
/**
* Use with {@link #focusSearch(int)}. Move focus to the next selectable
* item.
*/
public static final int FOCUS_FORWARD = 0x00000002;
/**
* Use with {@link #focusSearch(int)}. Move focus to the left.
*/
public static final int FOCUS_LEFT = 0x00000011;
/**
* Use with {@link #focusSearch(int)}. Move focus up.
*/
public static final int FOCUS_UP = 0x00000021;
/**
* Use with {@link #focusSearch(int)}. Move focus to the right.
*/
public static final int FOCUS_RIGHT = 0x00000042;
/**
* Use with {@link #focusSearch(int)}. Move focus down.
*/
public static final int FOCUS_DOWN = 0x00000082;
/**
* SoundEffectConstants.CLICK
*/
public static final int CLICK = SoundEffectConstants.CLICK;
/**
* Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits
* for functions that combine both width and height into a single int,
* such as {@link #getMeasuredState()} and the childState argument of
* {@link #resolveSizeAndState(int, int, int)}.
*/
public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;
/**
* Bit of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that indicates the measured size
* is smaller that the space the view would like to have.
*/
public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
int mPrivateFlags = 0;
/** {@hide} */
static final int PFLAG_FOCUSED = 0x00000002;
/** {@hide} */
static final int PFLAG_SELECTED = 0x00000004;
/** {@hide} */
static final int PFLAG_IS_ROOT_NAMESPACE = 0x00000008;
/** {@hide} */
static final int PFLAG_HAS_BOUNDS = 0x00000010;
/** {@hide} */
// static final int PFLAG_FORCE_LAYOUT = 0x00001000;
/** {@hide} */
static final int PFLAG_FORCE_LAYOUT = 0x00001000;
/** {@hide} */
static final int PFLAG_LAYOUT_REQUIRED = 0x00002000;
static final int PFLAG_MEASURED_DIMENSION_SET = 0x00000800;
private static final int PFLAG_PRESSED = 0x00004000;
/**
* Flag used to indicate that this view should be drawn once more (and only once
* more) after its animation has completed.
* {@hide}
*/
static final int PFLAG_ANIMATION_STARTED = 0x00010000;
int mPrivateTransformationFlags = 0;
/**
* Indicates that the View returned true when onSetAlpha() was called and that
* the alpha must be restored.
* {@hide}
*/
static final int PFLAG_ALPHA_SET = 0x00040000;
static final int PFLAG_TRANSLATE_SET = 0x00000001;
static final int PFLAG_ROTATION_SET = 0x00000002;
static final int PFLAG_SCALE_SET = 0x00000004;
/** {@hide} */
static final int PFLAG_DRAWABLE_STATE_DIRTY = 0x00000400;
/**
* Indicates whether the background is opaque.
*
* @hide
*/
static final int PFLAG_OPAQUE_BACKGROUND = 0x00800000;
/**
* Indicates whether the scrollbars are opaque.
*
* @hide
*/
static final int PFLAG_OPAQUE_SCROLLBARS = 0x01000000;
/**
* Indicates whether the view is opaque.
*
* @hide
*/
static final int PFLAG_OPAQUE_MASK = 0x01800000;
/**
* Indicates a prepressed state;
* the short time between ACTION_DOWN and recognizing
* a 'real' press. Prepressed is used to recognize quick taps
* even when they are shorter than ViewConfiguration.getTapTimeout().
*
* @hide
*/
private static final int PFLAG_PREPRESSED = 0x02000000;
/**
* Indicates whether the view is temporarily detached.
*
* @hide
*/
static final int PFLAG_CANCEL_NEXT_UP_EVENT = 0x04000000;
/**
* Indicates that we should awaken scroll bars once attached
*
* @hide
*/
private static final int PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
/**
* Indicates that the view has received HOVER_ENTER. Cleared on HOVER_EXIT.
* @hide
*/
private static final int PFLAG_HOVERED = 0x10000000;
/** {@hide} */
static final int PFLAG_ACTIVATED = 0x40000000;
/**
* Masks for mPrivateFlags2, as generated by dumpFlags():
*
* -------|-------|-------|-------|
* PFLAG2_TEXT_ALIGNMENT_FLAGS[0]
* PFLAG2_TEXT_DIRECTION_FLAGS[0]
* 1 PFLAG2_DRAG_CAN_ACCEPT
* 1 PFLAG2_DRAG_HOVERED
* 1 PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT
* 11 PFLAG2_TEXT_DIRECTION_MASK_SHIFT
* 1 1 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT
* 11 PFLAG2_LAYOUT_DIRECTION_MASK
* 11 1 PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT
* 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL
* 1 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT
* 1 1 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT
* 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED
* 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK
* 1 PFLAG2_TEXT_DIRECTION_FLAGS[1]
* 1 PFLAG2_TEXT_DIRECTION_FLAGS[2]
* 11 PFLAG2_TEXT_DIRECTION_FLAGS[3]
* 1 PFLAG2_TEXT_DIRECTION_FLAGS[4]
* 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5]
* 111 PFLAG2_TEXT_DIRECTION_MASK
* 1 PFLAG2_TEXT_DIRECTION_RESOLVED
* 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT
* 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK
* 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1]
* 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2]
* 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3]
* 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4]
* 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5]
* 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6]
* 111 PFLAG2_TEXT_ALIGNMENT_MASK
* 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED
* 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT
* 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK
* 11 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK
* 1 PFLAG2_HAS_TRANSIENT_STATE
* 1 PFLAG2_ACCESSIBILITY_FOCUSED
* 1 PFLAG2_ACCESSIBILITY_STATE_CHANGED
* 1 PFLAG2_VIEW_QUICK_REJECTED
* 1 PFLAG2_PADDING_RESOLVED
* -------|-------|-------|-------|
*/
int mPrivateFlags2;
/**
* Indicates that this view has reported that it can accept the current drag's content.
* Cleared when the drag operation concludes.
* @hide
*/
static final int PFLAG2_DRAG_CAN_ACCEPT = 0x00000001;
/**
* Indicates that this view is currently directly under the drag location in a
* drag-and-drop operation involving content that it can accept. Cleared when
* the drag exits the view, or when the drag operation concludes.
* @hide
*/
static final int PFLAG2_DRAG_HOVERED = 0x00000002;
/**
* Horizontal layout direction of this view is from Left to Right.
* Use with {@link #setLayoutDirection}.
*/
public static final int LAYOUT_DIRECTION_LTR = LayoutDirection.LTR;
/**
* Horizontal layout direction of this view is from Right to Left.
* Use with {@link #setLayoutDirection}.
*/
public static final int LAYOUT_DIRECTION_RTL = LayoutDirection.RTL;
/**
* Horizontal layout direction of this view is inherited from its parent.
* Use with {@link #setLayoutDirection}.
*/
public static final int LAYOUT_DIRECTION_INHERIT = LayoutDirection.INHERIT;
/**
* Horizontal layout direction of this view is from deduced from the default language
* script for the locale. Use with {@link #setLayoutDirection}.
*/
public static final int LAYOUT_DIRECTION_LOCALE = LayoutDirection.LOCALE;
/**
* Default horizontal layout direction.
*/
private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
/**
* Default horizontal layout direction.
* @hide
*/
static final int LAYOUT_DIRECTION_RESOLVED_DEFAULT = LAYOUT_DIRECTION_LTR;
/**
* Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
* @hide
*/
static final int PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT = 2;
/**
* Mask for use with private flags indicating bits used for horizontal layout direction.
* @hide
*/
static final int PFLAG2_LAYOUT_DIRECTION_MASK = 0x00000003 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
/**
* Indicates whether the view horizontal layout direction has been resolved and drawn to the
* right-to-left direction.
* @hide
*/
static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL = 4 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
/**
* Indicates whether the view horizontal layout direction has been resolved.
* @hide
*/
static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED = 8 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
/**
* Mask for use with private flags indicating bits used for resolved horizontal layout direction.
* @hide
*/
static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK = 0x0000000C
<< PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
/*
* Array of horizontal layout direction flags for mapping attribute "layoutDirection" to correct
* flag value.
* @hide
*/
private static final int[] LAYOUT_DIRECTION_FLAGS = {
LAYOUT_DIRECTION_LTR,
LAYOUT_DIRECTION_RTL,
LAYOUT_DIRECTION_INHERIT,
LAYOUT_DIRECTION_LOCALE
};
/**
* Flag indicating that start/end padding has been resolved into left/right padding
* for use in measurement, layout, drawing, etc. This is set by {@link #resolvePadding()}
* and checked by {@link #measure(int, int)} to determine if padding needs to be resolved
* during measurement. In some special cases this is required such as when an adapter-based
* view measures prospective children without attaching them to a window.
*/
static final int PFLAG2_PADDING_RESOLVED = 0x20000000;
/**
* Flag indicating that the start/end drawables has been resolved into left/right ones.
*/
static final int PFLAG2_DRAWABLE_RESOLVED = 0x40000000;
/**
* Indicates that the view is tracking some sort of transient state
* that the app should not need to be aware of, but that the framework
* should take special care to preserve.
*/
static final int PFLAG2_HAS_TRANSIENT_STATE = 0x80000000;
/**
* Group of bits indicating that RTL properties resolution is done.
*/
static final int ALL_RTL_PROPERTIES_RESOLVED = PFLAG2_LAYOUT_DIRECTION_RESOLVED |
/*PFLAG2_TEXT_DIRECTION_RESOLVED |
PFLAG2_TEXT_ALIGNMENT_RESOLVED |*/
PFLAG2_PADDING_RESOLVED |
PFLAG2_DRAWABLE_RESOLVED;
int mPrivateFlags3;
/**
* This view's request for the visibility of the status bar.
* @hide
*/
int mSystemUiVisibility;
/**
* <p>Indicates that this view gets its drawable states from its direct parent
* and ignores its original internal states.</p>
*
* @hide
*/
static final int DUPLICATE_PARENT_STATE = 0x00400000;
protected int mLeft, mTop, mRight, mBottom;
protected int mXCenter, mYCenter;
protected int mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom;
/**
* Predicate for matching a view by its id.
*/
private MatchIdPredicate mMatchIdPredicate;
/**
* Cache the paddingRight set by the user to append to the scrollbar's size.
*
* @hide
*/
protected int mUserPaddingRight;
/**
* Cache the paddingBottom set by the user to append to the scrollbar's size.
*
* @hide
*/
protected int mUserPaddingBottom;
/**
* Cache the paddingLeft set by the user to append to the scrollbar's size.
*
* @hide
*/
protected int mUserPaddingLeft;
/**
* Cache the paddingStart set by the user to append to the scrollbar's size.
*
*/
int mUserPaddingStart;
/**
* Cache the paddingEnd set by the user to append to the scrollbar's size.
*
*/
int mUserPaddingEnd;
/**
* Cache initial left padding.
*
* @hide
*/
int mUserPaddingLeftInitial;
/**
* Cache initial right padding.
*
* @hide
*/
int mUserPaddingRightInitial;
/**
* Default undefined padding
*/
private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
/**
* Cache if a left padding has been defined
*/
private boolean mLeftPaddingDefined = false;
/**
* Cache if a right padding has been defined
*/
private boolean mRightPaddingDefined = false;
/**
* Reference count for transient state.
* @see #setHasTransientState(boolean)
*/
int mTransientStateCount = 0;
/**
* Count of how many windows this view has been attached to.
*/
int mWindowAttachCount;
/**
* The layout parameters associated with this view and used by the parent
* {@link ViewGroup} to determine how this view should be
* laid out.
* {@hide}
*/
protected ViewGroup.LayoutParams mLayoutParams;
public final static int NO_IN_PARENT = -1;
/**
* Cache the touch slop from the context that created the view.
*/
private int mTouchSlop;
/**
* Briefly describes the view and is primarily used for accessibility support.
*/
private CharSequence mContentDescription;
public final static int CREATE_BACKGROUND = 1;
private ScrollabilityCache mScrollCache;
private int[] mDrawableState = null;
/**
* Animator that automatically runs based on state changes.
*/
private StateListAnimator mStateListAnimator;
/**set drawable state**/
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
// 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;
/**
* Indicates the view is enabled and selected.
*
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] ENABLED_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;
/**
* Indicates the view is focused and selected.
*
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view has the focus and that its window has the focus.
*
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is selected and that its window has the focus.
*
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET;
// Triples
/**
* Indicates the view is enabled, focused and selected.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view is enabled, focused and its window has the focus.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is enabled, selected and its window has the focus.
*
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is focused, selected and its window has the focus.
*
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is enabled, focused, selected and its window
* has the focus.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_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;
/**
* 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;
/**
* Indicates the view is pressed and focused.
*
* @see #PRESSED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, focused and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, focused and selected.
*
* @see #PRESSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view is pressed, focused, selected and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_SELECTED_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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* The order here is very important to {@link #getDrawableState()}
*/
private static final int[][] VIEW_STATE_SETS;
static final int VIEW_STATE_WINDOW_FOCUSED = 1;
static final int VIEW_STATE_SELECTED = 1 << 1;
static final int VIEW_STATE_FOCUSED = 1 << 2;
static final int VIEW_STATE_ENABLED = 1 << 3;
static final int VIEW_STATE_PRESSED = 1 << 4;
static final int VIEW_STATE_ACTIVATED = 1 << 5;
static final int VIEW_STATE_ACCELERATED = 1 << 6;
static final int VIEW_STATE_HOVERED = 1 << 7;
static final int VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8;
static final int VIEW_STATE_DRAG_HOVERED = 1 << 9;
static final int[] VIEW_STATE_IDS = new int[] {
com.glview.R.attr.state_window_focused, VIEW_STATE_WINDOW_FOCUSED,
com.glview.R.attr.state_selected, VIEW_STATE_SELECTED,
com.glview.R.attr.state_focused, VIEW_STATE_FOCUSED,
com.glview.R.attr.state_enabled, VIEW_STATE_ENABLED,
com.glview.R.attr.state_pressed, VIEW_STATE_PRESSED,
com.glview.R.attr.state_activated, VIEW_STATE_ACTIVATED,
com.glview.R.attr.state_accelerated, VIEW_STATE_ACCELERATED,
com.glview.R.attr.state_hovered, VIEW_STATE_HOVERED,
com.glview.R.attr.state_drag_can_accept, VIEW_STATE_DRAG_CAN_ACCEPT,
com.glview.R.attr.state_drag_hovered, VIEW_STATE_DRAG_HOVERED
};
static {
if ((VIEW_STATE_IDS.length/2) != com.glview.R.styleable.ViewDrawableStates.length) {
throw new IllegalStateException(
"VIEW_STATE_IDs array length does not match ViewDrawableStates style array");
}
int[] orderedIds = new int[VIEW_STATE_IDS.length];
int drawStatesLength = com.glview.R.styleable.ViewDrawableStates.length;
int viewStateIdsLength = 0;
for (int i = 0; i < drawStatesLength; i++) {
int viewState = com.glview.R.styleable.ViewDrawableStates[i];
viewStateIdsLength = VIEW_STATE_IDS.length;
for (int j = 0; j< viewStateIdsLength; j += 2) {
if (VIEW_STATE_IDS[j] == viewState) {
orderedIds[i * 2] = viewState;
orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
}
}
}
final int NUM_BITS = VIEW_STATE_IDS.length / 2;
VIEW_STATE_SETS = new int[1 << NUM_BITS][];
int viewStateSetsLength = VIEW_STATE_SETS.length;
int orderedIdsLength = 0;
for (int i = 0; i < viewStateSetsLength; i++) {
int numBits = Integer.bitCount(i);
int[] set = new int[numBits];
int pos = 0;
orderedIdsLength = orderedIds.length;
for (int j = 0; j < orderedIdsLength; j += 2) {
if ((i & orderedIds[j+1]) != 0) {
set[pos++] = orderedIds[j];
}
}
VIEW_STATE_SETS[i] = set;
}
EMPTY_STATE_SET = VIEW_STATE_SETS[0];
WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_WINDOW_FOCUSED];
SELECTED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_SELECTED];
SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED];
FOCUSED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_FOCUSED];
FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED];
FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED];
FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_FOCUSED];
ENABLED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_ENABLED];
ENABLED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_ENABLED];
ENABLED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_ENABLED];
ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_ENABLED];
ENABLED_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_FOCUSED | VIEW_STATE_ENABLED];
ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED
| VIEW_STATE_ENABLED];
ENABLED_FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED
| VIEW_STATE_ENABLED];
ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_FOCUSED| VIEW_STATE_ENABLED];
PRESSED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_PRESSED];
PRESSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_PRESSED];
PRESSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_PRESSED];
PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_PRESSED];
PRESSED_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_FOCUSED | VIEW_STATE_PRESSED];
PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED
| VIEW_STATE_PRESSED];
PRESSED_FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED
| VIEW_STATE_PRESSED];
PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_FOCUSED | VIEW_STATE_PRESSED];
PRESSED_ENABLED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_ENABLED
| VIEW_STATE_PRESSED];
PRESSED_ENABLED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_ENABLED
| VIEW_STATE_PRESSED];
PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
PRESSED_ENABLED_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_FOCUSED | VIEW_STATE_ENABLED
| VIEW_STATE_PRESSED];
PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED
| VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED
| VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
| VIEW_STATE_FOCUSED| VIEW_STATE_ENABLED
| VIEW_STATE_PRESSED];
}
/**
* Temporary Rect currently for use in setBackground(). This will probably
* be extended in the future to hold our own class with more than just
* a Rect. :)
*/
static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>();
/**
* Map used to store views' tags.
*/
private SparseArray<Object> mKeyedTags;
public View(Context context){
mContext = context;
// mResources = context != null ? context.getResources() : null;
mPrivateFlags |= PFLAG_HAS_BOUNDS;
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
}
/**
* Constructor that is called when inflating a view from XML. This is called
* when a view is being constructed from an XML file, supplying attributes
* that were specified in the XML file. This version uses a default style of
* 0, so the only attribute values applied are those in the Context's Theme
* and the given AttributeSet.
*
* <p>
* The method onFinishInflate() will be called after all children have been
* added.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @see #GLView(Context, AttributeSet, int)
*/
public View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute. This constructor of View allows subclasses to use their
* own base style when they are inflating. For example, a Button class's
* constructor would call this version of the super class constructor and
* supply <code>R.attr.buttonStyle</code> for <var>defStyleAttr</var>; this
* allows the theme's button style to modify all of the base view attributes
* (in particular its background) as well as the Button class's attributes.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @see #View(Context, AttributeSet)
*/
public View(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute or style resource. This constructor of View allows
* subclasses to use their own base style when they are inflating.
* <p>
* When determining the final value of a particular attribute, there are
* four inputs that come into play:
* <ol>
* <li>Any attribute values in the given AttributeSet.
* <li>The style resource specified in the AttributeSet (named "style").
* <li>The default style specified by <var>defStyleAttr</var>.
* <li>The default style specified by <var>defStyleRes</var>.
* <li>The base values in this theme.
* </ol>
* <p>
* Each of these inputs is considered in-order, with the first listed taking
* precedence over the following ones. In other words, if in the
* AttributeSet you have supplied <code><Button * textColor="#ff000000"></code>
* , then the button's text will <em>always</em> be black, regardless of
* what is specified in any of the styles.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @param defStyleRes A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.
* @see #View(Context, AttributeSet, int)
*/
public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);
Drawable background = null;
int leftPadding = -1;
int topPadding = -1;
int rightPadding = -1;
int bottomPadding = -1;
int startPadding = UNDEFINED_PADDING;
int endPadding = UNDEFINED_PADDING;
int padding = -1;
int viewFlagValues = 0;
int viewFlagMasks = 0;
boolean setScrollContainer = false;
int x = 0;
int y = 0;
float tx = 0;
float ty = 0;
float tz = 0;
float elevation = 0;
float rotation = 0;
float rotationX = 0;
float rotationY = 0;
float sx = 1f;
float sy = 1f;
boolean transformSet = false;
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
int overScrollMode = mOverScrollMode;
boolean initializeScrollbars = false;
boolean startPaddingDefined = false;
boolean endPaddingDefined = false;
boolean leftPaddingDefined = false;
boolean rightPaddingDefined = false;
// final int targetSdkVersion =
// context.getApplicationInfo().targetSdkVersion;
final TypedArray a = context.obtainStyledAttributes(attrs,
com.glview.R.styleable.View, defStyleAttr, defStyleRes);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
if (attr == com.glview.R.styleable.View_background) {
background = GLContext.get().getResources().getDrawable(a.getResourceId(attr, 0));
} else if (attr == com.glview.R.styleable.View_padding) {
padding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
leftPaddingDefined = true;
rightPaddingDefined = true;
} else if (attr == com.glview.R.styleable.View_paddingLeft) {
leftPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = leftPadding;
leftPaddingDefined = true;
} else if (attr == com.glview.R.styleable.View_paddingTop) {
topPadding = a.getDimensionPixelSize(attr, -1);
} else if (attr == com.glview.R.styleable.View_paddingRight) {
rightPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingRightInitial = rightPadding;
rightPaddingDefined = true;
} else if (attr == com.glview.R.styleable.View_paddingBottom) {
bottomPadding = a.getDimensionPixelSize(attr, -1);
} else if (attr == com.glview.R.styleable.View_paddingStart) {
startPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING);
startPaddingDefined = (startPadding != UNDEFINED_PADDING);
} else if (attr == com.glview.R.styleable.View_paddingEnd) {
endPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING);
endPaddingDefined = (endPadding != UNDEFINED_PADDING);
} else if (attr == com.glview.R.styleable.View_scrollX) {
x = a.getDimensionPixelOffset(attr, 0);
} else if (attr == com.glview.R.styleable.View_scrollY) {
y = a.getDimensionPixelOffset(attr, 0);
} else if (attr == com.glview.R.styleable.View_alpha) {
setAlpha(a.getFloat(attr, 1f));
} else if (attr == com.glview.R.styleable.View_transformPivotX) {
// setPivotX(a.getDimensionPixelOffset(attr, 0));
} else if (attr == com.glview.R.styleable.View_transformPivotY) {
// setPivotY(a.getDimensionPixelOffset(attr, 0));
} else if (attr == com.glview.R.styleable.View_translationX) {
tx = a.getDimensionPixelOffset(attr, 0);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_translationY) {
ty = a.getDimensionPixelOffset(attr, 0);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_rotation) {
rotation = a.getFloat(attr, 0);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_rotationX) {
rotationX = a.getFloat(attr, 0);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_rotationY) {
rotationY = a.getFloat(attr, 0);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_scaleX) {
sx = a.getFloat(attr, 1f);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_scaleY) {
sy = a.getFloat(attr, 1f);
transformSet = true;
} else if (attr == com.glview.R.styleable.View_id) {
mID = a.getResourceId(attr, NO_ID);
} else if (attr == com.glview.R.styleable.View_tag) {
mTag = a.getText(attr);
} else if (attr == com.glview.R.styleable.View_focusable) {
if (a.getBoolean(attr, false)) {
viewFlagValues |= FOCUSABLE;
viewFlagMasks |= FOCUSABLE_MASK;
}
} else if (attr == com.glview.R.styleable.View_focusableInTouchMode) {
if (a.getBoolean(attr, false)) {
viewFlagValues |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE;
viewFlagMasks |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE_MASK;
}
} else if (attr == com.glview.R.styleable.View_clickable) {
if (a.getBoolean(attr, false)) {
viewFlagValues |= CLICKABLE;
viewFlagMasks |= CLICKABLE;
}
} else if (attr == com.glview.R.styleable.View_longClickable) {
if (a.getBoolean(attr, false)) {
viewFlagValues |= LONG_CLICKABLE;
viewFlagMasks |= LONG_CLICKABLE;
}
} else if (attr == com.glview.R.styleable.View_duplicateParentState) {
if (a.getBoolean(attr, false)) {
viewFlagValues |= DUPLICATE_PARENT_STATE;
viewFlagMasks |= DUPLICATE_PARENT_STATE;
}
} else if (attr == com.glview.R.styleable.View_visibility) {
final int visibility = a.getInt(attr, 0);
if (visibility != 0) {
viewFlagValues |= VISIBILITY_FLAGS[visibility];
viewFlagMasks |= VISIBILITY_MASK;
}
} else if (attr == com.glview.R.styleable.View_contentDescription) {
// setContentDescription(a.getString(attr));
} else if (attr == com.glview.R.styleable.View_soundEffectsEnabled) {
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
viewFlagMasks |= SOUND_EFFECTS_ENABLED;
}
} else if (attr == com.glview.R.styleable.View_scrollbars) {
final int scrollbars = a.getInt(attr, SCROLLBARS_NONE);
if (scrollbars != SCROLLBARS_NONE) {
viewFlagValues |= scrollbars;
viewFlagMasks |= SCROLLBARS_MASK;
initializeScrollbars = true;
}
} else if (attr == com.glview.R.styleable.View_keepScreenOn) {
if (a.getBoolean(attr, false)) {
viewFlagValues |= KEEP_SCREEN_ON;
viewFlagMasks |= KEEP_SCREEN_ON;
}
} else if (attr == com.glview.R.styleable.View_nextFocusLeft) {
mNextFocusLeftId = a.getResourceId(attr, View.NO_ID);
} else if (attr == com.glview.R.styleable.View_nextFocusRight) {
mNextFocusRightId = a.getResourceId(attr, View.NO_ID);
} else if (attr == com.glview.R.styleable.View_nextFocusUp) {
mNextFocusUpId = a.getResourceId(attr, View.NO_ID);
} else if (attr == com.glview.R.styleable.View_nextFocusDown) {
mNextFocusDownId = a.getResourceId(attr, View.NO_ID);
} else if (attr == com.glview.R.styleable.View_nextFocusForward) {
mNextFocusForwardId = a.getResourceId(attr, View.NO_ID);
} else if (attr == com.glview.R.styleable.View_minWidth) {
mMinWidth = a.getDimensionPixelSize(attr, 0);
} else if (attr == com.glview.R.styleable.View_minHeight) {
mMinHeight = a.getDimensionPixelSize(attr, 0);
} else if (attr == com.glview.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) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + View.this.getClass() + idText, 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);
}
}
});
}
} else if (attr == com.glview.R.styleable.View_overScrollMode) {
overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);
} else if (attr == com.glview.R.styleable.View_stateListAnimator) {
setStateListAnimator(AnimatorInflater.loadStateListAnimator(context,
a.getResourceId(attr, 0)));
} else if (attr == com.glview.R.styleable.View_transitionName) {
setTransitionName(a.getString(attr));
}
}
setOverScrollMode(overScrollMode);
// Cache start/end user padding as we cannot fully resolve padding here (we dont have yet
// the resolved layout direction). Those cached values will be used later during padding
// resolution.
mUserPaddingStart = startPadding;
mUserPaddingEnd = endPadding;
if (background != null) {
setBackground(background);
}
// setBackground above will record that padding is currently provided by the background.
// If we have padding specified via xml, record that here instead and use it.
mLeftPaddingDefined = leftPaddingDefined;
mRightPaddingDefined = rightPaddingDefined;
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
rightPadding = padding;
bottomPadding = padding;
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
}
// Jelly Bean MR1 and after case: if start/end defined, they will override any left/right
// values defined. Otherwise, left /right values are used.
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
final boolean hasRelativePadding = startPaddingDefined || endPaddingDefined;
if (mLeftPaddingDefined && !hasRelativePadding) {
mUserPaddingLeftInitial = leftPadding;
}
if (mRightPaddingDefined && !hasRelativePadding) {
mUserPaddingRightInitial = rightPadding;
}
internalSetPadding(
mUserPaddingLeftInitial,
topPadding >= 0 ? topPadding : mPaddingTop,
mUserPaddingRightInitial,
bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
if (viewFlagMasks != 0) {
setFlags(viewFlagValues, viewFlagMasks);
}
if (initializeScrollbars) {
initializeScrollbarsInternal(a);
}
a.recycle();
// Needs to be called after mViewFlags is set
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
recomputePadding();
}
if (x != 0 || y != 0) {
scrollTo(x, y);
}
if (transformSet) {
setTranslationX(tx);
setTranslationY(ty);
setTranslationZ(tz);
// setElevation(elevation);
setRotation(rotation);
setRotationX(rotationX);
setRotationY(rotationY);
setScaleX(sx);
setScaleY(sy);
}
}
/**
* Non-public constructor for use in testing
*/
View() {
}
// Sets the visiblity of this GLView (either GLView.VISIBLE or
// GLView.INVISIBLE).
public void setVisibility(int visibility) {
setFlags(visibility, VISIBILITY_MASK);
if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
}
// Returns GLView.VISIBLE or GLView.INVISIBLE
public int getVisibility() {
return mViewFlags & VISIBILITY_MASK;
}
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
// FIXME for getDrawingCache
mViewRootImpl = mAttachInfo.mViewRootImpl;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount ++;
if (mFloatingTreeObserver != null) {
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
needGlobalAttributesUpdate(false);
}
void dispatchDetachedFromWindow() {
onDetachedFromWindow();
onDetachedFromWindowInternal();
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewDetachedFromWindow(this);
}
}
mAttachInfo = null;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
}
/**
* @return The number of times this view has been attached to a window
*/
protected int getWindowAttachCount() {
return mWindowAttachCount;
}
public int getWidth() {
return mRight - mLeft;
}
public int getHeight() {
return mBottom - mTop;
}
/**
* 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) {
//noinspection DoubleNegation
if (((mPrivateFlags & PFLAG_SELECTED) != 0) != selected) {
mPrivateFlags = (mPrivateFlags & ~PFLAG_SELECTED) | (selected ? PFLAG_SELECTED : 0);
if (!selected) resetPressedState();
invalidate(true);
refreshDrawableState();
dispatchSetSelected(selected);
// notifyViewAccessibilityStateChangedIfNeeded(
// AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
/**
* 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
*/
public boolean isSelected() {
return (mPrivateFlags & PFLAG_SELECTED) != 0;
}
/**
* Changes the activated state of this view. A view can be activated or not.
* Note that activation is not the same as selection. Selection is
* a transient property, representing the view (hierarchy) the user is
* currently interacting with. Activation is a longer-term state that the
* user can move views in and out of. For example, in a list view with
* single or multiple selection enabled, the views in the current selection
* set are activated. (Um, yeah, we are deeply sorry about the terminology
* here.) The activated state is propagated down to children of the view it
* is set on.
*
* @param activated true if the view must be activated, false otherwise
*/
public void setActivated(boolean activated) {
//noinspection DoubleNegation
if (((mPrivateFlags & PFLAG_ACTIVATED) != 0) != activated) {
mPrivateFlags = (mPrivateFlags & ~PFLAG_ACTIVATED) | (activated ? PFLAG_ACTIVATED : 0);
invalidate(true);
refreshDrawableState();
dispatchSetActivated(activated);
}
}
/**
* Dispatch setActivated to all of this View's children.
*
* @see #setActivated(boolean)
*
* @param activated The new activated state
*/
protected void dispatchSetActivated(boolean activated) {
}
/**
* Indicates the activation state of this view.
*
* @return true if the view is activated, false otherwise
*/
public boolean isActivated() {
return (mPrivateFlags & PFLAG_ACTIVATED) != 0;
}
/**
* Returns the ViewTreeObserver for this view's hierarchy. The view tree
* observer can be used to get notifications when global events, like
* layout, happen.
*
* The returned ViewTreeObserver observer is not guaranteed to remain
* valid for the lifetime of this View. If the caller of this method keeps
* a long-lived reference to ViewTreeObserver, it should always check for
* the return value of {@link ViewTreeObserver#isAlive()}.
*
* @return The ViewTreeObserver for this view's hierarchy.
*/
public ViewTreeObserver getViewTreeObserver() {
if (mAttachInfo != null) {
return mAttachInfo.mTreeObserver;
}
if (mFloatingTreeObserver == null) {
mFloatingTreeObserver = new ViewTreeObserver();
}
return mFloatingTreeObserver;
}
/**
* <p>Finds the topmost view in the current view hierarchy.</p>
*
* @return the topmost view containing this view
*/
public View getRootView() {
if (mAttachInfo != null) {
final View v = mAttachInfo.mRootView;
if (v != null) {
return v;
}
}
View parent = this;
while (parent.mParent != null) {
parent = (View) parent.mParent;
}
return parent;
}
/**
* @hide
* @return
*/
public GLRootView getViewRootImpl() {
return mAttachInfo != null ? mAttachInfo.mViewRootImpl : null;
}
/**
* <p>Computes the coordinates of this view on the screen. The argument
* must be an array of two integers. After the method returns, the array
* contains the x and y location in that order.</p>
*
* @param location an array of two integers in which to hold the coordinates
*/
public void getLocationOnScreen(int[] location){
if(mAttachInfo == null){
location[0] = location[1] = 0;
return;
}
getLocationInWindow(location);
int[] glSurfaceViewLocation = new int[2];
mAttachInfo.mViewRootImpl.getLocationOnScreen(glSurfaceViewLocation);
location[0] += glSurfaceViewLocation[0];
location[1] += glSurfaceViewLocation[1];
}
/**
* <p>Computes the coordinates of this view in its window. The argument
* must be an array of two integers. After the method returns, the array
* contains the x and y location in that order.</p>
*
* @param location an array of two integers in which to hold the coordinates
*/
public void getLocationInWindow(int[] location) {
if (location == null || location.length < 2) {
throw new IllegalArgumentException("location must be an array of two integers");
}
if (mAttachInfo == null) {
// When the view is not attached to a window, this method does not make sense
location[0] = location[1] = 0;
return;
}
mAttachInfo.mViewRootImpl.getLocationInWindow(location);
// float[] position = mAttachInfo.mTmpTransformLocation;
float[] position = new float[2];
// position[0] = position[1] = 0.0f;
position[0] = location != null ? location[0] : 0.0f;
position[1] = location != null ? location[1] : 0.0f;
// if (!hasIdentityMatrix()) {
// getMatrix().mapPoints(position);
// }
position[0] += mLeft;
position[1] += mTop;
ViewGroup viewParent = mParent;
while (viewParent instanceof View) {
final View view = (View) viewParent;
position[0] -= view.mScrollX;
position[1] -= view.mScrollY;
// if (!view.hasIdentityMatrix()) {
// view.getMatrix().mapPoints(position);
// }
position[0] += view.mLeft;
position[1] += view.mTop;
viewParent = view.mParent;
}
location[0] = (int) (position[0] + 0.5f);
location[1] = (int) (position[1] + 0.5f);
}
// Request re-rendering of the view hierarchy.
// This is used for animation or when the contents changed.
/*public void invalidate() {
GLRoot root = getGLRoot();
if (root != null) root.requestRender();
}*/
public void setBlurOffset(int offset)
{
return;
}
// Request re-layout of the view hierarchy.
public void requestLayout() {
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null) {
mParent.requestLayout();
} else if(mAttachInfo != null){
// Is this a content pane ?
mAttachInfo.mViewRootImpl.requestLayoutGLContentView();
}
}
/**
* Forces this view to be laid out during the next layout pass.
* This method does not call requestLayout() or forceLayout()
* on the parent.
*/
public void forceLayout() {
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
}
void pdraw(GLCanvas canvas) {
draw(canvas);
}
public void draw(GLCanvas canvas) {
canvas.save();
drawAnimation(canvas);
//render background
drawBackground(canvas);
if(enableFillColor) renderColorDebug(canvas, fillColor);
//render child or other operator
onDraw(canvas);
dispatchDraw(canvas);
onDrawScrollBars(canvas);
canvas.restore();
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
}
/**
* If the View draws content inside its padding and enables fading edges,
* it needs to support padding offsets. Padding offsets are added to the
* fading edges to extend the length of the fade so that it covers pixels
* drawn inside the padding.
*
* Subclasses of this class should override this method if they need
* to draw content inside the padding.
*
* @return True if padding offset must be applied, false otherwise.
*
* @see #getLeftPaddingOffset()
* @see #getRightPaddingOffset()
* @see #getTopPaddingOffset()
* @see #getBottomPaddingOffset()
*
* @since CURRENT
*/
protected boolean isPaddingOffsetRequired() {
return false;
}
/**
* Amount by which to extend the left fading region. Called only when
* {@link #isPaddingOffsetRequired()} returns true.
*
* @return The left padding offset in pixels.
*
* @see #isPaddingOffsetRequired()
*
* @since CURRENT
*/
protected int getLeftPaddingOffset() {
return 0;
}
/**
* Amount by which to extend the right fading region. Called only when
* {@link #isPaddingOffsetRequired()} returns true.
*
* @return The right padding offset in pixels.
*
* @see #isPaddingOffsetRequired()
*
* @since CURRENT
*/
protected int getRightPaddingOffset() {
return 0;
}
/**
* Amount by which to extend the top fading region. Called only when
* {@link #isPaddingOffsetRequired()} returns true.
*
* @return The top padding offset in pixels.
*
* @see #isPaddingOffsetRequired()
*
* @since CURRENT
*/
protected int getTopPaddingOffset() {
return 0;
}
/**
* Amount by which to extend the bottom fading region. Called only when
* {@link #isPaddingOffsetRequired()} returns true.
*
* @return The bottom padding offset in pixels.
*
* @see #isPaddingOffsetRequired()
*
* @since CURRENT
*/
protected int getBottomPaddingOffset() {
return 0;
}
protected void drawAnimation(GLCanvas canvas){
boolean more = false;
if(mCurrentAnimation != null){
Animation a = mCurrentAnimation;
long currentTime = getDrawingTime();
initAnimation(a, currentTime);
more = a.getTransformation(currentTime, 1f, canvas);
}
if(more){
invalidate();
} else if (mCurrentAnimation != null && !mCurrentAnimation.getFillAfter()) {
clearAnimation();
}
if (!more && mCurrentAnimation != null && mParent != null) {
mParent.finishAnimatingView(this, mCurrentAnimation);
}
}
/**
* <p>Return the time at which the drawing of the view hierarchy started.</p>
*
* @return the drawing start time in milliseconds
*/
public long getDrawingTime() {
return mAttachInfo != null ? mAttachInfo.mDrawingTime : 0;
}
public void outOfRenderRect(){
if(mCurrentAnimation != null){
initAnimation(mCurrentAnimation, AnimationUtils.currentAnimationTimeMillis());
}
}
protected void initAnimation(Animation a, long currentTime){
boolean initialized = a.isInitialized();
if(!initialized){
a.setStartTime(currentTime);
ViewGroup parent = getParent();
int parentWidth = parent != null ? parent.getWidth() : 0;
int parentHeight = parent != null ? parent.getHeight() : 0;
a.initialize(mRight - mLeft, mBottom - mTop, parentWidth, parentHeight);
onAnimationStart();
}
}
GLPaint mDebugPaint = new GLPaint();
protected void renderColorDebug(GLCanvas canvas, int debugColor){
mDebugPaint.setColor(debugColor);
canvas.drawRect(mScrollX, mScrollY, mScrollX + getWidth(), mScrollY + getHeight(), mDebugPaint);
}
boolean enableFillColor = false;
int fillColor = 0xffffffff;
public void enableFillColor(boolean enable, int fillColor){
this.enableFillColor = enable;
this.fillColor = fillColor;
}
/**
* Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}
* on all Drawable objects associated with this view.
*/
public void jumpDrawablesToCurrentState() {
if (mBackground != null) {
mBackground.jumpToCurrentState();
}
if (mStateListAnimator != null) {
mStateListAnimator.jumpToCurrentState();
}
}
protected void drawBackground(GLCanvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
}
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
/**
* Returns the overlay for this view, creating it if it does not yet exist.
* Adding drawables to the overlay will cause them to be displayed whenever
* the view itself is redrawn. Objects in the overlay should be actively
* managed: remove them when they should not be displayed anymore. The
* overlay will always have the same size as its host view.
*
* <p>Note: Overlays do not currently work correctly with {@link
* SurfaceView} or {@link TextureView}; contents in overlays for these
* types of views may not display correctly.</p>
*
* @return The ViewOverlay object for this view.
* @see ViewOverlay
*/
public ViewOverlay getOverlay() {
if (mOverlay == null) {
mOverlay = new ViewOverlay(mContext, this);
}
return mOverlay;
}
public void clearState(GLCanvas canvas){
}
/**
* Implement this method to handle generic motion events.
* <p>
* Generic motion events describe joystick movements, mouse hovers, track pad
* touches, scroll wheel movements and other input events. The
* {@link MotionEvent#getSource() source} of the motion event specifies
* the class of input that was received. Implementations of this method
* must examine the bits in the source before processing the event.
* The following code example shows how this is done.
* </p><p>
* Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
* are delivered to the view under the pointer. All other generic motion events are
* delivered to the focused view.
* </p>
* <pre> public boolean onGenericMotionEvent(MotionEvent event) {
* if (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) {
* if (event.getAction() == MotionEvent.ACTION_MOVE) {
* // process the joystick movement...
* return true;
* }
* }
* if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
* switch (event.getAction()) {
* case MotionEvent.ACTION_HOVER_MOVE:
* // process the mouse hover movement...
* return true;
* case MotionEvent.ACTION_SCROLL:
* // process the scroll wheel movement...
* return true;
* }
* }
* return super.onGenericMotionEvent(event);
* }</pre>
*
* @param event The generic motion event being processed.
* @return True if the event was handled, false otherwise.
*/
public boolean onGenericMotionEvent(MotionEvent event) {
return false;
}
/**
* Gets a scale factor that determines the distance the view should scroll
* vertically in response to {@link MotionEvent#ACTION_SCROLL}.
* @return The vertical scroll scale factor.
* @hide
*/
protected float getVerticalScrollFactor() {
if (mVerticalScrollFactor == 0) {
TypedValue outValue = new TypedValue();
mVerticalScrollFactor = outValue.getDimension(
mContext.getResources().getDisplayMetrics());
}
return mVerticalScrollFactor;
}
/**
* 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() {
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(boolean)} or
* {@link #setHorizontalFadingEdgeEnabled(boolean)} to enable the fading edge
* for the vertical or horizontal fading edges.
*
* @param length The size in pixels of the faded edge used to indicate that more
* content in this view is visible.
*/
public void setFadingEdgeLength(int 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() {
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() {
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() {
return 0;
}
/**
* <p>
* Initializes the scrollbars from a given set of styled attributes. This
* method should be called by subclasses that need scrollbars and when an
* instance of these subclasses is created programmatically rather than
* being inflated from XML. This method is automatically called when the XML
* is inflated.
* </p>
*
* @param a the styled attributes set to initialize the scrollbars from
*
* @removed
*/
protected void initializeScrollbars(TypedArray a) {
// It's not safe to use this method from apps. The parameter 'a' must have been obtained
// using the View filter array which is not available to the SDK. As such, internal
// framework usage now uses initializeScrollbarsInternal and we grab a default
// TypedArray with the right filter instead here.
TypedArray arr = mContext.obtainStyledAttributes(com.glview.R.styleable.View);
initializeScrollbarsInternal(arr);
// We ignored the method parameter. Recycle the one we actually did use.
arr.recycle();
}
/**
* <p>
* Initializes the scrollbars from a given set of styled attributes. This
* method should be called by subclasses that need scrollbars and when an
* instance of these subclasses is created programmatically rather than
* being inflated from XML. This method is automatically called when the XML
* is inflated.
* </p>
*
* @param a the styled attributes set to initialize the scrollbars from
* @hide
*/
protected void initializeScrollbarsInternal(TypedArray a) {
initScrollCache();
final ScrollabilityCache scrollabilityCache = mScrollCache;
if (scrollabilityCache.scrollBar == null) {
scrollabilityCache.scrollBar = new ScrollBarDrawable();
}
final boolean fadeScrollbars = a.getBoolean(com.glview.R.styleable.View_fadeScrollbars, true);
if (!fadeScrollbars) {
scrollabilityCache.state = ScrollabilityCache.ON;
}
scrollabilityCache.fadeScrollBars = fadeScrollbars;
scrollabilityCache.scrollBarFadeDuration = a.getInt(
com.glview.R.styleable.View_scrollbarFadeDuration, ViewConfiguration
.getScrollBarFadeDuration());
scrollabilityCache.scrollBarDefaultDelayBeforeFade = a.getInt(
com.glview.R.styleable.View_scrollbarDefaultDelayBeforeFade,
ViewConfiguration.getScrollDefaultDelay());
scrollabilityCache.scrollBarSize = a.getDimensionPixelSize(
com.glview.R.styleable.View_scrollbarSize,
ViewConfiguration.get(mContext).getScaledScrollBarSize());
Drawable track = GLContext.get().getResources().getDrawable(a.getResourceId(com.glview.R.styleable.View_scrollbarTrackHorizontal, 0));
scrollabilityCache.scrollBar.setHorizontalTrackDrawable(track);
Drawable thumb = GLContext.get().getResources().getDrawable(a.getResourceId(com.glview.R.styleable.View_scrollbarThumbHorizontal, 0));
if (thumb != null) {
scrollabilityCache.scrollBar.setHorizontalThumbDrawable(thumb);
}
boolean alwaysDraw = a.getBoolean(com.glview.R.styleable.View_scrollbarAlwaysDrawHorizontalTrack,
false);
if (alwaysDraw) {
scrollabilityCache.scrollBar.setAlwaysDrawHorizontalTrack(true);
}
track = GLContext.get().getResources().getDrawable(a.getResourceId(com.glview.R.styleable.View_scrollbarTrackVertical, 0));
scrollabilityCache.scrollBar.setVerticalTrackDrawable(track);
thumb = GLContext.get().getResources().getDrawable(a.getResourceId(com.glview.R.styleable.View_scrollbarThumbVertical, 0));
if (thumb != null) {
scrollabilityCache.scrollBar.setVerticalThumbDrawable(thumb);
}
alwaysDraw = a.getBoolean(com.glview.R.styleable.View_scrollbarAlwaysDrawVerticalTrack,
alwaysDraw);
if (alwaysDraw) {
scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true);
}
// Apply layout direction to the new Drawables if needed
final int layoutDirection = getLayoutDirection();
if (track != null) {
track.setLayoutDirection(layoutDirection);
}
if (thumb != null) {
thumb.setLayoutDirection(layoutDirection);
}
// Re-apply user/background padding so that scrollbar(s) get added
resolvePadding();
a.recycle();
}
/**
* <p>
* Initalizes the scrollability cache if necessary.
* </p>
*/
private void initScrollCache() {
if (mScrollCache == null) {
mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext), this);
}
}
private ScrollabilityCache getScrollCache() {
initScrollCache();
return mScrollCache;
}
/**
* Set the position of the vertical scroll bar. Should be one of
* {@link #SCROLLBAR_POSITION_DEFAULT}, {@link #SCROLLBAR_POSITION_LEFT} or
* {@link #SCROLLBAR_POSITION_RIGHT}.
*
* @param position Where the vertical scroll bar should be positioned.
*/
public void setVerticalScrollbarPosition(int position) {
if (mVerticalScrollbarPosition != position) {
mVerticalScrollbarPosition = position;
computeOpaqueFlags();
resolvePadding();
}
}
/**
* @return The position where the vertical scroll bar will show, if applicable.
* @see #setVerticalScrollbarPosition(int)
*/
public int getVerticalScrollbarPosition() {
return mVerticalScrollbarPosition;
}
/**
* Gets a scale factor that determines the distance the view should scroll
* horizontally in response to {@link MotionEvent#ACTION_SCROLL}.
* @return The horizontal scroll scale factor.
* @hide
*/
protected float getHorizontalScrollFactor() {
// TODO: Should use something else.
return getVerticalScrollFactor();
}
/**
* @hide
*/
protected void computeOpaqueFlags() {
// Opaque if:
// - Has a background
// - Background is opaque
// - Doesn't have scrollbars or scrollbars overlay
if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;
} else {
mPrivateFlags &= ~PFLAG_OPAQUE_BACKGROUND;
}
final int flags = mViewFlags;
if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {
mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;
} else {
mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;
}
}
/**
* @hide
*/
protected boolean hasOpaqueScrollbars() {
return (mPrivateFlags & PFLAG_OPAQUE_SCROLLBARS) == PFLAG_OPAQUE_SCROLLBARS;
}
/**
* Implement this method to handle hover events.
* <p>
* This method is called whenever a pointer is hovering into, over, or out of the
* bounds of a view and the view is not currently being touched.
* Hover events are represented as pointer events with action
* {@link MotionEvent#ACTION_HOVER_ENTER}, {@link MotionEvent#ACTION_HOVER_MOVE},
* or {@link MotionEvent#ACTION_HOVER_EXIT}.
* </p>
* <ul>
* <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_ENTER}
* when the pointer enters the bounds of the view.</li>
* <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_MOVE}
* when the pointer has already entered the bounds of the view and has moved.</li>
* <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_EXIT}
* when the pointer has exited the bounds of the view or when the pointer is
* about to go down due to a button click, tap, or similar user action that
* causes the view to be touched.</li>
* </ul>
* <p>
* The view should implement this method to return true to indicate that it is
* handling the hover event, such as by changing its drawable state.
* </p><p>
* The default implementation calls {@link #setHovered} to update the hovered state
* of the view when a hover enter or hover exit event is received, if the view
* is enabled and is clickable. The default implementation also sends hover
* accessibility events.
* </p>
*
* @param event The motion event that describes the hover.
* @return True if the view handled the hover event.
*
* @see #isHovered
* @see #setHovered
* @see #onHoverChanged
*/
/*public boolean onHoverEvent(MotionEvent event) {
// The root view may receive hover (or touch) events that are outside the bounds of
// the window. This code ensures that we only send accessibility events for
// hovers that are actually within the bounds of the root view.
final int action = event.getActionMasked();
if (!mSendingHoverAccessibilityEvents) {
if ((action == MotionEvent.ACTION_HOVER_ENTER
|| action == MotionEvent.ACTION_HOVER_MOVE)
&& !hasHoveredChild()
&& pointInView(event.getX(), event.getY())) {
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
mSendingHoverAccessibilityEvents = true;
}
} else {
if (action == MotionEvent.ACTION_HOVER_EXIT
|| (action == MotionEvent.ACTION_MOVE
&& !pointInView(event.getX(), event.getY()))) {
mSendingHoverAccessibilityEvents = false;
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
}
}
if (isHoverable()) {
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
setHovered(true);
break;
case MotionEvent.ACTION_HOVER_EXIT:
setHovered(false);
break;
}
// Dispatch the event to onGenericMotionEvent before returning true.
// This is to provide compatibility with existing applications that
// handled HOVER_MOVE events in onGenericMotionEvent and that would
// break because of the new default handling for hoverable views
// in onHoverEvent.
// Note that onGenericMotionEvent will be called by default when
// onHoverEvent returns false (refer to dispatchGenericMotionEvent).
dispatchGenericMotionEventInternal(event);
// The event was already handled by calling setHovered(), so always
// return true.
return true;
}
return false;
}*/
/**
* Returns true if the view should handle {@link #onHoverEvent}
* by calling {@link #setHovered} to change its hovered state.
*
* @return True if the view is hoverable.
*/
private boolean isHoverable() {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
return false;
}
return (viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
}
/**
* Returns true if the view is currently hovered.
*
* @return True if the view is currently hovered.
*
* @see #setHovered
* @see #onHoverChanged
*/
public boolean isHovered() {
return (mPrivateFlags & PFLAG_HOVERED) != 0;
}
/**
* Sets whether the view is currently hovered.
* <p>
* Calling this method also changes the drawable state of the view. This
* enables the view to react to hover by using different drawable resources
* to change its appearance.
* </p><p>
* The {@link #onHoverChanged} method is called when the hovered state changes.
* </p>
*
* @param hovered True if the view is hovered.
*
* @see #isHovered
* @see #onHoverChanged
*/
public void setHovered(boolean hovered) {
if (hovered) {
if ((mPrivateFlags & PFLAG_HOVERED) == 0) {
mPrivateFlags |= PFLAG_HOVERED;
refreshDrawableState();
onHoverChanged(true);
}
} else {
if ((mPrivateFlags & PFLAG_HOVERED) != 0) {
mPrivateFlags &= ~PFLAG_HOVERED;
refreshDrawableState();
onHoverChanged(false);
}
}
}
/**
* Implement this method to handle hover state changes.
* <p>
* This method is called whenever the hover state changes as a result of a
* call to {@link #setHovered}.
* </p>
*
* @param hovered The current hover state, as returned by {@link #isHovered}.
*
* @see #isHovered
* @see #setHovered
*/
public void onHoverChanged(boolean hovered) {
}
/**
* Implement this method to handle touch screen motion events.
* <p>
* If this method is used to detect click actions, it is recommended that
* the actions be performed by implementing and calling
* {@link #performClick()}. This will ensure consistent system behavior,
* including:
* <ul>
* <li>obeying click sound preferences
* <li>dispatching OnClickListener calls
* <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
* accessibility features are enabled
* </ul>
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
// 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:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
break;
case MotionEvent.ACTION_MOVE:
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
/**
* @hide
*/
public boolean isInScrollingContainer() {
ViewGroup p = getParent();
while (p != null) {
if (((ViewGroup) p).shouldDelayChildPressedState()) {
return true;
}
p = p.getParent();
}
return false;
}
/**
* Remove the longpress detection timer.
*/
private void removeLongPressCallback() {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
}
/**
* Remove the pending click action
*/
private void removePerformClickCallback() {
if (mPerformClick != null) {
removeCallbacks(mPerformClick);
}
}
/**
* Remove the prepress detection timer.
*/
private void removeUnsetPressCallback() {
if ((mPrivateFlags & PFLAG_PRESSED) != 0 && mUnsetPressedState != null) {
setPressed(false);
removeCallbacks(mUnsetPressedState);
}
}
/**
* Remove the tap detection timer.
*/
private void removeTapCallback() {
if (mPendingCheckForTap != null) {
mPrivateFlags &= ~PFLAG_PREPRESSED;
removeCallbacks(mPendingCheckForTap);
}
}
/**
* Cancels a pending long press. Your subclass can use this if you
* want the context menu to come up if the user presses and holds
* at the same place, but you don't want it to come up if they press
* and then move around enough to cause scrolling.
*/
public void cancelLongPress() {
removeLongPressCallback();
/*
* The prepressed state handled by the tap callback is a display
* construct, but the tap callback will post a long press callback
* less its own timeout. Remove it here.
*/
removeTapCallback();
}
/**
* Set the horizontal scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param value the x position to scroll to
*/
public void setScrollX(int value) {
scrollTo(value, mScrollY);
}
/**
* Set the vertical scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param value the y position to scroll to
*/
public void setScrollY(int value) {
scrollTo(mScrollX, value);
}
/**
* 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;
invalidate();
// invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
/**
* 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;
}
}
/**
* 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);
}
/**
* <p>Trigger the scrollbars to draw. When invoked this method starts an
* animation to fade the scrollbars out after a default delay. If a subclass
* provides animated scrolling, the start delay should equal the duration
* of the scrolling animation.</p>
*
* <p>The animation starts only if at least one of the scrollbars is
* enabled, as specified by {@link #isHorizontalScrollBarEnabled()} and
* {@link #isVerticalScrollBarEnabled()}. When the animation is started,
* this method returns true, and false otherwise. If the animation is
* started, this method calls {@link #invalidate()}; in that case the
* caller should not call {@link #invalidate()}.</p>
*
* <p>This method should be invoked every time a subclass directly updates
* the scroll parameters.</p>
*
* <p>This method is automatically invoked by {@link #scrollBy(int, int)}
* and {@link #scrollTo(int, int)}.</p>
*
* @return true if the animation is played, false otherwise
*
* @see #awakenScrollBars(int)
* @see #scrollBy(int, int)
* @see #scrollTo(int, int)
* @see #isHorizontalScrollBarEnabled()
* @see #isVerticalScrollBarEnabled()
* @see #setHorizontalScrollBarEnabled(boolean)
* @see #setVerticalScrollBarEnabled(boolean)
*/
protected boolean awakenScrollBars() {
return mScrollCache != null &&
awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade, true);
}
/**
* Trigger the scrollbars to draw.
* This method differs from awakenScrollBars() only in its default duration.
* initialAwakenScrollBars() will show the scroll bars for longer than
* usual to give the user more of a chance to notice them.
*
* @return true if the animation is played, false otherwise.
*/
private boolean initialAwakenScrollBars() {
return mScrollCache != null &&
awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade * 4, true);
}
/**
* <p>
* Trigger the scrollbars to draw. When invoked this method starts an
* animation to fade the scrollbars out after a fixed delay. If a subclass
* provides animated scrolling, the start delay should equal the duration of
* the scrolling animation.
* </p>
*
* <p>
* The animation starts only if at least one of the scrollbars is enabled,
* as specified by {@link #isHorizontalScrollBarEnabled()} and
* {@link #isVerticalScrollBarEnabled()}. When the animation is started,
* this method returns true, and false otherwise. If the animation is
* started, this method calls {@link #invalidate()}; in that case the caller
* should not call {@link #invalidate()}.
* </p>
*
* <p>
* This method should be invoked everytime a subclass directly updates the
* scroll parameters.
* </p>
*
* @param startDelay the delay, in milliseconds, after which the animation
* should start; when the delay is 0, the animation starts
* immediately
* @return true if the animation is played, false otherwise
*
* @see #scrollBy(int, int)
* @see #scrollTo(int, int)
* @see #isHorizontalScrollBarEnabled()
* @see #isVerticalScrollBarEnabled()
* @see #setHorizontalScrollBarEnabled(boolean)
* @see #setVerticalScrollBarEnabled(boolean)
*/
protected boolean awakenScrollBars(int startDelay) {
return awakenScrollBars(startDelay, true);
}
/**
* <p>
* Trigger the scrollbars to draw. When invoked this method starts an
* animation to fade the scrollbars out after a fixed delay. If a subclass
* provides animated scrolling, the start delay should equal the duration of
* the scrolling animation.
* </p>
*
* <p>
* The animation starts only if at least one of the scrollbars is enabled,
* as specified by {@link #isHorizontalScrollBarEnabled()} and
* {@link #isVerticalScrollBarEnabled()}. When the animation is started,
* this method returns true, and false otherwise. If the animation is
* started, this method calls {@link #invalidate()} if the invalidate parameter
* is set to true; in that case the caller
* should not call {@link #invalidate()}.
* </p>
*
* <p>
* This method should be invoked everytime a subclass directly updates the
* scroll parameters.
* </p>
*
* @param startDelay the delay, in milliseconds, after which the animation
* should start; when the delay is 0, the animation starts
* immediately
*
* @param invalidate Wheter this method should call invalidate
*
* @return true if the animation is played, false otherwise
*
* @see #scrollBy(int, int)
* @see #scrollTo(int, int)
* @see #isHorizontalScrollBarEnabled()
* @see #isVerticalScrollBarEnabled()
* @see #setHorizontalScrollBarEnabled(boolean)
* @see #setVerticalScrollBarEnabled(boolean)
*/
protected boolean awakenScrollBars(int startDelay, boolean invalidate) {
final ScrollabilityCache scrollCache = mScrollCache;
if (scrollCache == null || !scrollCache.fadeScrollBars) {
return false;
}
if (scrollCache.scrollBar == null) {
scrollCache.scrollBar = new ScrollBarDrawable();
}
if (isHorizontalScrollBarEnabled() || isVerticalScrollBarEnabled()) {
if (invalidate) {
// Invalidate to show the scrollbars
postInvalidateOnAnimation();
}
if (scrollCache.state == ScrollabilityCache.OFF) {
// FIXME: this is copied from WindowManagerService.
// We should get this value from the system when it
// is possible to do so.
final int KEY_REPEAT_FIRST_DELAY = 750;
startDelay = Math.max(KEY_REPEAT_FIRST_DELAY, startDelay);
}
// Tell mScrollCache when we should start fading. This may
// extend the fade start time if one was already scheduled
long fadeStartTime = AnimationUtils.currentAnimationTimeMillis() + startDelay;
scrollCache.fadeStartTime = fadeStartTime;
scrollCache.state = ScrollabilityCache.ON;
// Schedule our fader to run, unscheduling any old ones first
if (mAttachInfo != null) {
mAttachInfo.mHandler.removeCallbacks(scrollCache);
mAttachInfo.mHandler.postAtTime(scrollCache, fadeStartTime);
}
return true;
}
return false;
}
/**
* Sets the identifier for this view. The identifier does not have to be
* unique in this view's hierarchy. The identifier should be a positive
* number.
*
* @see #NO_ID
* @see #getId()
* @see #findViewById(int)
*
* @param id a number used to identify the view
*
*/
public void setId(int id){
mID = id;
}
/**
* Returns this view's identifier.
*
* @return a positive integer used to identify the view or {@link #NO_ID}
* if the view has no ID
*
* @see #setId(int)
* @see #findViewById(int)
*/
public int getId(){
return mID;
}
/**
* {@hide}
*
* @param isRoot true if the view belongs to the root namespace, false
* otherwise
*/
public void setIsRootNamespace(boolean isRoot) {
if (isRoot) {
mPrivateFlags |= PFLAG_IS_ROOT_NAMESPACE;
} else {
mPrivateFlags &= ~PFLAG_IS_ROOT_NAMESPACE;
}
}
/**
* {@hide}
*
* @return true if the view belongs to the root namespace, false otherwise
*/
public boolean isRootNamespace() {
return (mPrivateFlags&PFLAG_IS_ROOT_NAMESPACE) != 0;
}
/**
* Look for a child view with the given id. If this view has the given
* id, return this view.
*
* @param id The id to search for.
* @return The view that has the given id in the hierarchy or null
*/
public final View findViewById(int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
/**
* {@hide}
* @param id the id of the view to be found
* @return the view of the specified id, null if cannot be found
*/
protected View findViewTraversal(int id) {
if (id == mID) {
return this;
}
return null;
}
/**
* {@hide}
* @param tag the tag of the view to be found
* @return the view of specified tag, null if cannot be found
*/
protected View findViewWithTagTraversal(Object tag) {
if (tag != null && tag.equals(mTag)) {
return this;
}
return null;
}
/**
* {@hide}
* @param predicate The predicate to evaluate.
* @param childToSkip If not null, ignores this child during the recursive traversal.
* @return The first view that matches the predicate or null.
*/
protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
if (predicate.apply(this)) {
return this;
}
return null;
}
/**
* Look for a child view with the given tag. If this view has the given
* tag, return this view.
*
* @param tag The tag to search for, using "tag.equals(getTag())".
* @return The View that has the given tag in the hierarchy or null
*/
public final View findViewWithTag(Object tag) {
if (tag == null) {
return null;
}
return findViewWithTagTraversal(tag);
}
/**
* {@hide}
* Look for a child view that matches the specified predicate.
* If this view matches the predicate, return this view.
*
* @param predicate The predicate to evaluate.
* @return The first view that matches the predicate or null.
*/
public final View findViewByPredicate(Predicate<View> predicate) {
return findViewByPredicateTraversal(predicate, null);
}
/**
* {@hide}
* Look for a child view that matches the specified predicate,
* starting with the specified view and its descendents and then
* recusively searching the ancestors and siblings of that view
* until this view is reached.
*
* This method is useful in cases where the predicate does not match
* a single unique view (perhaps multiple views use the same id)
* and we are trying to find the view that is "closest" in scope to the
* starting view.
*
* @param start The view to start from.
* @param predicate The predicate to evaluate.
* @return The first view that matches the predicate or null.
*/
public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) {
View childToSkip = null;
for (;;) {
View view = start.findViewByPredicateTraversal(predicate, childToSkip);
if (view != null || start == this) {
return view;
}
ViewGroup parent = start.getParent();
if (parent == null || !(parent instanceof View)) {
return null;
}
childToSkip = start;
start = (View) parent;
}
}
/**
* Set the enabled state of this view. The interpretation of the enabled
* state varies by subclass.
*
* @param enabled True if this view is enabled, false otherwise.
*/
public void setEnabled(boolean enabled) {
if (enabled == isEnabled()) return;
setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
/*
* The View most likely has to change its appearance, so refresh
* the drawable state.
*/
refreshDrawableState();
// Invalidate too, since the default behavior for views is to be
// be drawn at 50% alpha rather than to change the drawable.
invalidate();
}
/**
* Returns the enabled status for this view. The interpretation of the
* enabled state varies by subclass.
*
* @return True if this view is enabled, false otherwise.
*/
public boolean isEnabled() {
return (mViewFlags & ENABLED_MASK) == ENABLED;
}
/**
* 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 com.aliyun.image.common.Scroller Scroller}
* object.
*/
public void computeScroll() {
}
public boolean dispatchTouchEvent(MotionEvent event) {
if (!isEnabled())
return false;
boolean result = false;
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
public boolean dispatchKeyEvent(KeyEvent event) {
// Give any attached key listener a first crack at the event.
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
if (event.dispatch(this, null, this)) {
return true;
}
return false;
}
/**
* 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) {
if (!hasWindowFocus) {
if (isPressed()) {
setPressed(false);
}
removeLongPressCallback();
removeTapCallback();
onFocusLost();
}
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 view visibility change down the view hierarchy.
* ViewGroups should override to route to their children.
* @param changedView The view whose visibility changed. Could be 'this' or
* an ancestor view.
* @param visibility The new visibility of changedView: {@link #VISIBLE},
* {@link #INVISIBLE} or {@link #GONE}.
*/
protected void dispatchVisibilityChanged(View changedView,
int visibility) {
onVisibilityChanged(changedView, visibility);
}
/**
* Called when the visibility of the view or an ancestor of the view is changed.
* @param changedView The view whose visibility changed. Could be 'this' or
* an ancestor view.
* @param visibility The new visibility of changedView: {@link #VISIBLE},
* {@link #INVISIBLE} or {@link #GONE}.
*/
protected void onVisibilityChanged(View changedView, int visibility) {
if (visibility == VISIBLE) {
if (mAttachInfo != null) {
initialAwakenScrollBars();
} else {
mPrivateFlags |= PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
}
}
}
/**
* Dispatch a window visibility change down the view hierarchy.
* ViewGroups should override to route to their children.
*
* @param visibility The new visibility of the window.
*
* @see #onWindowVisibilityChanged(int)
*/
public void dispatchWindowVisibilityChanged(int visibility) {
onWindowVisibilityChanged(visibility);
}
/**
* Called when the window containing has change its visibility
* (between {@link #GONE}, {@link #INVISIBLE}, and {@link #VISIBLE}). Note
* that this tells you whether or not your window is being made visible
* to the window manager; this does <em>not</em> tell you whether or not
* your window is obscured by other windows on the screen, even if it
* is itself visible.
*
* @param visibility The new visibility of the window.
*/
protected void onWindowVisibilityChanged(int visibility) {
if (visibility == VISIBLE) {
initialAwakenScrollBars();
}
}
private void checkForLongClick(int delayOffset) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
}
}
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
* a sound, etc.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.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 if one of the above receivers consumed the event, false otherwise.
*/
public boolean performLongClick() {
boolean handled = false;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
}
/**
* Performs button-related actions during a touch down event.
*
* @param event The event.
* @return True if the down was consumed.
*
* @hide
*/
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
return false;
}
/**
* 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) {
resetResolvedPadding();
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
mUserPaddingLeftInitial = left;
mUserPaddingRightInitial = right;
mLeftPaddingDefined = true;
mRightPaddingDefined = true;
internalSetPadding(left, top, right, bottom);
}
/**
* @hide
*/
protected void internalSetPadding(int left, int top, int right, int bottom) {
mUserPaddingLeft = left;
mUserPaddingRight = right;
mUserPaddingBottom = bottom;
final int viewFlags = mViewFlags;
boolean changed = false;
// Common case is there are no scroll bars.
if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
if ((viewFlags & SCROLLBARS_VERTICAL) != 0) {
final int offset = (viewFlags & SCROLLBARS_INSET_MASK) == 0
? 0 : getVerticalScrollbarWidth();
switch (mVerticalScrollbarPosition) {
case SCROLLBAR_POSITION_DEFAULT:
if (isLayoutRtl()) {
left += offset;
} else {
right += offset;
}
break;
case SCROLLBAR_POSITION_RIGHT:
right += offset;
break;
case SCROLLBAR_POSITION_LEFT:
left += offset;
break;
}
}
if ((viewFlags & SCROLLBARS_HORIZONTAL) != 0) {
bottom += (viewFlags & SCROLLBARS_INSET_MASK) == 0
? 0 : getHorizontalScrollbarHeight();
}
}
if (mPaddingLeft != left) {
changed = true;
mPaddingLeft = left;
}
if (mPaddingTop != top) {
changed = true;
mPaddingTop = top;
}
if (mPaddingRight != right) {
changed = true;
mPaddingRight = right;
}
if (mPaddingBottom != bottom) {
changed = true;
mPaddingBottom = bottom;
}
if (changed) {
requestLayout();
}
}
/**
* Sets the relative padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingStart}, {@link #getPaddingTop},
* {@link #getPaddingEnd} and {@link #getPaddingBottom} may be different
* from the values set in this call.
*
* @attr ref android.R.styleable#View_padding
* @attr ref android.R.styleable#View_paddingBottom
* @attr ref android.R.styleable#View_paddingStart
* @attr ref android.R.styleable#View_paddingEnd
* @attr ref android.R.styleable#View_paddingTop
* @param start the start padding in pixels
* @param top the top padding in pixels
* @param end the end padding in pixels
* @param bottom the bottom padding in pixels
*/
public void setPaddingRelative(int start, int top, int end, int bottom) {
resetResolvedPadding();
mUserPaddingStart = start;
mUserPaddingEnd = end;
mLeftPaddingDefined = true;
mRightPaddingDefined = true;
switch(getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
mUserPaddingLeftInitial = end;
mUserPaddingRightInitial = start;
internalSetPadding(end, top, start, bottom);
break;
case LAYOUT_DIRECTION_LTR:
default:
mUserPaddingLeftInitial = start;
mUserPaddingRightInitial = end;
internalSetPadding(start, top, end, bottom);
}
}
Rect mTmpRect = new Rect();
/**
* Check if layout direction resolution can be done.
*
* @return true if layout direction resolution can be done otherwise return false.
*/
public boolean canResolveLayoutDirection() {
switch (getRawLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
if (mParent != null) {
try {
return mParent.canResolveLayoutDirection();
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
}
}
return false;
default:
return true;
}
}
/**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
*
* @return true if resolution has been done, false otherwise.
*
* @hide
*/
public boolean resolveLayoutDirection() {
// Clear any previous layout direction resolution
mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK;
if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
// Set resolved depending on layout direction
switch ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >>
PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) {
case LAYOUT_DIRECTION_INHERIT:
// We cannot resolve yet. LTR is by default and let the resolution happen again
// later to get the correct resolved value
if (!canResolveLayoutDirection()) return false;
// Parent has not yet resolved, LTR is still the default
try {
if (!mParent.isLayoutDirectionResolved()) return false;
if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
}
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
}
break;
case LAYOUT_DIRECTION_RTL:
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
break;
case LAYOUT_DIRECTION_LOCALE:
if((LAYOUT_DIRECTION_RTL ==
TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()))) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
}
break;
default:
// Nothing to do, LTR by default
}
}
// Set to resolved
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
return true;
}
/**
* Reset the resolved layout direction. Layout direction will be resolved during a call to
* {@link #onMeasure(int, int)}.
*
* @hide
*/
public void resetResolvedLayoutDirection() {
// Reset the current resolved bits
mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK;
}
/**
* @return true if the layout direction is inherited.
*
* @hide
*/
public boolean isLayoutDirectionInherited() {
return (getRawLayoutDirection() == LAYOUT_DIRECTION_INHERIT);
}
/**
* @return true if layout direction has been resolved.
*/
public boolean isLayoutDirectionResolved() {
return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED;
}
/**
* Return if padding has been resolved
*
* @hide
*/
boolean isPaddingResolved() {
return (mPrivateFlags2 & PFLAG2_PADDING_RESOLVED) == PFLAG2_PADDING_RESOLVED;
}
/**
* Resolves padding depending on layout direction, if applicable, and
* recomputes internal padding values to adjust for scroll bars.
*
* @hide
*/
public void resolvePadding() {
final int resolvedLayoutDirection = getLayoutDirection();
boolean isRtlCompatibilityMode = false;
if (!isRtlCompatibilityMode) {
// Post Jelly Bean MR1 case: we need to take the resolved layout direction into account.
// If start / end padding are defined, they will be resolved (hence overriding) to
// left / right or right / left depending on the resolved layout direction.
// If start / end padding are not defined, use the left / right ones.
if (mBackground != null && (!mLeftPaddingDefined || !mRightPaddingDefined)) {
Rect padding = mTmpRect;
mBackground.getPadding(padding);
if (!mLeftPaddingDefined) {
mUserPaddingLeftInitial = padding.left;
}
if (!mRightPaddingDefined) {
mUserPaddingRightInitial = padding.right;
}
}
switch (resolvedLayoutDirection) {
case LAYOUT_DIRECTION_RTL:
if (mUserPaddingStart != UNDEFINED_PADDING) {
mUserPaddingRight = mUserPaddingStart;
} else {
mUserPaddingRight = mUserPaddingRightInitial;
}
if (mUserPaddingEnd != UNDEFINED_PADDING) {
mUserPaddingLeft = mUserPaddingEnd;
} else {
mUserPaddingLeft = mUserPaddingLeftInitial;
}
break;
case LAYOUT_DIRECTION_LTR:
default:
if (mUserPaddingStart != UNDEFINED_PADDING) {
mUserPaddingLeft = mUserPaddingStart;
} else {
mUserPaddingLeft = mUserPaddingLeftInitial;
}
if (mUserPaddingEnd != UNDEFINED_PADDING) {
mUserPaddingRight = mUserPaddingEnd;
} else {
mUserPaddingRight = mUserPaddingRightInitial;
}
}
mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
}
internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom);
onRtlPropertiesChanged(resolvedLayoutDirection);
mPrivateFlags2 |= PFLAG2_PADDING_RESOLVED;
}
/**
* 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() {
if (!isPaddingResolved()) {
resolvePadding();
}
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() {
if (!isPaddingResolved()) {
resolvePadding();
}
return mPaddingRight;
}
/**
* Returns the start padding of this view depending on its resolved layout direction.
* If there are inset and enabled scrollbars, this value may include the space
* required to display the scrollbars as well.
*
* @return the start padding in pixels
*/
public int getPaddingStart() {
if (!isPaddingResolved()) {
resolvePadding();
}
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
mPaddingRight : mPaddingLeft;
}
/**
* Returns the end padding of this view depending on its resolved layout direction.
* If there are inset and enabled scrollbars, this value may include the space
* required to display the scrollbars as well.
*
* @return the end padding in pixels
*/
public int getPaddingEnd() {
if (!isPaddingResolved()) {
resolvePadding();
}
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
mPaddingLeft : mPaddingRight;
}
/**
* <p>Indicates whether or not this view's layout will be requested during
* the next hierarchy layout pass.</p>
*
* @return true if the layout will be forced during next layout pass
*/
public boolean isLayoutRequested() {
return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
}
public void layout(int left, int top, int right, int bottom) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean sizeChanged = setFrame(left, top, right, bottom);
// We call onLayout no matter sizeChanged is true or not because the
// orientation may change without changing the size of the View (for
// example, rotate the device by 180 degrees), and we want to handle
// orientation change in onLayout.
if(sizeChanged || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED){
onLayout(sizeChanged, left, top, right, bottom);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, left, top, right, bottom, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
/**
* 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
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth)
|| (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(left, top, right, bottom);
mXCenter = mLeft + ((mRight - mLeft) >> 1);
mYCenter = mTop + ((mBottom - mTop) >> 1);
mPrivateFlags |= PFLAG_HAS_BOUNDS;
if (sizeChanged) {
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
mBackgroundSizeChanged = true;
}
return changed;
}
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
if (mOverlay != null) {
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
}
/**
* 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(GLCanvas canvas) {
}
/**
* <p>
* This is called to find out how big a view should be. The parent
* supplies constraint information in the width and height parameters.
* </p>
*
* <p>
* The actual measurement work of a view is performed in
* {@link #onMeasure(int, int)}, called by this method. Therefore, only
* {@link #onMeasure(int, int)} can and must be overridden by subclasses.
* </p>
*
*
* @param widthMeasureSpec Horizontal space requirements as imposed by the
* parent
* @param heightMeasureSpec Vertical space requirements as imposed by the
* parent
*
* @see #onMeasure(int, int)
*/
@SuppressLint("WrongCall")
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
resolveRtlPropertiesIfNeeded();
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("" + this + "onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
// protected void loadBackgroundTexture(int resourceId){
///* int width = MeasureSpec.getSize(widthMeasureSpec);
// int height = MeasureSpec.getSize(heightMeasureSpec);*/
// if(mSetBackgroundType != NO_BACKGROUND && mBackgroundTexture == null){
// switch(mSetBackgroundType){
// case SET_BACKGROUND_BITMAP:
// mBackgroundTexture = createTexture(mBackgroundBitmap, CREATE_BACKGROUND);
// break;
// case SET_BACKGROUND_RESOURCE:
//
// break;
// }
// }
// }
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
/**
* <p>This mehod must be called by {@link #onMeasure(int, int)} to store the
* measured width and measured height. Failing to do so will trigger an
* exception at measurement time.</p>
*
* @param measuredWidth The measured width of this view. May be a complex
* bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
* @param measuredHeight The measured height of this view. May be a complex
* bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
*/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
/**
* Merge two states as returned by {@link #getMeasuredState()}.
* @param curState The current state as returned from a view or the result
* of combining multiple views.
* @param newState The new view state to combine.
* @return Returns a new integer reflecting the combination of the two
* states.
*/
public static int combineMeasuredStates(int curState, int newState) {
return curState | newState;
}
/**
* Version of {@link #resolveSizeAndState(int, int, int)}
* returning only the {@link #MEASURED_SIZE_MASK} bits of the result.
*/
public static int resolveSize(int size, int measureSpec) {
return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK;
}
/**
* Like {@link #getMeasuredWidthAndState()}, but only returns the
* raw width component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
*
* @return The raw measured width of this view.
*/
public int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
/**
* Return the full width measurement information for this view as computed
* by the most recent call to {@link #measure(int, int)}. This result is a bit mask
* as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getWidth()} to see how wide a view is after layout.
*
* @return The measured width of this view as a bit mask.
*/
public final int getMeasuredWidthAndState() {
return mMeasuredWidth;
}
/**
* Like {@link #getMeasuredHeightAndState()}, but only returns the
* raw width component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
*
* @return The raw measured height of this view.
*/
public int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
/**
* Return the full height measurement information for this view as computed
* by the most recent call to {@link #measure(int, int)}. This result is a bit mask
* as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getHeight()} to see how wide a view is after layout.
*
* @return The measured width of this view as a bit mask.
*/
public final int getMeasuredHeightAndState() {
return mMeasuredHeight;
}
/**
* Return only the state bits of {@link #getMeasuredWidthAndState()}
* and {@link #getMeasuredHeightAndState()}, combined into one integer.
* The width component is in the regular bits {@link #MEASURED_STATE_MASK}
* and the height component is at the shifted bits
* {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
*/
public final int getMeasuredState() {
return (mMeasuredWidth&MEASURED_STATE_MASK)
| ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT)
& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
}
/**
* Utility to reconcile a desired size and state, with constraints imposed
* by a MeasureSpec. Will take the desired size, unless a different size
* is imposed by the constraints. The returned value is a compound integer,
* with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
* optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
* size is smaller than the size the view wants to be.
*
* @param size How big the view wants to be
* @param measureSpec Constraints imposed by the parent
* @return Size information bit mask as defined by
* {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
*/
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState&MEASURED_STATE_MASK);
}
/**
* Returns the suggested minimum height that the view should use. This
* returns the maximum of the view's minimum height
* and the background's minimum height
* ({@link android.graphics.drawable.Drawable#getMinimumHeight()}).
* <p>
* When being used in {@link #onMeasure(int, int)}, the caller should still
* ensure the returned height is within the requirements of the parent.
*
* @return The suggested minimum height of the view.
*/
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
/**
* Returns the suggested minimum width that the view should use. This
* returns the maximum of the view's minimum width)
* and the background's minimum width
* ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
* <p>
* When being used in {@link #onMeasure(int, int)}, the caller should still
* ensure the returned width is within the requirements of the parent.
*
* @return The suggested minimum width of the view.
*/
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
/**
* Returns the minimum height of the view.
*
* @return the minimum height the view will try to be.
*
* @see #setMinimumHeight(int)
*
* @attr ref android.R.styleable#View_minHeight
*/
public int getMinimumHeight() {
return mMinHeight;
}
/**
* Sets the minimum height of the view. It is not guaranteed the view will
* be able to achieve this minimum height (for example, if its parent layout
* constrains it with less available height).
*
* @param minHeight The minimum height the view will try to be.
*
* @see #getMinimumHeight()
*
* @attr ref android.R.styleable#View_minHeight
*/
public void setMinimumHeight(int minHeight) {
mMinHeight = minHeight;
requestLayout();
}
/**
* Returns the minimum width of the view.
*
* @return the minimum width the view will try to be.
*
* @see #setMinimumWidth(int)
*
* @attr ref android.R.styleable#View_minWidth
*/
public int getMinimumWidth() {
return mMinWidth;
}
/**
* Sets the minimum width of the view. It is not guaranteed the view will
* be able to achieve this minimum width (for example, if its parent layout
* constrains it with less available width).
*
* @param minWidth The minimum width the view will try to be.
*
* @see #getMinimumWidth()
*
* @attr ref android.R.styleable#View_minWidth
*/
public void setMinimumWidth(int minWidth) {
mMinWidth = minWidth;
requestLayout();
}
/**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changeSize,
int left, int top, int right, int bottom) {
}
/**
* <p>Indicate whether the horizontal scrollbar should be drawn or not. The
* scrollbar is not drawn by default.</p>
*
* @return true if the horizontal scrollbar should be painted, false
* otherwise
*
* @see #setHorizontalScrollBarEnabled(boolean)
*/
public boolean isHorizontalScrollBarEnabled() {
return (mViewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
}
/**
* <p>Define whether the horizontal scrollbar should be drawn or not. The
* scrollbar is not drawn by default.</p>
*
* @param horizontalScrollBarEnabled true if the horizontal scrollbar should
* be painted
*
* @see #isHorizontalScrollBarEnabled()
*/
public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
if (isHorizontalScrollBarEnabled() != horizontalScrollBarEnabled) {
mViewFlags ^= SCROLLBARS_HORIZONTAL;
computeOpaqueFlags();
resolvePadding();
}
}
/**
* Returns the strength, or intensity, of the top faded edge. The strength is
* a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
* returns 0.0 or 1.0 but no value in between.
*
* Subclasses should override this method to provide a smoother fade transition
* when scrolling occurs.
*
* @return the intensity of the top fade as a float between 0.0f and 1.0f
*/
protected float getTopFadingEdgeStrength() {
return computeVerticalScrollOffset() > 0 ? 1.0f : 0.0f;
}
/**
* Returns the strength, or intensity, of the bottom faded edge. The strength is
* a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
* returns 0.0 or 1.0 but no value in between.
*
* Subclasses should override this method to provide a smoother fade transition
* when scrolling occurs.
*
* @return the intensity of the bottom fade as a float between 0.0f and 1.0f
*/
protected float getBottomFadingEdgeStrength() {
return computeVerticalScrollOffset() + computeVerticalScrollExtent() <
computeVerticalScrollRange() ? 1.0f : 0.0f;
}
/**
* Returns the strength, or intensity, of the left faded edge. The strength is
* a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
* returns 0.0 or 1.0 but no value in between.
*
* Subclasses should override this method to provide a smoother fade transition
* when scrolling occurs.
*
* @return the intensity of the left fade as a float between 0.0f and 1.0f
*/
protected float getLeftFadingEdgeStrength() {
return computeHorizontalScrollOffset() > 0 ? 1.0f : 0.0f;
}
/**
* Returns the strength, or intensity, of the right faded edge. The strength is
* a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
* returns 0.0 or 1.0 but no value in between.
*
* Subclasses should override this method to provide a smoother fade transition
* when scrolling occurs.
*
* @return the intensity of the right fade as a float between 0.0f and 1.0f
*/
protected float getRightFadingEdgeStrength() {
return computeHorizontalScrollOffset() + computeHorizontalScrollExtent() <
computeHorizontalScrollRange() ? 1.0f : 0.0f;
}
/**
* <p>Indicate whether the vertical scrollbar should be drawn or not. The
* scrollbar is not drawn by default.</p>
*
* @return true if the vertical scrollbar should be painted, false
* otherwise
*
* @see #setVerticalScrollBarEnabled(boolean)
*/
public boolean isVerticalScrollBarEnabled() {
return (mViewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL;
}
/**
* <p>Define whether the vertical scrollbar should be drawn or not. The
* scrollbar is not drawn by default.</p>
*
* @param verticalScrollBarEnabled true if the vertical scrollbar should
* be painted
*
* @see #isVerticalScrollBarEnabled()
*/
public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
if (isVerticalScrollBarEnabled() != verticalScrollBarEnabled) {
mViewFlags ^= SCROLLBARS_VERTICAL;
computeOpaqueFlags();
resolvePadding();
}
}
/**
* @hide
*/
protected void recomputePadding() {
internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom);
}
/**
* Define whether scrollbars will fade when the view is not scrolling.
*
* @param fadeScrollbars wheter to enable fading
*
* @attr ref android.R.styleable#View_fadeScrollbars
*/
public void setScrollbarFadingEnabled(boolean fadeScrollbars) {
initScrollCache();
final ScrollabilityCache scrollabilityCache = mScrollCache;
scrollabilityCache.fadeScrollBars = fadeScrollbars;
if (fadeScrollbars) {
scrollabilityCache.state = ScrollabilityCache.OFF;
} else {
scrollabilityCache.state = ScrollabilityCache.ON;
}
}
/**
*
* Returns true if scrollbars will fade when this view is not scrolling
*
* @return true if scrollbar fading is enabled
*
* @attr ref android.R.styleable#View_fadeScrollbars
*/
public boolean isScrollbarFadingEnabled() {
return mScrollCache != null && mScrollCache.fadeScrollBars;
}
/**
*
* Returns the delay before scrollbars fade.
*
* @return the delay before scrollbars fade
*
* @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
*/
public int getScrollBarDefaultDelayBeforeFade() {
return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
mScrollCache.scrollBarDefaultDelayBeforeFade;
}
/**
* Define the delay before scrollbars fade.
*
* @param scrollBarDefaultDelayBeforeFade - the delay before scrollbars fade
*
* @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
*/
public void setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade) {
getScrollCache().scrollBarDefaultDelayBeforeFade = scrollBarDefaultDelayBeforeFade;
}
/**
*
* Returns the scrollbar fade duration.
*
* @return the scrollbar fade duration
*
* @attr ref android.R.styleable#View_scrollbarFadeDuration
*/
public int getScrollBarFadeDuration() {
return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
mScrollCache.scrollBarFadeDuration;
}
/**
* Define the scrollbar fade duration.
*
* @param scrollBarFadeDuration - the scrollbar fade duration
*
* @attr ref android.R.styleable#View_scrollbarFadeDuration
*/
public void setScrollBarFadeDuration(int scrollBarFadeDuration) {
getScrollCache().scrollBarFadeDuration = scrollBarFadeDuration;
}
/**
*
* Returns the scrollbar size.
*
* @return the scrollbar size
*
* @attr ref android.R.styleable#View_scrollbarSize
*/
public int getScrollBarSize() {
return mScrollCache == null ? ViewConfiguration.get(mContext).getScaledScrollBarSize() :
mScrollCache.scrollBarSize;
}
/**
* Define the scrollbar size.
*
* @param scrollBarSize - the scrollbar size
*
* @attr ref android.R.styleable#View_scrollbarSize
*/
public void setScrollBarSize(int scrollBarSize) {
getScrollCache().scrollBarSize = scrollBarSize;
}
/**
* <p>Specify the style of the scrollbars. The scrollbars can be overlaid or
* inset. When inset, they add to the padding of the view. And the scrollbars
* can be drawn inside the padding area or on the edge of the view. For example,
* if a view has a background drawable and you want to draw the scrollbars
* inside the padding specified by the drawable, you can use
* SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to
* appear at the edge of the view, ignoring the padding, then you can use
* SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.</p>
* @param style the style of the scrollbars. Should be one of
* SCROLLBARS_INSIDE_OVERLAY, SCROLLBARS_INSIDE_INSET,
* SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.
* @see #SCROLLBARS_INSIDE_OVERLAY
* @see #SCROLLBARS_INSIDE_INSET
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
*
* @attr ref android.R.styleable#View_scrollbarStyle
*/
public void setScrollBarStyle(int style) {
if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) {
mViewFlags = (mViewFlags & ~SCROLLBARS_STYLE_MASK) | (style & SCROLLBARS_STYLE_MASK);
computeOpaqueFlags();
resolvePadding();
}
}
/**
* <p>Returns the current scrollbar style.</p>
* @return the current scrollbar style
* @see #SCROLLBARS_INSIDE_OVERLAY
* @see #SCROLLBARS_INSIDE_INSET
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
*
* @attr ref android.R.styleable#View_scrollbarStyle
*/
public int getScrollBarStyle() {
return mViewFlags & SCROLLBARS_STYLE_MASK;
}
/**
* <p>Compute the horizontal range that the horizontal scrollbar
* represents.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollExtent()} and
* {@link #computeHorizontalScrollOffset()}.</p>
*
* <p>The default range is the drawing width of this view.</p>
*
* @return the total horizontal range represented by the horizontal
* scrollbar
*
* @see #computeHorizontalScrollExtent()
* @see #computeHorizontalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
protected int computeHorizontalScrollRange() {
return getWidth();
}
/**
* <p>Compute the horizontal offset of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute the position
* of the thumb within the scrollbar's track.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeHorizontalScrollExtent()}.</p>
*
* <p>The default offset is the scroll offset of this view.</p>
*
* @return the horizontal offset of the scrollbar's thumb
*
* @see #computeHorizontalScrollRange()
* @see #computeHorizontalScrollExtent()
* @see android.widget.ScrollBarDrawable
*/
protected int computeHorizontalScrollOffset() {
return mScrollX;
}
/**
* <p>Compute the horizontal extent of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute the length
* of the thumb within the scrollbar's track.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeHorizontalScrollOffset()}.</p>
*
* <p>The default extent is the drawing width of this view.</p>
*
* @return the horizontal extent of the scrollbar's thumb
*
* @see #computeHorizontalScrollRange()
* @see #computeHorizontalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
protected int computeHorizontalScrollExtent() {
return getWidth();
}
/**
* <p>Compute the vertical range that the vertical scrollbar represents.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollExtent()} and
* {@link #computeVerticalScrollOffset()}.</p>
*
* @return the total vertical range represented by the vertical scrollbar
*
* <p>The default range is the drawing height of this view.</p>
*
* @see #computeVerticalScrollExtent()
* @see #computeVerticalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
protected int computeVerticalScrollRange() {
return getHeight();
}
/**
* <p>Compute the vertical offset of the vertical scrollbar's thumb
* within the horizontal range. This value is used to compute the position
* of the thumb within the scrollbar's track.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollExtent()}.</p>
*
* <p>The default offset is the scroll offset of this view.</p>
*
* @return the vertical offset of the scrollbar's thumb
*
* @see #computeVerticalScrollRange()
* @see #computeVerticalScrollExtent()
* @see android.widget.ScrollBarDrawable
*/
protected int computeVerticalScrollOffset() {
return mScrollY;
}
/**
* <p>Compute the vertical extent of the vertical scrollbar's thumb
* within the vertical range. This value is used to compute the length
* of the thumb within the scrollbar's track.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollOffset()}.</p>
*
* <p>The default extent is the drawing height of this view.</p>
*
* @return the vertical extent of the scrollbar's thumb
*
* @see #computeVerticalScrollRange()
* @see #computeVerticalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
protected int computeVerticalScrollExtent() {
return getHeight();
}
/**
* Check if this view can be scrolled horizontally in a certain direction.
*
* @param direction Negative to check scrolling left, positive to check scrolling right.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollHorizontally(int direction) {
final int offset = computeHorizontalScrollOffset();
final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
/**
* Check if this view can be scrolled vertically in a certain direction.
*
* @param direction Negative to check scrolling up, positive to check scrolling down.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollVertically(int direction) {
final int offset = computeVerticalScrollOffset();
final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
/**
* <p>Request the drawing of the horizontal and the vertical scrollbar. The
* scrollbars are painted only if they have been awakened first.</p>
*
* @param canvas the canvas on which to draw the scrollbars
*
* @see #awakenScrollBars(int)
*/
protected final void onDrawScrollBars(GLCanvas canvas) {
// scrollbars are drawn only when the animation is running
final ScrollabilityCache cache = mScrollCache;
if (cache != null) {
int state = cache.state;
if (state == ScrollabilityCache.OFF) {
return;
}
boolean invalidate = false;
if (state == ScrollabilityCache.FADING) {
// We're fading -- get our fade interpolation
if (cache.interpolatorValues == null) {
cache.interpolatorValues = new float[1];
}
float[] values = cache.interpolatorValues;
// Stops the animation if we're done
if (cache.scrollBarInterpolator.timeToValues(values) ==
Interpolator.Result.FREEZE_END) {
cache.state = ScrollabilityCache.OFF;
} else {
cache.scrollBar.setAlpha(Math.round(values[0]));
}
// This will make the scroll bars inval themselves after
// drawing. We only want this when we're fading so that
// we prevent excessive redraws
invalidate = true;
} else {
// We're just on -- but we may have been fading before so
// reset alpha
cache.scrollBar.setAlpha(255);
}
final int viewFlags = mViewFlags;
final boolean drawHorizontalScrollBar =
(viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
final boolean drawVerticalScrollBar =
(viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL
&& !isVerticalScrollBarHidden();
if (drawVerticalScrollBar || drawHorizontalScrollBar) {
final int width = mRight - mLeft;
final int height = mBottom - mTop;
final ScrollBarDrawable scrollBar = cache.scrollBar;
final int scrollX = mScrollX;
final int scrollY = mScrollY;
final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
int left;
int top;
int right;
int bottom;
if (drawHorizontalScrollBar) {
int size = scrollBar.getSize(false);
if (size <= 0) {
size = cache.scrollBarSize;
}
scrollBar.setParameters(computeHorizontalScrollRange(),
computeHorizontalScrollOffset(),
computeHorizontalScrollExtent(), false);
final int verticalScrollBarGap = drawVerticalScrollBar ?
getVerticalScrollbarWidth() : 0;
top = scrollY + height - size - (mUserPaddingBottom & inside);
left = scrollX + (mPaddingLeft & inside);
right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
bottom = top + size;
onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom);
if (invalidate) {
invalidate(left, top, right, bottom);
}
}
if (drawVerticalScrollBar) {
int size = scrollBar.getSize(true);
if (size <= 0) {
size = cache.scrollBarSize;
}
scrollBar.setParameters(computeVerticalScrollRange(),
computeVerticalScrollOffset(),
computeVerticalScrollExtent(), true);
int verticalScrollbarPosition = mVerticalScrollbarPosition;
if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) {
verticalScrollbarPosition = isLayoutRtl() ?
SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT;
}
switch (verticalScrollbarPosition) {
default:
case SCROLLBAR_POSITION_RIGHT:
left = scrollX + width - size - (mUserPaddingRight & inside);
break;
case SCROLLBAR_POSITION_LEFT:
left = scrollX + (mUserPaddingLeft & inside);
break;
}
top = scrollY + (mPaddingTop & inside);
right = left + size;
bottom = scrollY + height - (mUserPaddingBottom & inside);
onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom);
if (invalidate) {
invalidate(left, top, right, bottom);
}
}
}
}
}
/**
* Override this if the vertical scrollbar needs to be hidden in a subclass, like when
* FastScroller is visible.
* @return whether to temporarily hide the vertical scrollbar
* @hide
*/
protected boolean isVerticalScrollBarHidden() {
return false;
}
/**
* <p>Draw the horizontal scrollbar if
* {@link #isHorizontalScrollBarEnabled()} returns true.</p>
*
* @param canvas the canvas on which to draw the scrollbar
* @param scrollBar the scrollbar's drawable
*
* @see #isHorizontalScrollBarEnabled()
* @see #computeHorizontalScrollRange()
* @see #computeHorizontalScrollExtent()
* @see #computeHorizontalScrollOffset()
* @see android.widget.ScrollBarDrawable
* @hide
*/
protected void onDrawHorizontalScrollBar(GLCanvas canvas, Drawable scrollBar,
int l, int t, int r, int b) {
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
/**
* <p>Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()}
* returns true.</p>
*
* @param canvas the canvas on which to draw the scrollbar
* @param scrollBar the scrollbar's drawable
*
* @see #isVerticalScrollBarEnabled()
* @see #computeVerticalScrollRange()
* @see #computeVerticalScrollExtent()
* @see #computeVerticalScrollOffset()
* @see android.widget.ScrollBarDrawable
* @hide
*/
protected void onDrawVerticalScrollBar(GLCanvas canvas, Drawable scrollBar,
int l, int t, int r, int b) {
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
/**
* Implement this to do your drawing.
*
* @param canvas the canvas on which the background will be drawn
*/
protected void onDraw(GLCanvas canvas) {
}
/**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
* guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
* however it may be called any time before the first onDraw -- including
* before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
protected void onAttachedToWindow() {
if ((mPrivateFlags & PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH) != 0) {
initialAwakenScrollBars();
mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
}
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
}
/**
* Returns true if this view is currently attached to a window.
*/
public boolean isAttachedToWindow() {
return mAttachInfo != null;
}
/**
* Returns true if this view has been through at least one layout since it
* was last attached to or detached from a window.
*/
public boolean isLaidOut() {
return (mPrivateFlags3 & PFLAG3_IS_LAID_OUT) == PFLAG3_IS_LAID_OUT;
}
/**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw(android.graphics.Canvas)}
* you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
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
*/
public boolean willNotDraw() {
return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
}
protected void onDetachedFromWindow() {
}
/**
* This is a framework-internal mirror of onDetachedFromWindow() that's called
* after onDetachedFromWindow().
*
* If you override this you *MUST* call super.onDetachedFromWindowInternal()!
* The super method should be called at the end of the overriden method to ensure
* subclasses are destroyed first
*
* @hide
*/
protected void onDetachedFromWindowInternal() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
stopNestedScroll();
// Anything that started animating right before detach should already
// be in its final state when re-attached.
jumpDrawablesToCurrentState();
cleanupDraw();
mCurrentAnimation = null;
}
private void cleanupDraw() {
resetDisplayList();
if (mAttachInfo != null) {
// mAttachInfo.mViewRootImpl.cancelInvalidate(this);
}
}
/**
* Retrieve the {@link WindowId} for the window this view is
* currently attached to.
*/
public WindowId getWindowId() {
if (mAttachInfo == null) {
return null;
}
return mAttachInfo.mWindowId;
}
/**
* @hide
*/
public void dispatchStartTemporaryDetach() {
onStartTemporaryDetach();
}
/**
* This is called when a container is going to temporarily detach a child, with
* {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}.
* It will either be followed by {@link #onFinishTemporaryDetach()} or
* {@link #onDetachedFromWindow()} when the container is done.
*/
public void onStartTemporaryDetach() {
removeUnsetPressCallback();
mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
}
/**
* @hide
*/
public void dispatchFinishTemporaryDetach() {
onFinishTemporaryDetach();
}
/**
* Called after {@link #onStartTemporaryDetach} when the container is done
* changing the view.
*/
public void onFinishTemporaryDetach() {
}
// This is for debugging only.
// Dump the view hierarchy into log.
void dumpTree(String prefix) {
Log.d(VIEW_LOG_TAG, prefix + getClass().getSimpleName());
if(this instanceof ViewGroup){
ViewGroup parent = (ViewGroup)this;
int childCount = parent.mChildrenCount;
for (int i = 0; i < childCount; ++i) {
parent.mChildren[i].dumpTree(prefix + "....");
}
}
}
/**
* If a user manually specified the next view id for a particular direction,
* use the root to look up the view.
* @param root The root view of the hierarchy containing this view.
* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD,
* or FOCUS_BACKWARD.
* @return The user specified next view, or null if there is none.
*/
View findUserSetNextFocus(View root, int direction) {
switch (direction) {
case FOCUS_LEFT:
if (mNextFocusLeftId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusLeftId);
case FOCUS_RIGHT:
if (mNextFocusRightId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusRightId);
case FOCUS_UP:
if (mNextFocusUpId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusUpId);
case FOCUS_DOWN:
if (mNextFocusDownId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusDownId);
case FOCUS_FORWARD:
if (mNextFocusForwardId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusForwardId);
case FOCUS_BACKWARD: {
if (mID == View.NO_ID) return null;
final int id = mID;
return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
@Override
public boolean apply(View t) {
return t.mNextFocusForwardId == id;
}
});
}
}
return null;
}
private View findViewInsideOutShouldExist(View root, int id) {
if (mMatchIdPredicate == null) {
mMatchIdPredicate = new MatchIdPredicate();
}
mMatchIdPredicate.mId = id;
View result = root.findViewByPredicateInsideOut(this, mMatchIdPredicate);
if (result == null) {
Log.w(VIEW_LOG_TAG, "couldn't find view with id " + id);
}
return result;
}
/**
* Find and return all focusable views that are descendants of this view,
* possibly including this view if it is focusable itself.
*
* @param direction The direction of the focus
* @return A list of focusable views
*/
public ArrayList<View> getFocusables(int direction) {
ArrayList<View> result = new ArrayList<View>(24);
addFocusables(result, direction);
return result;
}
/**
* Add any focusable views that are descendants of this view (possibly
* including this view if it is focusable itself) to views. If we are in touch mode,
* only add views that are also focusable in touch mode.
*
* @param views Focusable views found so far
* @param direction The direction of the focus
*/
public void addFocusables(ArrayList<View> views, int direction) {
addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
}
/**
* Adds any focusable views that are descendants of this view (possibly
* including this view if it is focusable itself) to views. This method
* adds all focusable views regardless if we are in touch mode or
* only views focusable in touch mode if we are in touch mode or
* only views that can take accessibility focus if accessibility is enabeld
* depending on the focusable mode paramater.
*
* @param views Focusable views found so far or null if all we are interested is
* the number of focusables.
* @param direction The direction of the focus.
* @param focusableMode The type of focusables to be added.
*
* @see #FOCUSABLES_ALL
* @see #FOCUSABLES_TOUCH_MODE
*/
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
if (views == null) {
return;
}
if (!isFocusable()) {
return;
}
if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
&& isInTouchMode() && !isFocusableInTouchMode()) {
return;
}
views.add(this);
}
/**
* Invoked whenever this view loses focus, either by losing window focus or by losing
* focus within its window. This method can be used to clear any state tied to the
* focus. For instance, if a button is held pressed with the trackball and the window
* loses focus, this method can be used to cancel the press.
*
* Subclasses of View overriding this method should always call super.onFocusLost().
*
* @see #onFocusChanged(boolean, int, android.graphics.Rect)
* @see #onWindowFocusChanged(boolean)
*
* @hide pending API council approval
*/
protected void onFocusLost() {
resetPressedState();
}
private void resetPressedState() {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return;
}
if (isPressed()) {
setPressed(false);
}
}
/**
* Returns true if this view has focus
*
* @return True if this view has focus, false otherwise.
*/
public boolean isFocused() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
/**
* Returns whether this View is able to take focus.
*
* @return True if this view can take focus, or false otherwise.
*/
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
*/
public final boolean isFocusableInTouchMode() {
return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE);
}
/**
* 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;
}
/**
* Find and return all touchable views that are descendants of this view,
* possibly including this view if it is touchable itself.
*
* @return A list of touchable views
*/
public ArrayList<View> getTouchables() {
ArrayList<View> result = new ArrayList<View>();
addTouchables(result);
return result;
}
/**
* Add any touchable views that are descendants of this view (possibly
* including this view if it is touchable itself) to views.
*
* @param views Touchable views found so far
*/
public void addTouchables(ArrayList<View> views) {
final int viewFlags = mViewFlags;
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
&& (viewFlags & ENABLED_MASK) == ENABLED) {
views.add(this);
}
}
/**
* 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.
*/
public boolean hasFocus() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
/**
* Returns true if this view is focusable or if it contains a reachable View
* for which {@link #hasFocusable()} returns 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.
*
*/
public boolean hasFocusable() {
return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
}
/**
* 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 ViewGroup getParent() {
return mParent;
}
/**
* <p>Return the offset of the widget's text baseline from the widget's top
* boundary. If this widget does not support baseline alignment, this
* method returns -1. </p>
*
* @return the offset of the baseline within the widget's bounds or -1
* if baseline alignment is not supported
*/
public int getBaseline() {
return -1;
}
/**
* Set the background to a given Drawable, or remove the background. If the
* background has padding, this View's padding is set to the background's
* padding. However, when a background is removed, this View's padding isn't
* touched. If setting the padding is desired, please use
* {@link #setPadding(int, int, int, int)}.
*
* @param background The Drawable to use as the background, or null to remove the
* background
*/
public void setBackground(Drawable background) {
//noinspection deprecation
setBackgroundDrawable(background);
}
/**
* @deprecated use {@link #setBackground(Drawable)} instead
*/
@Deprecated
public void setBackgroundDrawable(Drawable background) {
computeOpaqueFlags();
if (background == mBackground) {
return;
}
boolean requestLayout = false;
mBackgroundResource = 0;
/*
* Regardless of whether we're setting a new background or not, we want
* to clear the previous drawable.
*/
if (mBackground != null) {
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
}
if (background != null) {
Rect padding = sThreadLocal.get();
if (padding == null) {
padding = new Rect();
sThreadLocal.set(padding);
}
resetResolvedDrawables();
background.setLayoutDirection(getLayoutDirection());
if (background.getPadding(padding)) {
resetResolvedPadding();
switch (background.getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
mUserPaddingLeftInitial = padding.right;
mUserPaddingRightInitial = padding.left;
internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
break;
case LAYOUT_DIRECTION_LTR:
default:
mUserPaddingLeftInitial = padding.left;
mUserPaddingRightInitial = padding.right;
internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
}
mLeftPaddingDefined = false;
mRightPaddingDefined = false;
}
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
// if it has a different minimum size, we should layout again
if (mBackground == null
|| mBackground.getMinimumHeight() != background.getMinimumHeight()
|| mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
background.setCallback(this);
if (background.isStateful()) {
background.setState(getDrawableState());
}
background.setVisible(getVisibility() == VISIBLE, false);
mBackground = background;
// applyBackgroundTint();
//
// if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
// mPrivateFlags &= ~PFLAG_SKIP_DRAW;
// mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;
// requestLayout = true;
// }
} else {
/* Remove the background */
mBackground = null;
// if ((mPrivateFlags & PFLAG_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 &= ~PFLAG_ONLY_DRAWS_BACKGROUND;
// mPrivateFlags |= PFLAG_SKIP_DRAW;
// }
/*
* When the background is set, we try to apply its padding to this
* View. When the background is removed, we don't touch this View's
* padding. This is noted in the Javadocs. Hence, we don't need to
* requestLayout(), the invalidate() below is sufficient.
*/
// The old background's minimum size could have affected this
// View's layout, so let's requestLayout
requestLayout = true;
}
computeOpaqueFlags();
if (requestLayout) {
requestLayout();
}
mBackgroundSizeChanged = true;
invalidate(true);
}
/**
* Gets the background drawable
*
* @return The drawable used as the background for this view, if any.
*
* @see #setBackground(Drawable)
*
* @attr ref android.R.styleable#View_background
*/
public Drawable getBackground() {
return mBackground;
}
public void setBackgroundColor(int color) {
if (mBackground instanceof ColorDrawable) {
((ColorDrawable) mBackground.mutate()).setColor(color);
computeOpaqueFlags();
mBackgroundResource = 0;
} else {
setBackground(new ColorDrawable(color));
}
}
/**
* Set the background to a given resource. The resource should refer to
* a Drawable object or 0 to remove the background.
* @param resid The identifier of the resource.
*
*/
public void setBackgroundResource(int resid) {
if (resid == mBackgroundResource) {
return;
}
Drawable d= null;
if (resid != 0) {
d = GLContext.get().getResources().getDrawable(resid);
}
setBackground(d);
mBackgroundResource = resid;
}
/**
* 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;
}
/**
* Find the view in the hierarchy rooted at this view that currently has
* focus.
*
* @return The view that currently has focus, or null if no focused view can
* be found.
*/
public View findFocus() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0 ? this : null;
}
/**
* Called internally by the view system when a new view is getting focus.
* This is what clears the old focus.
*/
void unFocus() {
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
onFocusChanged(false, 0, null);
refreshDrawableState();
}
}
/**
* Called by the view system when the focus state of this view changes.
* When the focus change event is caused by directional navigation, direction
* and previouslyFocusedRect provide insight into where the focus is coming from.
* When overriding, be sure to call up through to the super class so that
* the standard focus handling will occur.
*
* @param gainFocus True if the View has focus; false otherwise.
* @param direction The direction focus has moved when requestFocus()
* is called to give this view focus. Values are
* {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT},
* {@link #FOCUS_RIGHT}, {@link #FOCUS_FORWARD}, or {@link #FOCUS_BACKWARD}.
* It may not always apply, in which case use the default.
* @param previouslyFocusedRect The rectangle, in this view's coordinate
* system, of the previously focused view. If applicable, this will be
* passed in as finer grained information about where the focus is coming
* from (in addition to direction). Will be <code>null</code> otherwise.
*/
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnFocusChangeListener != null) {
li.mOnFocusChangeListener.onFocusChange(this, gainFocus);
}
}
/**
* Set whether this view can receive the focus.
*
* Setting this to false will also ensure that this view is not focusable
* in touch mode.
*
* @param focusable If true, this view can receive the focus.
*
*/
public void setFocusable(boolean focusable) {
if (!focusable) {
setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
}
setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);
}
/**
* Set whether this view can receive focus while in touch mode.
*
* Setting this to true will also ensure that this view is focusable.
*
* @param focusableInTouchMode If true, this view can receive the focus while
* in touch mode.
*
* @see #setFocusable(boolean)
* @attr ref android.R.styleable#View_focusableInTouchMode
*/
public void setFocusableInTouchMode(boolean focusableInTouchMode) {
// Focusable in touch mode should always be set before the focusable flag
// otherwise, setting the focusable flag will trigger a focusableViewAvailable()
// which, in touch mode, will not successfully request focus on this view
// because the focusable in touch mode flag is not set
setFlags(focusableInTouchMode ? FOCUSABLE_IN_TOUCH_MODE : 0, FOCUSABLE_IN_TOUCH_MODE);
if (focusableInTouchMode) {
setFlags(FOCUSABLE, FOCUSABLE_MASK);
}
}
/**
* Returns whether the screen should remain on, corresponding to the current
* value of {@link #KEEP_SCREEN_ON}.
*
* @return Returns true if {@link #KEEP_SCREEN_ON} is set.
*
* @see #setKeepScreenOn(boolean)
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
public boolean getKeepScreenOn() {
return (mViewFlags & KEEP_SCREEN_ON) != 0;
}
/**
* Controls whether the screen should remain on, modifying the
* value of {@link #KEEP_SCREEN_ON}.
*
* @param keepScreenOn Supply true to set {@link #KEEP_SCREEN_ON}.
*
* @see #getKeepScreenOn()
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
public void setKeepScreenOn(boolean keepScreenOn) {
setFlags(keepScreenOn ? KEEP_SCREEN_ON : 0, KEEP_SCREEN_ON);
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_LEFT}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusLeft
*/
public int getNextFocusLeftId() {
return mNextFocusLeftId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_LEFT}.
* @param nextFocusLeftId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusLeft
*/
public void setNextFocusLeftId(int nextFocusLeftId) {
mNextFocusLeftId = nextFocusLeftId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusRight
*/
public int getNextFocusRightId() {
return mNextFocusRightId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}.
* @param nextFocusRightId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusRight
*/
public void setNextFocusRightId(int nextFocusRightId) {
mNextFocusRightId = nextFocusRightId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_UP}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusUp
*/
public int getNextFocusUpId() {
return mNextFocusUpId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_UP}.
* @param nextFocusUpId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusUp
*/
public void setNextFocusUpId(int nextFocusUpId) {
mNextFocusUpId = nextFocusUpId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_DOWN}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusDown
*/
public int getNextFocusDownId() {
return mNextFocusDownId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_DOWN}.
* @param nextFocusDownId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusDown
*/
public void setNextFocusDownId(int nextFocusDownId) {
mNextFocusDownId = nextFocusDownId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusForward
*/
public int getNextFocusForwardId() {
return mNextFocusForwardId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}.
* @param nextFocusForwardId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusForward
*/
public void setNextFocusForwardId(int nextFocusForwardId) {
mNextFocusForwardId = nextFocusForwardId;
}
/**
* Returns the visibility of this view and all of its ancestors
*
* @return True if this view and all of its ancestors are {@link #VISIBLE}
*/
public boolean isShown() {
View current = this;
//noinspection ConstantConditions
do {
if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
ViewGroup parent = current.mParent;
if (parent == null) {
return false; // We are not attached to the view root
}
if (!(parent instanceof View)) {
return true;
}
current = (View) parent;
} while (current != null);
return false;
}
/**
* Set whether this view should have sound effects enabled for events such as
* clicking and touching.
*
* <p>You may wish to disable sound effects for a view if you already play sounds,
* for instance, a dial key that plays dtmf tones.
*
* @param soundEffectsEnabled whether sound effects are enabled for this view.
* @see #isSoundEffectsEnabled()
* @see #playSoundEffect(int)
* @attr ref android.R.styleable#View_soundEffectsEnabled
*/
public void setSoundEffectsEnabled(boolean soundEffectsEnabled) {
setFlags(soundEffectsEnabled ? SOUND_EFFECTS_ENABLED: 0, SOUND_EFFECTS_ENABLED);
}
/**
* @return whether this view should have sound effects enabled for events such as
* clicking and touching.
*
* @see #setSoundEffectsEnabled(boolean)
* @see #playSoundEffect(int)
* @attr ref android.R.styleable#View_soundEffectsEnabled
*/
public boolean isSoundEffectsEnabled() {
return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
}
/**
* Set whether this view should have haptic feedback for events such as
* long presses.
*
* <p>You may wish to disable haptic feedback if your view already controls
* its own haptic feedback.
*
* @param hapticFeedbackEnabled whether haptic feedback enabled for this view.
* @see #isHapticFeedbackEnabled()
* @see #performHapticFeedback(int)
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
*/
public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED);
}
/**
* @return whether this view should have haptic feedback enabled for events
* long presses.
*
* @see #setHapticFeedbackEnabled(boolean)
* @see #performHapticFeedback(int)
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
*/
public boolean isHapticFeedbackEnabled() {
return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
}
/**
* 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
*/
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 state for this view and provides a touch coordinate for
* animation hinting.
*
* @param pressed Pass true to set the View's internal state to "pressed",
* or false to reverts the View's internal state from a
* previously set "pressed" state.
* @param x The x coordinate of the touch that caused the press
* @param y The y coordinate of the touch that caused the press
*/
private void setPressed(boolean pressed, float x, float y) {
if (pressed) {
// drawableHotspotChanged(x, y);
}
setPressed(pressed);
}
/**
* Sets the pressed state for this view.
*
* @see #isClickable()
* @see #setClickable(boolean)
*
* @param pressed Pass true to set the View's internal state to "pressed", or false to reverts
* the View's internal state from a previously set "pressed" state.
*/
public void setPressed(boolean pressed) {
final boolean needsRefresh = pressed != ((mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED);
if (pressed) {
mPrivateFlags |= PFLAG_PRESSED;
} else {
mPrivateFlags &= ~PFLAG_PRESSED;
}
if (needsRefresh) {
refreshDrawableState();
}
dispatchSetPressed(pressed);
}
/**
* Dispatch setPressed to all of this View's children.
*
* @see #setPressed(boolean)
*
* @param pressed The new pressed state
*/
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(boolean)
* @see #isClickable()
* @see #setClickable(boolean)
*
* @return true if the view is currently pressed, false otherwise
*/
public boolean isPressed() {
return (mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED;
}
/**
* 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;
}
/* Check if the FOCUSABLE bit has changed */
int privateFlags = mPrivateFlags;
/* Check if the FOCUSABLE bit has changed */
if (((changed & FOCUSABLE_MASK) != 0) &&
((privateFlags & PFLAG_HAS_BOUNDS) !=0)) {
if (((old & FOCUSABLE_MASK) == FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) != 0)) {
/* Give up focus if we are no longer focusable */
clearFocus();
} else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) == 0)) {
/*
* Tell the view system that we are now available to take focus
* if no one else already has it.
*/
if (mParent != null) mParent.focusableViewAvailable(this);
}
}
final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE) {
if ((changed & VISIBILITY_MASK) != 0) {
/*
* If this view is becoming visible, invalidate it in case it changed while
* it was not visible. Marking it drawn ensures that the invalidation will
* go through.
*/
invalidate(true);
needGlobalAttributesUpdate(true);
// a view becoming visible is worth notifying the parent
// about in case nothing has focus. even if this specific view
// isn't focusable, it may contain something that is, so let
// the root view try to give this focus if nothing else does.
if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {
mParent.focusableViewAvailable(this);
}
}
}
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
requestLayout();
if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
if (hasFocus()) clearFocus();
if (mParent instanceof View) {
// GONE views noop invalidation, so invalidate the parent
((View) mParent).invalidate(true);
}
// Mark the view drawn to ensure that it gets invalidated properly the next
// time it is visible and gets invalidated
// mPrivateFlags |= PFLAG_DRAWN;
}
if (mAttachInfo != null) {
// mAttachInfo.mViewVisibilityChanged = true;
}
}
if ((changed & VISIBILITY_MASK) != 0) {
// If the view is invisible, cleanup its display list to free up resources
if (newVisibility != VISIBLE) {
cleanupDraw();
}
if (mParent != null) {
mParent.onChildVisibilityChanged(this,
(changed & VISIBILITY_MASK), newVisibility);
mParent.invalidate();
}
dispatchVisibilityChanged(this, newVisibility);
}
}
/**
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* <p>
* <strong>Note:</strong> When a View clears focus the framework is trying
* to give focus to the first focusable View from the top. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
* </p>
*/
public void clearFocus() {
if (DBG) {
System.out.println(this + " clearFocus()");
}
clearFocusInternal(null, true, true);
}
/**
* Clears focus from the view, optionally propagating the change up through
* the parent hierarchy and requesting that the root view place new focus.
*
* @param propagate whether to propagate the change up through the parent
* hierarchy
* @param refocus when propagate is true, specifies whether to request the
* root view place new focus
*/
void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
}
onFocusChanged(false, 0, null);
refreshDrawableState();
if (propagate && (!refocus || !rootViewRequestFocus())) {
notifyGlobalFocusCleared(this);
}
}
}
void notifyGlobalFocusCleared(View oldFocus) {
if (oldFocus != null && mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
}
}
boolean rootViewRequestFocus() {
final View root = getRootView();
return root != null && root.requestFocus();
}
/**
* Called internally by the view system when a new view is getting focus.
* This is what clears the old focus.
* <p>
* <b>NOTE:</b> The parent view's focused child must be updated manually
* after calling this method. Otherwise, the view hierarchy may be left in
* an inconstent state.
*/
void unFocus(View focused) {
if (DBG) {
System.out.println(this + " unFocus()");
}
clearFocusInternal(focused, false, false);
}
/**
* Call this to try to give focus to a specific view or to one of its
* descendants.
*
* A view will not actually take focus if it is not focusable ({@link #isFocusable} returns
* false), or if it is focusable and it is not focusable in touch mode
* ({@link #isFocusableInTouchMode}) while the device is in touch mode.
*
* See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* This is equivalent to calling {@link #requestFocus(int, Rect)} with arguments
* {@link #FOCUS_DOWN} and <code>null</code>.
*
* @return Whether this view or one of its descendants actually took focus.
*/
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(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* This is equivalent to calling {@link #requestFocus(int, Rect)} with
* <code>null</code> set for the previously focused rectangle.
*
* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
* @return Whether this view or one of its descendants actually took focus.
*/
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
* equal to
*
* See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* You may wish to override this method if your custom {@link View} has an internal
* {@link View} that it wishes to forward the request to.
*
* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
* @param previouslyFocusedRect The rectangle (in this View's coordinate system)
* to give a finer grained hint about where focus is coming from. May be null
* if there is no hint.
* @return Whether this view or one of its descendants actually took focus.
*/
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
return requestFocusNoSearch(direction, previouslyFocusedRect);
}
private boolean requestFocusNoSearch(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;
}
/**
* @return Whether any ancestor of this view blocks descendant focus.
*/
private boolean hasAncestorThatBlocksDescendantFocus() {
final boolean focusableInTouchMode = isFocusableInTouchMode();
ViewGroup 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;
}
/**
* Give this view focus. This will cause
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} to be called.
*
* Note: this does not check whether this {@link View} should get focus, it just
* gives it focus no matter what. It should only be called internally by framework
* code that knows what it is doing, namely {@link #requestFocus(int, Rect)}.
*
* @param direction values are {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
* {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT}. This is the direction which
* focus moved when requestFocus() is called. It may not always
* apply, in which case use the default View.FOCUS_DOWN.
* @param previouslyFocusedRect The rectangle of the view that had focus
* prior in this View's coordinate system.
*/
void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
mPrivateFlags |= PFLAG_FOCUSED;
View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
if (mParent != null) {
mParent.requestChildFocus(this, this);
}
if (mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
}
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
}
}
/**
* Scroll the view with standard behavior for scrolling beyond the normal
* content boundaries. Views that call this method should override
* {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
* results of an over-scroll operation.
*
* Views can use this method to handle any touch or fling-based scrolling.
*
* @param deltaX Change in X in pixels
* @param deltaY Change in Y in pixels
* @param scrollX Current X scroll value in pixels before applying deltaX
* @param scrollY Current Y scroll value in pixels before applying deltaY
* @param scrollRangeX Maximum content scroll range along the X axis
* @param scrollRangeY Maximum content scroll range along the Y axis
* @param maxOverScrollX Number of pixels to overscroll by in either direction
* along the X axis.
* @param maxOverScrollY Number of pixels to overscroll by in either direction
* along the Y axis.
* @param isTouchEvent true if this scroll operation is the result of a touch event.
* @return true if scrolling was clamped to an over-scroll boundary along either
* axis, false otherwise.
*/
@SuppressWarnings({"UnusedParameters"})
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
final int overScrollMode = mOverScrollMode;
final boolean canScrollHorizontal =
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
final boolean canScrollVertical =
computeVerticalScrollRange() > computeVerticalScrollExtent();
final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS ||
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
int newScrollX = scrollX + deltaX;
if (!overScrollHorizontal) {
maxOverScrollX = 0;
}
int newScrollY = scrollY + deltaY;
if (!overScrollVertical) {
maxOverScrollY = 0;
}
// Clamp values if at the limits and record
final int left = -maxOverScrollX;
final int right = maxOverScrollX + scrollRangeX;
final int top = -maxOverScrollY;
final int bottom = maxOverScrollY + scrollRangeY;
boolean clampedX = false;
if (newScrollX > right) {
newScrollX = right;
clampedX = true;
} else if (newScrollX < left) {
newScrollX = left;
clampedX = true;
}
boolean clampedY = false;
if (newScrollY > bottom) {
newScrollY = bottom;
clampedY = true;
} else if (newScrollY < top) {
newScrollY = top;
clampedY = true;
}
onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
return clampedX || clampedY;
}
/**
* Called by {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)} to
* respond to the results of an over-scroll operation.
*
* @param scrollX New X scroll value in pixels
* @param scrollY New Y scroll value in pixels
* @param clampedX True if scrollX was clamped to an over-scroll boundary
* @param clampedY True if scrollY was clamped to an over-scroll boundary
*/
protected void onOverScrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
// Intentionally empty.
}
/**
* Returns the over-scroll mode for this view. The result will be
* one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
* (allow over-scrolling only if the view content is larger than the container),
* or {@link #OVER_SCROLL_NEVER}.
*
* @return This view's over-scroll mode.
*/
public int getOverScrollMode() {
return mOverScrollMode;
}
/**
* Set the over-scroll mode for this view. Valid over-scroll modes are
* {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
* (allow over-scrolling only if the view content is larger than the container),
* or {@link #OVER_SCROLL_NEVER}.
*
* Setting the over-scroll mode of a view will have an effect only if the
* view is capable of scrolling.
*
* @param overScrollMode The new over-scroll mode for this view.
*/
public void setOverScrollMode(int overScrollMode) {
if (overScrollMode != OVER_SCROLL_ALWAYS &&
overScrollMode != OVER_SCROLL_IF_CONTENT_SCROLLS &&
overScrollMode != OVER_SCROLL_NEVER) {
throw new IllegalArgumentException("Invalid overscroll mode " + overScrollMode);
}
mOverScrollMode = overScrollMode;
}
/**
* Enable or disable nested scrolling for this view.
*
* <p>If this property is set to true the view will be permitted to initiate nested
* scrolling operations with a compatible parent view in the current hierarchy. If this
* view does not implement nested scrolling this will have no effect. Disabling nested scrolling
* while a nested scroll is in progress has the effect of {@link #stopNestedScroll() stopping}
* the nested scroll.</p>
*
* @param enabled true to enable nested scrolling, false to disable
*
* @see #isNestedScrollingEnabled()
*/
public void setNestedScrollingEnabled(boolean enabled) {
if (enabled) {
mPrivateFlags3 |= PFLAG3_NESTED_SCROLLING_ENABLED;
} else {
stopNestedScroll();
mPrivateFlags3 &= ~PFLAG3_NESTED_SCROLLING_ENABLED;
}
}
/**
* Returns true if nested scrolling is enabled for this view.
*
* <p>If nested scrolling is enabled and this View class implementation supports it,
* this view will act as a nested scrolling child view when applicable, forwarding data
* about the scroll operation in progress to a compatible and cooperating nested scrolling
* parent.</p>
*
* @return true if nested scrolling is enabled
*
* @see #setNestedScrollingEnabled(boolean)
*/
public boolean isNestedScrollingEnabled() {
return (mPrivateFlags3 & PFLAG3_NESTED_SCROLLING_ENABLED) ==
PFLAG3_NESTED_SCROLLING_ENABLED;
}
/**
* Begin a nestable scroll operation along the given axes.
*
* <p>A view starting a nested scroll promises to abide by the following contract:</p>
*
* <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
* of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
* In the case of touch scrolling the nested scroll will be terminated automatically in
* the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
* In the event of programmatic scrolling the caller must explicitly call
* {@link #stopNestedScroll()} to indicate the end of the nested scroll.</p>
*
* <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
* If it returns false the caller may ignore the rest of this contract until the next scroll.
* Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
*
* <p>At each incremental step of the scroll the caller should invoke
* {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll}
* once it has calculated the requested scrolling delta. If it returns true the nested scrolling
* parent at least partially consumed the scroll and the caller should adjust the amount it
* scrolls by.</p>
*
* <p>After applying the remainder of the scroll delta the caller should invoke
* {@link #dispatchNestedScroll(int, int, int, int, int[]) dispatchNestedScroll}, passing
* both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
* these values differently. See {@link ViewParent#onNestedScroll(View, int, int, int, int)}.
* </p>
*
* @param axes Flags consisting of a combination of {@link #SCROLL_AXIS_HORIZONTAL} and/or
* {@link #SCROLL_AXIS_VERTICAL}.
* @return true if a cooperative parent was found and nested scrolling has been enabled for
* the current gesture.
*
* @see #stopNestedScroll()
* @see #dispatchNestedPreScroll(int, int, int[], int[])
* @see #dispatchNestedScroll(int, int, int, int, int[])
*/
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {
ViewGroup p = getParent();
View child = this;
while (p != null) {
try {
if (p.onStartNestedScroll(child, this, axes)) {
mNestedScrollingParent = p;
p.onNestedScrollAccepted(child, this, axes);
return true;
}
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, "ViewParent " + p + " does not implement interface " +
"method onStartNestedScroll", e);
// Allow the search upward to continue
}
if (p instanceof View) {
child = (View) p;
}
p = p.getParent();
}
}
return false;
}
/**
* Stop a nested scroll in progress.
*
* <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
*
* @see #startNestedScroll(int)
*/
public void stopNestedScroll() {
if (mNestedScrollingParent != null) {
mNestedScrollingParent.onStopNestedScroll(this);
mNestedScrollingParent = null;
}
}
/**
* Returns true if this view has a nested scrolling parent.
*
* <p>The presence of a nested scrolling parent indicates that this view has initiated
* a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
*
* @return whether this view has a nested scrolling parent
*/
public boolean hasNestedScrollingParent() {
return mNestedScrollingParent != null;
}
/**
* Dispatch one step of a nested scroll in progress.
*
* <p>Implementations of views that support nested scrolling should call this to report
* info about a scroll in progress to the current nested scrolling parent. If a nested scroll
* is not currently in progress or nested scrolling is not
* {@link #isNestedScrollingEnabled() enabled} for this view this method does nothing.</p>
*
* <p>Compatible View implementations should also call
* {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll} before
* consuming a component of the scroll event themselves.</p>
*
* @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
* @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
* @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
* @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
* @param offsetInWindow Optional. If not null, on return this will contain the offset
* in local view coordinates of this view from before this operation
* to after it completes. View implementations may use this to adjust
* expected input coordinate tracking.
* @return true if the event was dispatched, false if it could not be dispatched.
* @see #dispatchNestedPreScroll(int, int, int[], int[])
*/
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
int startX = 0;
int startY = 0;
if (offsetInWindow != null) {
getLocationInWindow(offsetInWindow);
startX = offsetInWindow[0];
startY = offsetInWindow[1];
}
mNestedScrollingParent.onNestedScroll(this, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
if (offsetInWindow != null) {
getLocationInWindow(offsetInWindow);
offsetInWindow[0] -= startX;
offsetInWindow[1] -= startY;
}
return true;
} else if (offsetInWindow != null) {
// No motion, no dispatch. Keep offsetInWindow up to date.
offsetInWindow[0] = 0;
offsetInWindow[1] = 0;
}
}
return false;
}
/**
* Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
*
* <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
* <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested
* scrolling operation to consume some or all of the scroll operation before the child view
* consumes it.</p>
*
* @param dx Horizontal scroll distance in pixels
* @param dy Vertical scroll distance in pixels
* @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
* and consumed[1] the consumed dy.
* @param offsetInWindow Optional. If not null, on return this will contain the offset
* in local view coordinates of this view from before this operation
* to after it completes. View implementations may use this to adjust
* expected input coordinate tracking.
* @return true if the parent consumed some or all of the scroll delta
* @see #dispatchNestedScroll(int, int, int, int, int[])
*/
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
if (dx != 0 || dy != 0) {
int startX = 0;
int startY = 0;
if (offsetInWindow != null) {
getLocationInWindow(offsetInWindow);
startX = offsetInWindow[0];
startY = offsetInWindow[1];
}
if (consumed == null) {
if (mTempNestedScrollConsumed == null) {
mTempNestedScrollConsumed = new int[2];
}
consumed = mTempNestedScrollConsumed;
}
consumed[0] = 0;
consumed[1] = 0;
mNestedScrollingParent.onNestedPreScroll(this, dx, dy, consumed);
if (offsetInWindow != null) {
getLocationInWindow(offsetInWindow);
offsetInWindow[0] -= startX;
offsetInWindow[1] -= startY;
}
return consumed[0] != 0 || consumed[1] != 0;
} else if (offsetInWindow != null) {
offsetInWindow[0] = 0;
offsetInWindow[1] = 0;
}
}
return false;
}
/**
* Dispatch a fling to a nested scrolling parent.
*
* <p>This method should be used to indicate that a nested scrolling child has detected
* suitable conditions for a fling. Generally this means that a touch scroll has ended with a
* {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
* the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
* along a scrollable axis.</p>
*
* <p>If a nested scrolling child view would normally fling but it is at the edge of
* its own content, it can use this method to delegate the fling to its nested scrolling
* parent instead. The parent may optionally consume the fling or observe a child fling.</p>
*
* @param velocityX Horizontal fling velocity in pixels per second
* @param velocityY Vertical fling velocity in pixels per second
* @param consumed true if the child consumed the fling, false otherwise
* @return true if the nested scrolling parent consumed or otherwise reacted to the fling
*/
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
return mNestedScrollingParent.onNestedFling(this, velocityX, velocityY, consumed);
}
return false;
}
/**
* Dispatch a fling to a nested scrolling parent before it is processed by this view.
*
* <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
* and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
* offsets an opportunity for the parent view in a nested fling to fully consume the fling
* before the child view consumes it. If this method returns <code>true</code>, a nested
* parent view consumed the fling and this view should not scroll as a result.</p>
*
* <p>For a better user experience, only one view in a nested scrolling chain should consume
* the fling at a time. If a parent view consumed the fling this method will return false.
* Custom view implementations should account for this in two ways:</p>
*
* <ul>
* <li>If a custom view is paged and needs to settle to a fixed page-point, do not
* call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
* position regardless.</li>
* <li>If a nested parent does consume the fling, this view should not scroll at all,
* even to settle back to a valid idle position.</li>
* </ul>
*
* <p>Views should also not offer fling velocities to nested parent views along an axis
* where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
* should not offer a horizontal fling velocity to its parents since scrolling along that
* axis is not permitted and carrying velocity along that motion does not make sense.</p>
*
* @param velocityX Horizontal fling velocity in pixels per second
* @param velocityY Vertical fling velocity in pixels per second
* @return true if a nested scrolling parent consumed the fling
*/
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
return mNestedScrollingParent.onNestedPreFling(this, velocityX, velocityY);
}
return false;
}
/**
* 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);
}
/**
* Determines whether the given point, in local coordinates is inside the view.
*/
/*package*/ final boolean pointInView(float localX, float localY) {
return localX >= 0 && localX < (mRight - mLeft)
&& localY >= 0 && localY < (mBottom - mTop);
}
/**
* Utility method to determine whether the given point, in local coordinates,
* is inside the view, where the area of the view is expanded by the slop factor.
* This method is called while processing touch-move events to determine if the event
* is still within the view.
*
* @hide
*/
public boolean pointInView(float localX, float localY, float slop) {
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
/**
* When a view has focus and the user navigates away from it, the next view is searched for
* starting from the rectangle filled in by this method.
*
* By default, the rectangle is the {@link #getDrawingRect(android.graphics.Rect)})
* of the view. However, if your view maintains some idea of internal selection,
* such as a cursor, or a selected row or column, you should override this method and
* fill in a more specific rectangle.
*
* @param r The rectangle to fill in, in this view's coordinates.
*/
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 (without taking possible View rotations into account), 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);
}
int[] location = new int[2];
getLocationInWindow(location);
r.offset(location[0], location[1]);
// return mParent == null || mParent.getChildVisibleRect(this, r, globalOffset);
return true;
}
return false;
}
public final boolean getGlobalVisibleRect(Rect r) {
return getGlobalVisibleRect(r, null);
}
/**
* 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) {
if (offset != 0) {
mTop += offset;
mBottom += offset;
mRenderNode.setTop(mTop);
mRenderNode.setBottom(mBottom);
invalidate();
}
}
/**
* Offset this view's horizontal location by the specified amount of pixels.
*
* @param offset the number of pixels to offset the view by
*/
public void offsetLeftAndRight(int offset) {
if (offset != 0) {
mLeft += offset;
mRight += offset;
mRenderNode.setLeft(mLeft);
mRenderNode.setRight(mRight);
invalidate();
}
}
public ViewGroup.LayoutParams getLayoutParams() {
return mLayoutParams;
}
/**
* Set the layout parameters associated with this view. These supply
* parameters to the <i>parent</i> of this view specifying how it should be
* arranged. There are many subclasses of GLViewGroup.GLLayoutParams, and these
* correspond to the different subclasses of GLViewGroup that are responsible
* for arranging their children.
*
* @param params The layout parameters for this view, cannot be null
*/
public void setLayoutParams(ViewGroup.LayoutParams params){
if (params == null) {
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
resolveLayoutParams();
// mParent.onSetLayoutParams(this, params);
requestLayout();
}
/**
* Resolve the layout parameters depending on the resolved layout direction
*
* @hide
*/
public void resolveLayoutParams() {
if (mLayoutParams != null) {
mLayoutParams.resolveLayoutDirection(getLayoutDirection());
}
}
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*
* <p>Even if the subclass overrides onFinishInflate, they should always be
* sure to call the super method, so that we get called.
*/
protected void onFinishInflate() {
}
/**
* Returns the resources associated with this view.
*
* @return Resources object.
*/
public GLResources getResources() {
return GLContext.get().getResources();
}
/**
* 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 |= PFLAG_DRAWABLE_STATE_DIRTY;
drawableStateChanged();
ViewGroup parent = mParent;
if (parent != null) {
parent.childDrawableStateChanged(this);
}
}
/**
* This function is called whenever the state of the view changes in such
* a way that it impacts the state of drawables being shown.
*
* <p>Be sure to call through to the superclass when overriding this
* function.
*
* @see Drawable#setState(int[])
*/
protected void drawableStateChanged() {
final Drawable d = mBackground;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
if (mStateListAnimator != null) {
mStateListAnimator.setState(getDrawableState());
}
}
/**
* Return an array of resource IDs of the drawable states representing the
* current state of the view.
*
* @return The current drawable state
*
* @see Drawable#setState(int[])
* @see #drawableStateChanged()
* @see #onCreateDrawableState(int)
*/
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY) == 0)) {
return mDrawableState;
} else {
mDrawableState = onCreateDrawableState(0);
mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY;
return mDrawableState;
}
}
/**
* Generate the new {@link android.graphics.drawable.Drawable} state for
* this view. This is called by the view
* system when the cached Drawable state is determined to be invalid. To
* retrieve the current state, you should use {@link #getDrawableState}.
*
* @param extraSpace if non-zero, this is the number of extra entries you
* would like in the returned array in which you can place your own
* states.
*
* @return Returns an array holding the current {@link Drawable} state of
* the view.
*
* @see #mergeDrawableStates(int[], int[])
*/
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 = 0;
if ((privateFlags & PFLAG_PRESSED) != 0) viewStateIndex |= VIEW_STATE_PRESSED;
if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= VIEW_STATE_ENABLED;
if (isFocused()) viewStateIndex |= VIEW_STATE_FOCUSED;
if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= VIEW_STATE_SELECTED;
if (hasWindowFocus()) viewStateIndex |= VIEW_STATE_WINDOW_FOCUSED;
if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= VIEW_STATE_ACTIVATED;
// if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
// HardwareRenderer.isAvailable()) {
// // This is set if HW acceleration is requested, even if the current
// // process doesn't allow it. This is just to allow app preview
// // windows to better match their app.
// viewStateIndex |= VIEW_STATE_ACCELERATED;
// }
if ((privateFlags & PFLAG_HOVERED) != 0) viewStateIndex |= VIEW_STATE_HOVERED;
final int privateFlags2 = mPrivateFlags2;
if ((privateFlags2 & PFLAG2_DRAG_CAN_ACCEPT) != 0) viewStateIndex |= VIEW_STATE_DRAG_CAN_ACCEPT;
if ((privateFlags2 & PFLAG2_DRAG_HOVERED) != 0) viewStateIndex |= VIEW_STATE_DRAG_HOVERED;
drawableState = VIEW_STATE_SETS[viewStateIndex];
//noinspection ConstantIfStatement
// if (false) {
// Log.i("View", "drawableStateIndex=" + viewStateIndex);
// Log.i("View", toString()
// + " pressed=" + ((privateFlags & PFLAG_PRESSED) != 0)
// + " en=" + ((mViewFlags & ENABLED_MASK) == ENABLED)
// + " fo=" + hasFocus()
// + " sl=" + ((privateFlags & PFLAG_SELECTED) != 0)
// + " wf=" + hasWindowFocus()
// + ": " + Arrays.toString(drawableState));
// }
if (extraSpace == 0) {
return drawableState;
}
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;
}
/**
* If your view subclass is displaying its own Drawable objects, it should
* override this function and return true for any Drawable it is
* displaying. This allows animations for those drawables to be
* scheduled.
*
* <p>Be sure to call through to the super class when overriding this
* function.
*
* @param who The Drawable to verify. Return true if it is one you are
* displaying, else return the result of calling through to the
* super class.
*
* @return boolean If true than the Drawable is being displayed in the
* view; else false and it is not allowed to animate.
*
* @see #unscheduleDrawable(android.graphics.drawable.Drawable)
* @see #drawableStateChanged()
*/
protected boolean verifyDrawable(Drawable who) {
return who == mBackground;
}
/**
* Merge your own state values in <var>additionalState</var> into the base
* state values <var>baseState</var> that were returned by
* {@link #onCreateDrawableState(int)}.
*
* @param baseState The base state values returned by
* {@link #onCreateDrawableState(int)}, which will be modified to also hold your
* own additional state values.
*
* @param additionalState The additional state values you would like
* added to <var>baseState</var>; this array is not modified.
*
* @return As a convenience, the <var>baseState</var> array you originally
* passed into the function is returned.
*
* @see #onCreateDrawableState(int)
*/
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;
}
/**
* Returns the layout direction for this view.
*
* @return One of {@link #LAYOUT_DIRECTION_LTR},
* {@link #LAYOUT_DIRECTION_RTL},
* {@link #LAYOUT_DIRECTION_INHERIT} or
* {@link #LAYOUT_DIRECTION_LOCALE}.
*
* @attr ref android.R.styleable#View_layoutDirection
*
* @hide
*/
public int getRawLayoutDirection() {
return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
}
/**
* Set the layout direction for this view. This will propagate a reset of layout direction
* resolution to the view's children and resolve layout direction for this view.
*
* @param layoutDirection the layout direction to set. Should be one of:
*
* {@link #LAYOUT_DIRECTION_LTR},
* {@link #LAYOUT_DIRECTION_RTL},
* {@link #LAYOUT_DIRECTION_INHERIT},
* {@link #LAYOUT_DIRECTION_LOCALE}.
*
* Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution
* proceeds up the parent chain of the view to get the value. If there is no parent, then it
* will return the default {@link #LAYOUT_DIRECTION_LTR}.
*
* @attr ref android.R.styleable#View_layoutDirection
*/
public void setLayoutDirection(int layoutDirection) {
if (getRawLayoutDirection() != layoutDirection) {
// Reset the current layout direction and the resolved one
mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_MASK;
resetRtlProperties();
// Set the new layout direction (filtered)
mPrivateFlags2 |=
((layoutDirection << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) & PFLAG2_LAYOUT_DIRECTION_MASK);
// We need to resolve all RTL properties as they all depend on layout direction
resolveRtlPropertiesIfNeeded();
requestLayout();
invalidate(true);
}
}
/**
* Reset resolution of all RTL related properties.
*
* @hide
*/
public void resetRtlProperties() {
resetResolvedLayoutDirection();
resetResolvedPadding();
resetResolvedDrawables();
}
/**
* Called when layout direction has been resolved.
*
* The default implementation does nothing.
*
* @param layoutDirection The resolved layout direction.
*
* @see #LAYOUT_DIRECTION_LTR
* @see #LAYOUT_DIRECTION_RTL
*
* @hide
*/
public void onResolveDrawables(int layoutDirection) {
}
/**
* @hide
*/
protected void resetResolvedDrawables() {
mPrivateFlags2 &= ~PFLAG2_DRAWABLE_RESOLVED;
}
private boolean isDrawablesResolved() {
return (mPrivateFlags2 & PFLAG2_DRAWABLE_RESOLVED) == PFLAG2_DRAWABLE_RESOLVED;
}
/**
* Reset the resolved layout direction.
*
* @hide
*/
public void resetResolvedPadding() {
mPrivateFlags2 &= ~PFLAG2_PADDING_RESOLVED;
}
/**
* Resolve all RTL related properties.
*
* @return true if resolution of RTL properties has been done
*
* @hide
*/
public boolean resolveRtlPropertiesIfNeeded() {
if (!needRtlPropertiesResolution()) return false;
// Order is important here: LayoutDirection MUST be resolved first
if (!isLayoutDirectionResolved()) {
resolveLayoutDirection();
resolveLayoutParams();
}
// Should resolve Drawables before Padding because we need the layout direction of the
// Drawable to correctly resolve Padding.
if (!isDrawablesResolved()) {
resolveDrawables();
}
if (!isPaddingResolved()) {
resolvePadding();
}
onRtlPropertiesChanged(getLayoutDirection());
return true;
}
/**
* Resolve the Drawables depending on the layout direction. This is implicitly supposing
* that the View directionality can and will be resolved before its Drawables.
*
* Will call {@link View#onResolveDrawables} when resolution is done.
*
* @hide
*/
protected void resolveDrawables() {
// Drawables resolution may need to happen before resolving the layout direction (which is
// done only during the measure() call).
// If the layout direction is not resolved yet, we cannot resolve the Drawables except in
// one case: when the raw layout direction has not been defined as LAYOUT_DIRECTION_INHERIT.
// So, if the raw layout direction is LAYOUT_DIRECTION_LTR or LAYOUT_DIRECTION_RTL or
// LAYOUT_DIRECTION_LOCALE, we can "cheat" and we don't need to wait for the layout
// direction to be resolved as its resolved value will be the same as its raw value.
if (!isLayoutDirectionResolved() &&
getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT) {
return;
}
final int layoutDirection = isLayoutDirectionResolved() ?
getLayoutDirection() : getRawLayoutDirection();
if (mBackground != null) {
mBackground.setLayoutDirection(layoutDirection);
}
mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
onResolveDrawables(layoutDirection);
}
/**
* Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or
* RTL not supported)
*/
private boolean isRtlCompatibilityMode() {
/*final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
return targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport();*/
return true;
}
/**
* @return true if RTL properties need resolution.
*
*/
private boolean needRtlPropertiesResolution() {
return (mPrivateFlags2 & ALL_RTL_PROPERTIES_RESOLVED) != ALL_RTL_PROPERTIES_RESOLVED;
}
/**
* Called when any RTL property (layout direction or text direction or text alignment) has
* been changed.
*
* Subclasses need to override this method to take care of cached information that depends on the
* resolved layout direction, or to inform child views that inherit their layout direction.
*
* The default implementation does nothing.
*
* @param layoutDirection the direction of the layout
*
* @see #LAYOUT_DIRECTION_LTR
* @see #LAYOUT_DIRECTION_RTL
*/
public void onRtlPropertiesChanged(int layoutDirection) {
}
/**
* Returns the resolved layout direction for this view.
*
* @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
* {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
*
* For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version
* is lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}.
*
* @attr ref android.R.styleable#View_layoutDirection
*/
public int getLayoutDirection() {
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
if (targetSdkVersion < JELLY_BEAN_MR1) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
return LAYOUT_DIRECTION_RESOLVED_DEFAULT;
}
return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ==
PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
/**
* Indicates whether or not this view's layout is right-to-left. This is resolved from
* layout attribute and/or the inherited value from the parent
*
* @return true if the layout is right-to-left.
*
* @hide
*/
public boolean isLayoutRtl() {
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
/**
* Indicates whether the view is currently tracking transient state that the
* app should not need to concern itself with saving and restoring, but that
* the framework should take special note to preserve when possible.
*
* <p>A view with transient state cannot be trivially rebound from an external
* data source, such as an adapter binding item views in a list. This may be
* because the view is performing an animation, tracking user selection
* of content, or similar.</p>
*
* @return true if the view has transient state
*/
public boolean hasTransientState() {
return (mPrivateFlags2 & PFLAG2_HAS_TRANSIENT_STATE) == PFLAG2_HAS_TRANSIENT_STATE;
}
/**
* Set whether this view is currently tracking transient state that the
* framework should attempt to preserve when possible. This flag is reference counted,
* so every call to setHasTransientState(true) should be paired with a later call
* to setHasTransientState(false).
*
* <p>A view with transient state cannot be trivially rebound from an external
* data source, such as an adapter binding item views in a list. This may be
* because the view is performing an animation, tracking user selection
* of content, or similar.</p>
*
* @param hasTransientState true if this view has transient state
*/
public void setHasTransientState(boolean hasTransientState) {
mTransientStateCount = hasTransientState ? mTransientStateCount + 1 :
mTransientStateCount - 1;
if (mTransientStateCount < 0) {
mTransientStateCount = 0;
Log.e(VIEW_LOG_TAG, "hasTransientState decremented below 0: " +
"unmatched pair of setHasTransientState calls");
} else if ((hasTransientState && mTransientStateCount == 1) ||
(!hasTransientState && mTransientStateCount == 0)) {
// update flag if we've just incremented up from 0 or decremented down to 0
mPrivateFlags2 = (mPrivateFlags2 & ~PFLAG2_HAS_TRANSIENT_STATE) |
(hasTransientState ? PFLAG2_HAS_TRANSIENT_STATE : 0);
if (mParent != null) {
try {
mParent.childHasTransientStateChanged(this, hasTransientState);
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
}
}
}
}
/**
* 判断是否为scrollView控件
* @return true 为可以滚动的控件,比如listview,或者scrollview
*/
public boolean isScrollView(){
return false;
}
/**
* Return the visible drawing bounds of your view. Fills in the output
* rectangle with the values from getScrollX(), getScrollY(),
* getWidth(), and getHeight(). These bounds do not account for any
* transformation properties currently set on the view, such as
* {@link #setScaleX(float)} or {@link #setRotation(float)}.
*
* @param outRect The (scrolled) drawing bounds of the view.
*/
public void getDrawingRect(Rect outRect) {
outRect.left = mScrollX;
outRect.top = mScrollY;
outRect.right = mScrollX + (mRight - mLeft);
outRect.bottom = mScrollY + (mBottom - mTop);
}
/**
* Register a callback to be invoked when a hardware key is pressed in this view.
* Key presses in software input methods will generally not trigger the methods of
* this listener.
* @param l the key listener to attach to this view
*/
public void setOnKeyListener(OnKeyListener l) {
getListenerInfo().mOnKeyListener = l;
}
/**
* Register a callback to be invoked when a touch event is sent to this view.
* @param l the touch listener to attach to this view
*/
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
/**
* 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);
}
getListenerInfo().mOnClickListener = l;
}
/**
* Return whether this view has an attached OnClickListener. Returns
* true if there is a listener, false if there is none.
*/
public boolean hasOnClickListeners() {
ListenerInfo li = mListenerInfo;
return (li != null && li.mOnClickListener != null);
}
/**
* Register a callback to be invoked when this view is clicked and held. If this view is not
* long clickable, it becomes long clickable.
*
* @param l The callback that will run
*
* @see #setLongClickable(boolean)
*/
public void setOnLongClickListener(OnLongClickListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
getListenerInfo().mOnLongClickListener = l;
}
/**
* Register a callback to be invoked when focus of this view changed.
*
* @param l The callback that will run.
*/
public void setOnFocusChangeListener(OnFocusChangeListener l) {
getListenerInfo().mOnFocusChangeListener = l;
}
/**
* Returns the focus-change callback registered for this view.
*
* @return The callback, or null if one is not registered.
*/
public OnFocusChangeListener getOnFocusChangeListener() {
ListenerInfo li = mListenerInfo;
return li != null ? li.mOnFocusChangeListener : null;
}
/**
* 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.
*/
public boolean isInTouchMode() {
if (mAttachInfo != null) {
// return mAttachInfo.mViewRootImpl.isInTouchMode();
return mAttachInfo.mInTouchMode;
}
return false;
}
/**
* 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#onKeyDown(int, KeyEvent)
* KeyEvent.Callback.onKeyDown()}: perform press of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
* is released, if the view is enabled and clickable.
*
* <p>Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*
* @param keyCode A key code that represents the button pressed, from
* {@link android.view.KeyEvent}.
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean result = false;
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER/*KeyEvent.isConfirmKey(keyCode)*/) {
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);
checkForLongClick(0);
return true;
}
}
return result;
}
/**
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
* the event).
* <p>Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*/
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
return false;
}
/**
* Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
* KeyEvent.Callback.onKeyUp()}: perform clicking of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or
* {@link KeyEvent#KEYCODE_ENTER} is released.
* <p>Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*
* @param keyCode A key code that represents the button pressed, from
* {@link android.view.KeyEvent}.
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER/*KeyEvent.isConfirmKey(keyCode)*/) {
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
removeLongPressCallback();
return performClick();
}
}
}
return false;
}
/**
* Returns this GLView's tag.
*
* @return the Object stored in this view as a tag
*
* @see #setTag(Object)
* @see #getTag(int)
*/
public Object getTag() {
return mTag;
}
/**
* Sets the tag associated with this view. A tag can be used to mark
* a view in its hierarchy and does not have to be unique within the
* hierarchy. Tags can also be used to store data within a view without
* resorting to another data structure.
*
* @param tag an Object to tag the view with
*
* @see #getTag()
* @see #setTag(int, Object)
*/
public void setTag(final Object tag) {
mTag = tag;
}
/**
* Returns the tag associated with this view and the specified key.
*
* @param key The key identifying the tag
*
* @return the Object stored in this view as a tag, or {@code null} if not
* set
*
* @see #setTag(int, Object)
* @see #getTag()
*/
public Object getTag(int key) {
if (mKeyedTags != null) return mKeyedTags.get(key);
return null;
}
/**
* Sets a tag associated with this view and a key. A tag can be used
* to mark a view in its hierarchy and does not have to be unique within
* the hierarchy. Tags can also be used to store data within a view
* without resorting to another data structure.
*
* The specified key should be an id declared in the resources of the
* application to ensure it is unique (see the <a
* href={@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>).
* Keys identified as belonging to
* the Android framework or not associated with any package will cause
* an {@link IllegalArgumentException} to be thrown.
*
* @param key The key identifying the tag
* @param tag An Object to tag the view with
*
* @throws IllegalArgumentException If they specified key is not valid
*
* @see #setTag(Object)
* @see #getTag(int)
*/
public void setTag(int key, final Object tag) {
// If the package id is 0x00 or 0x01, it's either an undefined package
// or a framework id
/*if ((key >>> 24) < 2) {
throw new IllegalArgumentException("The key must be an application-specific "
+ "resource id.");
}*/
setKeyedTag(key, tag);
}
/**
* Variation of {@link #setTag(int, Object)} that enforces the key to be a
* framework id.
*
* @hide
*/
public void setTagInternal(int key, Object tag) {
/*if ((key >>> 24) != 0x1) {
throw new IllegalArgumentException("The key must be a framework-specific "
+ "resource id.");
}*/
setKeyedTag(key, tag);
}
private void setKeyedTag(int key, Object tag) {
if (mKeyedTags == null) {
mKeyedTags = new SparseArray<Object>(2);
}
mKeyedTags.put(key, tag);
}
/**
* Sets the {@link View} description. It briefly describes the view and is
* primarily used for accessibility support. Set this property to enable
* better accessibility support for your application. This is especially
* true for views that do not have textual representation (For example,
* ImageButton).
*
* @param contentDescription The content description.
*
*/
public void setContentDescription(CharSequence contentDescription) {
if (mContentDescription == null) {
if (contentDescription == null) {
return;
}
} else if (mContentDescription.equals(contentDescription)) {
return;
}
mContentDescription = contentDescription;
}
@Override
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
return false;
}
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
ListenerInfo mListenerInfo;
static class ListenerInfo {
/**
* Listener used to dispatch focus change events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnFocusChangeListener mOnFocusChangeListener;
/**
* Listeners for layout change events.
*/
private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
/**
* Listeners for attach events.
*/
private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
/**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
public OnClickListener mOnClickListener;
/**
* Listener used to dispatch long click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnLongClickListener mOnLongClickListener;
/**
* Listener used to build the context menu.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnCreateContextMenuListener mOnCreateContextMenuListener;
private OnKeyListener mOnKeyListener;
private OnTouchListener mOnTouchListener;
private OnHoverListener mOnHoverListener;
private OnGenericMotionListener mOnGenericMotionListener;
private OnDragListener mOnDragListener;
private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
}
/**
* Interface definition for a callback to be invoked when a hardware key event is
* dispatched to this view. The callback will be invoked before the key event is
* given to the view. This is only useful for hardware keyboards; a software input
* method has no obligation to trigger this listener.
*/
public interface OnKeyListener {
/**
* Called when a hardware key is dispatched to a view. This allows listeners to
* get a chance to respond before the target view.
* <p>Key presses in software keyboards will generally NOT trigger this method,
* although some may elect to do so in some situations. Do not assume a
* software input method has to be key-based; even if it is, it may use key presses
* in a different way than you expect, so there is no way to reliably catch soft
* input key presses.
*
* @param v The GLView the key has been dispatched to.
* @param keyCode The code for the physical key that was pressed
* @param event The KeyEvent object containing full information about
* the event.
* @return True if the listener has consumed the event, false otherwise.
*/
boolean onKey(View v, int keyCode, KeyEvent event);
}
/**
* Interface definition for a callback to be invoked when the focus state of
* a view changed.
*/
public interface OnFocusChangeListener {
/**
* Called when the focus state of a view has changed.
*
* @param v The view whose state has changed.
* @param hasFocus The new focus state of v.
*/
void onFocusChange(View v, boolean hasFocus);
}
/**
* Interface definition for a callback to be invoked when a touch event is
* dispatched to this view. The callback will be invoked before the touch
* event is given to the view.
*/
public interface OnTouchListener {
/**
* Called when a touch event is dispatched to a view. This allows listeners to
* get a chance to respond before the target view.
*
* @param v The view the touch event has been dispatched to.
* @param event The MotionEvent object containing full information about
* the event.
* @return True if the listener has consumed the event, false otherwise.
*/
boolean onTouch(View v, MotionEvent event);
}
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
/**
* Interface definition for a callback to be invoked when a view has been clicked and held.
*/
public interface OnLongClickListener {
/**
* Called when a view has been clicked and held.
*
* @param v The view that was clicked and held.
*
* @return true if the callback consumed the long click, false otherwise.
*/
boolean onLongClick(View v);
}
@Override
public String toString() {
return super.toString() + " , id = " + mID;
}
public void forceStopFocusAnimation(){
}
/**
* Interface definition for a callback to be invoked when the layout bounds of a view
* changes due to layout processing.
*/
public interface OnLayoutChangeListener {
/**
* Called when the focus state of a view has changed.
*
* @param v The view whose state has changed.
* @param left The new value of the view's left property.
* @param top The new value of the view's top property.
* @param right The new value of the view's right property.
* @param bottom The new value of the view's bottom property.
* @param oldLeft The previous value of the view's left property.
* @param oldTop The previous value of the view's top property.
* @param oldRight The previous value of the view's right property.
* @param oldBottom The previous value of the view's bottom property.
*/
void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom);
}
/**
* Interface definition for a callback to be invoked when the status bar changes
* visibility. This reports <strong>global</strong> changes to the system UI
* state, not what the application is requesting.
*
* @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
*/
public interface OnSystemUiVisibilityChangeListener {
/**
* Called when the status bar changes visibility because of a call to
* {@link View#setSystemUiVisibility(int)}.
*
* @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, and {@link #SYSTEM_UI_FLAG_FULLSCREEN}.
* This tells you the <strong>global</strong> state of these UI visibility
* flags, not what your app is currently applying.
*/
public void onSystemUiVisibilityChange(int visibility);
}
/**
* Interface definition for a callback to be invoked when this view is attached
* or detached from its window.
*/
public interface OnAttachStateChangeListener {
/**
* Called when the view is attached to a window.
* @param v The view that was attached
*/
public void onViewAttachedToWindow(View v);
/**
* Called when the view is detached from a window.
* @param v The view that was detached
*/
public void onViewDetachedFromWindow(View v);
}
/**
* Add a listener that will be called when the bounds of the view change due to
* layout processing.
*
* @param listener The listener that will be called when layout bounds change.
*/
public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {
ListenerInfo li = getListenerInfo();
if (li.mOnLayoutChangeListeners == null) {
li.mOnLayoutChangeListeners = new ArrayList<OnLayoutChangeListener>();
}
if (!li.mOnLayoutChangeListeners.contains(listener)) {
li.mOnLayoutChangeListeners.add(listener);
}
}
/**
* Remove a listener for layout changes.
*
* @param listener The listener for layout bounds change.
*/
public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) {
ListenerInfo li = mListenerInfo;
if (li == null || li.mOnLayoutChangeListeners == null) {
return;
}
li.mOnLayoutChangeListeners.remove(listener);
}
/**
* Add a listener for attach state changes.
*
* This listener will be called whenever this view is attached or detached
* from a window. Remove the listener using
* {@link #removeOnAttachStateChangeListener(OnAttachStateChangeListener)}.
*
* @param listener Listener to attach
* @see #removeOnAttachStateChangeListener(OnAttachStateChangeListener)
*/
public void addOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
ListenerInfo li = getListenerInfo();
if (li.mOnAttachStateChangeListeners == null) {
li.mOnAttachStateChangeListeners
= new CopyOnWriteArrayList<OnAttachStateChangeListener>();
}
li.mOnAttachStateChangeListeners.add(listener);
}
/**
* Remove a listener for attach state changes. The listener will receive no further
* notification of window attach/detach events.
*
* @param listener Listener to remove
* @see #addOnAttachStateChangeListener(OnAttachStateChangeListener)
*/
public void removeOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
ListenerInfo li = mListenerInfo;
if (li == null || li.mOnAttachStateChangeListeners == null) {
return;
}
li.mOnAttachStateChangeListeners.remove(listener);
}
public Animation getAnimation(){
return mCurrentAnimation;
}
public void startAnimation(Animation animation){
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidate(true);
}
public void cancelAnimation(){
if(mCurrentAnimation != null) mCurrentAnimation.cancel();
}
/**
* Cancels any animations for this view.
*/
public void clearAnimation() {
if (mCurrentAnimation != null) {
mCurrentAnimation.detach();
invalidate();
}
mCurrentAnimation = null;
}
public void setAnimation(Animation animation){
mCurrentAnimation = animation;
if (animation != null) {
// If the screen is off assume the animation start time is now instead of
// the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
// would cause the animation to start when the screen turns back on
if (animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
// animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
animation.reset();
}
}
/**
* Invoked by a parent ViewGroup to notify the start of the animation
* currently associated with this view. If you override this method,
* always call super.onAnimationStart();
*
* @see #setAnimation(android.view.animation.Animation)
* @see #getAnimation()
*/
protected void onAnimationStart() {
mPrivateFlags |= PFLAG_ANIMATION_STARTED;
}
/**
* Invoked by a parent ViewGroup to notify the end of the animation
* currently associated with this view. If you override this method,
* always call super.onAnimationEnd();
*
* @see #setAnimation(android.view.animation.Animation)
* @see #getAnimation()
*/
protected void onAnimationEnd() {
mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
}
/**
* Play a sound effect for this view.
*
* <p>The framework will play sound effects for some built in actions, such as
* clicking, but you may wish to play these effects in your widget,
* for instance, for internal navigation.
*
* <p>The sound effect will only be played if sound effects are enabled by the user, and
* {@link #isSoundEffectsEnabled()} is true.
*
* @param soundConstant One of the constants defined in {@link SoundEffectConstants}
*/
public void playSoundEffect(int soundConstant) {
if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
return;
}
mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
}
/**
* BZZZTT!!1!
*
* <p>Provide haptic feedback to the user for this view.
*
* <p>The framework will provide haptic feedback for some built in actions,
* such as long presses, but you may wish to provide feedback for your
* own widget.
*
* <p>The feedback will only be performed if
* {@link #isHapticFeedbackEnabled()} is true.
*
* @param feedbackConstant One of the constants defined in
* {@link HapticFeedbackConstants}
*/
public boolean performHapticFeedback(int feedbackConstant) {
return performHapticFeedback(feedbackConstant, 0);
}
/**
* BZZZTT!!1!
*
* <p>Like {@link #performHapticFeedback(int)}, with additional options.
*
* @param feedbackConstant One of the constants defined in
* {@link HapticFeedbackConstants}
* @param flags Additional flags as per {@link HapticFeedbackConstants}.
*/
public boolean performHapticFeedback(int feedbackConstant, int flags) {
if (mAttachInfo == null) {
return false;
}
//noinspection SimplifiableIfStatement
if ((flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
&& !isHapticFeedbackEnabled()) {
return false;
}
return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant,
(flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
}
/**
* Request that the visibility of the status bar or other screen/window
* decorations be changed.
*
* <p>This method is used to put the over device UI into temporary modes
* where the user's attention is focused more on the application content,
* by dimming or hiding surrounding system affordances. This is typically
* used in conjunction with {@link Window#FEATURE_ACTION_BAR_OVERLAY
* Window.FEATURE_ACTION_BAR_OVERLAY}, allowing the applications content
* to be placed behind the action bar (and with these flags other system
* affordances) so that smooth transitions between hiding and showing them
* can be done.
*
* <p>Two representative examples of the use of system UI visibility is
* implementing a content browsing application (like a magazine reader)
* and a video playing application.
*
* <p>The first code shows a typical implementation of a View in a content
* browsing application. In this implementation, the application goes
* into a content-oriented mode by hiding the status bar and action bar,
* and putting the navigation elements into lights out mode. The user can
* then interact with content while in this mode. Such an application should
* provide an easy way for the user to toggle out of the mode (such as to
* check information in the status bar or access notifications). In the
* implementation here, this is done simply by tapping on the content.
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/view/ContentBrowserActivity.java
* content}
*
* <p>This second code sample shows a typical implementation of a View
* in a video playing application. In this situation, while the video is
* playing the application would like to go into a complete full-screen mode,
* to use as much of the display as possible for the video. When in this state
* the user can not interact with the application; the system intercepts
* touching on the screen to pop the UI out of full screen mode. See
* {@link #fitSystemWindows(Rect)} for a sample layout that goes with this code.
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java
* content}
*
* @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
* and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
*/
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
} else if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
getViewRootImpl().recomputeViewAttributes(this);
}
}
}
/**
* Returns the last {@link #setSystemUiVisibility(int)} that this view has requested.
* @return Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
* and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
*/
public int getSystemUiVisibility() {
return mSystemUiVisibility;
}
/**
* Returns the current system UI visibility that is currently set for
* the entire window. This is the combination of the
* {@link #setSystemUiVisibility(int)} values supplied by all of the
* views in the window.
*/
public int getWindowSystemUiVisibility() {
return mAttachInfo != null ? mAttachInfo.mSystemUiVisibility : 0;
}
/**
* Override to find out when the window's requested system UI visibility
* has changed, that is the value returned by {@link #getWindowSystemUiVisibility()}.
* This is different from the callbacks received through
* {@link #setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener)}
* in that this is only telling you about the local request of the window,
* not the actual values applied by the system.
*/
public void onWindowSystemUiVisibilityChanged(int visible) {
}
/**
* Dispatch callbacks to {@link #onWindowSystemUiVisibilityChanged(int)} down
* the view hierarchy.
*/
public void dispatchWindowSystemUiVisiblityChanged(int visible) {
onWindowSystemUiVisibilityChanged(visible);
}
/**
* Set a listener to receive callbacks when the visibility of the system bar changes.
* @param l The {@link OnSystemUiVisibilityChangeListener} to receive callbacks.
*/
public void setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener l) {
getListenerInfo().mOnSystemUiVisibilityChangeListener = l;
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}
/**
* Dispatch callbacks to {@link #setOnSystemUiVisibilityChangeListener} down
* the view hierarchy.
*/
public void dispatchSystemUiVisibilityChanged(int visibility) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
li.mOnSystemUiVisibilityChangeListener.onSystemUiVisibilityChange(
visibility & PUBLIC_STATUS_BAR_VISIBILITY_MASK);
}
}
boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
int val = (mSystemUiVisibility&~localChanges) | (localValue&localChanges);
if (val != mSystemUiVisibility) {
setSystemUiVisibility(val);
return true;
}
return false;
}
/**
* Private function to aggregate all per-view attributes in to the view
* root.
*/
void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
performCollectViewAttributes(attachInfo, visibility);
}
void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {
if ((visibility & VISIBILITY_MASK) == VISIBLE) {
if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {
attachInfo.mKeepScreenOn = true;
}
attachInfo.mSystemUiVisibility |= mSystemUiVisibility;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
attachInfo.mHasSystemUiListeners = true;
}
}
}
void needGlobalAttributesUpdate(boolean force) {
final AttachInfo ai = mAttachInfo;
if (ai != null && !ai.mRecomputeGlobalAttributes) {
if (force || ai.mKeepScreenOn || (ai.mSystemUiVisibility != 0)
|| ai.mHasSystemUiListeners) {
ai.mRecomputeGlobalAttributes = true;
}
}
}
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
* Each MeasureSpec represents a requirement for either the width or the height.
* A MeasureSpec is comprised of a size and a mode. There are three possible
* modes:
* <dl>
* <dt>UNSPECIFIED</dt>
* <dd>
* The parent has not imposed any constraint on the child. It can be whatever size
* it wants.
* </dd>
*
* <dt>EXACTLY</dt>
* <dd>
* The parent has determined an exact size for the child. The child is going to be
* given those bounds regardless of how big it wants to be.
* </dd>
*
* <dt>AT_MOST</dt>
* <dd>
* The child can be as large as it wants up to the specified size.
* </dd>
* </dl>
*
* MeasureSpecs are implemented as ints to reduce object allocation. This class
* is provided to pack and unpack the <size, mode> tuple into the int.
*/
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
/**
* Measure specification mode: The parent has not imposed any constraint
* on the child. It can be whatever size it wants.
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* Measure specification mode: The parent has determined an exact size
* for the child. The child is going to be given those bounds regardless
* of how big it wants to be.
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* Measure specification mode: The child can be as large as it wants up
* to the specified size.
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
/**
* Creates a measure specification based on the supplied size and mode.
*
* The mode must always be one of the following:
* <ul>
* <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
* <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
* @param size the size of the measure specification
* @param mode the mode of the measure specification
* @return the measure specification based on size and mode
*/
public static int makeMeasureSpec(int size, int mode) {
return size + mode;
}
/**
* Extracts the mode from the supplied measure specification.
*
* @param measureSpec the measure specification to extract the mode from
* @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
* {@link android.view.View.MeasureSpec#AT_MOST} or
* {@link android.view.View.MeasureSpec#EXACTLY}
*/
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
/**
* Extracts the size from the supplied measure specification.
*
* @param measureSpec the measure specification to extract the size from
* @return the size in pixels defined in the supplied measure specification
*/
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
/**
* Returns a String representation of the specified measure
* specification.
*
* @param measureSpec the measure specification to convert to a String
* @return a String with the following format: "MeasureSpec: MODE SIZE"
*/
public static String toString(int measureSpec) {
int mode = getMode(measureSpec);
int size = getSize(measureSpec);
StringBuilder sb = new StringBuilder("MeasureSpec: ");
if (mode == UNSPECIFIED)
sb.append("UNSPECIFIED ");
else if (mode == EXACTLY)
sb.append("EXACTLY ");
else if (mode == AT_MOST)
sb.append("AT_MOST ");
else
sb.append(mode).append(" ");
sb.append(size);
return sb.toString();
}
}
private final class CheckForLongPress implements Runnable {
private int mOriginalWindowAttachCount;
@Override
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
if (performLongClick()) {
mHasPerformedLongPress = true;
}
}
}
public void rememberWindowAttachCount() {
mOriginalWindowAttachCount = mWindowAttachCount;
}
}
private final class CheckForTap implements Runnable {
public float x;
public float y;
@Override
public void run() {
mPrivateFlags &= ~PFLAG_PREPRESSED;
setPressed(true, x, y);
checkForLongClick(ViewConfiguration.getTapTimeout());
}
}
private final class PerformClick implements Runnable {
@Override
public void run() {
performClick();
}
}
private final class UnsetPressedState implements Runnable {
@Override
public void run() {
setPressed(false);
}
}
public Context getContext(){
return mContext;
}
@Override
public void invalidateDrawable(Drawable who) {
invalidate();
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (mAttachInfo != null) {
mAttachInfo.mHandler.postAtTime(what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
if (mAttachInfo != null) {
mAttachInfo.mHandler.removeCallbacks(what);
}
}
/**
* Unschedule any events associated with the given Drawable. This can be
* used when selecting a new Drawable into a view, so that the previous
* one is completely unscheduled.
*
* @param who The Drawable to unschedule.
*
* @see #drawableStateChanged
*/
public void unscheduleDrawable(Drawable who) {
}
/**
* A set of information given to a view when it is attached to its parent
* window.
*/
final static class AttachInfo {
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
}
boolean mInTouchMode;
/**
* Indicates that ViewAncestor should trigger a global layout change
* the next time it performs a traversal
*/
boolean mRecomputeGlobalAttributes;
/**
* Always report new attributes at next traversal.
*/
boolean mForceReportNewAttributes;
/**
* Set during a traveral if any views want to keep the screen on.
*/
boolean mKeepScreenOn;
/**
* Bitwise-or of all of the values that views have passed to setSystemUiVisibility().
*/
int mSystemUiVisibility;
/**
* Hack to force certain system UI visibility flags to be cleared.
*/
int mDisabledSystemUiVisibility;
/**
* Last global system UI visibility reported by the window manager.
*/
int mGlobalSystemUiVisibility;
/**
* True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener
* attached.
*/
boolean mHasSystemUiListeners;
/**
* Set to true if a view has been scrolled.
*/
boolean mViewScrollChanged;
View mRootView;
/**
* The view tree observer used to dispatch global events like
* layout, pre-draw, touch mode change, etc.
*/
final ViewTreeObserver mTreeObserver = new ViewTreeObserver();
/**
* Indicates whether the view's window currently has the focus.
*/
boolean mHasWindowFocus;
/**
* Indicates the time at which drawing started to occur.
*/
long mDrawingTime;
/**
* A Handler supplied by a view's {@link android.view.ViewRootImpl}. This
* handler can be used to pump events in the UI events queue.
*/
final Handler mHandler;
final android.os.Handler mAndroidHandler;
final Callbacks mRootCallbacks;
final WindowId mWindowId;
final GLRootView mViewRootImpl;
/**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
* @param handler the events handler the view must use
*/
AttachInfo(GLRootView glRootView, WindowId windowId, Handler handler, android.os.Handler androidHandler, Callbacks effectPlayer) {
mViewRootImpl = glRootView;
mWindowId = windowId;
mHandler = handler;
mAndroidHandler = androidHandler;
mRootCallbacks = effectPlayer;
}
}
/**
* <p>ScrollabilityCache holds various fields used by a View when scrolling
* is supported. This avoids keeping too many unused fields in most
* instances of View.</p>
*/
private static class ScrollabilityCache implements Runnable {
/**
* Scrollbars are not visible
*/
public static final int OFF = 0;
/**
* Scrollbars are visible
*/
public static final int ON = 1;
/**
* Scrollbars are fading away
*/
public static final int FADING = 2;
public boolean fadeScrollBars;
public int fadingEdgeLength;
public int scrollBarDefaultDelayBeforeFade;
public int scrollBarFadeDuration;
public int scrollBarSize;
public ScrollBarDrawable scrollBar;
public float[] interpolatorValues;
public View host;
public final GLPaint paint;
public final Matrix matrix;
public BaseShader shader;
public final Interpolator scrollBarInterpolator = new Interpolator(1, 2);
private static final float[] OPAQUE = { 255 };
private static final float[] TRANSPARENT = { 0.0f };
/**
* When fading should start. This time moves into the future every time
* a new scroll happens. Measured based on SystemClock.uptimeMillis()
*/
public long fadeStartTime;
/**
* The current state of the scrollbars: ON, OFF, or FADING
*/
public int state = OFF;
private int mLastColor;
public ScrollabilityCache(ViewConfiguration configuration, View host) {
fadingEdgeLength = configuration.getScaledFadingEdgeLength();
scrollBarSize = configuration.getScaledScrollBarSize();
scrollBarDefaultDelayBeforeFade = ViewConfiguration.getScrollDefaultDelay();
scrollBarFadeDuration = ViewConfiguration.getScrollBarFadeDuration();
paint = new GLPaint();
matrix = new Matrix();
// use use a height of 1, and then wack the matrix each time we
// actually use it.
shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, TileMode.CLAMP);
paint.setShader(shader);
this.host = host;
}
public void setFadeColor(int color) {
if (color != mLastColor) {
mLastColor = color;
if (color != 0) {
shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000,
color & 0x00FFFFFF, TileMode.CLAMP);
paint.setShader(shader);
// Restore the default transfer mode (src_over)
} else {
shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, TileMode.CLAMP);
paint.setShader(shader);
}
}
}
public void run() {
long now = AnimationUtils.currentAnimationTimeMillis();
if (now >= fadeStartTime) {
// the animation fades the scrollbars out by changing
// the opacity (alpha) from fully opaque to fully
// transparent
int nextFrame = (int) now;
int framesCount = 0;
Interpolator interpolator = scrollBarInterpolator;
// Start opaque
interpolator.setKeyFrame(framesCount++, nextFrame, OPAQUE);
// End transparent
nextFrame += scrollBarFadeDuration;
interpolator.setKeyFrame(framesCount, nextFrame, TRANSPARENT);
state = FADING;
// Kick off the fade animation
host.invalidate(true);
}
}
}
// TODO RenderNode begin
/**
* Indicates that this view was specifically invalidated, not just dirtied because some
* child view was invalidated. The flag is used to determine when we need to recreate
* a view's display list (as opposed to just returning a reference to its existing
* display list).
*
* @hide
*/
static final int PFLAG_INVALIDATED = 0x80000000;
/** {@hide} */
static final int PFLAG_DRAWING_CACHE_VALID = 0x00008000;
/**
* Flag indicating that the view has been through at least one layout since it
* was last attached to a window.
*/
static final int PFLAG3_IS_LAID_OUT = 0x4;
/**
* Flag indicating that a call to measure() was skipped and should be done
* instead when layout() is invoked.
*/
static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8;
/**
* Flag indicating that nested scrolling is enabled for this view.
* The view will optionally cooperate with views up its parent chain to allow for
* integrated nested scrolling along the same axis.
*/
static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80;
/**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
* @see #getOverScrollMode()
* @see #setOverScrollMode(int)
*/
public static final int OVER_SCROLL_ALWAYS = 0;
/**
* Allow a user to over-scroll this view only if the content is large
* enough to meaningfully scroll, provided it is a view that can scroll.
*
* @see #getOverScrollMode()
* @see #setOverScrollMode(int)
*/
public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
/**
* Never allow a user to over-scroll this view.
*
* @see #getOverScrollMode()
* @see #setOverScrollMode(int)
*/
public static final int OVER_SCROLL_NEVER = 2;
/**
* Special constant for {@link #setSystemUiVisibility(int)}: View has
* requested the system UI (status bar) to be visible (the default).
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View has requested the
* system UI to enter an unobtrusive "low profile" mode.
*
* <p>This is for use in games, book readers, video players, or any other
* "immersive" application where the usual system chrome is deemed too distracting.
*
* <p>In low profile mode, the status bar and/or navigation icons may dim.
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View has requested that the
* system navigation be temporarily hidden.
*
* <p>This is an even less obtrusive state than that called for by
* {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls
* (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause
* those to disappear. This is useful (in conjunction with the
* {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
* {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN}
* window flags) for displaying content using every last pixel on the display.
*
* <p>There is a limitation: because navigation controls are so important, the least user
* interaction will cause them to reappear immediately. When this happens, both
* this flag and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be cleared automatically,
* so that both elements reappear at the same time.
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View has requested to go
* into the normal fullscreen mode so that its content can take over the screen
* while still allowing the user to interact with the application.
*
* <p>This has the same visual effect as
* {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN
* WindowManager.LayoutParams.FLAG_FULLSCREEN},
* meaning that non-critical screen decorations (such as the status bar) will be
* hidden while the user is in the View's window, focusing the experience on
* that content. Unlike the window flag, if you are using ActionBar in
* overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
* Window.FEATURE_ACTION_BAR_OVERLAY}, then enabling this flag will also
* hide the action bar.
*
* <p>This approach to going fullscreen is best used over the window flag when
* it is a transient state -- that is, the application does this at certain
* points in its user interaction where it wants to allow the user to focus
* on content, but not as a continuous state. For situations where the application
* would like to simply stay full screen the entire time (such as a game that
* wants to take over the screen), the
* {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN window flag}
* is usually a better approach. The state set here will be removed by the system
* in various situations (such as the user moving to another application) like
* the other system UI states.
*
* <p>When using this flag, the application should provide some easy facility
* for the user to go out of it. A common example would be in an e-book
* reader, where tapping on the screen brings back whatever screen and UI
* decorations that had been hidden while the user was immersed in reading
* the book.
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
/**
* Flag for {@link #setSystemUiVisibility(int)}: When using other layout
* flags, we would like a stable view of the content insets given to
* {@link #fitSystemWindows(Rect)}. This means that the insets seen there
* will always represent the worst case that the application can expect
* as a continuous state. In the stock Android UI this is the space for
* the system bar, nav bar, and status bar, but not more transient elements
* such as an input method.
*
* The stable layout your UI sees is based on the system UI modes you can
* switch to. That is, if you specify {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}
* then you will get a stable layout for changes of the
* {@link #SYSTEM_UI_FLAG_FULLSCREEN} mode; if you specify
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} and
* {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}, then you can transition
* to {@link #SYSTEM_UI_FLAG_FULLSCREEN} and {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}
* with a stable layout. (Note that you should avoid using
* {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} by itself.)
*
* If you have set the window flag {@link WindowManager.LayoutParams#FLAG_FULLSCREEN}
* to hide the status bar (instead of using {@link #SYSTEM_UI_FLAG_FULLSCREEN}),
* then a hidden status bar will be considered a "stable" state for purposes
* here. This allows your UI to continually hide the status bar, while still
* using the system UI flags to hide the action bar while still retaining
* a stable layout. Note that changing the window fullscreen flag will never
* provide a stable layout for a clean transition.
*
* <p>If you are using ActionBar in
* overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
* Window.FEATURE_ACTION_BAR_OVERLAY}, this flag will also impact the
* insets it adds to those given to the application.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like its window
* to be layed out as if it has requested
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, even if it currently hasn't. This
* allows it to avoid artifacts when switching in and out of that mode, at
* the expense that some of its user interface may be covered by screen
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for the navigation system UI through the
* {@link #fitSystemWindows(Rect)} method.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like its window
* to be layed out as if it has requested
* {@link #SYSTEM_UI_FLAG_FULLSCREEN}, even if it currently hasn't. This
* allows it to avoid artifacts when switching in and out of that mode, at
* the expense that some of its user interface may be covered by screen
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for non-fullscreen system UI through the
* {@link #fitSystemWindows(Rect)} method.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when
* hiding the navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. If this flag is
* not set, {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} will be force cleared by the system on any
* user interaction.
* <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only
* has an effect when used in combination with that flag.</p>
*/
public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when
* hiding the status bar with {@link #SYSTEM_UI_FLAG_FULLSCREEN} and/or hiding the navigation
* bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. Use this flag to create an immersive
* experience while also hiding the system bars. If this flag is not set,
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} will be force cleared by the system on any user
* interaction, and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be force-cleared by the system
* if the user swipes from the top of the screen.
* <p>When system bars are hidden in immersive mode, they can be revealed temporarily with
* system gestures, such as swiping from the top of the screen. These transient system bars
* will overlay app’s content, may have some degree of transparency, and will automatically
* hide after a short timeout.
* </p><p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_FULLSCREEN} and
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination
* with one or both of those flags.</p>
*/
public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
/**
* @hide
*
* Makes system ui transparent.
*/
public static final int SYSTEM_UI_TRANSPARENT = 0x00008000;
/**
* @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FFF;
/**
* Indicates that the view does not have a layer.
*
* @see #getLayerType()
* @see #setLayerType(int, android.graphics.Paint)
* @see #LAYER_TYPE_SOFTWARE
* @see #LAYER_TYPE_HARDWARE
*/
public static final int LAYER_TYPE_NONE = 0;
/**
* <p>Indicates that the view has a software layer. A software layer is backed
* by a bitmap and causes the view to be rendered using Android's software
* rendering pipeline, even if hardware acceleration is enabled.</p>
*
* <p>Software layers have various usages:</p>
* <p>When the application is not using hardware acceleration, a software layer
* is useful to apply a specific color filter and/or blending mode and/or
* translucency to a view and all its children.</p>
* <p>When the application is using hardware acceleration, a software layer
* is useful to render drawing primitives not supported by the hardware
* accelerated pipeline. It can also be used to cache a complex view tree
* into a texture and reduce the complexity of drawing operations. For instance,
* when animating a complex view tree with a translation, a software layer can
* be used to render the view tree only once.</p>
* <p>Software layers should be avoided when the affected view tree updates
* often. Every update will require to re-render the software layer, which can
* potentially be slow (particularly when hardware acceleration is turned on
* since the layer will have to be uploaded into a hardware texture after every
* update.)</p>
*
* @see #getLayerType()
* @see #setLayerType(int, android.graphics.Paint)
* @see #LAYER_TYPE_NONE
* @see #LAYER_TYPE_HARDWARE
*/
public static final int LAYER_TYPE_SOFTWARE = 1;
/**
* <p>Indicates that the view has a hardware layer. A hardware layer is backed
* by a hardware specific texture (generally Frame Buffer Objects or FBO on
* OpenGL hardware) and causes the view to be rendered using Android's hardware
* rendering pipeline, but only if hardware acceleration is turned on for the
* view hierarchy. When hardware acceleration is turned off, hardware layers
* behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}.</p>
*
* <p>A hardware layer is useful to apply a specific color filter and/or
* blending mode and/or translucency to a view and all its children.</p>
* <p>A hardware layer can be used to cache a complex view tree into a
* texture and reduce the complexity of drawing operations. For instance,
* when animating a complex view tree with a translation, a hardware layer can
* be used to render the view tree only once.</p>
* <p>A hardware layer can also be used to increase the rendering quality when
* rotation transformations are applied on a view. It can also be used to
* prevent potential clipping issues when applying 3D transforms on a view.</p>
*
* @see #getLayerType()
* @see #setLayerType(int, android.graphics.Paint)
* @see #LAYER_TYPE_NONE
* @see #LAYER_TYPE_SOFTWARE
*/
public static final int LAYER_TYPE_HARDWARE = 2;
/**
* Flag to indicate that this view was marked INVALIDATED, or had its display list
* invalidated, prior to the current drawing iteration. If true, the view must re-draw
* its display list. This flag, used only when hw accelerated, allows us to clear the
* flag while retaining this information until it's needed (at getDisplayList() time and
* in drawChild(), when we decide to draw a view's children's display lists into our own).
*
* {@hide}
*/
boolean mRecreateDisplayList = false;
RenderNode mRenderNode = new RenderNode();
/**
* The view's overlay layer. Developers get a reference to the overlay via getOverlay()
* and add/remove objects to/from the overlay directly through the Overlay methods.
*/
ViewOverlay mOverlay;
ViewPropertyAnimator mAnimator;
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate
* specific properties on this View.
*
* @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
*/
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}
/**
* Sets the name of the View to be used to identify Views in Transitions.
* Names should be unique in the View hierarchy.
*
* @param transitionName The name of the View to uniquely identify it for Transitions.
*/
public final void setTransitionName(String transitionName) {
mTransitionName = transitionName;
}
/**
* Returns the name of the View to be used to identify Views in Transitions.
* Names should be unique in the View hierarchy.
*
* <p>This returns null if the View has not been given a name.</p>
*
* @return The name used of the View to be used to identify Views in Transitions or null
* if no name has been given.
*/
public String getTransitionName() {
return mTransitionName;
}
public void setAlpha(float alpha) {
if (mRenderNode.setAlpha(alpha)) {
invalidateViewProperty();
}
}
/**
* This property is hidden and intended only for use by the Fade transition, which
* animates it to produce a visual translucency that does not side-effect (or get
* affected by) the real alpha property. This value is composited with the other
* alpha value (and the AlphaAnimation value, when that is present) to produce
* a final visual translucency result, which is what is passed into the DisplayList.
*
* @hide
*/
public void setTransitionAlpha(float alpha) {
setAlpha(alpha);
}
/**
* This property is hidden and intended only for use by the Fade transition, which
* animates it to produce a visual translucency that does not side-effect (or get
* affected by) the real alpha property. This value is composited with the other
* alpha value (and the AlphaAnimation value, when that is present) to produce
* a final visual translucency result, which is what is passed into the DisplayList.
*
* @hide
*/
public float getTransitionAlpha() {
return mRenderNode.getAlpha();
}
public float getAlpha() {
return mRenderNode.getAlpha();
}
/**
* Top position of this view relative to its parent.
*
* @return The top of this view, in pixels.
*/
public final int getTop() {
return mTop;
}
/**
* Sets the top position of this view relative to its parent. This method is meant to be called
* by the layout system and should not generally be called otherwise, because the property
* may be changed at any time by the layout.
*
* @param top The top of this view, in pixels.
*/
public final void setTop(int top) {
if (top != mTop) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate(true);
int width = mRight - mLeft;
int oldHeight = mBottom - mTop;
mTop = top;
mRenderNode.setTop(mTop);
sizeChange(width, mBottom - mTop, width, oldHeight);
invalidate(true);
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
}
}
/**
* Bottom position of this view relative to its parent.
*
* @return The bottom of this view, in pixels.
*/
public final int getBottom() {
return mBottom;
}
/**
* Sets the bottom position of this view relative to its parent. This method is meant to be
* called by the layout system and should not generally be called otherwise, because the
* property may be changed at any time by the layout.
*
* @param bottom The bottom of this view, in pixels.
*/
public final void setBottom(int bottom) {
if (bottom != mBottom) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate(true);
int width = mRight - mLeft;
int oldHeight = mBottom - mTop;
mBottom = bottom;
mRenderNode.setBottom(mBottom);
sizeChange(width, mBottom - mTop, width, oldHeight);
invalidate(true);
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
}
}
/**
* Left position of this view relative to its parent.
*
* @return The left edge of this view, in pixels.
*/
public final int getLeft() {
return mLeft;
}
/**
* Sets the left position of this view relative to its parent. This method is meant to be called
* by the layout system and should not generally be called otherwise, because the property
* may be changed at any time by the layout.
*
* @param left The left of this view, in pixels.
*/
public final void setLeft(int left) {
if (left != mLeft) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate(true);
int oldWidth = mRight - mLeft;
int height = mBottom - mTop;
mLeft = left;
mRenderNode.setLeft(left);
sizeChange(mRight - mLeft, height, oldWidth, height);
invalidate(true);
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
}
}
/**
* Right position of this view relative to its parent.
*
* @return The right edge of this view, in pixels.
*/
public final int getRight() {
return mRight;
}
/**
* Sets the right position of this view relative to its parent. This method is meant to be called
* by the layout system and should not generally be called otherwise, because the property
* may be changed at any time by the layout.
*
* @param right The right of this view, in pixels.
*/
public final void setRight(int right) {
if (right != mRight) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate(true);
int oldWidth = mRight - mLeft;
int height = mBottom - mTop;
mRight = right;
mRenderNode.setRight(mRight);
sizeChange(mRight - mLeft, height, oldWidth, height);
invalidate(true);
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
}
}
/**
* The visual x position of this view, in pixels. This is equivalent to the
* {@link #setTranslationX(float) translationX} property plus the current
* {@link #getLeft() left} property.
*
* @return The visual x position of this view, in pixels.
*/
public float getX() {
return mLeft + getTranslationX();
}
/**
* Sets the visual x position of this view, in pixels. This is equivalent to setting the
* {@link #setTranslationX(float) translationX} property to be the difference between
* the x value passed in and the current {@link #getLeft() left} property.
*
* @param x The visual x position of this view, in pixels.
*/
public void setX(float x) {
setTranslationX(x - mLeft);
}
/**
* The visual y position of this view, in pixels. This is equivalent to the
* {@link #setTranslationY(float) translationY} property plus the current
* {@link #getTop() top} property.
*
* @return The visual y position of this view, in pixels.
*/
public float getY() {
return mTop + getTranslationY();
}
/**
* Sets the visual y position of this view, in pixels. This is equivalent to setting the
* {@link #setTranslationY(float) translationY} property to be the difference between
* the y value passed in and the current {@link #getTop() top} property.
*
* @param y The visual y position of this view, in pixels.
*/
public void setY(float y) {
setTranslationY(y - mTop);
}
/**
* The visual z position of this view, in pixels. This is equivalent to the
* {@link #setTranslationZ(float) translationZ} property plus the current
* {@link #getElevation() elevation} property.
*
* @return The visual z position of this view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getZ() {
return /*getElevation() + */getTranslationZ();
}
/**
* Sets the visual z position of this view, in pixels. This is equivalent to setting the
* {@link #setTranslationZ(float) translationZ} property to be the difference between
* the x value passed in and the current {@link #getElevation() elevation} property.
*
* @param z The visual z position of this view, in pixels.
*/
public void setZ(float z) {
setTranslationZ(z/* - getElevation()*/);
}
/**
* Used to indicate that the parent of this view should be invalidated. This functionality
* is used to force the parent to rebuild its display list (when hardware-accelerated),
* which is necessary when various parent-managed properties of the view change, such as
* alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method will propagate
* an invalidation event to the parent.
*
* @hide
*/
protected void invalidateParentIfNeeded() {
if (mParent != null) {
((View) mParent).invalidate(true);
}
}
/**
* The horizontal location of this view relative to its {@link #getLeft() left} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The horizontal position of this view relative to its left position, in pixels.
*/
public float getTranslationX() {
return mRenderNode.getTranslationX();
}
public void setTranslationX(float translationX) {
if (mRenderNode.setTranslationX(translationX)) {
invalidateViewProperty();
}
}
public void setLayerType(int layerType) {
if (mRenderNode.setLayerType(layerType)) {
invalidate();
}
}
public int getLayerType() {
return mRenderNode.getLayerType();
}
/**
* The vertical location of this view relative to its {@link #getTop() top} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The vertical position of this view relative to its top position,
* in pixels.
*/
public float getTranslationY() {
return mRenderNode.getTranslationY();
}
public void setTranslationY(float translationY) {
if (mRenderNode.setTranslationY(translationY)) {
invalidateViewProperty();
}
}
/**
* The depth location of this view relative to its {@link #getElevation() elevation}.
*
* @return The depth of this view relative to its elevation.
*/
public float getTranslationZ() {
return mRenderNode.getTranslationZ();
}
public void setTranslationZ(float translationZ) {
if (mRenderNode.setTranslationZ(translationZ)) {
invalidateViewProperty();
}
}
/**
* The amount that the view is scaled in x around the pivot point, as a proportion of
* the view's unscaled width. A value of 1, the default, means that no scaling is applied.
*
* <p>By default, this is 1.0f.
*
* @see #getPivotX()
* @see #getPivotY()
* @return The scaling factor.
*/
public float getScaleX() {
return mRenderNode.getScaleX();
}
public void setScaleX(float scaleX) {
if (mRenderNode.setScaleX(scaleX)) {
invalidateViewProperty();
}
}
/**
* The amount that the view is scaled in y around the pivot point, as a proportion of
* the view's unscaled height. A value of 1, the default, means that no scaling is applied.
*
* <p>By default, this is 1.0f.
*
* @see #getPivotX()
* @see #getPivotY()
* @return The scaling factor.
*/
public float getScaleY() {
return mRenderNode.getScaleY();
}
public void setScaleY(float scaleY) {
if (mRenderNode.setScaleY(scaleY)) {
invalidateViewProperty();
}
}
public void setScale(float scale) {
setScaleX(scale);
setScaleY(scale);
}
/**
* The degrees that the view is rotated around the pivot point.
*
* @see #setRotation(float)
* @see #getPivotX()
* @see #getPivotY()
*
* @return The degrees of rotation.
*/
public float getRotation() {
return mRenderNode.getRotation();
}
public void setRotation(float rotation) {
if (mRenderNode.setRotation(rotation)) {
invalidateViewProperty();
}
}
/**
* The degrees that the view is rotated around the vertical axis through the pivot point.
*
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotationY(float)
*
* @return The degrees of Y rotation.
*/
public float getRotationY() {
return mRenderNode.getRotationY();
}
public void setRotationY(float rotationY) {
if (mRenderNode.setRotationY(rotationY)) {
invalidateViewProperty();
}
}
/**
* The degrees that the view is rotated around the horizontal axis through the pivot point.
*
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotationX(float)
*
* @return The degrees of X rotation.
*/
public float getRotationX() {
return mRenderNode.getRotationX();
}
public void setRotationX(float rotationX) {
if (mRenderNode.setRotationX(rotationX)) {
invalidateViewProperty();
}
}
/**
* Returns the current StateListAnimator if exists.
*
* @return StateListAnimator or null if it does not exists
* @see #setStateListAnimator(android.animation.StateListAnimator)
*/
public StateListAnimator getStateListAnimator() {
return mStateListAnimator;
}
/**
* Attaches the provided StateListAnimator to this View.
* <p>
* Any previously attached StateListAnimator will be detached.
*
* @param stateListAnimator The StateListAnimator to update the view
* @see {@link android.animation.StateListAnimator}
*/
public void setStateListAnimator(StateListAnimator stateListAnimator) {
if (mStateListAnimator == stateListAnimator) {
return;
}
if (mStateListAnimator != null) {
mStateListAnimator.setTarget(null);
}
mStateListAnimator = stateListAnimator;
if (stateListAnimator != null) {
stateListAnimator.setTarget(this);
if (isAttachedToWindow()) {
stateListAnimator.setState(getDrawableState());
}
}
}
/**
* Setting a solid background color for the drawing cache's bitmaps will improve
* performance and memory usage. Note, though that this should only be used if this
* view will always be drawn on top of a solid color.
*
* @param color The background color to use for the drawing cache's bitmap
*
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
*/
public void setDrawingCacheBackgroundColor(int color) {
if (color != mDrawingCacheBackgroundColor) {
mDrawingCacheBackgroundColor = color;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
}
/**
* @see #setDrawingCacheBackgroundColor(int)
*
* @return The background color to used for the drawing cache's bitmap
*/
public int getDrawingCacheBackgroundColor() {
return mDrawingCacheBackgroundColor;
}
/**
* Override this if your view is known to always be drawn on top of a solid color background,
* and needs to draw fading edges. Returning a non-zero color enables the view system to
* optimize the drawing of the fading edges. If you do return a non-zero color, the alpha
* should be set to 0xFF.
*
* @see #setVerticalFadingEdgeEnabled(boolean)
* @see #setHorizontalFadingEdgeEnabled(boolean)
*
* @return The known solid color background for this view, or 0 if the color may vary
*/
public int getSolidColor() {
return 0;
}
public RenderNode getDisplayList() {
updateDisplayListIfDirty();
return mRenderNode;
}
private void resetDisplayList() {
if (mRenderNode.isValid()) {
mRenderNode.destroy();
}
}
// FIXME
private GLRootView mViewRootImpl;
/**
* <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
*
* @return A non-scaled bitmap representing this view or null if cache is disabled.
*
* @see #getDrawingCache(boolean)
*/
public Bitmap getDrawingCache() {
if (mViewRootImpl != null && getWidth() > 0 && getHeight() > 0) {
return mViewRootImpl.buildDrawingCache(this);
}
return null;
}
private void updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.isValid()
|| mRecreateDisplayList) {
// Don't need to recreate the display list, just need to tell our
// children to restore/recreate theirs
if (renderNode.isValid()
&& !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
dispatchGetDisplayList();
return; // no work needed
}
GLCanvas canvas = renderNode.start(getWidth(), getHeight());
computeScroll();
canvas.translate(- mScrollX, - mScrollY);
pdraw(canvas);
canvas.translate(mScrollX, mScrollY);
renderNode.end(canvas);
mRecreateDisplayList = false;
}
}
void dispatchRender(GLCanvas canvas) {
canvas.drawRenderNode(updateViewDisplayList());
}
/**
* This method is used by ViewGroup to cause its children to restore or recreate their
* display lists. It is called by getDisplayList() when the parent ViewGroup does not need
* to recreate its own display list, which would happen if it went through the normal
* draw/dispatchDraw mechanisms.
*
* @hide
*/
protected void dispatchGetDisplayList() {}
public RenderNode updateViewDisplayList() {
mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED)
== PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_INVALIDATED;
getDisplayList();
mRecreateDisplayList = false;
return mRenderNode;
}
/**
* This is a view property invalidate.
* Only call a frame render, the render node will apply the transformation.
*/
void invalidateViewProperty() {
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.requestRender();
}
damageParentCache();
}
void damageParentCache() {
if (mParent != null) {
if (mParent.getLayerType() == LAYER_TYPE_HARDWARE) {
mParent.invalidate();
}
mParent.damageParentCache();
}
}
/**
* Mark the area defined by dirty as needing to be drawn. If the view is
* visible, {@link #onDraw(android.graphics.Canvas)} will be called at some
* point in the future.
* <p>
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
* <p>
* <b>WARNING:</b> In API 19 and below, this method may be destructive to
* {@code dirty}.
*
* @param dirty the rectangle representing the bounds of the dirty region
*/
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
dirty.right - scrollX, dirty.bottom - scrollY, true, false);
}
/**
* Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The
* coordinates of the dirty rect are relative to the view. If the view is
* visible, {@link #onDraw(android.graphics.Canvas)} will be called at some
* point in the future.
* <p>
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*
* @param l the left position of the dirty region
* @param t the top position of the dirty region
* @param r the right position of the dirty region
* @param b the bottom position of the dirty region
*/
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
}
public void invalidate() {
invalidate(true);
}
Runnable mInvalidateRunnable = new Runnable() {
@Override
public void run() {
invalidate();
}
};
public void postInvalidate() {
removeCallbacks(mInvalidateRunnable);
post(mInvalidateRunnable);
}
public void postInvalidateOnAnimation() {
postInvalidate();
}
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* @param invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View's contents or
* dimensions have not changed.
*/
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mAttachInfo == null) return;
if ((mPrivateFlags & PFLAG_INVALIDATED)
== PFLAG_INVALIDATED) {
return;
}
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
if (mParent != null) {
mParent.invalidateChild(this, l, t, r, b);
} else if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.requestRender();
}
}
/**
* @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() {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler;
}
return null;
}
/**
* <p>Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread.</p>
*
* @param action The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* @see #postDelayed
* @see #removeCallbacks
*/
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
return false;
}
public boolean postToAndroid(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mAndroidHandler.post(action);
}
return false;
}
public boolean removeCallbacksFromAndroid(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mAndroidHandler.removeCallbacks(action);
return true;
}
return false;
}
/**
* <p>Causes the Runnable to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the user interface thread.</p>
*
* @param action The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
*
* @return true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed --
* if the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*
* @see #post
* @see #removeCallbacks
*/
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
return false;
}
/**
* <p>Causes the Runnable to execute on the next animation time step.
* The runnable will be run on the user interface thread.</p>
*
* @param action The Runnable that will be executed.
*
* @see #postOnAnimationDelayed
* @see #removeCallbacks
*/
public void postOnAnimation(Runnable action) {
post(action);
}
/**
* <p>Causes the Runnable to execute on the next animation time step,
* after the specified amount of time elapses.
* The runnable will be run on the user interface thread.</p>
*
* @param action The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
*
* @see #postOnAnimation
* @see #removeCallbacks
*/
public void postOnAnimationDelayed(Runnable action, long delayMillis) {
postDelayed(action, delayMillis);
}
/**
* <p>Removes the specified Runnable from the message queue.</p>
*
* @param action The Runnable to remove from the message handling queue
*
* @return true if this view could ask the Handler to remove the Runnable,
* false otherwise. When the returned value is true, the Runnable
* may or may not have been actually removed from the message queue
* (for instance, if the Runnable was not in the queue already.)
*
* @see #post
* @see #postDelayed
* @see #postOnAnimation
* @see #postOnAnimationDelayed
*/
public boolean removeCallbacks(Runnable action) {
if (action != null) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mHandler.removeCallbacks(action);
}
}
return true;
}
//
// Properties
//
/**
* A Property wrapper around the <code>alpha</code> functionality handled by the
* {@link View#setAlpha(float)} and {@link View#getAlpha()} methods.
*/
public static final Property<View, Float> ALPHA = new FloatProperty<View>("alpha") {
@Override
public void setValue(View object, float value) {
object.setAlpha(value);
}
@Override
public Float get(View object) {
return object.getAlpha();
}
};
/**
* A Property wrapper around the <code>translationX</code> functionality handled by the
* {@link View#setTranslationX(float)} and {@link View#getTranslationX()} methods.
*/
public static final Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
@Override
public void setValue(View object, float value) {
object.setTranslationX(value);
}
@Override
public Float get(View object) {
return object.getTranslationX();
}
};
/**
* A Property wrapper around the <code>translationY</code> functionality handled by the
* {@link View#setTranslationY(float)} and {@link View#getTranslationY()} methods.
*/
public static final Property<View, Float> TRANSLATION_Y = new FloatProperty<View>("translationY") {
@Override
public void setValue(View object, float value) {
object.setTranslationY(value);
}
@Override
public Float get(View object) {
return object.getTranslationY();
}
};
/**
* A Property wrapper around the <code>translationZ</code> functionality handled by the
* {@link View#setTranslationZ(float)} and {@link View#getTranslationZ()} methods.
*/
public static final Property<View, Float> TRANSLATION_Z = new FloatProperty<View>("translationZ") {
@Override
public void setValue(View object, float value) {
object.setTranslationZ(value);
}
@Override
public Float get(View object) {
return object.getTranslationZ();
}
};
/**
* A Property wrapper around the <code>x</code> functionality handled by the
* {@link View#setX(float)} and {@link View#getX()} methods.
*/
public static final Property<View, Float> X = new FloatProperty<View>("x") {
@Override
public void setValue(View object, float value) {
object.setX(value);
}
@Override
public Float get(View object) {
return object.getX();
}
};
/**
* A Property wrapper around the <code>y</code> functionality handled by the
* {@link View#setY(float)} and {@link View#getY()} methods.
*/
public static final Property<View, Float> Y = new FloatProperty<View>("y") {
@Override
public void setValue(View object, float value) {
object.setY(value);
}
@Override
public Float get(View object) {
return object.getY();
}
};
/**
* A Property wrapper around the <code>z</code> functionality handled by the
* {@link View#setZ(float)} and {@link View#getZ()} methods.
*/
public static final Property<View, Float> Z = new FloatProperty<View>("z") {
@Override
public void setValue(View object, float value) {
object.setZ(value);
}
@Override
public Float get(View object) {
return object.getZ();
}
};
/**
* A Property wrapper around the <code>rotation</code> functionality handled by the
* {@link View#setRotation(float)} and {@link View#getRotation()} methods.
*/
public static final Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
@Override
public void setValue(View object, float value) {
object.setRotation(value);
}
@Override
public Float get(View object) {
return object.getRotation();
}
};
/**
* A Property wrapper around the <code>rotationX</code> functionality handled by the
* {@link View#setRotationX(float)} and {@link View#getRotationX()} methods.
*/
public static final Property<View, Float> ROTATION_X = new FloatProperty<View>("rotationX") {
@Override
public void setValue(View object, float value) {
object.setRotationX(value);
}
@Override
public Float get(View object) {
return object.getRotationX();
}
};
/**
* A Property wrapper around the <code>rotationY</code> functionality handled by the
* {@link View#setRotationY(float)} and {@link View#getRotationY()} methods.
*/
public static final Property<View, Float> ROTATION_Y = new FloatProperty<View>("rotationY") {
@Override
public void setValue(View object, float value) {
object.setRotationY(value);
}
@Override
public Float get(View object) {
return object.getRotationY();
}
};
/**
* A Property wrapper around the <code>scaleX</code> functionality handled by the
* {@link View#setScaleX(float)} and {@link View#getScaleX()} methods.
*/
public static final Property<View, Float> SCALE_X = new FloatProperty<View>("scaleX") {
@Override
public void setValue(View object, float value) {
object.setScaleX(value);
}
@Override
public Float get(View object) {
return object.getScaleX();
}
};
/**
* A Property wrapper around the <code>scaleY</code> functionality handled by the
* {@link View#setScaleY(float)} and {@link View#getScaleY()} methods.
*/
public static final Property<View, Float> SCALE_Y = new FloatProperty<View>("scaleY") {
@Override
public void setValue(View object, float value) {
object.setScaleY(value);
}
@Override
public Float get(View object) {
return object.getScaleY();
}
};
private class MatchIdPredicate implements Predicate<View> {
public int mId;
@Override
public boolean apply(View view) {
return (view.mID == mId);
}
}
}