欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android使用surfaceView自定義抽獎(jiǎng)大轉(zhuǎn)盤

 更新時(shí)間:2020年07月29日 12:23:38   作者:xiaohuanqi  
這篇文章主要為大家詳細(xì)介紹了Android使用surfaceView自定義抽獎(jiǎng)大轉(zhuǎn)盤,熟練掌握SurfaceVie實(shí)現(xiàn)抽獎(jiǎng)大轉(zhuǎn)盤,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

使用surfaceView自定義抽獎(jiǎng)大轉(zhuǎn)盤

話不多說(shuō),先上效果圖

完整代碼地址歡迎start

實(shí)現(xiàn)思路以及過(guò)程

1、首先了解SurfaceView的基本用法,它跟一般的View不太一樣,采用的雙緩存機(jī)制,可以在子線程中繪制View,不會(huì)因?yàn)槔L制耗時(shí)而失去流暢性,這也是選擇使用SurfaceView去自定義這個(gè)抽獎(jiǎng)大轉(zhuǎn)盤的原因,畢竟繪制這個(gè)轉(zhuǎn)盤的盤塊,獎(jiǎng)項(xiàng)的圖片和文字以及轉(zhuǎn)動(dòng)都是靠繪制出來(lái)的,是一個(gè)比較耗時(shí)的繪制過(guò)程。

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

一般會(huì)用到的成員變量

private SurfaceHolder mSurfaceHolder;
private Canvas  mCanvas;

初始化常亮

public SurfaceViewTemplate(Context context,AttributeSet attrs) {
 super(context, attrs);
 //初始化
 mSurfaceHolder = getHolder();
 mSurfaceHolder.addCallback(this);
 //設(shè)置可獲得焦點(diǎn)
 setFocusable(true);
 setFocusableInTouchMode(true);
 //這是常亮
 setKeepScreenOn(true);
}

給SurfaceView添加callback實(shí)現(xiàn)其中三個(gè)方法

 @Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
 //surface創(chuàng)建的時(shí)候
 mThread = new Thread(this);
 //創(chuàng)建的時(shí)候就開啟線程
 isRunning = true;
 mThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
 //變化的時(shí)候
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
 //銷毀的時(shí)候 關(guān)閉線程
 isRunning = false;
}

在子線程中定義一個(gè)死循環(huán)不斷的進(jìn)行繪制

 @Override
public void run() {
 //在子線程中不斷的繪制
 while (isRunning) {
  draw();
 }
}

private void draw() {
 try {
  mCanvas = mSurfaceHolder.lockCanvas();
  if (null != mCanvas) {
 //避免執(zhí)行到這里的時(shí)候程序已經(jīng)退出 surfaceView已經(jīng)銷毀那么獲取到canvas為null
  }
 } catch (Exception e) {
  //異??梢圆槐靥幚?
 } finally {
  //一定要釋放canvas避免泄露
  mSurfaceHolder.unlockCanvasAndPost(mCanvas);
 }
}

3、了解了SurfaceView的基本用法之后,接下來(lái)實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤

首先測(cè)量整個(gè)View的范圍,設(shè)置成正方形

@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;
  //設(shè)置中心點(diǎn)
  mCenter = width / 2;
  //設(shè)置成正方形
  setMeasuredDimension(width, width);
 }

在SurfaceView創(chuàng)建的時(shí)候初始化畫筆矩形范圍等,見代碼

public void surfaceCreated(SurfaceHolder surfaceHolder) {
  //初始化繪制Span的畫筆
  mSpanPaint = new Paint();
  mSpanPaint.setAntiAlias(true);
  mSpanPaint.setDither(true);
  //初始化繪制文本的畫筆
  mTextPaint = new Paint();
  mTextPaint.setTextSize(mTextSize);
  mTextPaint.setColor(0Xffa58453);
  //繪制圓環(huán)的畫筆
  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];
  //將獎(jiǎng)項(xiàng)的icon存儲(chǔ)為Bitmap
  for (int i = 0; i < mSpanCount; i++) {
   mImgIconBitmap[i] = BitmapFactory.decodeResource(getResources(), mPrizeIcon[i]);
  }

  //surface創(chuàng)建的時(shí)候
  mThread = new Thread(this);
  //創(chuàng)建的時(shí)候就開啟線程
  isRunning = true;
  mThread.start();
 }

接下來(lái)就是在開啟的子線程中進(jìn)行繪制

 @Override
 public void run() {
  //在子線程中不斷的繪制
  while (isRunning) {
   //保證繪制不低于50毫秒 優(yōu)化性能
   long start = SystemClock.currentThreadTimeMillis();
   draw();
   long end = SystemClock.currentThreadTimeMillis();
   if ((end - start) < 50) {
    //休眠到50毫秒
    SystemClock.sleep(50 - (end - start));
   }
  }
 }

重點(diǎn)就在draw()方法中了下面就實(shí)現(xiàn)draw方法:

注意:避免mCanvas帶來(lái)的內(nèi)存泄漏

 try {
  mCanvas = mSurfaceHolder.lockCanvas();
  if (null != mCanvas) {
   //避免執(zhí)行到這里的時(shí)候程序已經(jīng)退出 surfaceView已經(jīng)銷毀那么獲取到canvas為null
   //繪制背景
   drawBg();
   //繪制圓環(huán)
   mCanvas.drawCircle(mCenter, mCenter, mRadius / 2 + mPadding / 20, mCirclePaint);
   drawSpan();
  }
 } catch (Exception e) {
  //異??梢圆槐靥幚?
 } finally {
  //一定要釋放canvas避免泄露
  mSurfaceHolder.unlockCanvasAndPost(mCanvas);
 }

畫背景:

//繪制背景
 private void drawBg() {
  //背景設(shè)置為白色
  mCanvas.drawColor(0xffffffff);
  mCanvas.drawBitmap(mSpanBackground, null, new RectF(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredHeight() - mPadding / 2), mSpanPaint);
 }

參數(shù)解釋:

 mSpanBackground背景圖片
 new RectF(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredHeight() - mPadding / 2)
 //限制背景在一個(gè)矩形范圍之類 

繪制內(nèi)圓環(huán)

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

繪制中間八個(gè)盤塊

//定義一個(gè)變量臨時(shí)記錄開始轉(zhuǎn)動(dòng)的角度
float tempAngle = mStartSpanAngle;
//每個(gè)盤塊所占的角度 CIRCLE_ANGLE = 360
float sweepAngle = CIRCLE_ANGLE / mSpanCount;
//循環(huán)繪制八個(gè)板塊
 for (int i = 0; i < mSpanCount; i++) {
  //設(shè)置每個(gè)盤塊畫筆的顏色
  mSpanPaint.setColor(mSpanColor[i]);
  //繪制扇形盤塊,第四個(gè)參數(shù)為true就是扇形否則就是弧形
  mCanvas.drawArc(mRectCircleRange, tempAngle, sweepAngle, true, mSpanPaint);
  //繪制文字
  drawText(tempAngle, sweepAngle, mPrizeName[i]);
  //繪制獎(jiǎng)項(xiàng)Icon
  drawPrizeIcon(tempAngle, mImgIconBitmap[i]);
   //改變角度
   tempAngle += sweepAngle;
  }

繪制文字

文字繪制成圓環(huán)形狀,根據(jù)path繪制文字

private void drawText(float tempAngle, float sweepAngle, String text) {
 //繪制有弧度的文字 根據(jù)path繪制文字的路徑
 Path path = new Path();
 path.addArc(mRectRange, tempAngle, sweepAngle);
 //讓文字水平居中 那繪制文字的起點(diǎn)位子就是 弧度的一半 - 文字的一半
 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); //第三個(gè)四個(gè)參數(shù)是豎直和水平偏移量
}

繪制盤塊中的獎(jiǎng)品icon圖片

private void drawPrizeIcon(float tempAngle, Bitmap bitmap) {
 //圖片的大小設(shè)置成直徑的1/8
 int iconWidth = mRadius / 20;
 //根據(jù)角度計(jì)算icon中心點(diǎn)
 //角度計(jì)算 1度 == Math.PI / 180
 double angle = (tempAngle + CIRCLE_ANGLE / mSpanCount / 2) * Math.PI / 180;
 //根據(jù)三角函數(shù),計(jì)算中心點(diǎn)(x,y)
 int x = (int) (mCenter + mRadius / 4 * Math.cos(angle));
 int y = (int) (mCenter + mRadius / 4 * Math.sin(angle));
 //定義一個(gè)矩形 限制icon位置
 RectF rectF = new RectF(x - iconWidth, y - iconWidth, x + iconWidth, y + iconWidth);
 mCanvas.drawBitmap(bitmap, null, rectF, null);
}

大致的繪制基本完成,重點(diǎn)就是通過(guò)改變開始轉(zhuǎn)動(dòng)的角度讓轉(zhuǎn)盤轉(zhuǎn)動(dòng)起來(lái)。

 mStartSpanAngle += mSpeed;//mSpeed的數(shù)值控制轉(zhuǎn)動(dòng)的速度
  //聲明的一個(gè)結(jié)束標(biāo)志
 if (isSpanEnd) {
  mSpeed -= 1;
 }
 if (mSpeed <= 0) {
  //停止旋轉(zhuǎn)了
  mSpeed = 0;
  isSpanEnd = false;
  //定義一個(gè)回調(diào),監(jiān)控轉(zhuǎn)盤停止轉(zhuǎn)動(dòng)
  mSpanRollListener.onSpanRollListener(mSpeed);
 }

定義一個(gè)方法, 啟動(dòng)轉(zhuǎn)盤

//抽獎(jiǎng)轉(zhuǎn)盤重點(diǎn)就在這里,根據(jù)自己傳入的index控制抽到的獎(jiǎng)品
public void luckyStart(int index) {
 //根據(jù)index控制停留的位置 angle 是每個(gè)獎(jiǎng)品所占的角度范圍
 float angle = CIRCLE_ANGLE / mSpanCount;
 //計(jì)算指針停留在某個(gè)index下的角度范圍HALF_CIRCLE_ANGLE=180度
 float from = HALF_CIRCLE_ANGLE - (index - 1) * angle;
 float end = from + angle;

 //設(shè)置需要停下來(lái)的時(shí)候轉(zhuǎn)動(dòng)的距離 保證每次不停留的某個(gè)index下的同一個(gè)位置
 float targetFrom = 4 * CIRCLE_ANGLE + from;
 float targetEnd = 4 * CIRCLE_ANGLE + end;//最終停下來(lái)的位置在from-end之間,4 * CIRCLE_ANGLE 自定義要多轉(zhuǎn)幾圈

 //計(jì)算要停留下來(lái)的時(shí)候速度的范圍,這里注意:涉及到等差數(shù)列的公式,因?yàn)樯婕暗阶屴D(zhuǎn)盤停止轉(zhuǎn)動(dòng)是使mSpeed-=1;所以它是從 vFrom--0等差遞減的一個(gè)過(guò)程,所以可以算出來(lái)vFrom,同理計(jì)算出vEnd
 float vFrom = (float) ((Math.sqrt(1 + 8 * targetFrom) - 1) / 2);
 float vEnd = (float) ((Math.sqrt(1 + 8 * targetEnd) - 1) / 2);
 //在點(diǎn)擊開始轉(zhuǎn)動(dòng)的時(shí)候 傳遞進(jìn)來(lái)的index值就已經(jīng)決定停留在那一項(xiàng)上面了
 mSpeed = vFrom + Math.random() * (vEnd - vFrom);
 isSpanEnd = false;
}

停止轉(zhuǎn)動(dòng)

 public void luckStop() {
 //在停止轉(zhuǎn)盤的時(shí)候強(qiáng)制吧開始角度賦值為0 因?yàn)榭刂仆A糁付ㄎ恢玫慕嵌扔?jì)算是根據(jù)開始角度為0計(jì)算的
 mStartSpanAngle = 0;
 isSpanEnd = true;
}

具體實(shí)現(xiàn)牽涉到一些數(shù)學(xué)知識(shí),可能講述不太清楚,不過(guò)上代碼就比較好了,直接看代碼會(huì)更加清晰,代碼中注釋很詳細(xì),防止以后自己再回頭看的時(shí)候忘記。歡迎訪問(wèn)github地址,查看完整代碼,可以根據(jù)自己的需求去修改,順便學(xué)習(xí)一下自定義view了解一下SurfaceView的用法。

地址:完整代碼地址歡迎start,如有發(fā)現(xiàn)問(wèn)題請(qǐng)多多指點(diǎn)互相學(xué)習(xí)交流。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android實(shí)戰(zhàn)項(xiàng)目之實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器

    Android實(shí)戰(zhàn)項(xiàng)目之實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器

    隨著移動(dòng)互聯(lián)網(wǎng)的普及,手機(jī)應(yīng)用程序已經(jīng)成為人們生活中不可或缺的一部分,計(jì)算器是一類被廣泛使用的應(yīng)用程序之一,這篇文章主要給大家介紹了關(guān)于Android實(shí)戰(zhàn)項(xiàng)目之實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • 自定義ListView實(shí)現(xiàn)拖拽ListItem項(xiàng)交換位置(附源碼)

    自定義ListView實(shí)現(xiàn)拖拽ListItem項(xiàng)交換位置(附源碼)

    本文要實(shí)現(xiàn)的是拖拽ListView的Item項(xiàng),在布局方面還是用基于布局泵LayoutInflater來(lái)從不同的Layout模板拿到不同的布局然后將view返回,感興趣的朋友可以了解下哈
    2013-06-06
  • Android中自定義控件之液位指示器

    Android中自定義控件之液位指示器

    這篇文章主要介紹了Android中自定義控件之液位指示器 方法的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • Android自定義控件實(shí)現(xiàn)折線圖

    Android自定義控件實(shí)現(xiàn)折線圖

    這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)折線圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • 最新評(píng)論