SurfaceView实现抽奖转盘

在慕课上看了鸿洋大神的课程,在此记录下来,小伙伴们可以直接去网站下载素材,传送门
在这里插入图片描述
幸运转盘类

package com.example.surfaceviewdemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by FangJu on 2019/12/3.
 */
public class LuckyPan extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private SurfaceHolder mHolder;

    private Canvas mCanvas;

    //用于绘制的线程
    private Thread t;
    //控制线程的开关
    private boolean isRunning;

    //盘块的奖项
    private String[] mStrs = new String[]{"单反相机", "IPAD", "恭喜发财", "IPHONE", "服装一套", "恭喜发财"};

    //盘块的图片
    private int[] mImgs = new int[]{R.drawable.danfan, R.drawable.ipad, R.drawable.f040, R.drawable.iphone, R.drawable.meizi, R.drawable.f015};

    //盘块的颜色
    private int[] mColors = new int[]{0xFFFFC300, 0XFFF17E10, 0xFFFFC300, 0XFFF17E10, 0xFFFFC300, 0XFFF17E10};

    //与图片对应的Bitmap
    private Bitmap[] mImgsBitmap;

    //背景
    private Bitmap mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg2);

    //盘块的数量
    private int mItemCount = 6;

    //盘块的范围
    private RectF mRange = new RectF();

    //盘块的直径
    private int mRadius;

    //盘块的画笔
    private Paint mArcPaint;

    //盘块字体的画笔
    private Paint mTextPaint;

    //文字的大小
    private float mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics());

    //滚动的速度
    private double mSpeed = 0;

    //开始的角度
    private volatile float mStartAngle = 0;

    //转盘的中心位置
    private int mCenter;

    //转盘的外边距,以paddLeft为准
    private int mPadding;

    //判断是否点击了停止按钮
    private boolean isShouldEnd;

    public LuckyPan(Context context) {
        this(context, null);
    }

    public LuckyPan(Context context, AttributeSet attrs) {
        super(context, attrs);

        mHolder = getHolder();
        mHolder.addCallback(this);

        //可获得焦点
        setFocusable(true);
        setFocusableInTouchMode(true);
        //设置常量
        setKeepScreenOn(true);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
        mPadding = getPaddingLeft();
        //半径
        mRadius = width - mPadding * 2;
        //中心点
        mCenter = width / 2;

        setMeasuredDimension(width, width);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //初始化盘块的画笔
        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(true);
        mArcPaint.setDither(true);
        //初始化字体的画笔
        mTextPaint = new Paint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextSize(mTextSize);

        //初始化盘块的范围
        mRange = new RectF(mPadding, mPadding, mPadding + mRadius, mPadding + mRadius);

        //初始化图片
        mImgsBitmap = new Bitmap[mItemCount];

        for (int i = 0; i < mItemCount; i++) {
            mImgsBitmap[i] = BitmapFactory.decodeResource(getResources(), mImgs[i]);
        }

        isRunning = true;
        t = new Thread(this);
        t.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRunning = false;
    }

    @Override
    public void run() {
        while (isRunning) {
            long start = System.currentTimeMillis();
            draw();
            long end = System.currentTimeMillis();

            if (end - start < 50) {
                try {
                    Thread.sleep(50 - (end - start));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            if (mCanvas != null) {
                //绘制背景
                drawBg();
                //绘制盘块
                float tempAngle = mStartAngle;
                float sweepAngle = 360 / mItemCount;

                for (int i = 0; i < mItemCount; i++) {
                    //绘制盘块
                    mArcPaint.setColor(mColors[i]);
                    mCanvas.drawArc(mRange, tempAngle, sweepAngle, true, mArcPaint);
                    //绘制文本
                    drawText(tempAngle, sweepAngle, mStrs[i]);
                    //绘制Icon
                    drawIcon(tempAngle, mImgsBitmap[i]);

                    tempAngle += sweepAngle;
                }

                mStartAngle += mSpeed;
                //如果点击了停止按钮
                if (isShouldEnd) {
                    mSpeed -= 1;
                }

                if (mSpeed <= 0) {
                    mSpeed = 0;
                }
            }
        } catch (Exception e) {
        } finally {
            if (mCanvas != null)
                mHolder.unlockCanvasAndPost(mCanvas);
        }

    }

    /**
     * 点击启动旋转
     * @param index 奖项的位置
     */
    public void luckyStart(int index) {
        //计算每一项的角度
        float angle = 360 / mItemCount;
        //计算每一项中奖范围(当前index)
        //1-> 150~210
        //0-> 210~270
        float from = 270 - (index + 1) * angle;
        float end = from + angle;
        //设置停下来需要旋转的距离
        float targetFrom = 4 * 360 + from;
        float targetEnd = 4 * 360 + end;

        float v1 = (float) ((-1 + Math.sqrt(1 + 8 * targetFrom)) / 2);
        float v2 = (float) ((-1 + Math.sqrt(1 + 8 * targetEnd)) / 2);

        mSpeed = v1 + Math.random() * (v2 - v1);
        isShouldEnd = false;
    }

    public void luckyEnd() {
        mStartAngle = 0;
        isShouldEnd = true;
    }

    public boolean isStart() {
        return mSpeed != 0;
    }

    public boolean isShouldEnd() {
        return isShouldEnd;
    }

    /**
     * 绘制Icon
     *
     * @param tempAngle
     * @param mImg
     */
    private void drawIcon(float tempAngle, Bitmap mImg) {
        //设置图片的宽度为直径的1/8
        int imgWidth = mRadius / 8;
        //角度=(起始角度+每个盘块一半的角度)* 1度的大小
        float angle = (float) ((tempAngle + 360 / mItemCount / 2) * Math.PI / 180);
        //图片中心点的坐标
        int x = (int) (mCenter + mRadius / 2 / 2 * Math.cos(angle));
        int y = (int) (mCenter + mRadius / 2 / 2 * Math.sin(angle));
        //确定图片的位置
        Rect rect = new Rect(x - imgWidth / 2, y - imgWidth / 2, x + imgWidth / 2, y + imgWidth / 2);

        mCanvas.drawBitmap(mImg, null, rect, null);
    }

    /**
     * 绘制文本
     *
     * @param tempAngle
     * @param sweepAngle
     * @param mStr
     */
    private void drawText(float tempAngle, float sweepAngle, String mStr) {
        Path path = new Path();
        path.addArc(mRange, tempAngle, sweepAngle);
        //利用水平偏移量让文字居中
        float mTextWidth = mTextPaint.measureText(mStr);
        int hOffset = (int) ((mRadius * Math.PI / mItemCount) / 2 - mTextWidth / 2);
        //垂直偏移量
        int vOffset = mRadius / 2 / 6;
        mCanvas.drawTextOnPath(mStr, path, hOffset, vOffset, mTextPaint);
    }

    /**
     * 绘制背景
     */
    private void drawBg() {
        Log.d("TAG", "drawBg: ");
        mCanvas.drawColor(Color.WHITE);
        mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredHeight() - mPadding / 2), null);

    }
}

测试类

package com.example.surfaceviewdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private LuckyPan mLuckyPan;

    private ImageView mStartBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mLuckyPan = findViewById(R.id.luckyPan);
        mStartBtn = findViewById(R.id.start_btn);

        mStartBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mLuckyPan.isStart()) {
                    mLuckyPan.luckyStart(2);
                    mStartBtn.setImageResource(R.drawable.stop);
                } else {
                    if (!mLuckyPan.isShouldEnd()) {
                        mLuckyPan.luckyEnd();
                        mStartBtn.setImageResource(R.drawable.start);
                    }
                }
            }
        });
    }
}

测试布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context=".MainActivity">

    <com.example.surfaceviewdemo.LuckyPan
        android:id="@+id/luckyPan"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:padding="30dp">

    </com.example.surfaceviewdemo.LuckyPan>

    <ImageView
        android:id="@+id/start_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/start" />

</RelativeLayout>
发布了110 篇原创文章 · 获赞 19 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40833790/article/details/103414454