FileDocCategorySizeDatePackage
VerticalTextSpinner.javaAPI DocAndroid 1.5 API17302Wed May 06 22:41:56 BST 2009com.android.internal.widget

VerticalTextSpinner

public class VerticalTextSpinner extends android.view.View

Fields Summary
private static final int
SELECTOR_ARROW_HEIGHT
private static final int
TEXT_SPACING
private static final int
TEXT_MARGIN_RIGHT
private static final int
TEXT_SIZE
private static final int
TEXT1_Y
private static final int
TEXT2_Y
private static final int
TEXT3_Y
private static final int
TEXT4_Y
private static final int
TEXT5_Y
private static final int
SCROLL_MODE_NONE
private static final int
SCROLL_MODE_UP
private static final int
SCROLL_MODE_DOWN
private static final long
DEFAULT_SCROLL_INTERVAL_MS
private static final int
SCROLL_DISTANCE
private static final int
MIN_ANIMATIONS
private final android.graphics.drawable.Drawable
mBackgroundFocused
private final android.graphics.drawable.Drawable
mSelectorFocused
private final android.graphics.drawable.Drawable
mSelectorNormal
private final int
mSelectorDefaultY
private final int
mSelectorMinY
private final int
mSelectorMaxY
private final int
mSelectorHeight
private final android.text.TextPaint
mTextPaintDark
private final android.text.TextPaint
mTextPaintLight
private int
mSelectorY
private android.graphics.drawable.Drawable
mSelector
private int
mDownY
private boolean
isDraggingSelector
private int
mScrollMode
private long
mScrollInterval
private boolean
mIsAnimationRunning
private boolean
mStopAnimation
private boolean
mWrapAround
private int
mTotalAnimatedDistance
private int
mNumberOfAnimations
private long
mDelayBetweenAnimations
private int
mDistanceOfEachAnimation
private String[]
mTextList
private int
mCurrentSelectedPos
private OnChangedListener
mListener
private String
mText1
private String
mText2
private String
mText3
private String
mText4
private String
mText5
Constructors Summary
public VerticalTextSpinner(android.content.Context context)

    
       
         
                       
    
    
       
        this(context, null);
    
public VerticalTextSpinner(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public VerticalTextSpinner(android.content.Context context, android.util.AttributeSet attrs, int defStyle)

        super(context, attrs, defStyle);
        
        mBackgroundFocused = context.getResources().getDrawable(com.android.internal.R.drawable.pickerbox_background);
        mSelectorFocused = context.getResources().getDrawable(com.android.internal.R.drawable.pickerbox_selected);
        mSelectorNormal = context.getResources().getDrawable(com.android.internal.R.drawable.pickerbox_unselected);
        
        mSelectorHeight = mSelectorFocused.getIntrinsicHeight();
        mSelectorDefaultY = (mBackgroundFocused.getIntrinsicHeight() - mSelectorHeight) / 2;
        mSelectorMinY = 0;
        mSelectorMaxY = mBackgroundFocused.getIntrinsicHeight() - mSelectorHeight;
        
        mSelector = mSelectorNormal;
        mSelectorY = mSelectorDefaultY;
        
        mTextPaintDark = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaintDark.setTextSize(TEXT_SIZE);
        mTextPaintDark.setColor(context.getResources().getColor(com.android.internal.R.color.primary_text_light));
        
        mTextPaintLight = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaintLight.setTextSize(TEXT_SIZE);
        mTextPaintLight.setColor(context.getResources().getColor(com.android.internal.R.color.secondary_text_dark));
        
        mScrollMode = SCROLL_MODE_NONE;
        mScrollInterval = DEFAULT_SCROLL_INTERVAL_MS;
        calculateAnimationValues();
    
Methods Summary
private voidcalculateAnimationValues()

        mNumberOfAnimations = (int) mScrollInterval / SCROLL_DISTANCE;
        if (mNumberOfAnimations < MIN_ANIMATIONS) {
            mNumberOfAnimations = MIN_ANIMATIONS;
            mDistanceOfEachAnimation = SCROLL_DISTANCE / mNumberOfAnimations;
            mDelayBetweenAnimations = 0;
        } else {
            mDistanceOfEachAnimation = SCROLL_DISTANCE / mNumberOfAnimations;
            mDelayBetweenAnimations = mScrollInterval / mNumberOfAnimations;
        }
    
private voidcalculateTextPositions()
Called every time the text items or current position changes. We calculate store we don't have to calculate onDraw.

        mText1 = getTextToDraw(-2);
        mText2 = getTextToDraw(-1);
        mText3 = getTextToDraw(0);
        mText4 = getTextToDraw(1);
        mText5 = getTextToDraw(2);
    
private booleancanScrollDown()

        return (mCurrentSelectedPos > 0) || mWrapAround;
    
private booleancanScrollUp()

        return ((mCurrentSelectedPos < (mTextList.length - 1)) || mWrapAround);
    
private voiddrawText(android.graphics.Canvas canvas, java.lang.String text, int y, android.text.TextPaint paint)

        int width = (int) paint.measureText(text);
        int x = getMeasuredWidth() - width - TEXT_MARGIN_RIGHT;
        canvas.drawText(text, x, y, paint);
    
public intgetCurrentSelectedPos()

        return mCurrentSelectedPos;
    
private intgetNewIndex(int offset)

        int index = mCurrentSelectedPos + offset;
        if (index < 0) {
            if (mWrapAround) {
                index += mTextList.length;
            } else {
                return -1;
            }
        } else if (index >= mTextList.length) {
            if (mWrapAround) {
                index -= mTextList.length;
            } else {
                return -1;
            }
        }
        return index;
    
private java.lang.StringgetTextToDraw(int offset)

        int index = getNewIndex(offset);
        if (index < 0) {
            return "";
        }
        return mTextList[index];
    
protected voidonDraw(android.graphics.Canvas canvas)

        
        /* The bounds of the selector */
        final int selectorLeft = 0;
        final int selectorTop = mSelectorY;
        final int selectorRight = mMeasuredWidth;
        final int selectorBottom = mSelectorY + mSelectorHeight;
        
        /* Draw the selector */
        mSelector.setBounds(selectorLeft, selectorTop, selectorRight, selectorBottom);
        mSelector.draw(canvas);
        
        if (mTextList == null) {
            
            /* We're not setup with values so don't draw anything else */
            return;
        }
        
        final TextPaint textPaintDark = mTextPaintDark;
        if (hasFocus()) {
            
            /* The bounds of the top area where the text should be light */
            final int topLeft = 0;
            final int topTop = 0;
            final int topRight = selectorRight;
            final int topBottom = selectorTop + SELECTOR_ARROW_HEIGHT;

            /* Assign a bunch of local finals for performance */
            final String text1 = mText1;
            final String text2 = mText2;
            final String text3 = mText3;
            final String text4 = mText4;
            final String text5 = mText5;
            final TextPaint textPaintLight = mTextPaintLight;
            
            /*
             * Draw the 1st, 2nd and 3rd item in light only, clip it so it only
             * draws in the area above the selector
             */
            canvas.save();
            canvas.clipRect(topLeft, topTop, topRight, topBottom);
            drawText(canvas, text1, TEXT1_Y
                    + mTotalAnimatedDistance, textPaintLight);
            drawText(canvas, text2, TEXT2_Y
                    + mTotalAnimatedDistance, textPaintLight);
            drawText(canvas, text3,
                    TEXT3_Y + mTotalAnimatedDistance, textPaintLight);
            canvas.restore();

            /*
             * Draw the 2nd, 3rd and 4th clipped to the selector bounds in dark
             * paint
             */
            canvas.save();
            canvas.clipRect(selectorLeft, selectorTop + SELECTOR_ARROW_HEIGHT,
                    selectorRight, selectorBottom - SELECTOR_ARROW_HEIGHT);
            drawText(canvas, text2, TEXT2_Y
                    + mTotalAnimatedDistance, textPaintDark);
            drawText(canvas, text3,
                    TEXT3_Y + mTotalAnimatedDistance, textPaintDark);
            drawText(canvas, text4,
                    TEXT4_Y + mTotalAnimatedDistance, textPaintDark);
            canvas.restore();

            /* The bounds of the bottom area where the text should be light */
            final int bottomLeft = 0;
            final int bottomTop = selectorBottom - SELECTOR_ARROW_HEIGHT;
            final int bottomRight = selectorRight;
            final int bottomBottom = mMeasuredHeight;

            /*
             * Draw the 3rd, 4th and 5th in white text, clip it so it only draws
             * in the area below the selector.
             */
            canvas.save();
            canvas.clipRect(bottomLeft, bottomTop, bottomRight, bottomBottom);
            drawText(canvas, text3,
                    TEXT3_Y + mTotalAnimatedDistance, textPaintLight);
            drawText(canvas, text4,
                    TEXT4_Y + mTotalAnimatedDistance, textPaintLight);
            drawText(canvas, text5,
                    TEXT5_Y + mTotalAnimatedDistance, textPaintLight);
            canvas.restore();
            
        } else {
            drawText(canvas, mText3, TEXT3_Y, textPaintDark);
        }
        if (mIsAnimationRunning) {
            if ((Math.abs(mTotalAnimatedDistance) + mDistanceOfEachAnimation) > SCROLL_DISTANCE) {
                mTotalAnimatedDistance = 0;
                if (mScrollMode == SCROLL_MODE_UP) {
                    int oldPos = mCurrentSelectedPos;
                    int newPos = getNewIndex(1);
                    if (newPos >= 0) {
                        mCurrentSelectedPos = newPos;
                        if (mListener != null) {
                            mListener.onChanged(this, oldPos, mCurrentSelectedPos, mTextList);
                        }
                    }
                    if (newPos < 0 || ((newPos >= mTextList.length - 1) && !mWrapAround)) {
                        mStopAnimation = true;
                    }
                    calculateTextPositions();
                } else if (mScrollMode == SCROLL_MODE_DOWN) {
                    int oldPos = mCurrentSelectedPos;
                    int newPos = getNewIndex(-1);
                    if (newPos >= 0) {
                        mCurrentSelectedPos = newPos;
                        if (mListener != null) {
                            mListener.onChanged(this, oldPos, mCurrentSelectedPos, mTextList);
                        }
                    }
                    if (newPos < 0 || (newPos == 0 && !mWrapAround)) {
                        mStopAnimation = true;
                    }
                    calculateTextPositions();
                }
                if (mStopAnimation) {
                    final int previousScrollMode = mScrollMode;
                    
                    /* No longer scrolling, we wait till the current animation
                     * completes then we stop.
                     */
                    mIsAnimationRunning = false;
                    mStopAnimation = false;
                    mScrollMode = SCROLL_MODE_NONE;
                    
                    /* If the current selected item is an empty string
                     * scroll past it.
                     */
                    if ("".equals(mTextList[mCurrentSelectedPos])) {
                       mScrollMode = previousScrollMode;
                       scroll();
                       mStopAnimation = true;
                    }
                }
            } else {
                if (mScrollMode == SCROLL_MODE_UP) {
                    mTotalAnimatedDistance -= mDistanceOfEachAnimation;
                } else if (mScrollMode == SCROLL_MODE_DOWN) {
                    mTotalAnimatedDistance += mDistanceOfEachAnimation;
                }
            }
            if (mDelayBetweenAnimations > 0) {
                postInvalidateDelayed(mDelayBetweenAnimations);
            } else {
                invalidate();
            }
        }
    
protected voidonFocusChanged(boolean gainFocus, int direction, android.graphics.Rect previouslyFocusedRect)

        if (gainFocus) {
            setBackgroundDrawable(mBackgroundFocused);
            mSelector = mSelectorFocused;
        } else {
            setBackgroundDrawable(null);
            mSelector = mSelectorNormal;
            mSelectorY = mSelectorDefaultY;
        }
    
public booleanonKeyDown(int keyCode, android.view.KeyEvent event)

        
        /* This is a bit confusing, when we get the key event
         * DPAD_DOWN we actually roll the spinner up. When the
         * key event is DPAD_UP we roll the spinner down.
         */
        if ((keyCode == KeyEvent.KEYCODE_DPAD_UP) && canScrollDown()) {
            mScrollMode = SCROLL_MODE_DOWN;
            scroll();
            mStopAnimation = true;
            return true;
        } else if ((keyCode == KeyEvent.KEYCODE_DPAD_DOWN) && canScrollUp()) {
            mScrollMode = SCROLL_MODE_UP;
            scroll();
            mStopAnimation = true;
            return true;
        }
        return super.onKeyDown(keyCode, event);
    
public booleanonTouchEvent(android.view.MotionEvent event)

        
        final int action = event.getAction();
        final int y = (int) event.getY();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            requestFocus();
            mDownY = y;
            isDraggingSelector = (y >= mSelectorY) && (y <= (mSelectorY + mSelector.getIntrinsicHeight()));
            break;

        case MotionEvent.ACTION_MOVE:
            if (isDraggingSelector) {
                int top = mSelectorDefaultY + (y - mDownY);
                if (top <= mSelectorMinY && canScrollDown()) {
                    mSelectorY = mSelectorMinY;
                    mStopAnimation = false;
                    if (mScrollMode != SCROLL_MODE_DOWN) {
                        mScrollMode = SCROLL_MODE_DOWN;
                        scroll();
                    }
                } else if (top >= mSelectorMaxY && canScrollUp()) {
                    mSelectorY = mSelectorMaxY;
                    mStopAnimation = false;
                    if (mScrollMode != SCROLL_MODE_UP) {
                        mScrollMode = SCROLL_MODE_UP;
                        scroll();
                    }
                } else {
                    mSelectorY = top;
                    mStopAnimation = true;
                }
            }
            break;
            
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
        default:
            mSelectorY = mSelectorDefaultY;
            mStopAnimation = true;
            invalidate();
            break;
        }
        return true;
    
private voidscroll()

        if (mIsAnimationRunning) {
            return;
        }
        mTotalAnimatedDistance = 0;
        mIsAnimationRunning = true;
        invalidate();
    
public voidsetItems(java.lang.String[] textList)

        mTextList = textList;
        calculateTextPositions();
    
public voidsetOnChangeListener(com.android.internal.widget.VerticalTextSpinner$OnChangedListener listener)

        mListener = listener;
    
public voidsetScrollInterval(long interval)

        mScrollInterval = interval;
        calculateAnimationValues();
    
public voidsetSelectedPos(int selectedPos)

        mCurrentSelectedPos = selectedPos;
        calculateTextPositions();
        postInvalidate();
    
public voidsetWrapAround(boolean wrap)

        mWrapAround = wrap;