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

概述使用surfaceView自定义抽奖转盘话不多说,先上效果图完整代码地址欢迎start实现思路以及过程

使用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实现其中三个方法

 @OverrIDepublic voID surfaceCreated(SurfaceHolder surfaceHolder) {  //surface创建的时候  mThread = new Thread(this);  //创建的时候就开启线程  isRunning = true;  mThread.start();}@OverrIDepublic voID surfaceChanged(SurfaceHolder surfaceHolder,int i,int i1,int i2) {  //变化的时候}@OverrIDepublic voID surfaceDestroyed(SurfaceHolder surfaceHolder) {  //销毁的时候 关闭线程  isRunning = false;}

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

 @OverrIDepublic 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,getMeasuredHeight() - mpadding / 2)  //限制背景在一个矩形范围之类 

绘制内圆环

mCanvas.drawCircle(mCenter,mCirclePaint); 

绘制中间八个盘块

//定义一个变量临时记录开始转动的角度float tempAngle = mStartSpanAngle;//每个盘块所占的角度 CIRCLE_ANGLE = 360float 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,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,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,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,如有发现问题请多多指点互相学习交流。

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

总结

以上是内存溢出为你收集整理的Android使用surfaceView自定义抽奖大转盘全部内容,希望文章能够帮你解决Android使用surfaceView自定义抽奖大转盘所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1147518.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存