在android中创建旋转轮?

在android中创建旋转轮?,android,animation,Android,Animation,我需要实现一个小型游戏作为轮盘赌,用户可以按播放旋转,并给用户一个随机的结果 你能给我一些建议如何做一个如下图所示的布局吗?多谢各位 我建议您可以使用滚轮菜单 <com.anupcowkur.wheelmenu.WheelMenu android:id="@+id/wheelMenu" android:layout_width="300dp" android:layout_height="300dp" /> 我需要用户旋转这个拨号器,这是代码。这段代码深受我的启发,在阅

我需要实现一个小型游戏作为轮盘赌,用户可以按播放旋转,并给用户一个随机的结果

你能给我一些建议如何做一个如下图所示的布局吗?多谢各位


我建议您可以使用滚轮菜单

<com.anupcowkur.wheelmenu.WheelMenu
android:id="@+id/wheelMenu"
android:layout_width="300dp"
android:layout_height="300dp" />

我需要用户旋转这个拨号器,这是代码。这段代码深受我的启发,在阅读我的代码之前,您可以阅读其中的解释

currentAngle是用于监视已旋转的总角度拨号器的变量。所以在360度旋转后,每次旋转都会减去360度,所以角度总是在0-360度之间。您可以在rotateDialer功能中检查它。在onSingleTapConfirmed功能中,我们将发送单击坐标(即x和y),然后将其更改为角度,并将其保存在clickedAngle中。拨号器有8个部分除以45度角。拨号器的每个部分都定义了角度范围。因此,它将从每个角度范围(即0-45,0-90..等)中减去当前角度,因此每个截面范围都将实时更改。 例如让拨号器的当前角度在+方向上为20度,因此第一部分的角度范围现在更改为(0-20)到(45-20),即-20到25度

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.Guideline;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

public class MainActivityNew extends AppCompatActivity {

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;

ImageView menu_circle;
Guideline menuWheelGuidline;

float currentAngle; //The current angle of the dialer
double clickedAngle; //The angle which user has clicked

@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_new);
    setUpMenuWheel();

    // load the image only once
    if (imageOriginal == null) {
        imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.menu_wheel);
    }
    // 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 = findViewById(R.id.menu_wheel);
    dialer.setOnTouchListener(new MyOnTouchListener());
    dialer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.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) {
    if(Math.abs(currentAngle)/360>1){
        if(currentAngle>0){
            currentAngle =currentAngle-360+degrees;
        }else{
            currentAngle +=360+degrees;
        }
    }else{
        currentAngle=currentAngle+degrees;
    }
    matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);
    dialer.setImageMatrix(matrix);
    Log.d("Angle Sum",""+currentAngle);
}

/**
 * @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:
            return 0;// ignore, does not happen
    }
}

/**
 * @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 View.OnTouchListener} for registering the dialer's touch events.
 */
private class MyOnTouchListener implements View.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 GestureDetector.SimpleOnGestureListener} for detecting a fling event.
 */
private class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        iconClicked(e.getX(),e.getY());
        return super.onSingleTapConfirmed(e);
    }

    @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;
    float totalAngle;

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

    @Override
    public void run() {
        if (Math.abs(velocity) > 5 && allowRotating) {
            rotateDialer(velocity / 75);
            totalAngle=totalAngle+velocity/75;
            velocity /= 1.0666F;
            // post this instance again
            dialer.post(this);
        }
    }
}

//To position the menu wheel
public void setUpMenuWheel(){
    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    int width = displayMetrics.widthPixels;
    int height = displayMetrics.heightPixels;
    menu_circle = findViewById(R.id.menu_wheel);
    menu_circle.getLayoutParams().height=width;
    menuWheelGuidline = findViewById(R.id.menuWheelGuidline);
    ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams) menuWheelGuidline.getLayoutParams();
    lp.guideBegin = height-(width/2);
    menuWheelGuidline.setLayoutParams(lp);
}

public void iconClicked(double x,double y){
    clickedAngle=getAngle(x,y);
    Log.d("Clicked angle",""+(getAngle(x,y)));
    if(clickedAngle>0-currentAngle && clickedAngle<45-currentAngle){
        Log.e("Icon 1","Clicked");
    }if(clickedAngle>45-currentAngle && clickedAngle<90-currentAngle){
        Log.e("Icon 2","Clicked");
    }if(clickedAngle>90-currentAngle && clickedAngle<135-currentAngle){
        Log.e("Icon 3","Clicked");
    }if(clickedAngle>135-currentAngle && clickedAngle<180-currentAngle){
        Log.e("Icon 4","Clicked");
    }if(clickedAngle>180-currentAngle && clickedAngle<225-currentAngle){
        Log.e("Icon 5","Clicked");
    }if(clickedAngle>225-currentAngle && clickedAngle<270-currentAngle){
        Log.e("Icon 6","Clicked");
    }if(clickedAngle>270-currentAngle && clickedAngle<315-currentAngle){
        Log.e("Icon 7","Clicked");
    }if(clickedAngle>315-currentAngle && clickedAngle<359-currentAngle){
        Log.e("Icon 8","Clicked");
    }
}
}
导入android.annotation.SuppressLint;
导入android.graphics.Bitmap;
导入android.graphics.BitmapFactory;
导入android.graphics.Matrix;
导入android.support.constraint.ConstraintLayout;
导入android.support.constraint.Guideline;
导入android.support.v7.app.AppActivity;
导入android.os.Bundle;
导入android.util.DisplayMetrics;
导入android.util.Log;
导入android.view.GestureDetector;
导入android.view.MotionEvent;
导入android.view.view;
导入android.view.ViewTreeObserver;
导入android.widget.ImageView;
公共类MainActivityNew扩展了AppCompatActivity{
私有静态位图imageOriginal,imageScaled;
私有静态矩阵;
专用图像视图拨号器;
专用国际拨号键,拨号键;
私人手势检测器;
//需要检测反向旋转
私有布尔[]象限;
私有布尔allowRotating;
图像视图菜单_圈;
指南手册指南行;
float currentAngle;//拨号程序的当前角度
双击角度;//用户单击的角度
@SuppressLint(“ClickableViewAccessibility”)
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\u main\u new);
setUpMenuWheel();
//仅加载图像一次
如果(imageOriginal==null){
imageOriginal=BitmapFactory.decodeResource(getResources(),R.drawable.menu\u wheel);
}
//只初始化矩阵一次
if(矩阵==null){
矩阵=新矩阵();
}否则{
//不需要,您也可以立即发布矩阵以恢复旧状态
matrix.reset();
}
detector=新的GestureDetector(这个,新的MyGestureDetector());
//没有第0象限,为了保持简单,第一个值被忽略
quadrantTouched=新布尔[]{false,false,false,false};
allowRotating=true;
拨号器=findViewById(R.id.menu\u wheel);
setOnTouchListener(新的MyOnTouchListener());
dialer.getViewTreeObserver().addOnGlobalLayoutListener(新ViewTreeObserver.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在度数范围内,拨号器应旋转。
*/
专用空心旋转测试器(浮动度){
如果(数学绝对值(当前角度)/360>1){
如果(当前角度>0){
currentAngle=currentAngle-360+度;
}否则{
当前角度+=360度;
}
}否则{
currentAngle=currentAngle+度;
}
矩阵。后旋转(度,拨号宽度/2,拨号高度/2);
拨号器。设置图像矩阵(矩阵);
Log.d(“角度和”,“当前角度”);
}
/**
*@返回单位圆与图像视图中心的角度
*/
专用双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;
}埃尔