国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

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

2019-12-12 04:22:16
字體:
供稿:網(wǎng)友

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

話不多說,先上效果圖

完整代碼地址歡迎start

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

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

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

一般會用到的成員變量

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)其中三個方法

 @Overridepublic void surfaceCreated(SurfaceHolder surfaceHolder) {  //surface創(chuàng)建的時候  mThread = new Thread(this);  //創(chuàng)建的時候就開啟線程  isRunning = true;  mThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {  //變化的時候}@Overridepublic void surfaceDestroyed(SurfaceHolder surfaceHolder) {  //銷毀的時候 關(guān)閉線程  isRunning = false;}

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

 @Overridepublic void run() {  //在子線程中不斷的繪制  while (isRunning) {    draw();  }}private void draw() {  try {    mCanvas = mSurfaceHolder.lockCanvas();    if (null != mCanvas) {  //避免執(zhí)行到這里的時候程序已經(jīng)退出 surfaceView已經(jīng)銷毀那么獲取到canvas為null    }  } catch (Exception e) {    //異常可以不必處理  } finally {    //一定要釋放canvas避免泄露    mSurfaceHolder.unlockCanvasAndPost(mCanvas);  }}

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

首先測量整個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)建的時候初始化畫筆矩形范圍等,見代碼

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];    //將獎項(xiàng)的icon存儲為Bitmap    for (int i = 0; i < mSpanCount; i++) {      mImgIconBitmap[i] = BitmapFactory.decodeResource(getResources(), mPrizeIcon[i]);    }    //surface創(chuàng)建的時候    mThread = new Thread(this);    //創(chuàng)建的時候就開啟線程    isRunning = true;    mThread.start();  }

接下來就是在開啟的子線程中進(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帶來的內(nèi)存泄漏

 try {    mCanvas = mSurfaceHolder.lockCanvas();    if (null != mCanvas) {      //避免執(zhí)行到這里的時候程序已經(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)  //限制背景在一個矩形范圍之類 

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

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

繪制中間八個盤塊

//定義一個變量臨時記錄開始轉(zhuǎn)動的角度float tempAngle = mStartSpanAngle;//每個盤塊所占的角度 CIRCLE_ANGLE = 360float sweepAngle = CIRCLE_ANGLE / mSpanCount;//循環(huán)繪制八個板塊  for (int i = 0; i < mSpanCount; i++) {    //設(shè)置每個盤塊畫筆的顏色    mSpanPaint.setColor(mSpanColor[i]);    //繪制扇形盤塊,第四個參數(shù)為true就是扇形否則就是弧形    mCanvas.drawArc(mRectCircleRange, tempAngle, sweepAngle, true, mSpanPaint);    //繪制文字    drawText(tempAngle, sweepAngle, mPrizeName[i]);    //繪制獎項(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); //第三個四個參數(shù)是豎直和水平偏移量}

繪制盤塊中的獎品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));  //定義一個矩形 限制icon位置  RectF rectF = new RectF(x - iconWidth, y - iconWidth, x + iconWidth, y + iconWidth);  mCanvas.drawBitmap(bitmap, null, rectF, null);}

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

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

定義一個方法, 啟動轉(zhuǎn)盤

//抽獎轉(zhuǎn)盤重點(diǎn)就在這里,根據(jù)自己傳入的index控制抽到的獎品public void luckyStart(int index) {  //根據(jù)index控制停留的位置 angle 是每個獎品所占的角度范圍  float angle = CIRCLE_ANGLE / mSpanCount;  //計(jì)算指針停留在某個index下的角度范圍HALF_CIRCLE_ANGLE=180度  float from = HALF_CIRCLE_ANGLE - (index - 1) * angle;  float end = from + angle;  //設(shè)置需要停下來的時候轉(zhuǎn)動的距離 保證每次不停留的某個index下的同一個位置  float targetFrom = 4 * CIRCLE_ANGLE + from;  float targetEnd = 4 * CIRCLE_ANGLE + end;//最終停下來的位置在from-end之間,4 * CIRCLE_ANGLE 自定義要多轉(zhuǎn)幾圈  //計(jì)算要停留下來的時候速度的范圍,這里注意:涉及到等差數(shù)列的公式,因?yàn)樯婕暗阶屴D(zhuǎn)盤停止轉(zhuǎn)動是使mSpeed-=1;所以它是從 vFrom--0等差遞減的一個過程,所以可以算出來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)動的時候 傳遞進(jìn)來的index值就已經(jīng)決定停留在那一項(xiàng)上面了  mSpeed = vFrom + Math.random() * (vEnd - vFrom);  isSpanEnd = false;}

停止轉(zhuǎn)動

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

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

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 兰溪市| 收藏| 临澧县| 富阳市| 鹤庆县| 钟山县| 乌什县| 新蔡县| 小金县| 中宁县| 宜兰市| 本溪| 高阳县| 屯留县| 云南省| 英山县| 永康市| 剑川县| 茶陵县| 龙口市| 霸州市| 和龙市| 天峻县| 额尔古纳市| 衡阳市| 揭阳市| 徐闻县| 巢湖市| 东兴市| 石城县| 乐陵市| 扎兰屯市| 宿州市| 仪征市| 南靖县| 天柱县| 江达县| 赣榆县| 遂平县| 铜山县| 阳山县|