| /* |
| * Copyright (C) 2012-2013, The Linux Foundation. All rights reserved. |
| * Not a Contribution |
| * |
| * Copyright (C) 2008 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.caf.fmradio; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.AnimatorSet; |
| import android.animation.ObjectAnimator; |
| import android.annotation.Widget; |
| import android.content.Context; |
| import android.content.res.ColorStateList; |
| import android.content.res.TypedArray; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.Paint; |
| import android.graphics.Paint.Align; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.text.InputFilter; |
| import android.text.InputType; |
| import android.text.Spanned; |
| import android.text.TextUtils; |
| import android.text.method.NumberKeyListener; |
| import android.util.AttributeSet; |
| import android.util.DisplayMetrics; |
| import android.util.Log; |
| import android.util.SparseArray; |
| import android.util.TypedValue; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.LayoutInflater.Filter; |
| import android.view.MotionEvent; |
| import android.view.VelocityTracker; |
| import android.view.View; |
| import android.view.ViewConfiguration; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityManager; |
| import android.view.animation.DecelerateInterpolator; |
| import android.view.inputmethod.InputMethodManager; |
| import android.widget.EditText; |
| import android.widget.ImageButton; |
| import android.widget.LinearLayout; |
| import android.widget.Scroller; |
| import android.widget.TextView; |
| |
| import com.android.internal.R; |
| |
| /** |
| * A widget that enables the user to select a number form a predefined range. |
| * The widget presents an input filed and up and down buttons for selecting the |
| * current value. Pressing/long pressing the up and down buttons increments and |
| * decrements the current value respectively. Touching the input filed shows a |
| * scroll wheel, tapping on which while shown and not moving allows direct edit |
| * of the current value. Sliding motions up or down hide the buttons and the |
| * input filed, show the scroll wheel, and rotate the latter. Flinging is also |
| * supported. The widget enables mapping from positions to strings such that |
| * instead the position index the corresponding string is displayed. |
| * <p> |
| * For an example of using this widget, see {@link android.widget.TimePicker}. |
| * </p> |
| */ |
| @Widget |
| public class HorizontalNumberPicker extends LinearLayout { |
| |
| /** |
| * The default update interval during long press. |
| */ |
| private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300; |
| |
| /** |
| * The index of the middle selector item. |
| */ |
| // private static final int SELECTOR_MIDDLE_ITEM_INDEX = 10; |
| |
| /** |
| * The coefficient by which to adjust (divide) the max fling velocity. |
| */ |
| private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8; |
| |
| /** |
| * The the duration for adjusting the selector wheel. |
| */ |
| private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800; |
| |
| /** |
| * The duration of scrolling to the next/previous value while changing the |
| * current value by one, i.e. increment or decrement. |
| */ |
| private static final int CHANGE_CURRENT_BY_ONE_SCROLL_DURATION = 300; |
| |
| /** |
| * The the delay for showing the input controls after a single tap on the |
| * input text. |
| */ |
| private static final int SHOW_INPUT_CONTROLS_DELAY_MILLIS = ViewConfiguration |
| .getDoubleTapTimeout(); |
| |
| /** |
| * The strength of fading in the top and bottom while drawing the selector. |
| */ |
| private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f; |
| |
| /** |
| * The default unscaled height of the selection divider. |
| */ |
| private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2; |
| |
| /** |
| * In this state the selector wheel is not shown. |
| */ |
| private static final int SELECTOR_WHEEL_STATE_NONE = 0; |
| |
| /** |
| * In this state the selector wheel is small. |
| */ |
| private static final int SELECTOR_WHEEL_STATE_SMALL = 1; |
| |
| /** |
| * In this state the selector wheel is large. |
| */ |
| private static final int SELECTOR_WHEEL_STATE_LARGE = 2; |
| |
| /** |
| * The alpha of the selector wheel when it is bright. |
| */ |
| private static final int SELECTOR_WHEEL_BRIGHT_ALPHA = 255; |
| |
| /** |
| * The alpha of the selector wheel when it is dimmed. |
| */ |
| private static final int SELECTOR_WHEEL_DIM_ALPHA = 60; |
| |
| /** |
| * The alpha for the increment/decrement button when it is transparent. |
| */ |
| private static final int BUTTON_ALPHA_TRANSPARENT = 0; |
| |
| /** |
| * The alpha for the increment/decrement button when it is opaque. |
| */ |
| private static final int BUTTON_ALPHA_OPAQUE = 1; |
| |
| /** |
| * The property for setting the selector paint. |
| */ |
| private static final String PROPERTY_SELECTOR_PAINT_ALPHA = "selectorPaintAlpha"; |
| |
| /** |
| * The property for setting the increment/decrement button alpha. |
| */ |
| private static final String PROPERTY_BUTTON_ALPHA = "alpha"; |
| |
| /** |
| * The numbers accepted by the input text's {@link Filter} |
| */ |
| private static final char[] DIGIT_CHARACTERS = new char[] { '0', '1', '2', |
| '3', '4', '5', '6', '7', '8', '9' }; |
| |
| /** |
| * Constant for unspecified size. |
| */ |
| private static final int SIZE_UNSPECIFIED = -1; |
| |
| /** |
| * Use a custom NumberPicker formatting callback to use two-digit minutes |
| * strings like "01". Keeping a static formatter etc. is the most efficient |
| * way to do this; it avoids creating temporary objects on every call to |
| * format(). |
| * |
| * @hide |
| */ |
| public static final HorizontalNumberPicker.Formatter TWO_DIGIT_FORMATTER = new HorizontalNumberPicker.Formatter() { |
| final StringBuilder mBuilder = new StringBuilder(); |
| |
| final java.util.Formatter mFmt = new java.util.Formatter(mBuilder, |
| java.util.Locale.US); |
| |
| final Object[] mArgs = new Object[1]; |
| |
| public String format(int value) { |
| mArgs[0] = value; |
| mBuilder.delete(0, mBuilder.length()); |
| mFmt.format("%02d", mArgs); |
| return mFmt.toString(); |
| } |
| }; |
| |
| private static final String TAG = "HorizontalNumberPicker"; |
| |
| /** |
| * The increment button. |
| */ |
| // private final ImageButton mIncrementButton; |
| |
| /** |
| * The decrement button. |
| */ |
| // private final ImageButton mDecrementButton; |
| |
| /** |
| * The text for showing the current value. |
| */ |
| // private final EditText mInputText; |
| |
| /** |
| * The min height of this widget. |
| */ |
| private final int mMinHeight = 0; |
| |
| /** |
| * The max height of this widget. |
| */ |
| private int mMaxHeight; |
| |
| /** |
| * The max width of this widget. |
| */ |
| private final int mMinWidth = 0; |
| |
| /** |
| * The max width of this widget. |
| */ |
| private int mMaxWidth; |
| |
| /** |
| * Flag whether to compute the max width. |
| */ |
| private final boolean mComputeMaxWidth; |
| |
| /** |
| * The height of the text. |
| */ |
| private int mTextSize = 20; |
| /** |
| * The height of the gap between text elements if the selector wheel. |
| */ |
| private int mSelectorTextGapHeight; |
| |
| /** |
| * The width of the gap between text elements if the selector wheel. |
| */ |
| private int mSelectorTextGapWidth; |
| |
| /** |
| * The values to be displayed instead the indices. |
| */ |
| private String[] mDisplayedValues; |
| |
| /** |
| * Lower value of the range of numbers allowed for the NumberPicker |
| */ |
| private int mMinValue; |
| |
| /** |
| * Upper value of the range of numbers allowed for the NumberPicker |
| */ |
| private int mMaxValue; |
| |
| /** |
| * Current value of this NumberPicker |
| */ |
| private int mValue; |
| |
| /** |
| * Listener to be notified upon current value change. |
| */ |
| private OnValueChangeListener mOnValueChangeListener; |
| |
| /** |
| * Listener to be notified upon scroll state change. |
| */ |
| private OnScrollListener mOnScrollListener; |
| |
| /** |
| * Formatter for for displaying the current value. |
| */ |
| private Formatter mFormatter; |
| |
| /** |
| * The speed for updating the value form long press. |
| */ |
| private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL; |
| |
| /** |
| * Cache for the string representation of selector indices. |
| */ |
| private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>(); |
| |
| /** |
| * The selector indices whose value are show by the selector. |
| */ |
| private final int[] SELECTOR_INDICES_MEDIUM = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE |
| }; |
| private final int[] SELECTOR_INDICES_SMALL = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, |
| }; |
| private final int[] SELECTOR_INDICES_LARGE = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, |
| Integer.MIN_VALUE, Integer.MIN_VALUE, |
| }; |
| private int[] mSelectorIndices = SELECTOR_INDICES_MEDIUM; |
| private int mSelectorMiddleItemIndex = mSelectorIndices.length / 2; |
| /** |
| * The offset to middle of selector. |
| */ |
| private static final int SELECTOR_OFFSET_ZERO = 0; |
| /** |
| * The colors alpha of selector text. |
| */ |
| private static final int SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE = 255; |
| |
| private static float mDensity = 1.0f; |
| private static final float LDPI = 0.75f; |
| private static final float MDPI = 1.0f; |
| private static final float HDPI = 1.5f; |
| private static final float XHDPI = 2.0f; |
| |
| private float mScaleWidth = 2; |
| private float mScaleLengthShort = 10; |
| private float mScaleLengthLong = 20; |
| private float mGapBetweenNumAndScale = 18; |
| private float mHdpiPositionAdjust = 18; |
| |
| public enum Scale { |
| SCALE_SMALL, |
| SCALE_MEDIUM, |
| SCALE_LARGE |
| }; |
| /** |
| * The {@link Paint} for drawing the selector. |
| */ |
| private final Paint mSelectorWheelPaint; |
| |
| /** |
| * The height of a selector element (text + gap). |
| */ |
| private int mSelectorElementHeight; |
| |
| /** |
| * The width of a selector element (text + gap). |
| */ |
| private int mSelectorElementWidth; |
| |
| /** |
| * The initial offset of the scroll selector. |
| */ |
| private int mInitialScrollOffset = Integer.MIN_VALUE; |
| |
| /** |
| * The initial offset for horizontal scroll selector . |
| */ |
| private static final int INIT_SCROLL_OFFSET_HORIZONTAL = 0; |
| |
| /** |
| * The initial offset for vertical scroll selector . |
| */ |
| private static final int INIT_SCROLL_OFFSET_VERTICAL = 0; |
| /** |
| * The current offset of the scroll selector. |
| */ |
| private int mCurrentScrollOffset; |
| |
| /** |
| * The {@link Scroller} responsible for flinging the selector. |
| */ |
| private final Scroller mFlingScroller; |
| |
| /** |
| * The {@link Scroller} responsible for adjusting the selector. |
| */ |
| private final Scroller mAdjustScroller; |
| |
| /** |
| * The previous Y coordinate while scrolling the selector. |
| */ |
| private int mPreviousScrollerY; |
| |
| /** |
| * The previous Y coordinate while scrolling the selector. |
| */ |
| private int mPreviousScrollerX; |
| |
| /** |
| * Handle to the reusable command for setting the input text selection. |
| */ |
| private SetSelectionCommand mSetSelectionCommand; |
| |
| /** |
| * Handle to the reusable command for adjusting the scroller. |
| */ |
| private AdjustScrollerCommand mAdjustScrollerCommand; |
| |
| /** |
| * Handle to the reusable command for changing the current value from long |
| * press by one. |
| */ |
| private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand; |
| |
| /** |
| * {@link Animator} for showing the up/down arrows. |
| */ |
| // private final AnimatorSet mShowInputControlsAnimator; |
| |
| /** |
| * {@link Animator} for dimming the selector wheel. |
| */ |
| // private final Animator mDimSelectorWheelAnimator; |
| |
| /** |
| * The Y position of the last down event. |
| */ |
| private float mLastDownEventY; |
| /** |
| * The X position of the last down event. |
| */ |
| private float mLastDownEventX; |
| |
| /** |
| * The Y position of the last motion event. |
| */ |
| private float mLastMotionEventY; |
| |
| /** |
| * The X position of the last motion event. |
| */ |
| private float mLastMotionEventX; |
| |
| /** |
| * Flag if to begin edit on next up event. |
| */ |
| private boolean mBeginEditOnUpEvent; |
| |
| /** |
| * Flag if to adjust the selector wheel on next up event. |
| */ |
| private boolean mAdjustScrollerOnUpEvent; |
| |
| /** |
| * The state of the selector wheel. |
| */ |
| private int mSelectorWheelState; |
| |
| /** |
| * Determines speed during touch scrolling. |
| */ |
| private VelocityTracker mVelocityTracker; |
| |
| /** |
| * @see ViewConfiguration#getScaledTouchSlop() |
| */ |
| private int mTouchSlop; |
| |
| /** |
| * @see ViewConfiguration#getScaledMinimumFlingVelocity() |
| */ |
| private int mMinimumFlingVelocity; |
| |
| /** |
| * @see ViewConfiguration#getScaledMaximumFlingVelocity() |
| */ |
| private int mMaximumFlingVelocity; |
| |
| /** |
| * Flag whether the selector should wrap around. |
| */ |
| private boolean mWrapSelectorWheel; |
| |
| /** |
| * The back ground color used to optimize scroller fading. |
| */ |
| private final int mSolidColor; |
| |
| /** |
| * Flag indicating if this widget supports flinging. |
| */ |
| private final boolean mFlingable; |
| |
| /** |
| * Divider for showing item to be selected while scrolling |
| */ |
| // private final Drawable mSelectionDivider; |
| |
| /** |
| * The height of the selection divider. |
| */ |
| // private final int mSelectionDividerHeight; |
| |
| /** |
| * Reusable {@link Rect} instance. |
| */ |
| private final Rect mTempRect = new Rect(); |
| |
| /** |
| * The current scroll state of the number picker. |
| */ |
| private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE; |
| |
| /** |
| * The duration of the animation for showing the input controls. |
| */ |
| private final long mShowInputControlsAnimimationDuration; |
| |
| /** |
| * Flag whether the scoll wheel and the fading edges have been initialized. |
| */ |
| private boolean mScrollWheelAndFadingEdgesInitialized; |
| |
| private boolean mHorizontal = true; |
| |
| /** |
| * Interface to listen for changes of the current value. |
| */ |
| public interface OnValueChangeListener { |
| |
| /** |
| * Called upon a change of the current value. |
| * |
| * @param picker |
| * The NumberPicker associated with this listener. |
| * @param oldVal |
| * The previous value. |
| * @param newVal |
| * The new value. |
| */ |
| void onValueChange(HorizontalNumberPicker picker, int oldVal, int newVal); |
| } |
| |
| |
| private OnScrollFinishListener mOnScrollFinishListener; |
| |
| public interface OnScrollFinishListener{ |
| public void onScrollFinish(int value); |
| } |
| /** |
| * Interface to listen for the picker scroll state. |
| */ |
| public interface OnScrollListener { |
| |
| /** |
| * The view is not scrolling. |
| */ |
| public static int SCROLL_STATE_IDLE = 0; |
| |
| /** |
| * The user is scrolling using touch, and their finger is still on the |
| * screen. |
| */ |
| public static int SCROLL_STATE_TOUCH_SCROLL = 1; |
| |
| /** |
| * The user had previously been scrolling using touch and performed a |
| * fling. |
| */ |
| public static int SCROLL_STATE_FLING = 2; |
| |
| /** |
| * Callback invoked while the number picker scroll state has changed. |
| * |
| * @param view |
| * The view whose scroll state is being reported. |
| * @param scrollState |
| * The current scroll state. One of |
| * {@link #SCROLL_STATE_IDLE}, |
| * {@link #SCROLL_STATE_TOUCH_SCROLL} or |
| * {@link #SCROLL_STATE_IDLE}. |
| */ |
| public void onScrollStateChange(HorizontalNumberPicker view, |
| int scrollState); |
| } |
| |
| /** |
| * Interface used to format current value into a string for presentation. |
| */ |
| public interface Formatter { |
| |
| /** |
| * Formats a string representation of the current value. |
| * |
| * @param value |
| * The currently selected value. |
| * @return A formatted string representation. |
| */ |
| public String format(int value); |
| } |
| |
| /** |
| * Create a new number picker. |
| * |
| * @param context |
| * The application environment. |
| */ |
| public HorizontalNumberPicker(Context context) { |
| this(context, null); |
| } |
| |
| /** |
| * Create a new number picker. |
| * |
| * @param context |
| * The application environment. |
| * @param attrs |
| * A collection of attributes. |
| */ |
| public HorizontalNumberPicker(Context context, AttributeSet attrs) { |
| this(context, attrs, R.attr.numberPickerStyle); |
| } |
| |
| /** |
| * Create a new number picker |
| * |
| * @param context |
| * the application environment. |
| * @param attrs |
| * a collection of attributes. |
| * @param defStyle |
| * The default style to apply to this view. |
| */ |
| public HorizontalNumberPicker(Context context, AttributeSet attrs, |
| int defStyle) { |
| super(context, attrs, defStyle); |
| |
| // process style attributes |
| TypedArray attributesArray = context.obtainStyledAttributes(attrs, |
| R.styleable.NumberPicker, defStyle, 0); |
| mSolidColor = attributesArray.getColor( |
| R.styleable.NumberPicker_solidColor, 0); |
| //mFlingable = attributesArray.getBoolean( |
| // R.styleable.NumberPicker_flingable, true); |
| mFlingable = true; |
| |
| mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE); |
| attributesArray.recycle(); |
| |
| mShowInputControlsAnimimationDuration = getResources().getInteger( |
| R.integer.config_longAnimTime); |
| |
| // By default Linearlayout that we extend is not drawn. This is |
| // its draw() method is not called but dispatchDraw() is called |
| // directly (see ViewGroup.drawChild()). However, this class uses |
| // the fading edge effect implemented by View and we need our |
| // draw() method to be called. Therefore, we declare we will draw. |
| setWillNotDraw(false); |
| setSelectorWheelState(SELECTOR_WHEEL_STATE_NONE); |
| |
| |
| // initialize constants |
| mTouchSlop = ViewConfiguration.getTapTimeout(); |
| ViewConfiguration configuration = ViewConfiguration.get(context); |
| mTouchSlop = configuration.getScaledTouchSlop(); |
| mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); |
| mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity() |
| / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT; |
| // mTextSize = (int) mInputText.getTextSize(); |
| // create the selector wheel paint |
| Paint paint = new Paint(); |
| paint.setAntiAlias(true); |
| paint.setTextAlign(Align.CENTER); |
| paint.setTextSize(mTextSize); |
| paint.setColor(Color.WHITE); |
| mSelectorWheelPaint = paint; |
| |
| |
| // create the fling and adjust scrollers |
| mFlingScroller = new Scroller(getContext(), null, true); |
| mAdjustScroller = new Scroller(getContext(), |
| new DecelerateInterpolator(2.5f)); |
| // updateInputTextView(); |
| |
| updateIncrementAndDecrementButtonsVisibilityState(); |
| |
| if (mFlingable) { |
| if (isInEditMode()) { |
| setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL); |
| } else { |
| // Start with shown selector wheel and hidden controls. When |
| // made |
| // visible hide the selector and fade-in the controls to suggest |
| // fling interaction. |
| setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE); |
| } |
| } |
| } |
| |
| @Override |
| protected void onLayout(boolean changed, int left, int top, int right, |
| int bottom) { |
| final int msrdWdth = getMeasuredWidth(); |
| final int msrdHght = getMeasuredHeight(); |
| |
| if (!mScrollWheelAndFadingEdgesInitialized) { |
| mScrollWheelAndFadingEdgesInitialized = true; |
| // need to do all this when we know our size |
| initializeSelectorWheel(); |
| initializeFadingEdges(); |
| } |
| setVerticalFadingEdgeEnabled(false); |
| } |
| public void setTextSize(int textSize){ |
| if(textSize > 0 ){ |
| mTextSize = textSize; |
| mSelectorWheelPaint.setTextSize(textSize); |
| } |
| } |
| public void setDensity(int density){ |
| switch(density){ |
| case DisplayMetrics.DENSITY_LOW : |
| mDensity = LDPI; |
| break; |
| case DisplayMetrics.DENSITY_MEDIUM: |
| mDensity = MDPI; |
| break; |
| case DisplayMetrics.DENSITY_HIGH: |
| mDensity = HDPI; |
| break; |
| case DisplayMetrics.DENSITY_XHIGH: |
| mDensity = XHDPI; |
| break; |
| default: |
| mDensity = MDPI; |
| break; |
| } |
| } |
| |
| public void setScale(Scale scale){ |
| switch(scale){ |
| case SCALE_SMALL: |
| mSelectorIndices = SELECTOR_INDICES_SMALL; |
| break; |
| case SCALE_MEDIUM: |
| mSelectorIndices = SELECTOR_INDICES_MEDIUM; |
| break; |
| case SCALE_LARGE: |
| mSelectorIndices = SELECTOR_INDICES_LARGE; |
| break; |
| default : |
| break; |
| } |
| |
| mSelectorMiddleItemIndex = mSelectorIndices.length / 2; |
| initializeSelectorWheel(); |
| // invalidate(); |
| } |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| |
| final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, |
| mMaxWidth); |
| final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, |
| mMaxHeight); |
| super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); |
| // Flag if we are measured with width or height less than the respective |
| // min. |
| final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, |
| getMeasuredWidth(), widthMeasureSpec); |
| final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, |
| getMeasuredHeight(), heightMeasureSpec); |
| setMeasuredDimension(widthSize, heightSize); |
| } |
| |
| @Override |
| public boolean onInterceptTouchEvent(MotionEvent event) { |
| if (!isEnabled() || !mFlingable) { |
| return false; |
| } |
| switch (event.getActionMasked()) { |
| case MotionEvent.ACTION_DOWN: |
| mLastMotionEventY = mLastDownEventY = event.getY(); |
| mLastMotionEventX = mLastDownEventX = event.getX(); |
| removeAllCallbacks(); |
| mBeginEditOnUpEvent = false; |
| mAdjustScrollerOnUpEvent = true; |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { |
| mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); |
| boolean scrollersFinished = mFlingScroller.isFinished() |
| && mAdjustScroller.isFinished(); |
| if (!scrollersFinished) { |
| mFlingScroller.forceFinished(true); |
| mAdjustScroller.forceFinished(true); |
| onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); |
| } |
| mBeginEditOnUpEvent = scrollersFinished; |
| mAdjustScrollerOnUpEvent = true; |
| return true; |
| } |
| |
| mAdjustScrollerOnUpEvent = false; |
| setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE); |
| // hideInputControls(); |
| return true; |
| case MotionEvent.ACTION_MOVE: |
| float currentMoveY = event.getY(); |
| float currentMoveX = event.getX(); |
| int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY); |
| int deltaDownX = (int) Math.abs(currentMoveX - mLastDownEventX); |
| if(mHorizontal){ |
| if (mLastDownEventX > mTouchSlop) { |
| mBeginEditOnUpEvent = false; |
| onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); |
| return true; |
| } |
| }else{ |
| if (deltaDownY > mTouchSlop) { |
| mBeginEditOnUpEvent = false; |
| onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); |
| return true; |
| } |
| } |
| break; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent ev) { |
| if (!isEnabled()) { |
| return false; |
| } |
| if (mVelocityTracker == null) { |
| mVelocityTracker = VelocityTracker.obtain(); |
| } |
| mVelocityTracker.addMovement(ev); |
| int action = ev.getActionMasked(); |
| |
| switch (action) { |
| case MotionEvent.ACTION_MOVE: |
| if (!mHorizontal) { |
| float currentMoveY = ev.getY(); |
| if (mBeginEditOnUpEvent |
| || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { |
| int deltaDownY = (int) Math.abs(currentMoveY |
| - mLastDownEventY); |
| if (deltaDownY > mTouchSlop) { |
| mBeginEditOnUpEvent = false; |
| onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); |
| } |
| } |
| int deltaMoveY = (int) (currentMoveY - mLastMotionEventY); |
| scrollBy(0, deltaMoveY); |
| invalidate(); |
| mLastMotionEventY = currentMoveY; |
| } else { |
| float currentMoveX = ev.getX(); |
| if (mBeginEditOnUpEvent |
| || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { |
| int deltaDownX = (int) Math.abs(currentMoveX |
| - mLastDownEventX); |
| if (deltaDownX > mTouchSlop) { |
| mBeginEditOnUpEvent = false; |
| onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); |
| } |
| } |
| int deltaMoveX = (int) (currentMoveX - mLastMotionEventX); |
| scrollBy(deltaMoveX, 0); |
| invalidate(); |
| mLastMotionEventX = currentMoveX; |
| } |
| break; |
| case MotionEvent.ACTION_UP: |
| if (mBeginEditOnUpEvent) { |
| setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL); |
| return true; |
| } |
| // VelocityTracker velocityTracker = mVelocityTracker; |
| // velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); |
| // int initialVelocity=0; |
| // if(!mHorizontal){ |
| // initialVelocity = (int) velocityTracker.getYVelocity(); |
| // }else{ |
| // initialVelocity = (int)velocityTracker.getXVelocity(); |
| // } |
| // if (Math.abs(initialVelocity) > mMinimumFlingVelocity) { |
| // // fling after up |
| // fling(initialVelocity); |
| // onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); |
| // } else { |
| if (mAdjustScrollerOnUpEvent) { |
| if (mFlingScroller.isFinished() |
| && mAdjustScroller.isFinished()) { |
| postAdjustScrollerCommand(0); |
| } |
| } else { |
| postAdjustScrollerCommand(SHOW_INPUT_CONTROLS_DELAY_MILLIS); |
| } |
| // } |
| mVelocityTracker.recycle(); |
| mVelocityTracker = null; |
| break; |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean dispatchTouchEvent(MotionEvent event) { |
| final int action = event.getActionMasked(); |
| switch (action) { |
| case MotionEvent.ACTION_MOVE: |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { |
| removeAllCallbacks(); |
| forceCompleteChangeCurrentByOneViaScroll(); |
| } |
| break; |
| case MotionEvent.ACTION_CANCEL: |
| case MotionEvent.ACTION_UP: |
| removeAllCallbacks(); |
| break; |
| } |
| return super.dispatchTouchEvent(event); |
| } |
| |
| @Override |
| public boolean dispatchKeyEvent(KeyEvent event) { |
| int keyCode = event.getKeyCode(); |
| if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER |
| || keyCode == KeyEvent.KEYCODE_ENTER) { |
| removeAllCallbacks(); |
| } |
| return super.dispatchKeyEvent(event); |
| } |
| |
| @Override |
| public boolean dispatchTrackballEvent(MotionEvent event) { |
| int action = event.getActionMasked(); |
| if (action == MotionEvent.ACTION_CANCEL |
| || action == MotionEvent.ACTION_UP) { |
| removeAllCallbacks(); |
| } |
| return super.dispatchTrackballEvent(event); |
| } |
| |
| @Override |
| public void computeScroll() { |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) { |
| return; |
| } |
| Scroller scroller = mFlingScroller; |
| if (scroller.isFinished()) { |
| scroller = mAdjustScroller; |
| if (scroller.isFinished()) { |
| return; |
| } |
| } |
| scroller.computeScrollOffset(); |
| |
| if (mHorizontal) { |
| int currentScrollerX = scroller.getCurrX(); |
| if (mPreviousScrollerX == 0) { |
| mPreviousScrollerX = scroller.getStartX(); |
| |
| } |
| |
| scrollBy(currentScrollerX - mPreviousScrollerX, 0); |
| mPreviousScrollerX = currentScrollerX; |
| |
| } else { |
| int currentScrollerY = scroller.getCurrY(); |
| if (mPreviousScrollerY == 0) { |
| mPreviousScrollerY = scroller.getStartY(); |
| } |
| scrollBy(0, currentScrollerY - mPreviousScrollerY); |
| mPreviousScrollerY = currentScrollerY; |
| } |
| |
| if (scroller.isFinished()) { |
| onScrollerFinished(scroller); |
| } else { |
| invalidate(); |
| } |
| } |
| |
| @Override |
| public void setEnabled(boolean enabled) { |
| super.setEnabled(enabled); |
| } |
| |
| @Override |
| public void scrollBy(int x, int y) { |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) { |
| return; |
| } |
| int[] selectorIndices = mSelectorIndices; |
| if (!mHorizontal) { |
| if (!mWrapSelectorWheel && y > 0 |
| && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| return; |
| } |
| if (!mWrapSelectorWheel && y < 0 |
| && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| return; |
| } |
| mCurrentScrollOffset += y; |
| while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) { |
| mCurrentScrollOffset -= mSelectorElementHeight; |
| decrementSelectorIndices(selectorIndices); |
| changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); |
| if (!mWrapSelectorWheel |
| && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| } |
| } |
| while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) { |
| mCurrentScrollOffset += mSelectorElementHeight; |
| incrementSelectorIndices(selectorIndices); |
| changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); |
| if (!mWrapSelectorWheel |
| && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| } |
| } |
| } else { |
| if (!mWrapSelectorWheel && x > 0 |
| && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| return; |
| } |
| if (!mWrapSelectorWheel && x < 0 |
| && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| return; |
| } |
| mCurrentScrollOffset += x; |
| while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapWidth) { |
| mCurrentScrollOffset -= mSelectorElementWidth; |
| decrementSelectorIndices(selectorIndices); |
| changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); |
| if (!mWrapSelectorWheel |
| && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| } |
| } |
| while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapWidth) { |
| mCurrentScrollOffset += mSelectorElementWidth; |
| incrementSelectorIndices(selectorIndices); |
| changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); |
| if (!mWrapSelectorWheel |
| && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { |
| mCurrentScrollOffset = mInitialScrollOffset; |
| } |
| } |
| } |
| |
| } |
| |
| @Override |
| public int getSolidColor() { |
| return mSolidColor; |
| } |
| |
| /** |
| * Sets the listener to be notified on change of the current value. |
| * |
| * @param onValueChangedListener |
| * The listener. |
| */ |
| public void setOnValueChangedListener( |
| OnValueChangeListener onValueChangedListener) { |
| mOnValueChangeListener = onValueChangedListener; |
| } |
| |
| /** |
| * Set listener to be notified for scroll state changes. |
| * |
| * @param onScrollListener |
| * The listener. |
| */ |
| public void setOnScrollListener(OnScrollListener onScrollListener) { |
| mOnScrollListener = onScrollListener; |
| } |
| public void setOnScrollFinishedListener(OnScrollFinishListener onScrollFinishListener){ |
| mOnScrollFinishListener = onScrollFinishListener; |
| } |
| /** |
| * Set the formatter to be used for formatting the current value. |
| * <p> |
| * Note: If you have provided alternative values for the values this |
| * formatter is never invoked. |
| * </p> |
| * |
| * @param formatter |
| * The formatter object. If formatter is <code>null</code>, |
| * {@link String#valueOf(int)} will be used. |
| * |
| * @see #setDisplayedValues(String[]) |
| */ |
| public void setFormatter(Formatter formatter) { |
| if (formatter == mFormatter) { |
| return; |
| } |
| mFormatter = formatter; |
| initializeSelectorWheelIndices(); |
| // updateInputTextView(); |
| } |
| |
| /** |
| * Set the current value for the number picker. |
| * <p> |
| * If the argument is less than the {@link NumberPicker#getMinValue()} and |
| * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the |
| * current value is set to the {@link NumberPicker#getMinValue()} value. |
| * </p> |
| * <p> |
| * If the argument is less than the {@link NumberPicker#getMinValue()} and |
| * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the |
| * current value is set to the {@link NumberPicker#getMaxValue()} value. |
| * </p> |
| * <p> |
| * If the argument is less than the {@link NumberPicker#getMaxValue()} and |
| * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the |
| * current value is set to the {@link NumberPicker#getMaxValue()} value. |
| * </p> |
| * <p> |
| * If the argument is less than the {@link NumberPicker#getMaxValue()} and |
| * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the |
| * current value is set to the {@link NumberPicker#getMinValue()} value. |
| * </p> |
| * |
| * @param value |
| * The current value. |
| * @see #setWrapSelectorWheel(boolean) |
| * @see #setMinValue(int) |
| * @see #setMaxValue(int) |
| */ |
| public void setValue(int value) { |
| if (mValue == value) { |
| return; |
| } |
| if (value < mMinValue) { |
| value = mWrapSelectorWheel ? mMaxValue : mMinValue; |
| } |
| if (value > mMaxValue) { |
| value = mWrapSelectorWheel ? mMinValue : mMaxValue; |
| } |
| mValue = value; |
| initializeSelectorWheelIndices(); |
| updateInputTextView(); |
| updateIncrementAndDecrementButtonsVisibilityState(); |
| invalidate(); |
| } |
| |
| /** |
| * Computes the max width if no such specified as an attribute. |
| */ |
| private void tryComputeMaxWidth() { |
| if (!mComputeMaxWidth) { |
| return; |
| } |
| int maxTextWidth = 0; |
| if (mDisplayedValues == null) { |
| float maxDigitWidth = 0; |
| for (int i = 0; i <= 9; i++) { |
| final float digitWidth = mSelectorWheelPaint.measureText(String |
| .valueOf(i)); |
| if (digitWidth > maxDigitWidth) { |
| maxDigitWidth = digitWidth; |
| } |
| } |
| int numberOfDigits = 0; |
| int current = mMaxValue; |
| while (current > 0) { |
| numberOfDigits++; |
| current = current / 10; |
| } |
| maxTextWidth = (int) (numberOfDigits * maxDigitWidth); |
| } else { |
| final int valueCount = mDisplayedValues.length; |
| for (int i = 0; i < valueCount; i++) { |
| final float textWidth = mSelectorWheelPaint |
| .measureText(mDisplayedValues[i]); |
| if (textWidth > maxTextWidth) { |
| maxTextWidth = (int) textWidth; |
| } |
| } |
| } |
| // maxTextWidth += mInputText.getPaddingLeft() |
| // + mInputText.getPaddingRight(); |
| if (mMaxWidth != maxTextWidth) { |
| if (maxTextWidth > mMinWidth) { |
| mMaxWidth = maxTextWidth; |
| } else { |
| mMaxWidth = mMinWidth; |
| } |
| invalidate(); |
| } |
| } |
| |
| /** |
| * Gets whether the selector wheel wraps when reaching the min/max value. |
| * |
| * @return True if the selector wheel wraps. |
| * |
| * @see #getMinValue() |
| * @see #getMaxValue() |
| */ |
| public boolean getWrapSelectorWheel() { |
| return mWrapSelectorWheel; |
| } |
| |
| /** |
| * Sets whether the selector wheel shown during flinging/scrolling should |
| * wrap around the {@link NumberPicker#getMinValue()} and |
| * {@link NumberPicker#getMaxValue()} values. |
| * <p> |
| * By default if the range (max - min) is more than five (the number of |
| * items shown on the selector wheel) the selector wheel wrapping is |
| * enabled. |
| * </p> |
| * |
| * @param wrapSelectorWheel |
| * Whether to wrap. |
| */ |
| public void setWrapSelectorWheel(boolean wrapSelectorWheel) { |
| if (wrapSelectorWheel |
| && (mMaxValue - mMinValue) < mSelectorIndices.length) { |
| throw new IllegalStateException( |
| "Range less than selector items count."); |
| } |
| if (wrapSelectorWheel != mWrapSelectorWheel) { |
| mWrapSelectorWheel = wrapSelectorWheel; |
| updateIncrementAndDecrementButtonsVisibilityState(); |
| } |
| } |
| |
| /** |
| * Sets the speed at which the numbers be incremented and decremented when |
| * the up and down buttons are long pressed respectively. |
| * <p> |
| * The default value is 300 ms. |
| * </p> |
| * |
| * @param intervalMillis |
| * The speed (in milliseconds) at which the numbers will be |
| * incremented and decremented. |
| */ |
| public void setOnLongPressUpdateInterval(long intervalMillis) { |
| mLongPressUpdateInterval = intervalMillis; |
| } |
| |
| /** |
| * Returns the value of the picker. |
| * |
| * @return The value. |
| */ |
| public int getValue() { |
| return mValue; |
| } |
| |
| /** |
| * Returns the min value of the picker. |
| * |
| * @return The min value |
| */ |
| public int getMinValue() { |
| return mMinValue; |
| } |
| |
| /** |
| * Sets the min value of the picker. |
| * |
| * @param minValue |
| * The min value. |
| */ |
| public void setMinValue(int minValue) { |
| if (mMinValue == minValue) { |
| return; |
| } |
| if (minValue < 0) { |
| throw new IllegalArgumentException("minValue must be >= 0"); |
| } |
| mMinValue = minValue; |
| if (mMinValue > mValue) { |
| mValue = mMinValue; |
| } |
| boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length; |
| setWrapSelectorWheel(wrapSelectorWheel); |
| initializeSelectorWheelIndices(); |
| updateInputTextView(); |
| tryComputeMaxWidth(); |
| } |
| |
| /** |
| * Returns the max value of the picker. |
| * |
| * @return The max value. |
| */ |
| public int getMaxValue() { |
| return mMaxValue; |
| } |
| |
| /** |
| * Sets the max value of the picker. |
| * |
| * @param maxValue |
| * The max value. |
| */ |
| public void setMaxValue(int maxValue) { |
| if (mMaxValue == maxValue) { |
| return; |
| } |
| if (maxValue < 0) { |
| throw new IllegalArgumentException("maxValue must be >= 0"); |
| } |
| mMaxValue = maxValue; |
| if (mMaxValue < mValue) { |
| mValue = mMaxValue; |
| } |
| boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length; |
| setWrapSelectorWheel(wrapSelectorWheel); |
| initializeSelectorWheelIndices(); |
| tryComputeMaxWidth(); |
| } |
| |
| /** |
| * Gets the values to be displayed instead of string values. |
| * |
| * @return The displayed values. |
| */ |
| public String[] getDisplayedValues() { |
| return mDisplayedValues; |
| } |
| |
| /** |
| * Sets the values to be displayed. |
| * |
| * @param displayedValues |
| * The displayed values. |
| */ |
| public void setDisplayedValues(String[] displayedValues) { |
| if (mDisplayedValues == displayedValues) { |
| return; |
| } |
| mDisplayedValues = displayedValues; |
| if (mDisplayedValues != null) { |
| // Allow text entry rather than strictly numeric entry. |
| // mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT |
| // | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); |
| } else { |
| // mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER); |
| } |
| // updateInputTextView(); |
| initializeSelectorWheelIndices(); |
| tryComputeMaxWidth(); |
| } |
| /** |
| * Sets the values to be displayed.If autoCalMinMax passed true, will calculate |
| * and set min value and max value. |
| * |
| * @param displayedValues |
| * The displayed values. |
| * @param autoCalMinMax |
| * Whether auto calculate and set the min value and max value. |
| */ |
| public void setDisplayedValues(String[] displayeValues , boolean autoCalculateMinMax) { |
| if(autoCalculateMinMax){ |
| mMinValue = 0; |
| mMaxValue = displayeValues.length - 1; |
| } |
| setDisplayedValues(displayeValues); |
| } |
| |
| @Override |
| protected float getTopFadingEdgeStrength() { |
| return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; |
| } |
| |
| @Override |
| protected float getBottomFadingEdgeStrength() { |
| return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| // make sure we show the controls only the very |
| // first time the user sees this widget |
| if (mFlingable && !isInEditMode()) { |
| // animate a bit slower the very first time |
| showInputControls(mShowInputControlsAnimimationDuration * 2); |
| } |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| removeAllCallbacks(); |
| } |
| |
| @Override |
| protected void dispatchDraw(Canvas canvas) { |
| // There is a good reason for doing this. See comments in draw(). |
| } |
| |
| @Override |
| public void draw(Canvas canvas) { |
| // Dispatch draw to our children only if we are not currently running |
| // the animation for simultaneously dimming the scroll wheel and |
| // showing in the buttons. This class takes advantage of the View |
| // implementation of fading edges effect to draw the selector wheel. |
| // However, in View.draw(), the fading is applied after all the children |
| // have been drawn and we do not want this fading to be applied to the |
| // buttons. Therefore, we draw our children after we have completed |
| // drawing ourselves. |
| super.draw(canvas); |
| |
| // Draw our children if we are not showing the selector wheel of fading |
| // it out |
| // if (mShowInputControlsAnimator.isRunning() |
| // || mSelectorWheelState != SELECTOR_WHEEL_STATE_LARGE) { |
| // long drawTime = getDrawingTime(); |
| // for (int i = 0, count = getChildCount(); i < count; i++) { |
| // View child = getChildAt(i); |
| // if (!child.isShown()) { |
| // continue; |
| // } |
| // drawChild(canvas, getChildAt(i), drawTime); |
| // } |
| // } |
| } |
| |
| @Override |
| protected void onDraw(Canvas canvas) { |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) { |
| return; |
| } |
| |
| float x = 0.0f; |
| float y = 0.0f; |
| if (!mHorizontal) { |
| x = (mRight - mLeft) / 2; |
| y = mCurrentScrollOffset; |
| } else { |
| x = mCurrentScrollOffset; |
| y = (mBottom - mTop) / 2 + mTextSize/2; |
| if(Math.abs(mDensity - 1.5f) < 0.001f){ |
| y += mHdpiPositionAdjust; |
| } |
| } |
| |
| |
| final int restoreCount = canvas.save(); |
| |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_SMALL) { |
| Rect clipBounds = canvas.getClipBounds(); |
| clipBounds.inset(0, mSelectorElementHeight); |
| canvas.clipRect(clipBounds); |
| } |
| |
| // draw the selector wheel |
| int[] selectorIndices = mSelectorIndices; |
| for (int i = 0; i < selectorIndices.length; i++) { |
| int selectorIndex = selectorIndices[i]; |
| float fNumber = 0; |
| String scrollSelectorValue = mSelectorIndexToStringCache |
| .get(selectorIndex); |
| if(i - mSelectorMiddleItemIndex > 0 ){ |
| mSelectorWheelPaint.setColor(Color.WHITE); |
| mSelectorWheelPaint.setAlpha((2*mSelectorMiddleItemIndex - i) * SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE / mSelectorMiddleItemIndex); |
| }else if(i - mSelectorMiddleItemIndex < 0 ){ |
| mSelectorWheelPaint.setColor(Color.WHITE); |
| mSelectorWheelPaint.setAlpha(i * SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE / mSelectorMiddleItemIndex); |
| }else{ |
| mSelectorWheelPaint.setColor(Color.RED); |
| mSelectorWheelPaint.setAlpha(SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE); |
| } |
| try { |
| fNumber = Float.valueOf(scrollSelectorValue).floatValue(); |
| } catch(NumberFormatException e) { |
| e.printStackTrace(); |
| } |
| |
| boolean bShowNumber = false; |
| float fWidthOfScale = mScaleWidth ; |
| float fGapBetweenNumAndScale = mGapBetweenNumAndScale * mDensity; |
| float fScaleLength = mScaleLengthShort * mDensity; |
| //every 0.5MHz show number. |
| if((int)(fNumber * 100)%50 == 0 ){ |
| if(!(selectorIndex == getMaxValue())){ |
| bShowNumber = true; |
| fScaleLength = mScaleLengthLong * mDensity; |
| } |
| } |
| if(bShowNumber){ |
| canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint); |
| } |
| canvas.drawRect(x, y+fGapBetweenNumAndScale, x+fWidthOfScale, y + fGapBetweenNumAndScale + fScaleLength , mSelectorWheelPaint); |
| |
| // } |
| if (mHorizontal) { |
| x += mSelectorElementWidth; |
| } else { |
| y += mSelectorElementHeight; |
| } |
| } |
| canvas.restoreToCount(restoreCount); |
| } |
| |
| @Override |
| public void sendAccessibilityEvent(int eventType) { |
| // Do not send accessibility events - we want the user to |
| // perceive this widget as several controls rather as a whole. |
| } |
| |
| /** |
| * Makes a measure spec that tries greedily to use the max value. |
| * |
| * @param measureSpec |
| * The measure spec. |
| * @param maxSize |
| * The max value for the size. |
| * @return A measure spec greedily imposing the max size. |
| */ |
| private int makeMeasureSpec(int measureSpec, int maxSize) { |
| if (maxSize == SIZE_UNSPECIFIED) { |
| return measureSpec; |
| } |
| final int size = MeasureSpec.getSize(measureSpec); |
| final int mode = MeasureSpec.getMode(measureSpec); |
| switch (mode) { |
| case MeasureSpec.EXACTLY: |
| return measureSpec; |
| case MeasureSpec.AT_MOST: |
| return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), |
| MeasureSpec.EXACTLY); |
| case MeasureSpec.UNSPECIFIED: |
| return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY); |
| default: |
| throw new IllegalArgumentException("Unknown measure mode: " + mode); |
| } |
| } |
| |
| /** |
| * Utility to reconcile a desired size and state, with constraints imposed |
| * by a MeasureSpec. Tries to respect the min size, unless a different size |
| * is imposed by the constraints. |
| * |
| * @param minSize |
| * The minimal desired size. |
| * @param measuredSize |
| * The currently measured size. |
| * @param measureSpec |
| * The current measure spec. |
| * @return The resolved size and state. |
| */ |
| private int resolveSizeAndStateRespectingMinSize(int minSize, |
| int measuredSize, int measureSpec) { |
| if (minSize != SIZE_UNSPECIFIED) { |
| final int desiredWidth = Math.max(minSize, measuredSize); |
| return resolveSizeAndState(desiredWidth, measureSpec, 0); |
| } else { |
| return measuredSize; |
| } |
| } |
| |
| /** |
| * Resets the selector indices and clear the cached string representation of |
| * these indices. |
| */ |
| private void initializeSelectorWheelIndices() { |
| mSelectorIndexToStringCache.clear(); |
| int[] selectorIdices = mSelectorIndices; |
| int current = getValue(); |
| for (int i = 0; i < mSelectorIndices.length; i++) { |
| int selectorIndex = current + (i - mSelectorMiddleItemIndex); |
| if (mWrapSelectorWheel) { |
| selectorIndex = getWrappedSelectorIndex(selectorIndex); |
| } |
| mSelectorIndices[i] = selectorIndex; |
| ensureCachedScrollSelectorValue(mSelectorIndices[i]); |
| } |
| } |
| |
| /** |
| * Sets the current value of this NumberPicker, and sets mPrevious to the |
| * previous value. If current is greater than mEnd less than mStart, the |
| * value of mCurrent is wrapped around. Subclasses can override this to |
| * change the wrapping behavior |
| * |
| * @param current |
| * the new value of the NumberPicker |
| */ |
| private void changeCurrent(int current) { |
| if (mValue == current) { |
| return; |
| } |
| // Wrap around the values if we go past the start or end |
| if (mWrapSelectorWheel) { |
| current = getWrappedSelectorIndex(current); |
| } |
| int previous = mValue; |
| setValue(current); |
| notifyChange(previous, current); |
| } |
| |
| /** |
| * Changes the current value by one which is increment or decrement based on |
| * the passes argument. |
| * |
| * @param increment |
| * True to increment, false to decrement. |
| */ |
| private void changeCurrentByOne(boolean increment) { |
| if (mFlingable) { |
| mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); |
| mPreviousScrollerY = 0; |
| mPreviousScrollerX = 0; |
| forceCompleteChangeCurrentByOneViaScroll(); |
| if (increment) { |
| if (mHorizontal) { |
| mFlingScroller.startScroll(0, 0, -mSelectorElementHeight, |
| 0, CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); |
| } else { |
| mFlingScroller.startScroll(0, 0, 0, |
| -mSelectorElementHeight, |
| CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); |
| } |
| |
| } else { |
| if (mHorizontal) { |
| mFlingScroller.startScroll(0, 0, mSelectorElementHeight, 0, |
| CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); |
| } else { |
| mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, |
| CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); |
| } |
| |
| } |
| invalidate(); |
| } else { |
| if (increment) { |
| changeCurrent(mValue + 1); |
| } else { |
| changeCurrent(mValue - 1); |
| } |
| } |
| } |
| |
| /** |
| * Ensures that if we are in the process of changing the current value by |
| * one via scrolling the scroller gets to its final state and the value is |
| * updated. |
| */ |
| private void forceCompleteChangeCurrentByOneViaScroll() { |
| Scroller scroller = mFlingScroller; |
| if (!scroller.isFinished()) { |
| if (mHorizontal) { |
| final int xBeforeAbort = scroller.getCurrX(); |
| scroller.abortAnimation(); |
| final int xDelta = scroller.getCurrX() - xBeforeAbort; |
| scrollBy(xDelta, 0); |
| } else { |
| final int yBeforeAbort = scroller.getCurrY(); |
| scroller.abortAnimation(); |
| final int yDelta = scroller.getCurrY() - yBeforeAbort; |
| scrollBy(0, yDelta); |
| } |
| } |
| } |
| |
| /** |
| * Sets the <code>alpha</code> of the {@link Paint} for drawing the selector |
| * wheel. |
| */ |
| @SuppressWarnings("unused") |
| // Called via reflection |
| private void setSelectorPaintAlpha(int alpha) { |
| mSelectorWheelPaint.setAlpha(alpha); |
| invalidate(); |
| } |
| |
| /** |
| * @return If the <code>event</code> is in the visible <code>view</code>. |
| */ |
| private boolean isEventInVisibleViewHitRect(MotionEvent event, View view) { |
| if (view.getVisibility() == VISIBLE) { |
| view.getHitRect(mTempRect); |
| return mTempRect.contains((int) event.getX(), (int) event.getY()); |
| } |
| return false; |
| } |
| |
| /** |
| * Sets the <code>selectorWheelState</code>. |
| */ |
| private void setSelectorWheelState(int selectorWheelState) { |
| mSelectorWheelState = selectorWheelState; |
| if (selectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { |
| mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); |
| } |
| |
| if (mFlingable && selectorWheelState == SELECTOR_WHEEL_STATE_LARGE |
| && AccessibilityManager.getInstance(mContext).isEnabled()) { |
| AccessibilityManager.getInstance(mContext).interrupt(); |
| String text = mContext |
| .getString(R.string.number_picker_increment_scroll_action); |
| } |
| } |
| |
| private void initializeSelectorWheel() { |
| initializeSelectorWheelIndices(); |
| int[] selectorIndices = mSelectorIndices; |
| int totalTextHeight = selectorIndices.length * mTextSize; |
| int totalTextWidth = (selectorIndices.length - 1) * 2; |
| // set it horizontal |
| float totalTextGapHeight = (mBottom - mTop) - totalTextHeight; |
| float totalTextGapWidth = (mRight - mLeft) - totalTextWidth; |
| |
| float textGapCount = selectorIndices.length - 1; |
| if (mHorizontal) { |
| mSelectorTextGapWidth = (int) (totalTextGapWidth / textGapCount); |
| Log.d(TAG,"mSelectorTextGapWidth :" + mSelectorTextGapWidth); |
| mSelectorElementWidth = 2 + mSelectorTextGapWidth; |
| mInitialScrollOffset = INIT_SCROLL_OFFSET_HORIZONTAL; |
| } else { |
| mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f); |
| mSelectorElementHeight = mTextSize + mSelectorTextGapHeight; |
| mInitialScrollOffset = INIT_SCROLL_OFFSET_VERTICAL; |
| } |
| mCurrentScrollOffset = mInitialScrollOffset; |
| |
| } |
| |
| private void initializeFadingEdges() { |
| setVerticalFadingEdgeEnabled(true); |
| setFadingEdgeLength((mBottom - mTop - mTextSize) / 2); |
| } |
| |
| /** |
| * Callback invoked upon completion of a given <code>scroller</code>. |
| */ |
| private void onScrollerFinished(Scroller scroller) { |
| |
| if(mOnScrollFinishListener != null){ |
| mOnScrollFinishListener.onScrollFinish(mValue); |
| } |
| if (scroller == mFlingScroller) { |
| if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { |
| postAdjustScrollerCommand(0); |
| onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); |
| } else { |
| // updateInputTextView(); |
| // fadeSelectorWheel(mShowInputControlsAnimimationDuration); |
| } |
| } else { |
| // updateInputTextView(); |
| // showInputControls(mShowInputControlsAnimimationDuration); |
| } |
| } |
| |
| /** |
| * Handles transition to a given <code>scrollState</code> |
| */ |
| private void onScrollStateChange(int scrollState) { |
| if (mScrollState == scrollState) { |
| return; |
| } |
| mScrollState = scrollState; |
| if (mOnScrollListener != null) { |
| |
| mOnScrollListener.onScrollStateChange(this, scrollState); |
| } |
| } |
| |
| /** |
| * Flings the selector with the given <code>velocityY</code>. |
| */ |
| private void fling(int velocity) { |
| mPreviousScrollerY = 0; |
| mPreviousScrollerX = 0; |
| int velocityY = velocity; |
| int velocityX = velocity; |
| if (mHorizontal) { |
| if (velocityX > 0) { |
| mFlingScroller.fling(0, 0, velocityX, 0, 0, Integer.MAX_VALUE, |
| 0, 0); |
| } else { |
| mFlingScroller.fling(Integer.MAX_VALUE, 0, velocityX, 0, 0, |
| Integer.MAX_VALUE, 0, 0); |
| } |
| } else { |
| if (velocityY > 0) { |
| mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, |
| Integer.MAX_VALUE); |
| } else { |
| mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, |
| 0, Integer.MAX_VALUE); |
| } |
| } |
| |
| invalidate(); |
| } |
| |
| /** |
| * Hides the input controls which is the up/down arrows and the text field. |
| */ |
| private void hideInputControls() { |
| // mShowInputControlsAnimator.cancel(); |
| // mIncrementButton.setVisibility(INVISIBLE); |
| // mDecrementButton.setVisibility(INVISIBLE); |
| // mInputText.setVisibility(INVISIBLE); |
| } |
| |
| /** |
| * Show the input controls by making them visible and animating the alpha |
| * property up/down arrows. |
| * |
| * @param animationDuration |
| * The duration of the animation. |
| */ |
| private void showInputControls(long animationDuration) { |
| // updateIncrementAndDecrementButtonsVisibilityState(); |
| // mInputText.setVisibility(VISIBLE); |
| // mShowInputControlsAnimator.setDuration(animationDuration); |
| // mShowInputControlsAnimator.start(); |
| } |
| |
| /** |
| * Fade the selector wheel via an animation. |
| * |
| * @param animationDuration |
| * The duration of the animation. |
| */ |
| // mark this 1 |
| private void fadeSelectorWheel(long animationDuration) { |
| // mInputText.setVisibility(VISIBLE); |
| // mDimSelectorWheelAnimator.setDuration(animationDuration); |
| // mDimSelectorWheelAnimator.start(); |
| } |
| |
| /** |
| * Updates the visibility state of the increment and decrement buttons. |
| */ |
| private void updateIncrementAndDecrementButtonsVisibilityState() { |
| if (mWrapSelectorWheel || mValue < mMaxValue) { |
| // mIncrementButton.setVisibility(VISIBLE); |
| } else { |
| // mIncrementButton.setVisibility(INVISIBLE); |
| } |
| if (mWrapSelectorWheel || mValue > mMinValue) { |
| // mDecrementButton.setVisibility(VISIBLE); |
| } else { |
| // mDecrementButton.setVisibility(INVISIBLE); |
| } |
| } |
| |
| /** |
| * @return The wrapped index <code>selectorIndex</code> value. |
| */ |
| private int getWrappedSelectorIndex(int selectorIndex) { |
| if (selectorIndex > mMaxValue) { |
| return mMinValue + (selectorIndex - mMaxValue) |
| % (mMaxValue - mMinValue) - 1; |
| } else if (selectorIndex < mMinValue) { |
| return mMaxValue - (mMinValue - selectorIndex) |
| % (mMaxValue - mMinValue) + 1; |
| } |
| return selectorIndex; |
| } |
| |
| /** |
| * Increments the <code>selectorIndices</code> whose string representations |
| * will be displayed in the selector. |
| */ |
| private void incrementSelectorIndices(int[] selectorIndices) { |
| for (int i = 0; i < selectorIndices.length - 1; i++) { |
| selectorIndices[i] = selectorIndices[i + 1]; |
| } |
| int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1; |
| if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) { |
| nextScrollSelectorIndex = mMinValue; |
| } |
| selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex; |
| ensureCachedScrollSelectorValue(nextScrollSelectorIndex); |
| } |
| |
| /** |
| * Decrements the <code>selectorIndices</code> whose string representations |
| * will be displayed in the selector. |
| */ |
| private void decrementSelectorIndices(int[] selectorIndices) { |
| for (int i = selectorIndices.length - 1; i > 0; i--) { |
| selectorIndices[i] = selectorIndices[i - 1]; |
| } |
| int nextScrollSelectorIndex = selectorIndices[1] - 1; |
| if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) { |
| nextScrollSelectorIndex = mMaxValue; |
| } |
| selectorIndices[0] = nextScrollSelectorIndex; |
| ensureCachedScrollSelectorValue(nextScrollSelectorIndex); |
| } |
| |
| /** |
| * Ensures we have a cached string representation of the given <code> |
| * selectorIndex</code> |
| * to avoid multiple instantiations of the same string. |
| */ |
| private void ensureCachedScrollSelectorValue(int selectorIndex) { |
| SparseArray<String> cache = mSelectorIndexToStringCache; |
| String scrollSelectorValue = cache.get(selectorIndex); |
| if (scrollSelectorValue != null) { |
| return; |
| } |
| if (selectorIndex < mMinValue || selectorIndex > mMaxValue) { |
| scrollSelectorValue = ""; |
| } else { |
| if (mDisplayedValues != null) { |
| int displayedValueIndex = selectorIndex - mMinValue; |
| scrollSelectorValue = mDisplayedValues[displayedValueIndex]; |
| } else { |
| scrollSelectorValue = formatNumber(selectorIndex); |
| } |
| } |
| cache.put(selectorIndex, scrollSelectorValue); |
| } |
| |
| private String formatNumber(int value) { |
| return (mFormatter != null) ? mFormatter.format(value) : String |
| .valueOf(value); |
| } |
| |
| private void validateInputTextView(View v) { |
| String str = String.valueOf(((TextView) v).getText()); |
| if (TextUtils.isEmpty(str)) { |
| // Restore to the old value as we don't allow empty values |
| updateInputTextView(); |
| } else { |
| // Check the new value and ensure it's in range |
| int current = getSelectedPos(str.toString()); |
| changeCurrent(current); |
| } |
| } |
| |
| /** |
| * Updates the view of this NumberPicker. If displayValues were specified in |
| * the string corresponding to the index specified by the current value will |
| * be returned. Otherwise, the formatter specified in {@link #setFormatter} |
| * will be used to format the number. |
| */ |
| private void updateInputTextView() { |
| /* |
| * If we don't have displayed values then use the current number else |
| * find the correct value in the displayed values for the current |
| * number. |
| */ |
| // mark this 2 |
| if (mDisplayedValues == null) { |
| // mInputText.setText(TWO_DIGIT_FORMATTER.format(mValue)); |
| } else { |
| // mInputText.setText(mDisplayedValues[mValue - mMinValue]); |
| } |
| // mInputText.setSelection(mInputText.getText().length()); |
| |
| if (mFlingable |
| && AccessibilityManager.getInstance(mContext).isEnabled()) { |
| // String text = mContext.getString( |
| // R.string.number_picker_increment_scroll_mode, mInputText |
| // .getText()); |
| // mInputText.setContentDescription(text); |
| } |
| } |
| |
| /** |
| * Notifies the listener, if registered, of a change of the value of this |
| * NumberPicker. |
| */ |
| private void notifyChange(int previous, int current) { |
| if (mOnValueChangeListener != null) { |
| mOnValueChangeListener.onValueChange(this, previous, mValue); |
| } |
| } |
| |
| /** |
| * Posts a command for changing the current value by one. |
| * |
| * @param increment |
| * Whether to increment or decrement the value. |
| */ |
| private void postChangeCurrentByOneFromLongPress(boolean increment) { |
| // mInputText.clearFocus(); |
| removeAllCallbacks(); |
| if (mChangeCurrentByOneFromLongPressCommand == null) { |
| mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand(); |
| } |
| mChangeCurrentByOneFromLongPressCommand.setIncrement(increment); |
| post(mChangeCurrentByOneFromLongPressCommand); |
| } |
| |
| /** |
| * Removes all pending callback from the message queue. |
| */ |
| private void removeAllCallbacks() { |
| if (mChangeCurrentByOneFromLongPressCommand != null) { |
| removeCallbacks(mChangeCurrentByOneFromLongPressCommand); |
| } |
| if (mAdjustScrollerCommand != null) { |
| removeCallbacks(mAdjustScrollerCommand); |
| } |
| if (mSetSelectionCommand != null) { |
| removeCallbacks(mSetSelectionCommand); |
| } |
| } |
| |
| /** |
| * @return The selected index given its displayed <code>value</code>. |
| */ |
| private int getSelectedPos(String value) { |
| if (mDisplayedValues == null) { |
| try { |
| return Integer.parseInt(value); |
| } catch (NumberFormatException e) { |
| // Ignore as if it's not a number we don't care |
| } |
| } else { |
| for (int i = 0; i < mDisplayedValues.length; i++) { |
| // Don't force the user to type in jan when ja will do |
| value = value.toLowerCase(); |
| if (mDisplayedValues[i].toLowerCase().startsWith(value)) { |
| return mMinValue + i; |
| } |
| } |
| |
| /* |
| * The user might have typed in a number into the month field i.e. |
| * 10 instead of OCT so support that too. |
| */ |
| try { |
| return Integer.parseInt(value); |
| } catch (NumberFormatException e) { |
| |
| // Ignore as if it's not a number we don't care |
| } |
| } |
| return mMinValue; |
| } |
| |
| /** |
| * Posts an {@link SetSelectionCommand} from the given <code>selectionStart |
| * </code> to |
| * <code>selectionEnd</code>. |
| */ |
| private void postSetSelectionCommand(int selectionStart, int selectionEnd) { |
| if (mSetSelectionCommand == null) { |
| mSetSelectionCommand = new SetSelectionCommand(); |
| } else { |
| removeCallbacks(mSetSelectionCommand); |
| } |
| mSetSelectionCommand.mSelectionStart = selectionStart; |
| mSetSelectionCommand.mSelectionEnd = selectionEnd; |
| post(mSetSelectionCommand); |
| } |
| |
| /** |
| * Posts an {@link AdjustScrollerCommand} within the given <code> |
| * delayMillis</code> |
| * . |
| */ |
| private void postAdjustScrollerCommand(int delayMillis) { |
| if (mAdjustScrollerCommand == null) { |
| mAdjustScrollerCommand = new AdjustScrollerCommand(); |
| } else { |
| removeCallbacks(mAdjustScrollerCommand); |
| } |
| postDelayed(mAdjustScrollerCommand, delayMillis); |
| } |
| |
| /** |
| * Filter for accepting only valid indices or prefixes of the string |
| * representation of valid indices. |
| */ |
| class InputTextFilter extends NumberKeyListener { |
| |
| // XXX This doesn't allow for range limits when controlled by a |
| // soft input method! |
| public int getInputType() { |
| return InputType.TYPE_CLASS_TEXT; |
| } |
| |
| @Override |
| protected char[] getAcceptedChars() { |
| return DIGIT_CHARACTERS; |
| } |
| |
| @Override |
| public CharSequence filter(CharSequence source, int start, int end, |
| Spanned dest, int dstart, int dend) { |
| if (mDisplayedValues == null) { |
| CharSequence filtered = super.filter(source, start, end, dest, |
| dstart, dend); |
| if (filtered == null) { |
| filtered = source.subSequence(start, end); |
| } |
| |
| String result = String.valueOf(dest.subSequence(0, dstart)) |
| + filtered + dest.subSequence(dend, dest.length()); |
| |
| if ("".equals(result)) { |
| return result; |
| } |
| int val = getSelectedPos(result); |
| |
| /* |
| * Ensure the user can't type in a value greater than the max |
| * allowed. We have to allow less than min as the user might |
| * want to delete some numbers and then type a new number. |
| */ |
| if (val > mMaxValue) { |
| return ""; |
| } else { |
| return filtered; |
| } |
| } else { |
| CharSequence filtered = String.valueOf(source.subSequence( |
| start, end)); |
| if (TextUtils.isEmpty(filtered)) { |
| return ""; |
| } |
| String result = String.valueOf(dest.subSequence(0, dstart)) |
| + filtered + dest.subSequence(dend, dest.length()); |
| String str = String.valueOf(result).toLowerCase(); |
| for (String val : mDisplayedValues) { |
| String valLowerCase = val.toLowerCase(); |
| if (valLowerCase.startsWith(str)) { |
| postSetSelectionCommand(result.length(), val.length()); |
| return val.subSequence(dstart, val.length()); |
| } |
| } |
| return ""; |
| } |
| } |
| } |
| |
| /** |
| * Command for setting the input text selection. |
| */ |
| class SetSelectionCommand implements Runnable { |
| private int mSelectionStart; |
| |
| private int mSelectionEnd; |
| |
| public void run() { |
| // mInputText.setSelection(mSelectionStart, mSelectionEnd); |
| } |
| } |
| |
| /** |
| * Command for adjusting the scroller to show in its center the closest of |
| * the displayed items. |
| */ |
| class AdjustScrollerCommand implements Runnable { |
| public void run() { |
| mPreviousScrollerY = 0; |
| mPreviousScrollerX = 0; |
| if (mInitialScrollOffset == mCurrentScrollOffset) { |
| return; |
| } |
| if (mHorizontal) { |
| // adjust to the closest value |
| int deltaX = mInitialScrollOffset - mCurrentScrollOffset; |
| mAdjustScroller.startScroll(0, 0, deltaX, 0, |
| SELECTOR_ADJUSTMENT_DURATION_MILLIS); |
| } else { |
| // adjust to the closest value |
| int deltaY = mInitialScrollOffset - mCurrentScrollOffset; |
| if (Math.abs(deltaY) > mSelectorElementHeight / 2) { |
| deltaY += (deltaY > 0) ? -mSelectorElementHeight |
| : mSelectorElementHeight; |
| } |
| mAdjustScroller.startScroll(0, 0, 0, deltaY, |
| SELECTOR_ADJUSTMENT_DURATION_MILLIS); |
| } |
| |
| invalidate(); |
| } |
| } |
| |
| /** |
| * Command for changing the current value from a long press by one. |
| */ |
| class ChangeCurrentByOneFromLongPressCommand implements Runnable { |
| private boolean mIncrement; |
| |
| private void setIncrement(boolean increment) { |
| mIncrement = increment; |
| } |
| |
| public void run() { |
| changeCurrentByOne(mIncrement); |
| postDelayed(this, mLongPressUpdateInterval); |
| } |
| } |
| } |