/*
* android-spinnerwheel
* https://github.com/ai212983/android-spinnerwheel
*
* based on
*
* 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 antistatic.spinnerwheel;
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 spinnerwheel
*/
public abstract 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);
/**
* This callback is invoked when scroller has been touched
*/
void onTouch();
/**
* This callback is invoked when touch is up
*/
void onTouchUp();
/**
* 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;
protected Scroller scroller;
private int lastScrollPosition;
private float lastTouchedPosition;
private boolean isScrollingPerformed;
/**
* Constructor
* @param context the current context
* @param listener the scrolling listener
*/
public WheelScroller(Context context, ScrollingListener listener) {
gestureDetector = new GestureDetector(context, 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 spinnerwheel
return true;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
lastScrollPosition = 0;
scrollerFling(lastScrollPosition, (int) velocityX, (int) velocityY);
setNextMessage(MESSAGE_SCROLL);
return true;
}
// public boolean onDown(MotionEvent motionEvent);
});
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 spinnerwheel
* @param distance the scrolling distance
* @param time the scrolling duration
*/
public void scroll(int distance, int time) {
scroller.forceFinished(true);
lastScrollPosition = 0;
scrollerStartScroll(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:
lastTouchedPosition = getMotionEventPosition(event);
scroller.forceFinished(true);
clearMessages();
listener.onTouch();
break;
case MotionEvent.ACTION_UP:
if (scroller.isFinished())
listener.onTouchUp();
break;
case MotionEvent.ACTION_MOVE:
// perform scrolling
int distance = (int)(getMotionEventPosition(event) - lastTouchedPosition);
if (distance != 0) {
startScrolling();
listener.onScroll(distance);
lastTouchedPosition = getMotionEventPosition(event);
}
break;
}
if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {
justify();
}
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 currPosition = getCurrentScrollerPosition();
int delta = lastScrollPosition - currPosition;
lastScrollPosition = currPosition;
if (delta != 0) {
listener.onScroll(delta);
}
// scrolling is not finished when it comes to final Y
// so, finish it manually
if (Math.abs(currPosition - getFinalScrollerPosition()) < MIN_DELTA_FOR_SCROLLING) {
// currPosition = getFinalScrollerPosition();
scroller.forceFinished(true);
}
if (!scroller.isFinished()) {
animationHandler.sendEmptyMessage(msg.what);
} else if (msg.what == MESSAGE_SCROLL) {
justify();
} else {
finishScrolling();
}
}
};
/**
* Justifies spinnerwheel
*/
private void justify() {
listener.onJustify();
setNextMessage(MESSAGE_JUSTIFY);
}
/**
* Starts scrolling
*/
private void startScrolling() {
if (!isScrollingPerformed) {
isScrollingPerformed = true;
listener.onStarted();
}
}
/**
* Finishes scrolling
*/
protected void finishScrolling() {
if (isScrollingPerformed) {
listener.onFinished();
isScrollingPerformed = false;
}
}
protected abstract int getCurrentScrollerPosition();
protected abstract int getFinalScrollerPosition();
protected abstract float getMotionEventPosition(MotionEvent event);
protected abstract void scrollerStartScroll(int distance, int time);
protected abstract void scrollerFling(int position, int velocityX, int velocityY);
}