diff --git a/library/src/main/java/com/github/shchurov/horizontalwheelview/Drawer.java b/library/src/main/java/com/github/shchurov/horizontalwheelview/Drawer.java index 1a4ae88..ff1ab90 100644 --- a/library/src/main/java/com/github/shchurov/horizontalwheelview/Drawer.java +++ b/library/src/main/java/com/github/shchurov/horizontalwheelview/Drawer.java @@ -101,6 +101,10 @@ private void setupCursorRect() { cursorRect.right = cursorRect.left + cursorWidth; } + int getMarksCount() { + return marksCount; + } + void onDraw(Canvas canvas) { double step = 2 * PI / marksCount; double offset = (PI / 2 - view.getRadiansAngle()) % step; diff --git a/library/src/main/java/com/github/shchurov/horizontalwheelview/HorizontalWheelView.java b/library/src/main/java/com/github/shchurov/horizontalwheelview/HorizontalWheelView.java index e51fc39..d997854 100644 --- a/library/src/main/java/com/github/shchurov/horizontalwheelview/HorizontalWheelView.java +++ b/library/src/main/java/com/github/shchurov/horizontalwheelview/HorizontalWheelView.java @@ -32,7 +32,7 @@ public HorizontalWheelView(Context context, AttributeSet attrs) { super(context, attrs); readAttrs(attrs); drawer = new Drawer(this, attrs); - touchHandler = new TouchHandler(this); + touchHandler = new TouchHandler(this, attrs); } private void readAttrs(AttributeSet attrs) { @@ -172,6 +172,10 @@ public void onRestoreInstanceState(Parcelable state) { invalidate(); } + int getMarksCount() { + return drawer.getMarksCount(); + } + public static class Listener { public void onRotationChanged(double radians) { } diff --git a/library/src/main/java/com/github/shchurov/horizontalwheelview/TouchHandler.java b/library/src/main/java/com/github/shchurov/horizontalwheelview/TouchHandler.java index 8961ca6..ceb2380 100644 --- a/library/src/main/java/com/github/shchurov/horizontalwheelview/TouchHandler.java +++ b/library/src/main/java/com/github/shchurov/horizontalwheelview/TouchHandler.java @@ -3,6 +3,8 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.content.res.TypedArray; +import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.animation.DecelerateInterpolator; @@ -11,33 +13,49 @@ import static com.github.shchurov.horizontalwheelview.HorizontalWheelView.SCROLL_STATE_DRAGGING; import static com.github.shchurov.horizontalwheelview.HorizontalWheelView.SCROLL_STATE_IDLE; import static com.github.shchurov.horizontalwheelview.HorizontalWheelView.SCROLL_STATE_SETTLING; +import static java.lang.Math.PI; class TouchHandler extends GestureDetector.SimpleOnGestureListener { private static final float SCROLL_ANGLE_MULTIPLIER = 0.002f; - private static final float FLING_ANGLE_MULTIPLIER = 0.0004f; - private static final float FLING_DURATION_MULTIPLIER = 0.2f; - private static final Interpolator INTERPOLATOR = new DecelerateInterpolator(1.4f); + private static final float FLING_ANGLE_MULTIPLIER = 0.0002f; + private static final int SETTLING_DURATION_MULTIPLIER = 1000; + private static final boolean DEFAULT_SNAP_TO_MARKS = false; + private static final Interpolator INTERPOLATOR = new DecelerateInterpolator(2.5f); private HorizontalWheelView view; private HorizontalWheelView.Listener listener; private GestureDetector gestureDetector; - private ValueAnimator scrollAnimator; + private ValueAnimator settlingAnimator; + private boolean snapToMarks; private int scrollState = SCROLL_STATE_IDLE; - TouchHandler(HorizontalWheelView view) { + TouchHandler(HorizontalWheelView view, AttributeSet attrs) { this.view = view; + readAttrs(attrs); gestureDetector = new GestureDetector(view.getContext(), this); } + private void readAttrs(AttributeSet attrs) { + TypedArray a = view.getContext().obtainStyledAttributes(attrs, R.styleable.HorizontalWheelView); + snapToMarks = a.getBoolean(R.styleable.HorizontalWheelView_snapToMarks, DEFAULT_SNAP_TO_MARKS); + a.recycle(); + } + void setListener(HorizontalWheelView.Listener listener) { this.listener = listener; } boolean onTouchEvent(MotionEvent event) { gestureDetector.onTouchEvent(event); - if (event.getActionMasked() == MotionEvent.ACTION_UP && scrollState != SCROLL_STATE_SETTLING) { - updateScrollStateIfRequired(SCROLL_STATE_IDLE); + int action = event.getActionMasked(); + if (scrollState != SCROLL_STATE_SETTLING + && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) { + if (snapToMarks) { + playSettlingAnimation(findNearestMarkAngle(view.getRadiansAngle())); + } else { + updateScrollStateIfRequired(SCROLL_STATE_IDLE); + } } return true; } @@ -48,6 +66,12 @@ public boolean onDown(MotionEvent e) { return true; } + void cancelFling() { + if (scrollState == SCROLL_STATE_SETTLING) { + settlingAnimator.cancel(); + } + } + @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { double newAngle = view.getRadiansAngle() + distanceX * SCROLL_ANGLE_MULTIPLIER; @@ -65,21 +89,29 @@ private void updateScrollStateIfRequired(int newState) { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - runFlingAnimation(velocityX); - updateScrollStateIfRequired(SCROLL_STATE_SETTLING); + double endAngle = view.getRadiansAngle() - velocityX * FLING_ANGLE_MULTIPLIER; + if (snapToMarks) { + endAngle = (float) findNearestMarkAngle(endAngle); + } + playSettlingAnimation(endAngle); return true; } - private void runFlingAnimation(float velocity) { - int duration = (int) Math.abs(velocity * FLING_DURATION_MULTIPLIER); - float startAngle = (float) view.getRadiansAngle(); - float endAngle = startAngle - velocity * FLING_ANGLE_MULTIPLIER; - scrollAnimator = ValueAnimator.ofFloat(startAngle, endAngle) + private double findNearestMarkAngle(double angle) { + double step = 2 * PI / view.getMarksCount(); + return Math.round(angle / step) * step; + } + + private void playSettlingAnimation(double endAngle) { + updateScrollStateIfRequired(SCROLL_STATE_SETTLING); + double startAngle = view.getRadiansAngle(); + int duration = (int) (Math.abs(startAngle - endAngle) * SETTLING_DURATION_MULTIPLIER); + settlingAnimator = ValueAnimator.ofFloat((float) startAngle, (float) endAngle) .setDuration(duration); - scrollAnimator.setInterpolator(INTERPOLATOR); - scrollAnimator.addUpdateListener(flingAnimatorListener); - scrollAnimator.addListener(animatorListener); - scrollAnimator.start(); + settlingAnimator.setInterpolator(INTERPOLATOR); + settlingAnimator.addUpdateListener(flingAnimatorListener); + settlingAnimator.addListener(animatorListener); + settlingAnimator.start(); } private ValueAnimator.AnimatorUpdateListener flingAnimatorListener = new ValueAnimator.AnimatorUpdateListener() { @@ -96,10 +128,4 @@ public void onAnimationEnd(Animator animation) { } }; - void cancelFling() { - if (scrollAnimator != null && scrollAnimator.isRunning()) { - scrollAnimator.cancel(); - } - } - } diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 1920b54..518fd53 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -7,5 +7,6 @@ + \ No newline at end of file