在慕课上看了鸿洋大神的课程,在此记录下来,小伙伴们可以直接去网站下载素材,传送门
幸运转盘类
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>