Android 自定义NumberPicker

public class NumberPicker extends LinearLayout{
    
    private static final float GOLD_ROTE = 1.618f;
    
    /**
     * The number of items show in the selector wheel.
     */
    private static final int DEFAULT_DISPLAY_ITEM_COUNT = 5;
    
    private static final int DEFAULT_ITEM_HEIGHT = 64;
    
    private static final int DEFAULT_ITEM_WIDTH = (int)(DEFAULT_ITEM_HEIGHT * 1.618);
    
    private static final int DEFAULT_SHADER_LENGHT = 5;
    
    private static final int DEFAULT_MAX_VALUE = 2100;
    
    private static final int DEFAULT_MIN_VALUE = 1;
    
    private static final int DEFAULT_VALUE = DEFAULT_MIN_VALUE + (DEFAULT_MAX_VALUE - DEFAULT_MIN_VALUE) / 2;
    
    /**
     * this value will be limit the max velocity in fling
     */
    private static final int DEFAULT_MAX_VELOCITY_MASK = 2;
    
    private static final int DEFAULT_CHILD_SIZE = 24;
    
    private static final int DEFAULT_CHILD_COLOR = Color.WHITE;
    
    
    
    /**
     * the number of item is displayed on the screen
     */
    private int mDisplayItemCount;
    
    private int mChildCount;
    
    private int mMiddleChild;
    
    /**
     * the size of the both side edges of the gradation pattern
     */
    private int mShaderHeight;
    
    /**
     * the color of the both side edges of the gradation pattern
     */
    private int mShaderColor;
    
    private int mSelectorLineColor;
    
    private int mSelectorlineHeight;
    
    /**
     * the color of item word
     */
    private int mItemColor;
    
    private int mItemHeight;
    
    private int mItemWidth;
    
    private int mValue;
    
    private int mLastValue;
    
    /**
     * use to record the final value in every fling time
     */
    
    private int mLastY;
    
    private int mLastScrollY;
    
    private int mMaxValue;
    
    private int mMinValue;
    
    /**
     * Distance in pixels a touch can wander before we think the user is scrolling
     */
    private int mTouchSlop;
    
    /**
     * The current scroll state of the number picker.
     */
    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
    
    /**
     * this view Offset when setup
     */
    private int mInitialOffset;
    
    /**
     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
     */
    private int mMinimumFlingVelocity;
    
    /**
     * Control whether to cycle to scroll the wheel
     */
    private boolean mWrapSelectorWheel;
    
    /**
     * Control whether to draw shader
     */
    private boolean mShaderEnable;

    /**
     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
     */
    private int mMaximumFlingVelocity;
    
    private String[] mDisplayedValues;
    
    private Context mContext;
    
    /**
     * draw a fading edge on screen top and bottom
     */
    private Shader mShader;
    
    private Paint mShaderPaint;
    
    private Paint mChildPaint;
    
    /**
     * draw a picture to tell us where is the center
     */
    private Drawable mSelector;
    
    private Paint mSelectorPaint;
    
    private Scroller mScroller;
    
    private VelocityTracker mVelocityTracker;
    
    /**
     * Listener to be notified upon scroll state change.
     */
    private OnScrollListener mOnScrollListener;
    
    /**
     * Listener to be notified upon current value change.
     */
    private OnValueChangeListener mOnValueChangeListener;
    
    /**
     * Formatter for for displaying the current value.
     */
    private Formatter mFormatter;    
    
    /**
     * 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 his 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(NumberPicker view, int scrollState);
    }
    
    /**
     * 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(NumberPicker picker, int oldVal, int newVal);
    }
    
    /**
     * 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);
    }
    
    
    public NumberPicker(Context context) {
        this(context, null);
        
        // TODO Auto-generated constructor stub
    }
    

    public NumberPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        mContext = context;
        
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumberPicker);
        
        if(a != null){
            
            final int defaultH = getResources().getDimensionPixelSize(R.dimen.default_uidatepicker_picker_item_height);
            
            mSelector = a.getDrawable(R.styleable.NumberPicker_number_picker_selector);
            
            mItemWidth = a.getDimensionPixelSize(R.styleable.NumberPicker_number_picker_item_width, (int)(GOLD_ROTE * defaultH));
            
            mItemHeight = a.getDimensionPixelSize(R.styleable.NumberPicker_number_picker_item_height, defaultH);
            
            mShaderHeight = a.getDimensionPixelSize(R.styleable.NumberPicker_number_picker_shader_lenght, DEFAULT_SHADER_LENGHT);
            
            mMinValue = a.getInteger(R.styleable.NumberPicker_number_picker_min_value, DEFAULT_MIN_VALUE);
            
            mMaxValue = a.getInteger(R.styleable.NumberPicker_number_picker_max_value, DEFAULT_MAX_VALUE);
            
            mDisplayItemCount = a.getInteger(R.styleable.NumberPicker_number_picker_display_item_count, DEFAULT_DISPLAY_ITEM_COUNT);
            
            mValue = a.getInteger(R.styleable.NumberPicker_number_picker_vaule, DEFAULT_VALUE);
            
            
            if(mMinValue > mMaxValue){
                throw new IllegalArgumentException("minValue > maxValue");
            }
            
            if(mValue < mMinValue || mValue > mMaxValue){
                mValue = (mMaxValue - mMinValue) / 2 + mMinValue;
            }
            
            mLastValue = mValue;
            
            
            mWrapSelectorWheel = a.getBoolean(R.styleable.NumberPicker_number_picker_wrap_wheel, true);
            
            mChildPaint = new Paint();
            
            mChildPaint.setTextSize(a.getDimensionPixelSize(R.styleable.NumberPicker_number_picker_child_size, DEFAULT_CHILD_SIZE));
            
            mChildPaint.setColor(a.getColor(R.styleable.NumberPicker_number_picker_child_color, mChildPaint.getColor()));
            
            mShaderColor = a.getColor(R.styleable.NumberPicker_number_picker_shader_color, 0X55000000);
            
            mShaderHeight = a.getInteger(R.styleable.NumberPicker_number_picker_shader_height, defaultH / 3);
            
            mSelectorlineHeight = a.getInteger(R.styleable.NumberPicker_number_picker_selector_line_height, 3);
            
            mSelectorPaint = new Paint();
            
            mSelectorPaint.setAntiAlias(true);
            
            mSelectorPaint.setColor(a.getColor(R.styleable.NumberPicker_number_picker_selector_color, 0XFF0EA5F3));
            
            a.recycle();
        }
        
        init();
    }
    
    private void init(){
        
        mShaderPaint = new Paint();
        
        mShaderPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        
        mChildPaint.setTextAlign(Align.CENTER);
        mChildPaint.setAntiAlias(true);
        
        setClickable(true);
        
        setOrientation(VERTICAL);
        
        setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
        
        setWillNotDraw(false);
        
        mScroller = new Scroller(getContext());
        
        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        
        mTouchSlop = configuration.getScaledTouchSlop();
        
        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity() / DEFAULT_MAX_VELOCITY_MASK;
        
        
//        mChildCount = mDisplayItemCount + 2;
//        
//        mMiddleChild = mChildCount / 2;
//        
//        if(mChildCount % 2 == 0){
//            mChildCount ++;
//        }
//        
//        
//        for(int i=0, n=mChildCount; i<n; i++){
//            final LayoutParams lp = new LayoutParams(mItemWidth, mItemHeight);
//            NumberView tv = new DateNumberView(getContext());
//            
//            ((TextView)tv).setText(getNumberString(i));
//            ((TextView)tv).setGravity(Gravity.CENTER);
//            
//            addView((View)tv, lp);
//        }
        
    }
    
    /**
     * get the number from child index, use this to update the child view
     * @param childIndex the index of child list
     * @return if the index has no value, return ""
     */
    private String getNumberString(int childIndex){
        
        int value = mValue + childIndex - mMiddleChild;
        
        if(value < mMinValue){
            if(mWrapSelectorWheel){
                value = 1 + mMaxValue - (mMinValue - value);
            }
        }else if(value > mMaxValue){
            if(mWrapSelectorWheel){
                value = mMinValue + (value - mMaxValue) - 1;
            }
        }
        
        if(value >= mMinValue && value <= mMaxValue){
            
            return mDisplayedValues == null ? formatNumber(value)
                    : mDisplayedValues[value - mMinValue];
        }
        
        return "";
    }
    
    private String formatNumber(int value) {
        return (mFormatter != null) ? mFormatter.format(value) : String.valueOf(value);
    }
    
    /**
     * Creates a new measure specification
     * @param oldMeasureSpec the old measure specification
     * @param newSize the max size,
     * @return a new value
     */
    private int makeMeasureSpec(int oldMeasureSpec, int newSize){
        
        final int mode = MeasureSpec.getMode(oldMeasureSpec);
        final int size = MeasureSpec.getSize(oldMeasureSpec);
        
        if(mode == MeasureSpec.EXACTLY){
            return oldMeasureSpec;
        }else if(mode == MeasureSpec.AT_MOST){
            
            return MeasureSpec.makeMeasureSpec(Math.min(size, newSize), MeasureSpec.EXACTLY);
        }else if(mode == MeasureSpec.UNSPECIFIED){
            
            return MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.EXACTLY);
        }else{
            throw new IllegalArgumentException("must use the correct parameters");
        }
        
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        
        final int defX = mItemWidth;
        final int defY = mDisplayItemCount * mItemHeight;
        
        super.onMeasure(makeMeasureSpec(widthMeasureSpec, defX),
                makeMeasureSpec(heightMeasureSpec, defY));
        
        
        if(getMeasuredHeight() > mDisplayItemCount * mItemHeight){
            mDisplayItemCount = getMeasuredHeight() / mItemHeight;
            
            if(mDisplayItemCount % mItemHeight != 0){
                mDisplayItemCount++;
            }
        }
        
        mChildCount = getMeasuredHeight() / mItemHeight + 2;
        
        if(mChildCount % 2 == 0){
            mChildCount++;
        }
        
        mMiddleChild = mChildCount / 2;
        
        if(mChildCount % 2 == 0){
            mChildCount ++;
        }
        
        
//        for(int i=0, n=mChildCount; i<n; i++){
//            final LayoutParams lp = new LayoutParams(mItemWidth, mItemHeight);
//            TextView tv = new TextView(getContext());
//            
//            tv.setText(getNumberString(i));
//            tv.setGravity(Gravity.CENTER);
//            tv.setVisibility(View.INVISIBLE);
//            
//            addView(tv, lp);
//        }
        
        mInitialOffset = getScrollY();
        
        //make sure the selector wheel can cycle when display item count large than (mMaxValue - mMinValue)
        //确保在可显示个数大于最大最小差值的情况下可以循环滑动
        setWrapSelectorWheel(mWrapSelectorWheel);
        
    }


    
    @Override
    protected void dispatchDraw(Canvas canvas) {
        
        super.dispatchDraw(canvas);
        
        //draw child
        final int x = (getRight() - getLeft()) / 2;
        final int y = (getBottom() - getTop()) / 2 + mInitialOffset;
        final int curIndex = (mChildCount / 2);
        
        for(int i=0; i<mChildCount; i++){
            canvas.drawText(getNumberString(i), x, y + (i - curIndex) * mItemHeight + mChildPaint.getTextSize() / 2, mChildPaint);
        }
        
        //draw shader
        if(isShaderEnable()){
            
            //draw top
            final Rect shaderTop = new Rect(0, 0 + getScrollY(), getMeasuredWidth(), mShaderHeight + getScrollY());
            
            mShader = new LinearGradient(0, shaderTop.top, 0, shaderTop.bottom, mShaderColor, 0, Shader.TileMode.CLAMP);
            
            mShaderPaint.setShader(mShader);
            
            canvas.drawRect(shaderTop, mShaderPaint);
            
            //draw bottom
            final int bottomOfBottom = getMeasuredHeight() + getScrollY();
            final int topOfBottom = bottomOfBottom - mShaderHeight;
            final Rect shaderBottom = new Rect(0, topOfBottom, getMeasuredWidth(), bottomOfBottom);
            
            mShader = new LinearGradient(0, bottomOfBottom, 0, topOfBottom, mShaderColor, 0, Shader.TileMode.CLAMP);
            
            mShaderPaint.setShader(mShader);
            
            canvas.drawRect(shaderBottom, mShaderPaint);
        }
        
        //draw selector
        if(mSelector != null){
            
            int width = mSelector.getIntrinsicWidth();
            int height = mSelector.getIntrinsicHeight();
            
            if(width < 0){
                width = getWidth();
            }
            
            if(height < 0){
                height = mItemHeight;
            }
            
            final int top = getScrollY() + getHeight() / 2 - height / 2;
            final int bottom = top + height;
            final int left = getWidth() / 2 - width / 2;
            final int right = left + width;
            mSelector.setBounds(left, top, right, bottom);
            mSelector.draw(canvas);
            
        }else{
            
            // draw the top divider line
            final int topOfTopDivider = getScrollY() + (getHeight() - mItemHeight) / 2;
            int bottomOfTopDivider = topOfTopDivider + mSelectorlineHeight;
            final Rect topRect = new Rect(0, topOfTopDivider, getWidth(), bottomOfTopDivider);
            canvas.drawRect(topRect, mSelectorPaint);
            
            // draw the bottom divider line
            final int bottomOfBottomDivider = topOfTopDivider + mItemHeight;
            int topOfBottomDivider = bottomOfBottomDivider - mSelectorlineHeight;
            final Rect bottomRect = new Rect(0, topOfBottomDivider, getWidth(),bottomOfBottomDivider);
            canvas.drawRect(bottomRect, mSelectorPaint);
        }
        
        
    }
    
    
    public void setChlidSize(int size){
        mChildPaint.setTextSize(size);
    }
    
    public void setChildColor(int color){
        mChildPaint.setColor(color);
    }
    
    public void setChildPaint(Paint paint){
        if(paint == null){
            return;
        }
        
        mChildPaint = paint;
    }
    
    

    /**
     * Sets the values to be displayed.
     *
     * @param displayedValues The displayed values.
     */
    public void setDisplayedValues(String[] displayedValues) {
        if (mDisplayedValues == displayedValues) {
            return;
        }
        mDisplayedValues = displayedValues;
        
        updateChild();
    }
    
    /**
     * 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;
        updateChild();
    }
    
    /**
     * 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;
    }
    
    /**
     * Control whether to draw shader
     * @param enable
     */
    public void setShaderEnable(boolean enable){
        this.mShaderEnable = enable;
        
        invalidate();
    }
    
    /**
     * return the shader will can be draw
     * @return
     */
    public boolean isShaderEnable(){
        return this.mShaderEnable;
    }
    
    /**
     * set the min value on the wheel
     * @param value
     */
    public void setMinValue(int value){
        
        
        this.mMinValue = value;
        
        if(mValue < mMinValue){
            mValue = mMinValue;
        }
        
        if(mMaxValue < mMinValue){
            mMaxValue = mMinValue;
        }
        
        setWrapSelectorWheel(mWrapSelectorWheel);
    }
    
    /**
     * set the max value on the wheel
     * @param value
     */
    public void setMaxValue(int value){
        
        this.mMaxValue = value;
        
        if(mValue > mMaxValue){
            mValue = mMaxValue;
        }
        
        if(mMaxValue < mMinValue){
            mMinValue = mMaxValue;
        }
        
        setWrapSelectorWheel(mWrapSelectorWheel);
    }
    
    /**
     * set the current display value, no animation
     * <br>if the given value < {@link #getMinValue()}, the program will use {@link #getMinValue()}
     * <br>and the given value > {@link #getMaxValue()}, the program will use {@link #getMaxValue()}
     * @param value
     */
    public void setValue(int value){
        
        if(value < mMinValue){
            this.mValue = mMinValue;
        }else if(value > mMaxValue){
            this.mValue = mMaxValue;
        }else{
            this.mValue = value;
        }
        
//        onValueChange();
        
        updateChild();
    }
    
    /**
     * get the max value
     * @return
     */
    public int getMaxValue(){
        return mMaxValue;
    }
    
    /**
     *
     * @return
     */
    public int getMinValue(){
        return mMinValue;
    }
    
    /**
     *
     * @return
     */
    public int getValue(){
        return mValue;
    }
    
    /**
     * 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 the number of items shown
     * on the selector wheel the selector wheel wrapping is enabled.
     * </p>
     * <p>
     * <strong>Note:</strong> If the number of items, i.e. the range (
     * {@link #getMaxValue()} - {@link #getMinValue()}) is less than
     * the number of items shown on the selector wheel, the selector wheel will
     * not wrap. Hence, in such a case calling this method is a NOP.
     * </p>
     *
     * @param wrapSelectorWheel Whether to wrap.
     */
    public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
        final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mDisplayItemCount;
        
        mWrapSelectorWheel = wrappingAllowed && wrapSelectorWheel;
        
        updateChild();
    }
    
    /**
     * Gets the values to be displayed instead of string values.
     *
     * @return The displayed values.
     */
    public String[] getDisplayedValues() {
        return mDisplayedValues;
    }
    
    /**
     * get the number of displayed item on screen
     * @return the total number
     */
    public int getDisplayItemCount(){
        return mDisplayItemCount;
    }
    
    public void setDisplayItemCount(int count){
        this.mDisplayItemCount = count;
        invalidate();
    }
    
    /**
     * 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);
        }
    }
    
    /**
     * get the picker scroll state
     * @return
     */
    public int getScrollState(){
        return mScrollState;
    }
    
    /**
     * handles value change, for example the value from 4 -> 4 don't handle
     */
    private void onValueChange(){
        if(mValue != mLastValue){
            notifyValueChange(mLastValue, mValue);
            mLastValue = mValue;
        }
    }
    
    /**
     * Notifies the listener, if registered, of a change of the value of this
     * NumberPicker.
     */
    private void notifyValueChange(int previous, int current) {
        
        if (mOnValueChangeListener != null && previous != current) {
            System.out.println(mScrollState);
            mOnValueChangeListener.onValueChange(this, previous, current);
        }
    }
    
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            final int dy = mScroller.getCurrY() - mLastScrollY;
            scrollBy(0, -dy);
            mLastScrollY = mScroller.getCurrY();
            postInvalidate();
        }else{
            if(mScrollState == OnScrollListener.SCROLL_STATE_FLING){
                onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                adjustPosition();
            }else if(mScrollState == OnScrollListener.SCROLL_STATE_IDLE){
                
                onValueChange();
            }
            
            
        }
    }
    
    
    @Override
    public void scrollBy(int dx, int dy) {
        
        int scrollY = getScrollY();
        
        scrollY += dy;
        
        final int scrollOffset = scrollY - mInitialOffset;
        
        if (scrollOffset >= mItemHeight || scrollOffset <= -mItemHeight) {
            
            final int adjust = scrollOffset / mItemHeight;
            updateValue(adjust);
            
            updateChild();
            
            scrollY = mInitialOffset + scrollOffset % mItemHeight;
            
            playSoundEffect(SoundEffectConstants.CLICK);
            
        }
        
        super.scrollTo(getScrollX() + dx, scrollY);

    }
    
    @Override
    public void scrollTo(int x, int y) {
        // TODO Auto-generated method stub
        scrollBy( x - getScrollX(), y - getScrollY());
    }
    
    
    /**
     * update the current value
     * @param adjust
     */
    private void updateValue(int adjust){
        
        mValue += adjust;
        
        if(mValue < mMinValue){
            if(mWrapSelectorWheel){
                mValue = mMaxValue - (mMinValue - mValue) + 1;
            }
            
        }else if(mValue > mMaxValue){
            if(mWrapSelectorWheel){
                mValue = mMinValue + (mValue - mMaxValue) - 1;
            }
        }
    }
    
    /**
     * update the child display words
     */
    private void updateChild(){
        
//        for(int i=0, n=mChildCount; i<n; i++){
//            ((TextView)getChildAt(i)).setText(getNumberString(i));
//        }
        invalidate();
    }
    
    /**
     * Recalculate the display position
     */
    private void adjustPosition(){
        
        if(mValue < mMinValue){
            flingTo(mMinValue);
        }else if(mValue > mMaxValue){
            flingTo(mMaxValue);
        }else{
            
            final int  offset = getScrollY() - mInitialOffset;
            
            flingTo(mValue + offset / (mItemHeight / 2));
        }
        
    }
    
    /**
     * make the wheel fling to specified value
     * @param value
     */
    public void flingTo(int value){
        
        final int offset = getScrollY() - mInitialOffset;
        
        final int distance = (mValue - value) * mItemHeight + offset;
        
        mLastScrollY = getScrollY();
        
        mScroller.startScroll(0, mLastScrollY, 0, distance);
        
        invalidate();
    }
    
    /**
     * 根据指定的速度进行滑翔
     * use specified velocity to fling
     * @param velocity the Y velocity
     */
    private void fling(int velocityY){
        
        mLastScrollY = getScrollY();
        
        mScroller.fling(0, mLastScrollY, 0, velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);

        invalidate();
    }

    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        
        final int y = (int) event.getY();
        
        final int action = event.getActionMasked();
        
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        
        switch(action){
        
        case MotionEvent.ACTION_DOWN:
            
            if(mScroller.computeScrollOffset()){
                mScroller.abortAnimation();
            }
            
            //不让父控件获取手势
            if (getParent() != null) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }
            
            mLastY = y;
            
            break;
        
        case MotionEvent.ACTION_MOVE:
            
            final int distance = mLastY - y;
            
            //move优化
            //move optimization
            if(mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){
                
                if(Math.abs(distance) > mTouchSlop){
                    
                    onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                }
                
                break;
                
            }else
                
            scrollBy(0, distance);
            
            mLastY = y;
                        
            break;
            
        case MotionEvent.ACTION_UP:
            
            
            if (getParent() != null) {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            
            final VelocityTracker velocityTracker = mVelocityTracker;
            velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
            
            final int initialVelocity = (int) velocityTracker.getYVelocity();
            if(Math.abs(initialVelocity) > mMinimumFlingVelocity){
                fling(initialVelocity);
                onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
                
            }else{
                adjustPosition();
                
                onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
            }
            
            //回收资源
            if (mVelocityTracker != null) {
                
                mVelocityTracker.recycle();

                mVelocityTracker = null;

            }
            
            break;
        
        }
        
        return super.onTouchEvent(event);
    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值