Java 与数值对应的旋转对象

Java 与数值对应的旋转对象,java,android,rotation,translate-animation,Java,Android,Rotation,Translate Animation,我有一个360度旋转的组合锁 组合锁上有数字值,这些值纯粹是图形 我需要一种方法将图像的旋转转换为图形上的0-99值 在第一张图中,该值应该能够告诉我“0” 在该图中,用户旋转图像后,该值应能告诉我“72” 代码如下: package co.sts.combinationlock; import android.os.Bundle; import android.app.Activity; import android.graphics.Bitmap; import android.g

我有一个360度旋转的组合锁

组合锁上有数字值,这些值纯粹是图形

我需要一种方法将图像的旋转转换为图形上的0-99值

在第一张图中,该值应该能够告诉我“0”

在该图中,用户旋转图像后,该值应能告诉我“72”

代码如下:

package co.sts.combinationlock;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
import android.support.v4.app.NavUtils;

public class ComboLock extends Activity{

        private static Bitmap imageOriginal, imageScaled;
        private static Matrix matrix;

        private ImageView dialer;
        private int dialerHeight, dialerWidth;

        private GestureDetector detector;

        // needed for detecting the inversed rotations
        private boolean[] quadrantTouched;

        private boolean allowRotating;

        @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_combo_lock);

        // load the image only once
        if (imageOriginal == null) {
                imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.numbers);
        }

        // initialize the matrix only once
        if (matrix == null) {
                matrix = new Matrix();
        } else {
                // not needed, you can also post the matrix immediately to restore the old state
                matrix.reset();
        }

        detector = new GestureDetector(this, new MyGestureDetector());

        // there is no 0th quadrant, to keep it simple the first value gets ignored
        quadrantTouched = new boolean[] { false, false, false, false, false };

        allowRotating = true;

        dialer = (ImageView) findViewById(R.id.locknumbers);
        dialer.setOnTouchListener(new MyOnTouchListener());
        dialer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

                @Override
                        public void onGlobalLayout() {
                        // method called more than once, but the values only need to be initialized one time
                        if (dialerHeight == 0 || dialerWidth == 0) {
                                dialerHeight = dialer.getHeight();
                                dialerWidth = dialer.getWidth();

                                // resize
                                        Matrix resize = new Matrix();
                                        //resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
                                        imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);

                                        // translate to the image view's center
                                        float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
                                        float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
                                        matrix.postTranslate(translateX, translateY);

                                        dialer.setImageBitmap(imageScaled);
                                        dialer.setImageMatrix(matrix);
                        }
                        }
                });

    }

        /**
         * Rotate the dialer.
         *
         * @param degrees The degrees, the dialer should get rotated.
         */
        private void rotateDialer(float degrees) {
                matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);

                //need to print degrees

                dialer.setImageMatrix(matrix);
        }

        /**
         * @return The angle of the unit circle with the image view's center
         */
        private double getAngle(double xTouch, double yTouch) {
                double x = xTouch - (dialerWidth / 2d);
                double y = dialerHeight - yTouch - (dialerHeight / 2d);

                switch (getQuadrant(x, y)) {
                        case 1:
                                return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

                        case 2:
                        case 3:
                                return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);

                        case 4:
                                return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

                        default:
                                // ignore, does not happen
                                return 0;
                }
        }

        /**
         * @return The selected quadrant.
         */
        private static int getQuadrant(double x, double y) {
                if (x >= 0) {
                        return y >= 0 ? 1 : 4;
                } else {
                        return y >= 0 ? 2 : 3;
                }
        }

        /**
         * Simple implementation of an {@link OnTouchListener} for registering the dialer's touch events.
         */
        private class MyOnTouchListener implements OnTouchListener {

                private double startAngle;

                @Override
                public boolean onTouch(View v, MotionEvent event) {

                        switch (event.getAction()) {

                                case MotionEvent.ACTION_DOWN:

                                        // reset the touched quadrants
                                        for (int i = 0; i < quadrantTouched.length; i++) {
                                                quadrantTouched[i] = false;
                                        }

                                        allowRotating = false;

                                        startAngle = getAngle(event.getX(), event.getY());
                                        break;

                                case MotionEvent.ACTION_MOVE:
                                        double currentAngle = getAngle(event.getX(), event.getY());
                                        rotateDialer((float) (startAngle - currentAngle));
                                        startAngle = currentAngle;
                                        break;

                                case MotionEvent.ACTION_UP:
                                        allowRotating = true;
                                        break;
                        }

                        // set the touched quadrant to true
                        quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;

                        detector.onTouchEvent(event);

                        return true;
                }
        }

        /**
         * Simple implementation of a {@link SimpleOnGestureListener} for detecting a fling event.
         */
        private class MyGestureDetector extends SimpleOnGestureListener {
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

                        // get the quadrant of the start and the end of the fling
                        int q1 = getQuadrant(e1.getX() - (dialerWidth / 2), dialerHeight - e1.getY() - (dialerHeight / 2));
                        int q2 = getQuadrant(e2.getX() - (dialerWidth / 2), dialerHeight - e2.getY() - (dialerHeight / 2));

                        // the inversed rotations
                        if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY))
                                        || (q1 == 3 && q2 == 3)
                                        || (q1 == 1 && q2 == 3)
                                        || (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY))
                                        || ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
                                        || ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
                                        || (q1 == 2 && q2 == 4 && quadrantTouched[3])
                                        || (q1 == 4 && q2 == 2 && quadrantTouched[3])) {

                                dialer.post(new FlingRunnable(-1 * (velocityX + velocityY)));
                        } else {
                                // the normal rotation
                                dialer.post(new FlingRunnable(velocityX + velocityY));
                        }

                        return true;
                }
        }

        /**
         * A {@link Runnable} for animating the the dialer's fling.
         */
        private class FlingRunnable implements Runnable {

                private float velocity;

                public FlingRunnable(float velocity) {
                        this.velocity = velocity;
                }

                @Override
                public void run() {
                        if (Math.abs(velocity) > 5 && allowRotating) {
                                //rotateDialer(velocity / 75);
                                //velocity /= 1.0666F;

                                // post this instance again
                                dialer.post(this);
                        }
                }
        }
}
包装公司sts.combinationlock;
导入android.os.Bundle;
导入android.app.Activity;
导入android.graphics.Bitmap;
导入android.graphics.BitmapFactory;
导入android.graphics.Matrix;
导入android.util.Log;
导入android.view.GestureDetector;
导入android.view.Menu;
导入android.view.MenuItem;
导入android.view.MotionEvent;
导入android.view.view;
导入android.view.GestureDetector.SimpleOnGestureListener;
导入android.view.view.OnTouchListener;
导入android.view.ViewTreeObserver.OnGlobalLayoutListener;
导入android.widget.ImageView;
导入android.support.v4.app.NavUtils;
公共类ComboLock扩展活动{
私有静态位图imageOriginal,imageScaled;
私有静态矩阵;
专用图像视图拨号器;
专用国际拨号键,拨号键;
私人手势检测器;
//需要检测反向旋转
私有布尔[]象限;
私有布尔allowRotating;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\u组合锁);
//仅加载图像一次
如果(imageOriginal==null){
imageOriginal=BitmapFactory.decodeResource(getResources(),R.drawable.numbers);
}
//只初始化矩阵一次
if(矩阵==null){
矩阵=新矩阵();
}否则{
//不需要,您也可以立即发布矩阵以恢复旧状态
matrix.reset();
}
detector=新的GestureDetector(这个,新的MyGestureDetector());
//没有第0象限,为了保持简单,第一个值被忽略
quadrantTouched=新布尔[]{false,false,false,false};
allowRotating=true;
拨号器=(图像视图)findViewById(R.id.LockNumber);
setOnTouchListener(新的MyOnTouchListener());
dialer.getViewTreeObserver().addOnGlobalLayoutListener(新OnGlobalLayoutListener()){
@凌驾
公共图书馆{
//方法多次调用,但值只需初始化一次
如果(拨号键宽度==0 | |拨号键宽度==0){
dialerHeight=dialer.getHeight();
dialerWidth=dialer.getWidth();
//调整大小
矩阵大小=新矩阵();
//resize.postScale((float)Math.min(dialerWidth,dialerHeight)/(float)imageOriginal.getWidth(),(float)Math.min(dialerWidth,dialerHeight)/(float)imageOriginal.getHeight();
imageScaled=Bitmap.createBitmap(imageOriginal,0,0,imageOriginal.getWidth(),imageOriginal.getHeight(),resize,false);
//平移到图像视图的中心
float translateX=dialerWidth/2-imageScaled.getWidth()/2;
float translateY=dialerHeight/2-imageScaled.getHeight()/2;
矩阵。后翻译(translateX,translateY);
dialer.setImageBitmap(图像缩放);
拨号器。设置图像矩阵(矩阵);
}
}
});
}
/**
*旋转拨号器。
*
*@param degrees在度数范围内,拨号器应旋转。
*/
专用空心旋转测试器(浮动度){
矩阵。后旋转(度,拨号宽度/2,拨号高度/2);
//需要打印学位吗
拨号器。设置图像矩阵(矩阵);
}
/**
*@返回单位圆与图像视图中心的角度
*/
专用双getAngle(双X触摸、双Y触摸){
双x=x触摸-(拨号宽度/2d);
双y=拨号键-y触摸-(拨号键/2d);
开关(象限(x,y)){
案例1:
返回Math.asin(y/Math.hypot(x,y))*180/Math.PI;
案例2:
案例3:
返回180-(Math.asin(y/Math.hypot(x,y))*180/Math.PI);
案例4:
返回360+Math.asin(y/Math.hypot(x,y))*180/Math.PI;
违约:
//忽略,不会发生
返回0;
}
}
/**
*@返回所选象限。
*/
专用静态整型象限(双x,双y){
如果(x>=0){
返回y>=0?1:4;
}否则{
返回y>=0?2:3;
}
}
/**
*一个{@link-OnTouchListener}的简单实现,用于注册拨号程序的触摸事件。
*/
私有类MyOnTouchListener实现OnTouchListener{
私人双星缠结;
@凌驾
公共布尔onTouch(视图v,运动事件
float factor = 99f / 359f;
float scaled = rotationDegree * factor;
            // PointF a = touch start point
            // PointF b = current touch move point

            // Translate to origin:
            float x = b.x - a.x;
            float y = b.y - a.y;

            float radians = (float) ((Math.atan2(-y, x) + Math.PI + HALF_PI) % TWO_PI);
    private float rotationDegrees = 0;

    /**
     * Rotate the dialer.
     *
     * @param degrees The degrees, the dialer should get rotated.
     */
    private void rotateDialer(float degrees)
            matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);

            this.rotationDegrees += degrees;

            // Make sure we don't go over 360
            this.rotationDegrees = this.rotationDegrees % 360

            dialer.setImageMatrix(matrix);
    }
tickNumber = (int)rotation*100/360
// It could be negative
if (tickNumber < 0)
    tickNumber = 100 - tickNumber
// Record the angle at initial touch for use in dragging.
dialAngleAtTouch = dialAngle;
// Find angle from x-axis made by initial touch coordinate.
// y-coordinate might need to be negated due to y=0 -> screen top. 
// This will be obvious during testing.
a0 = Math.atan2(y0 - yDialCenter, x0 - xDialCenter);
// Find new angle to x-axis. Same comment as above on y coord.
a = Math.atan2(y - yDialCenter, x - xDialCenter);
// New dial angle is offset from the one at initial touch.
dialAngle = dialAngleAtTouch + (a - a0); 
// normalize angles to the interval [0..2pi)
while (dialAngle < 0) dialAngle += 2 * Math.PI;
while (dialAngle >= 2 * Math.PI) dialAngle -= 2 * Math.PI;

// Set the matrix for every frame drawn. Matrix API has a call
// for rotation about a point. Use it!
matrix.setRotate((float)dialAngle * (180 / 3.1415926f), xDialCenter, yDialCenter);

// Invalidate the view now so it's redrawn in with the new matrix value.
double fractionalTick = dialAngle / (2 * Math.Pi) * 100;
 int tick = (int)(fractionalTick + 0.5) % 100;
float touchAngle = getTouchAngle();
float mark = touchAngle / 3.6f;
if (mCurrentMark != mark) { 
    setDialMark(mark); 
}
void setDialMark(int mark) {  
    rotateDialBy(mCurrentMark - mark);  
    mCurrentMark = mark;    
}

void rotateDialBy(int rotateMarks) {
    rotationAngle = rotateMarks * 3.6;
    ...
    /* Rotate the dial by rotationAngle.   */
}