Android Open Source - AnAudioPlayer Rotating Uzumaki Slider






From Project

Back to project page AnAudioPlayer.

License

The source code is released under:

MIT License

If you think the Android project AnAudioPlayer listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package jp.gr.java_conf.neko_daisuki.android.widget;
/*from  www  .  java 2  s .c o m*/
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import jp.gr.java_conf.neko_daisuki.android.view.MotionEventDispatcher;

public class RotatingUzumakiSlider extends UzumakiSlider {

    public interface OnStartRotatingListener {

        public void onStartRotating(RotatingUzumakiSlider slider);
    }

    public interface OnStopRotatingListener {

        public void onStopRotating(RotatingUzumakiSlider slider);
    }

    private abstract class MotionEventProc implements MotionEventDispatcher.Proc {

        public boolean run(MotionEvent event) {
            return callback(RotatingUzumakiSlider.this, event);
        }

        protected abstract boolean callback(RotatingUzumakiSlider slider,
                                            MotionEvent event);
    }

    private class ActionDownProc extends MotionEventProc {

        protected boolean callback(RotatingUzumakiSlider slider,
                                   MotionEvent event) {
            return slider.onActionDown(event);
        }
    }

    private class ActionMoveProc extends MotionEventProc {

        protected boolean callback(RotatingUzumakiSlider slider,
                                   MotionEvent event) {
            return slider.onActionMove(event);
        }
    }

    private class ActionUpProc extends MotionEventProc {

        protected boolean callback(RotatingUzumakiSlider slider,
                                   MotionEvent event) {
            return slider.onActionUp(event);
        }
    }

    private List<OnStartRotatingListener> mOnStartRotatingListeners;
    private List<OnStopRotatingListener> mOnStopRotatingListeners;
    private MotionEventDispatcher mDispatcher;
    private double mRotationAngle;  // [degree]

    private int mHeaderSize;

    public RotatingUzumakiSlider(Context context) {
        super(context);
        initialize();
    }

    public RotatingUzumakiSlider(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public RotatingUzumakiSlider(Context context, AttributeSet attrs,
                                 int defStyle) {
        super(context, attrs, defStyle);
        initialize();
    }

    public boolean onTouchEvent(MotionEvent event) {
        return mDispatcher.dispatch(event);
    }

    public void addOnStartRotatingListener(OnStartRotatingListener listener) {
        mOnStartRotatingListeners.add(listener);
    }

    public void addOnStopRotatingListener(OnStopRotatingListener listener) {
        mOnStopRotatingListeners.add(listener);
    }

    public void slideHead(int progressOld, float deltaX, float deltaY) {
        int outerRadius = getAbsoluteOuterDiameter() / 2;
        int innerRadius = getAbsoluteInnerDiameter() / 2;
        int maxLen = outerRadius - innerRadius;
        int range = getMax() - getMin();
        float ratio = (float)range / maxLen;
        float deltaProgress = (-1) * deltaX * ratio;
        setProgress(progressOld + (int)deltaProgress);
    }

    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawDisc(canvas);
        drawRotatingUzumaki(canvas);

        // The following statement is useful in debug.
        //this.drawHeader(canvas);
    }

    protected void layoutHead(View head, int l, int t, int r, int b) {
        int x = computeHeadPosition();
        int y = getHeight() / 2;
        ((UzumakiHead)head).movePointer(l + x, t + y, l, t, r, b);
    }

    @SuppressWarnings("unused")
    private void drawHeader(Canvas canvas) {
        /*
         * I do not remove this unused method because this is useful in
         * debugging.
         */
        Path path = new Path();
        path.moveTo(computeHeadPosition(), getHeight() / 2);
        path.rLineTo(mHeaderSize, - mHeaderSize);
        path.rLineTo(-2 * mHeaderSize, 0);
        path.close();

        Paint paint = new Paint();
        paint.setColor(Color.YELLOW);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawPath(path, paint);
    }

    private void initialize() {
        mOnStartRotatingListeners = new ArrayList<OnStartRotatingListener>();
        mOnStopRotatingListeners = new ArrayList<OnStopRotatingListener>();
        mDispatcher = new MotionEventDispatcher();
        mDispatcher.setDownProc(new ActionDownProc());
        mDispatcher.setMoveProc(new ActionMoveProc());
        mDispatcher.setUpProc(new ActionUpProc());

        mHeaderSize = 42;
    }

    private int computeHeadPosition() {
        int center = getWidth() / 2;
        int innerRadius = getAbsoluteInnerDiameter() / 2;
        int step = getMax() - getProgress();
        int outerRadius = getAbsoluteOuterDiameter() / 2;
        int span = (outerRadius - innerRadius) * step / getSize();
        return center + innerRadius + span;
    }

    private float computeCurrentAngle() {
        return getSweepAngle() * (float)(getProgress() - getMin()) / getSize();
    }

    private void drawRotatingUzumaki(Canvas canvas) {
        int x = getWidth() / 2;
        int y = getHeight() / 2;
        float angle = computeCurrentAngle();
        canvas.save();
        try {
            canvas.rotate(- angle, x, y);
            drawUzumaki(canvas);
        }
        finally {
            canvas.restore();
        }
    }

    private boolean onActionDown(MotionEvent event) {
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        float x = event.getX();
        float y = event.getY();
        float deltaX = x - centerX;
        float deltaY = y - centerY;
        double len = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
        int outerRadius = getOutlineOuterDiameter() / 2;
        int innerRadius = getAbsoluteOutlineInnerDiameter() / 2;
        boolean isTarget = (innerRadius <= len) && (len <= outerRadius);

        return isTarget ? startRotating(x, y) : false;
    }

    private double computeRotationAngle(float x, float y) {
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        float deltaX = x - centerX;
        float deltaY = y - centerY;
        double radian = Math.atan2(deltaY, deltaX);
        return Math.toDegrees(radian);
    }

    private boolean startRotating(float x, float y) {
        mRotationAngle = computeRotationAngle(x, y);

        for (OnStartRotatingListener listener: mOnStartRotatingListeners) {
            listener.onStartRotating(this);
        }

        /*
         * Return value's type of this method must be void. But to make the
         * last expression of onActionDown() simple, this method always returns
         * true. This is a bad way exactly.
         */
        return true;
    }

    private boolean onActionMove(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        double angle = computeRotationAngle(x, y);
        double angleDelta = angle - mRotationAngle;
        double absDelta = Math.abs(angleDelta);
        double calibration = absDelta < 180 ? 0 : angleDelta / absDelta * 360;
        double actualDelta = angleDelta - calibration;
        double progressDelta = actualDelta / getSweepAngle() * getSize() + getMin();
        int direction = 0 < getSweepAngle() ? 1 : -1;
        setProgress(getProgress() + direction * (int)progressDelta);
        mRotationAngle = angle;

        return true;
    }

    private boolean onActionUp(MotionEvent event) {
        for (OnStopRotatingListener listener: mOnStopRotatingListeners) {
            listener.onStopRotating(this);
        }

        return true;
    }
}

// vim: tabstop=4 shiftwidth=4 expandtab softtabstop=4





Java Source Code List

jp.gr.java_conf.neko_daisuki.anaudioplayer.AboutActivity.java
jp.gr.java_conf.neko_daisuki.anaudioplayer.AudioService.java
jp.gr.java_conf.neko_daisuki.anaudioplayer.MainActivity.java
jp.gr.java_conf.neko_daisuki.android.util.ContextUtil.java
jp.gr.java_conf.neko_daisuki.android.view.MotionEventDispatcher.java
jp.gr.java_conf.neko_daisuki.android.widget.AutoScrollableListView.java
jp.gr.java_conf.neko_daisuki.android.widget.CircleImageButton.java
jp.gr.java_conf.neko_daisuki.android.widget.RotatingUzumakiSlider.java
jp.gr.java_conf.neko_daisuki.android.widget.UzumakiArmHead.java
jp.gr.java_conf.neko_daisuki.android.widget.UzumakiDiagram.java
jp.gr.java_conf.neko_daisuki.android.widget.UzumakiHead.java
jp.gr.java_conf.neko_daisuki.android.widget.UzumakiImageHead.java
jp.gr.java_conf.neko_daisuki.android.widget.UzumakiSlider.java