100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > android自定义大转盘 Android使用surfaceView自定义抽奖大转盘

android自定义大转盘 Android使用surfaceView自定义抽奖大转盘

时间:2019-06-18 18:34:30

相关推荐

android自定义大转盘 Android使用surfaceView自定义抽奖大转盘

使用surfaceView自定义抽奖大转盘

话不多说,先上效果图

完整代码地址欢迎start

实现思路以及过程

1、首先了解SurfaceView的基本用法,它跟一般的View不太一样,采用的双缓存机制,可以在子线程中绘制View,不会因为绘制耗时而失去流畅性,这也是选择使用SurfaceView去自定义这个抽奖大转盘的原因,毕竟绘制这个转盘的盘块,奖项的图片和文字以及转动都是靠绘制出来的,是一个比较耗时的绘制过程。

2、使用SurfaceView的一般模板样式

一般会用到的成员变量

private SurfaceHolder mSurfaceHolder;

private Canvas mCanvas;

初始化常亮

public SurfaceViewTemplate(Context context,AttributeSet attrs) {

super(context, attrs);

//初始化

mSurfaceHolder = getHolder();

mSurfaceHolder.addCallback(this);

//设置可获得焦点

setFocusable(true);

setFocusableInTouchMode(true);

//这是常亮

setKeepScreenOn(true);

}

给SurfaceView添加callback实现其中三个方法

@Override

public void surfaceCreated(SurfaceHolder surfaceHolder) {

//surface创建的时候

mThread = new Thread(this);

//创建的时候就开启线程

isRunning = true;

mThread.start();

}

@Override

public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

//变化的时候

}

@Override

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

//销毁的时候 关闭线程

isRunning = false;

}

在子线程中定义一个死循环不断的进行绘制

@Override

public void run() {

//在子线程中不断的绘制

while (isRunning) {

draw();

}

}

private void draw() {

try {

mCanvas = mSurfaceHolder.lockCanvas();

if (null != mCanvas) {

//避免执行到这里的时候程序已经退出 surfaceView已经销毁那么获取到canvas为null

}

} catch (Exception e) {

//异常可以不必处理

} finally {

//一定要释放canvas避免泄露

mSurfaceHolder.unlockCanvasAndPost(mCanvas);

}

}

3、了解了SurfaceView的基本用法之后,接下来实现抽奖转盘

首先测量整个View的范围,设置成正方形

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//直接控制Span为正方形

int width = Math.min(getMeasuredWidth(), getMeasuredHeight());

mPadding = getPaddingLeft();

//直径

mRadius = width - mPadding * 2;

//设置中心点

mCenter = width / 2;

//设置成正方形

setMeasuredDimension(width, width);

}

在SurfaceView创建的时候初始化画笔矩形范围等,见代码

public void surfaceCreated(SurfaceHolder surfaceHolder) {

//初始化绘制Span的画笔

mSpanPaint = new Paint();

mSpanPaint.setAntiAlias(true);

mSpanPaint.setDither(true);

//初始化绘制文本的画笔

mTextPaint = new Paint();

mTextPaint.setTextSize(mTextSize);

mTextPaint.setColor(0Xffa58453);

//绘制圆环的画笔

mCirclePaint = new Paint();

mCirclePaint.setAntiAlias(true);

mCirclePaint.setColor(0xffdfc89c);

//初始化Span的范围

mRectRange = new RectF(mPadding, mPadding, mPadding + mRadius, mPadding + mRadius);

mRectCircleRange = new RectF(mPadding * 3 / 2, mPadding * 3 / 2, getMeasuredWidth() - mPadding * 3 / 2, getMeasuredWidth() - mPadding * 3 / 2);

//初始化bitmap

mImgIconBitmap = new Bitmap[mSpanCount];

//将奖项的icon存储为Bitmap

for (int i = 0; i < mSpanCount; i++) {

mImgIconBitmap[i] = BitmapFactory.decodeResource(getResources(), mPrizeIcon[i]);

}

//surface创建的时候

mThread = new Thread(this);

//创建的时候就开启线程

isRunning = true;

mThread.start();

}

接下来就是在开启的子线程中进行绘制

@Override

public void run() {

//在子线程中不断的绘制

while (isRunning) {

//保证绘制不低于50毫秒 优化性能

long start = SystemClock.currentThreadTimeMillis();

draw();

long end = SystemClock.currentThreadTimeMillis();

if ((end - start) < 50) {

//休眠到50毫秒

SystemClock.sleep(50 - (end - start));

}

}

}

重点就在draw()方法中了下面就实现draw方法:

注意:避免mCanvas带来的内存泄漏

try {

mCanvas = mSurfaceHolder.lockCanvas();

if (null != mCanvas) {

//避免执行到这里的时候程序已经退出 surfaceView已经销毁那么获取到canvas为null

//绘制背景

drawBg();

//绘制圆环

mCanvas.drawCircle(mCenter, mCenter, mRadius / 2 + mPadding / 20, mCirclePaint);

drawSpan();

}

} catch (Exception e) {

//异常可以不必处理

} finally {

//一定要释放canvas避免泄露

mSurfaceHolder.unlockCanvasAndPost(mCanvas);

}

画背景:

//绘制背景

private void drawBg() {

//背景设置为白色

mCanvas.drawColor(0xffffffff);

mCanvas.drawBitmap(mSpanBackground, null, new RectF(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredHeight() - mPadding / 2), mSpanPaint);

}

参数解释:

mSpanBackground背景图片

new RectF(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredHeight() - mPadding / 2)

//限制背景在一个矩形范围之类

绘制内圆环

mCanvas.drawCircle(mCenter, mCenter, mRadius / 2 + mPadding / 20, mCirclePaint);

绘制中间八个盘块

//定义一个变量临时记录开始转动的角度

float tempAngle = mStartSpanAngle;

//每个盘块所占的角度 CIRCLE_ANGLE = 360

float sweepAngle = CIRCLE_ANGLE / mSpanCount;

//循环绘制八个板块

for (int i = 0; i < mSpanCount; i++) {

//设置每个盘块画笔的颜色

mSpanPaint.setColor(mSpanColor[i]);

//绘制扇形盘块,第四个参数为true就是扇形否则就是弧形

mCanvas.drawArc(mRectCircleRange, tempAngle, sweepAngle, true, mSpanPaint);

//绘制文字

drawText(tempAngle, sweepAngle, mPrizeName[i]);

//绘制奖项Icon

drawPrizeIcon(tempAngle, mImgIconBitmap[i]);

//改变角度

tempAngle += sweepAngle;

}

绘制文字

文字绘制成圆环形状,根据path绘制文字

private void drawText(float tempAngle, float sweepAngle, String text) {

//绘制有弧度的文字 根据path绘制文字的路径

Path path = new Path();

path.addArc(mRectRange, tempAngle, sweepAngle);

//让文字水平居中 那绘制文字的起点位子就是 弧度的一半 - 文字的一半

float textWidth = mTextPaint.measureText(text);

float hOval = (float) ((mRadius * Math.PI / mSpanCount / 2) - (textWidth / 2));

float vOval = mRadius / 15;//竖直偏移量可以自定义

mCanvas.drawTextOnPath(text, path, hOval, vOval, mTextPaint); //第三个四个参数是竖直和水平偏移量

}

绘制盘块中的奖品icon图片

private void drawPrizeIcon(float tempAngle, Bitmap bitmap) {

//图片的大小设置成直径的1/8

int iconWidth = mRadius / 20;

//根据角度计算icon中心点

//角度计算 1度 == Math.PI / 180

double angle = (tempAngle + CIRCLE_ANGLE / mSpanCount / 2) * Math.PI / 180;

//根据三角函数,计算中心点(x,y)

int x = (int) (mCenter + mRadius / 4 * Math.cos(angle));

int y = (int) (mCenter + mRadius / 4 * Math.sin(angle));

//定义一个矩形 限制icon位置

RectF rectF = new RectF(x - iconWidth, y - iconWidth, x + iconWidth, y + iconWidth);

mCanvas.drawBitmap(bitmap, null, rectF, null);

}

大致的绘制基本完成,重点就是通过改变开始转动的角度让转盘转动起来。

mStartSpanAngle += mSpeed;//mSpeed的数值控制转动的速度

//声明的一个结束标志

if (isSpanEnd) {

mSpeed -= 1;

}

if (mSpeed <= 0) {

//停止旋转了

mSpeed = 0;

isSpanEnd = false;

//定义一个回调,监控转盘停止转动

mSpanRollListener.onSpanRollListener(mSpeed);

}

定义一个方法, 启动转盘

//抽奖转盘重点就在这里,根据自己传入的index控制抽到的奖品

public void luckyStart(int index) {

//根据index控制停留的位置 angle 是每个奖品所占的角度范围

float angle = CIRCLE_ANGLE / mSpanCount;

//计算指针停留在某个index下的角度范围HALF_CIRCLE_ANGLE=180度

float from = HALF_CIRCLE_ANGLE - (index - 1) * angle;

float end = from + angle;

//设置需要停下来的时候转动的距离 保证每次不停留的某个index下的同一个位置

float targetFrom = 4 * CIRCLE_ANGLE + from;

float targetEnd = 4 * CIRCLE_ANGLE + end;//最终停下来的位置在from-end之间,4 * CIRCLE_ANGLE 自定义要多转几圈

//计算要停留下来的时候速度的范围,这里注意:涉及到等差数列的公式,因为涉及到让转盘停止转动是使mSpeed-=1;所以它是从 vFrom--0等差递减的一个过程,所以可以算出来vFrom,同理计算出vEnd

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

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

//在点击开始转动的时候 传递进来的index值就已经决定停留在那一项上面了

mSpeed = vFrom + Math.random() * (vEnd - vFrom);

isSpanEnd = false;

}

停止转动

public void luckStop() {

//在停止转盘的时候强制吧开始角度赋值为0 因为控制停留指定位置的角度计算是根据开始角度为0计算的

mStartSpanAngle = 0;

isSpanEnd = true;

}

具体实现牵涉到一些数学知识,可能讲述不太清楚,不过上代码就比较好了,直接看代码会更加清晰,代码中注释很详细,防止以后自己再回头看的时候忘记。欢迎访问github地址,查看完整代码,可以根据自己的需求去修改,顺便学习一下自定义view了解一下SurfaceView的用法。

地址:完整代码地址欢迎start,如有发现问题请多多指点互相学习交流。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。