OSDN Git Service

Move VerticalTextSpinner in to the music app, since that's the only thing using it...
authorMarco Nelissen <marcone@google.com>
Tue, 5 Jan 2010 22:13:21 +0000 (14:13 -0800)
committerMarco Nelissen <marcone@google.com>
Tue, 5 Jan 2010 22:14:01 +0000 (14:14 -0800)
src/com/android/music/VerticalTextSpinner.java [new file with mode: 0644]
src/com/android/music/WeekSelector.java

diff --git a/src/com/android/music/VerticalTextSpinner.java b/src/com/android/music/VerticalTextSpinner.java
new file mode 100644 (file)
index 0000000..38da814
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * 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.android.music;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+
+public class VerticalTextSpinner extends View {
+
+    private static final int SELECTOR_ARROW_HEIGHT = 15;
+    
+    private static final int TEXT_SPACING = 18;
+    private static final int TEXT_MARGIN_RIGHT = 25;
+    private static final int TEXT_SIZE = 22;
+    
+    /* Keep the calculations as this is really a for loop from
+     * -2 to 2 but precalculated so we don't have to do in the onDraw.
+     */
+    private static final int TEXT1_Y = (TEXT_SIZE * (-2 + 2)) + (TEXT_SPACING * (-2 + 1));
+    private static final int TEXT2_Y = (TEXT_SIZE * (-1 + 2)) + (TEXT_SPACING * (-1 + 1));
+    private static final int TEXT3_Y = (TEXT_SIZE * (0 + 2)) + (TEXT_SPACING * (0 + 1));
+    private static final int TEXT4_Y = (TEXT_SIZE * (1 + 2)) + (TEXT_SPACING * (1 + 1));
+    private static final int TEXT5_Y = (TEXT_SIZE * (2 + 2)) + (TEXT_SPACING * (2 + 1));
+    
+    private static final int SCROLL_MODE_NONE = 0;
+    private static final int SCROLL_MODE_UP = 1;
+    private static final int SCROLL_MODE_DOWN = 2;
+    
+    private static final long DEFAULT_SCROLL_INTERVAL_MS = 400;
+    private static final int SCROLL_DISTANCE = TEXT_SIZE + TEXT_SPACING;
+    private static final int MIN_ANIMATIONS = 4;
+    
+    private final Drawable mBackgroundFocused;
+    private final Drawable mSelectorFocused;
+    private final Drawable mSelectorNormal;
+    private final int mSelectorDefaultY;
+    private final int mSelectorMinY;
+    private final int mSelectorMaxY;
+    private final int mSelectorHeight;
+    private final TextPaint mTextPaintDark;
+    private final TextPaint mTextPaintLight;
+    
+    private int mSelectorY;
+    private Drawable mSelector;
+    private int mDownY;
+    private boolean isDraggingSelector;
+    private int mScrollMode;
+    private long mScrollInterval;
+    private boolean mIsAnimationRunning;
+    private boolean mStopAnimation;
+    private boolean mWrapAround = true;
+    
+    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;
+    
+    public interface OnChangedListener {
+        void onChanged(
+                VerticalTextSpinner spinner, int oldPos, int newPos, String[] items);
+    }
+    
+    public VerticalTextSpinner(Context context) {
+        this(context, null);
+    }
+    
+    public VerticalTextSpinner(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public VerticalTextSpinner(Context context, 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();
+    }
+    
+    public void setOnChangeListener(OnChangedListener listener) {
+        mListener = listener;
+    }
+    
+    public void setItems(String[] textList) {
+        mTextList = textList;
+        calculateTextPositions();
+    }
+    
+    public void setSelectedPos(int selectedPos) {
+        mCurrentSelectedPos = selectedPos;
+        calculateTextPositions();
+        postInvalidate();
+    }
+    
+    public void setScrollInterval(long interval) {
+        mScrollInterval = interval;
+        calculateAnimationValues();
+    }
+    
+    public void setWrapAround(boolean wrap) {
+        mWrapAround = wrap;
+    }
+    
+    @Override
+    public boolean onKeyDown(int keyCode, 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);
+    }
+
+    private boolean canScrollDown() {
+        return (mCurrentSelectedPos > 0) || mWrapAround;
+    }
+
+    private boolean canScrollUp() {
+        return ((mCurrentSelectedPos < (mTextList.length - 1)) || mWrapAround);
+    }
+    
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction,
+            Rect previouslyFocusedRect) {
+        if (gainFocus) {
+            setBackgroundDrawable(mBackgroundFocused);
+            mSelector = mSelectorFocused;
+        } else {
+            setBackgroundDrawable(null);
+            mSelector = mSelectorNormal;
+            mSelectorY = mSelectorDefaultY;
+        }
+    }
+    
+    @Override
+    public boolean onTouchEvent(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;
+    }
+    
+    @Override
+    protected void onDraw(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();
+            }
+        }
+    }
+
+    /**
+     * Called every time the text items or current position
+     * changes. We calculate store we don't have to calculate
+     * onDraw.
+     */
+    private void calculateTextPositions() {
+        mText1 = getTextToDraw(-2);
+        mText2 = getTextToDraw(-1);
+        mText3 = getTextToDraw(0);
+        mText4 = getTextToDraw(1);
+        mText5 = getTextToDraw(2);
+    }
+    
+    private String getTextToDraw(int offset) {
+        int index = getNewIndex(offset);
+        if (index < 0) {
+            return "";
+        }
+        return mTextList[index];
+    }
+
+    private int getNewIndex(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 void scroll() {
+        if (mIsAnimationRunning) {
+            return;
+        }
+        mTotalAnimatedDistance = 0;
+        mIsAnimationRunning = true;
+        invalidate();
+    }
+
+    private void calculateAnimationValues() {
+        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 void drawText(Canvas canvas, String text, int y, TextPaint paint) {
+        int width = (int) paint.measureText(text);
+        int x = getMeasuredWidth() - width - TEXT_MARGIN_RIGHT;
+        canvas.drawText(text, x, y, paint);
+    }
+    
+    public int getCurrentSelectedPos() {
+        return mCurrentSelectedPos;
+    }
+}
index 9fe5bdc..69b819f 100644 (file)
@@ -16,8 +16,6 @@
 
 package com.android.music;
 
-import com.android.internal.widget.VerticalTextSpinner;
-
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.ContentValues;