我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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,如有发现问题请多多指点互相学习交流。

您可能感兴趣的文章:Android自定义view制作抽奖转盘Android自定义View实现抽奖转盘Android自定义View实现QQ运动积分转盘抽奖功能Android中利用SurfaceView制作抽奖转盘的全流程攻略基于Android实现转盘按钮代码Android实现抽奖转盘实例代码Android实现可点击的幸运大转盘


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

使用surfaceView自定义抽奖大转盘 话不多说,先上效果图完整代码地址欢迎start 实现思路以及过程 1、首先了解SurfaceView的基本用法,它跟一般的View不太一样,采用的双缓存机制,可以在子线程中绘制View,不会因为绘
2022-06-06

Android中利用SurfaceView制作抽奖转盘的全流程攻略

一、概述 今天给大家带来SurfaceView的一个实战案例,话说自定义View也是各种写,一直没有写过SurfaceView,这个玩意是什么东西?什么时候用比较好呢? 可以看到SurfaceView也是继承了View,但是我们并不需要去实
2022-06-06

Android自定义View如何实现QQ运动积分转盘抽奖功能

这篇文章主要讲解了Android自定义View如何实现QQ运动积分转盘抽奖功能,内容清晰明了,对此有兴趣的小伙伴可以学习一下,相信大家阅读完之后会有帮助。因为偶尔关注QQ运动, 看到QQ运动的积分抽奖界面比较有意思,所以就尝试用自定义Vie
2023-05-30

Qt如何编写自定义控件实现抽奖转盘

本文小编为大家详细介绍“Qt如何编写自定义控件实现抽奖转盘”,内容详细,步骤清晰,细节处理妥当,希望这篇“Qt如何编写自定义控件实现抽奖转盘”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。具体代码如下#ifndef
2023-07-02

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录