Project: android-wheel-datetime-picker
/*
 *  Android Wheel Control. 
 *  https://code.google.com/p/android-wheel/ 
 *   
 *  Copyright 2011 Yuri Kanivets 
 * 
 *  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 kankan.wheel.widget; 
 
import android.content.Context; 
import android.os.Handler; 
import android.os.Message; 
import android.view.GestureDetector; 
import android.view.MotionEvent; 
import android.view.GestureDetector.SimpleOnGestureListener; 
import android.view.animation.Interpolator; 
import android.widget.Scroller; 
 
/**
 * Scroller class handles scrolling events and updates the  
 */
 
public class WheelScroller { 
    /**
     * Scrolling listener interface 
     */
 
    public interface ScrollingListener { 
        /**
         * Scrolling callback called when scrolling is performed. 
         * @param distance the distance to scroll 
         */
 
        void onScroll(int distance); 
 
        /**
         * Starting callback called when scrolling is started 
         */
 
        void onStarted(); 
         
        /**
         * Finishing callback called after justifying 
         */
 
        void onFinished(); 
         
        /**
         * Justifying callback called to justify a view when scrolling is ended 
         */
 
        void onJustify(); 
    } 
     
    /** Scrolling duration */ 
    private static final int SCROLLING_DURATION = 400
 
    /** Minimum delta for scrolling */ 
    public static final int MIN_DELTA_FOR_SCROLLING = 1
 
    // Listener 
    private ScrollingListener listener; 
     
    // Context 
    private Context context; 
     
    // Scrolling 
    private GestureDetector gestureDetector; 
    private Scroller scroller; 
    private int lastScrollY; 
    private float lastTouchedY; 
    private boolean isScrollingPerformed; 
 
    /**
     * Constructor 
     * @param context the current context 
     * @param listener the scrolling listener 
     */
 
    public WheelScroller(Context context, ScrollingListener listener) { 
        gestureDetector = new GestureDetector(context, gestureListener); 
        gestureDetector.setIsLongpressEnabled(false); 
         
        scroller = new Scroller(context); 
 
        this.listener = listener; 
        this.context = context; 
    } 
     
    /**
     * Set the the specified scrolling interpolator 
     * @param interpolator the interpolator 
     */
 
    public void setInterpolator(Interpolator interpolator) { 
        scroller.forceFinished(true); 
        scroller = new Scroller(context, interpolator); 
    } 
     
    /**
     * Scroll the wheel 
     * @param distance the scrolling distance 
     * @param time the scrolling duration 
     */
 
    public void scroll(int distance, int time) { 
        scroller.forceFinished(true); 
 
        lastScrollY = 0
         
        scroller.startScroll(000, distance, time != 0 ? time : SCROLLING_DURATION); 
        setNextMessage(MESSAGE_SCROLL); 
         
        startScrolling(); 
    } 
    
    /**
     * Stops scrolling 
     */
 
    public void stopScrolling() { 
        scroller.forceFinished(true); 
    } 
     
    /**
     * Handles Touch event  
     * @param event the motion event 
     * @return 
     */
 
    public boolean onTouchEvent(MotionEvent event) { 
        switch (event.getAction()) { 
            case MotionEvent.ACTION_DOWN: 
                lastTouchedY = event.getY(); 
                scroller.forceFinished(true); 
                clearMessages(); 
                break
     
            case MotionEvent.ACTION_MOVE: 
                // perform scrolling 
                int distanceY = (int)(event.getY() - lastTouchedY); 
                if (distanceY != 0) { 
                    startScrolling(); 
                    listener.onScroll(distanceY); 
                    lastTouchedY = event.getY(); 
                } 
                break
        } 
         
        if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) { 
            justify(); 
        } 
 
        return true
    } 
     
    // gesture listener 
    private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() { 
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
            // Do scrolling in onTouchEvent() since onScroll() are not call immediately 
            //  when user touch and move the wheel 
            return true
        } 
         
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
            lastScrollY = 0
            final int maxY = 0x7FFFFFFF
            final int minY = -maxY; 
            scroller.fling(0, lastScrollY, 0, (int) -velocityY, 00, minY, maxY); 
            setNextMessage(MESSAGE_SCROLL); 
            return true
        } 
    }; 
 
    // Messages 
    private final int MESSAGE_SCROLL = 0
    private final int MESSAGE_JUSTIFY = 1
     
    /**
     * Set next message to queue. Clears queue before. 
     *  
     * @param message the message to set 
     */
 
    private void setNextMessage(int message) { 
        clearMessages(); 
        animationHandler.sendEmptyMessage(message); 
    } 
 
    /**
     * Clears messages from queue 
     */
 
    private void clearMessages() { 
        animationHandler.removeMessages(MESSAGE_SCROLL); 
        animationHandler.removeMessages(MESSAGE_JUSTIFY); 
    } 
     
    // animation handler 
    private Handler animationHandler = new Handler() { 
        public void handleMessage(Message msg) { 
            scroller.computeScrollOffset(); 
            int currY = scroller.getCurrY(); 
            int delta = lastScrollY - currY; 
            lastScrollY = currY; 
            if (delta != 0) { 
                listener.onScroll(delta); 
            } 
             
            // scrolling is not finished when it comes to final Y 
            // so, finish it manually  
            if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) { 
                currY = scroller.getFinalY(); 
                scroller.forceFinished(true); 
            } 
            if (!scroller.isFinished()) { 
                animationHandler.sendEmptyMessage(msg.what); 
            } else if (msg.what == MESSAGE_SCROLL) { 
                justify(); 
            } else { 
                finishScrolling(); 
            } 
        } 
    }; 
     
    /**
     * Justifies wheel 
     */
 
    private void justify() { 
        listener.onJustify(); 
        setNextMessage(MESSAGE_JUSTIFY); 
    } 
 
    /**
     * Starts scrolling 
     */
 
    private void startScrolling() { 
        if (!isScrollingPerformed) { 
            isScrollingPerformed = true
            listener.onStarted(); 
        } 
    } 
 
    /**
     * Finishes scrolling 
     */
 
    void finishScrolling() { 
        if (isScrollingPerformed) { 
            listener.onFinished(); 
            isScrollingPerformed = false
        } 
    } 
}