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

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

(0)

相关推荐

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

    因为偶尔关注QQ运动, 看到QQ运动的积分抽奖界面比较有意思,所以就尝试用自定义View实现了下,原本想通过开发者选项查看下界面的一些信息,后来发现积分抽奖界面是在WebView中展示的,应该是在H5页面中用js代码实现的,暂时不去管它了. 这里的自定义View针对的是继承自View的情况,你可以将Canvas想象为画板, Paint为画笔,自定义View的过程和在画板上用画笔作画其实类似,想象在画板上作画的过程,你要画一个多大图形(对应View的测量 onMeasure方法),你要画什么样的图

  • Android实现抽奖转盘实例代码

    本文详述了android抽奖程序的实现方法,程序为一个抽奖大转盘代码,里面定义了很多图形方法和动画. 实现主要功能的SlyderView.java源代码如下: import android.app.Activity; import android.content.Context; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import

  • Android抽奖轮盘的制作方法

    本文实例为大家分享了Android抽奖轮盘的具体代码,供大家参考,具体内容如下 main布局(图片资源请自行寻找,抱歉) <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gra

  • Android打造流畅九宫格抽奖活动效果

    因为company项目中需要做九宫格抽奖活动,以前都没有做过类似的功能,虽然之前在浏览大神们的博客中,无意中也看到了好多关于抽奖的项目,但因为项目中没有需要,一直都没有点击进去看.这次不去看估计不行.直到公司计划要做抽奖功能,才迫不得已上网查找demo 网上找了大半天,好不容易找到了几个demo,下载下来,解压缩包发现竟然里面空空如也,只有几张九宫格的图片,害我白白浪费了几个CSDN积分.后面在eoe网站那发现了一个demo,于是好开心,下载下来后马上导入到工程中,运行看了效果,九宫格是出来了,

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

    一.概述 今天给大家带来SurfaceView的一个实战案例,话说自定义View也是各种写,一直没有写过SurfaceView,这个玩意是什么东西?什么时候用比较好呢? 可以看到SurfaceView也是继承了View,但是我们并不需要去实现它的draw方法来绘制自己,为什么呢? 因为它和View有一个很大的区别,View在UI线程去更新自己:而SurfaceView则在一个子线程中去更新自己:这也显示出了它的优势,当制作游戏等需要不断刷新View时,因为是在子线程,避免了对UI线程的阻塞. 知

  • Android简单实现圆盘抽奖界面

    闲来无事,做了一个简单的抽奖转盘的ui实现,供大家参考 package com.microchange.lucky; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet;

  • js和html5实现手机端刮刮卡抽奖效果完美兼容android/IOS

    绝对值得看的来篇,哈哈.本人亲自完成,有错误请大家指出: 现在的手机完美支持html5,所以如果手机端想要做个抽奖模块的话,用刮刮卡抽奖效果,相信这个互动体验是非常棒的 ​ps:由于本人没有wp8系统的手机,所以没法兼容wp8系统的,目前完美兼容android,IOS 如果要在pc浏览的话,得改下js,目前支持谷歌,火狐,ie>=10,如果网友想要的话我就去写个 代码如下: 复制代码 代码如下: <!DOCTYPE html> <html lang="en"&g

  • Android App中实现简单的刮刮卡抽奖效果的实例详解

    主要思想: 将一个view设计成多层:背景层,含中奖信息等: 遮盖层,用于刮奖,使用关联一个Bitmap的Canvas 在该Bitmap上,使用它的canvas.drawPath的api来处理 手势滑动(类似刮奖的动作) 使用paint.setXfermode 来进行消除手势滑动区域 public class GuaView extends View { private Bitmap mBitmap; //遮盖的图层 private Canvas mCanvas; //绘制遮盖图层 privat

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

    使用surfaceView自定义抽奖大转盘 话不多说,先上效果图 完整代码地址欢迎start 实现思路以及过程 1.首先了解SurfaceView的基本用法,它跟一般的View不太一样,采用的双缓存机制,可以在子线程中绘制View,不会因为绘制耗时而失去流畅性,这也是选择使用SurfaceView去自定义这个抽奖大转盘的原因,毕竟绘制这个转盘的盘块,奖项的图片和文字以及转动都是靠绘制出来的,是一个比较耗时的绘制过程. 2.使用SurfaceView的一般模板样式 一般会用到的成员变量 priva

  • Redis 抽奖大转盘的实战示例

    目录 1. 项目介绍 2. 项目演示 3. 表结构 4. 项目搭建 4.1 依赖 4.2 YML配置 4.3 代码生成 4.4 Redis 配置 4.5 常量管理 4.6 业务代码 4.7 总结 5. 项目地址 1. 项目介绍 这是一个基于Spring boot + Mybatis Plus + Redis 的简单案例. 主要是将活动内容.奖品信息.记录信息等缓存到Redis中,然后所有的抽奖过程全部从Redis中做数据的操作. 大致内容很简单,具体操作下面慢慢分析. 2. 项目演示 话不多说,

  • Android 美食大转盘详解流程

    目录 效果视频 前言 美食大转盘 初始化SurfaceView 测量 绘制 绘制盘块 开始旋转转盘 停止旋转转盘 自定义转盘等份 控件引用 沉浸式体验 效果图 Reveal Animator 效果视频 自定义转盘代码 XML布局代码 Activity代码 代码下载地址 效果视频 前言 你还在为明天吃什么而烦恼嘛 美食大赏帮你解决选择困难症 帮你做出最佳的选择 做吃货,我们是认真的 美食大转盘 本示例使用SurfaceView绘制而成,接下来逐步分析, 文末会贴出全部代码``文末会贴出全部代码``

  • Android实现可点击的幸运大转盘

    之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法. 这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的 方法.之前网上的都是带有指针的,或者可点击改变效果,但是并不知道选择的到底是哪个,即虚拟选择. 实现该功能的主要代码如下: 1.自定义一个布局,存放图片,实现圆形布局. /** * * * CircleMenuLayout.java * * @author wuxiaosu * */ public class CircleMenuLayou

  • js实现大转盘抽奖游戏实例

    本文实例讲述了js实现大转盘抽奖游戏.分享给大家供大家参考.具体实现方法如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <hea

  • 基于PHP代码实现中奖概率算法可用于刮刮卡、大转盘等抽奖算法

    大转盘中奖概率算法在我们的日常生活中,经常遇到,那么基于php代码是如何实现中奖概率算法的,下面通过一段代码实例给大家介绍php中奖概率算法,代码简单易懂,并且附有注释,具体代码如下所示: <?php /* * 经典的概率算法, * $proArr是一个预先设置的数组, * 假设数组为:array(100,200,300,400), * 开始是从1,1000 这个概率范围内筛选第一个数是否在他的出现概率范围之内, * 如果不在,则将概率空间,也就是k的值减去刚刚的那个数字的概率空间, * 在本例

  • php抽奖概率算法(刮刮卡,大转盘)

    本文实例为大家分享了php中奖概率算法,可用于刮刮卡,大转盘等抽奖算法,用法很简单,代码里有详细注释说明,供大家参考,具体内容如下 <?php /* * 经典的概率算法, * $proArr是一个预先设置的数组, * 假设数组为:array(100,200,300,400), * 开始是从1,1000 这个概率范围内筛选第一个数是否在他的出现概率范围之内, * 如果不在,则将概率空间,也就是k的值减去刚刚的那个数字的概率空间, * 在本例当中就是减去100,也就是说第二个数是在1,900这个范围

  • jquery——九宫格大转盘抽奖实例

     一.用到的图片 二.代码如下,重点是js部分 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>jQuery九宫格大转盘抽奖</title> <style> #lottery{width:570px;height:510

  • jquery实现九宫格大转盘抽奖

    下面我们来分享一个九宫格抽奖特效 特效说明: 一款jQuery九宫格大转盘抽奖代码网页特效,点击抽奖按钮开始随机抽奖选择奖品,可设置起点位置.奖品数量.转动次数.中奖位置参数.(兼容测试:IE7及以上.Firefox.Chrome.Opera.Safari.360等主流浏览器) HTML: <!--效果html开始--> <div id="lottery"> <table border="0" cellpadding="0&q

  • Unity 2017使用UGUI实现大转盘抽奖

    本文实例为大家分享了Unity 2017实现大转盘抽奖的具体代码,供大家参考,具体内容如下 涉及到的插件:Dotween 在"Hierarchy"面板创建一个Image,这个Image用来显示大转盘的那个圆盘,我用的UGUI里默认的那个圆圆的图片,放大后很模糊,不过不打紧 接着创建一个Panel名字改成"奖品组",这个是奖品的父物体,记得吧Panel里的Image,Canvas Renderer组件删了 然后在创建一个Panel名字改成"Awar"

随机推荐