一、SurfaceView认识及的应用的思路
- SurfaceView继承自(extends)View,View是在UI线程中进行绘制;
- 而SurfaceView是在一个子线程中对自己进行绘制,优势:避免造成UI线程阻塞;
- SurfaceView中包含一个专门用于绘制的Surface,Surface中包含一个Canvas;
- 获得Canvas:可以从SurfaceView中方法的getHolder()获得SurfaceHolder,从holder获得Canvas;
- holder还管理着SurfaceView的生命周期:
①surfaceCreated()创建子线程,子线程的run()方法中开启SurfaceView的绘制。
②surfaceChanged()。
③surfaceDestoryed()中关闭子线程。
二、SurfaceView的一般写法

1 package com.example.luckypan; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.util.AttributeSet; 6 import android.view.SurfaceHolder; 7 import android.view.SurfaceHolder.Callback; 8 import android.view.SurfaceView; 9 10 public class SurfaceViewTemplate extends SurfaceView implements Callback, Runnable { 11 12 private SurfaceHolder mHolder; 13 private Canvas mCanvas; 14 /** 15 * 用于绘制线程 16 */ 17 private Thread thread; 18 /** 19 * 线程的控制开关 20 */ 21 private boolean isRunning; 22 public SurfaceViewTemplate(Context context) { 23 this(context, null); 24 } 25 public SurfaceViewTemplate(Context context, AttributeSet attrs) { 26 super(context, attrs); 27 mHolder=getHolder(); 28 mHolder.addCallback(this); 29 //可获得焦点 30 setFocusable(true); 31 setFocusableInTouchMode(true); 32 //设置常量 33 setKeepScreenOn(true); 34 } 35 public void surfaceCreated(SurfaceHolder holder) { 36 isRunning=true; 37 thread=new Thread(this); 38 thread.start(); 39 40 } 41 public void surfaceChanged(SurfaceHolder holder, int format, int width, 42 int height) { 43 // TODO Auto-generated method stub 44 45 } 46 public void surfaceDestroyed(SurfaceHolder holder) { 47 isRunning=false; 48 49 } 50 public void run() { 51 //不断进行绘制 52 while (isRunning) 53 { 54 draw(); 55 } 56 } 57 private void draw() { 58 try { 59 mCanvas=mHolder.lockCanvas(); 60 if (mCanvas!=null) { 61 // 62 } 63 } 64 catch (Exception e) { 65 66 } 67 finally 68 { 69 if (mCanvas!=null) { 70 mHolder.unlockCanvasAndPost(mCanvas); 71 } 72 } 73 } 74 75 76 }
三、代码编写
- 绘制抽奖转盘的盘快
- 绘制背景:drawBg(), Canvas的两个方法:drawColor(color the color to draw onto the canvas),这里我设置背景色为白色0xFFFFFFFF、drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint),这个bitmap是背景图片,背景是一个矩形new Rect(),其他参数设置为null。
-
1 private void drawBg() { 2 mCanvas.drawColor(0xFFFFFFFF); 3 mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding/2, mPadding/2, getMeasuredWidth()-mPadding/2, getMeasuredHeight()-mPadding/2), null); 4 }
-
- 绘制盘快:

1 //绘制盘快 2 float tmpAngle=mStartAngle; 3 float sweepAngle=360/mItemCount; 4 for (int i = 0; i < mItemCount; i++) { 5 mArcPaint.setColor(mColor[i]); 6 //绘制盘快 7 mCanvas.drawArc(mRange, tmpAngle, sweepAngle,true, mArcPaint);
- 绘制抽奖转盘的奖项文字

1 /** 2 * 绘制每个盘快的文本 3 * @param tmpAngle 4 * @param sweepAngle 5 * @param string 6 */ 7 private void drawText(float tmpAngle, float sweepAngle, String string) { 8 Path path=new Path(); 9 path.addArc(mRange, tmpAngle, sweepAngle); 10 //利用水平偏移量让文字居中 11 float textWidth=mTextPaint.measureText(string); 12 int hOffset=(int) (mRadius*Math.PI/mItemCount/2-textWidth/2); 13 int vOffset=mRadius/2/6;//垂直偏移量 14 mCanvas.drawTextOnPath(string, path, hOffset, vOffset, mTextPaint); 15 }
- 绘制抽奖转盘的图片

1 /** 2 * 绘制图片 3 * @param tmpAngle 4 * @param bitmap 5 */ 6 private void drawIcon(float tmpAngle, Bitmap bitmap) { 7 //设置图片的宽度为直径的1/8 8 int imgWidth=mRadius/8; 9 float angle=(float) ((tmpAngle+360/mItemCount/2)*Math.PI/180); 10 int x=(int) (mCenter+mRadius/2/2*Math.cos(angle)); 11 int y=(int) (mCenter+mRadius/2/2*Math.sin(angle)); 12 //确定图片位置 13 Rect rect=new Rect(x-imgWidth/2, y-imgWidth/2, x+imgWidth/2, y+imgWidth/2); 14 mCanvas.drawBitmap(bitmap, null, rect,null); 15 }
- 转盘滚动及设置停止的指向

1 private void draw() { 2 try { 3 mCanvas=mHolder.lockCanvas(); 4 if (mCanvas!=null) { 5 //绘制背景 6 drawBg(); 7 //绘制盘快 8 float tmpAngle=mStartAngle; 9 float sweepAngle=360/mItemCount; 10 for (int i = 0; i < mItemCount; i++) { 11 mArcPaint.setColor(mColor[i]); 12 //绘制盘快 13 mCanvas.drawArc(mRange, tmpAngle, sweepAngle,true, mArcPaint); 14 //绘制文本 15 drawText(tmpAngle,sweepAngle,mStrings[i]); 16 //绘制图片 17 drawIcon(tmpAngle,mImagBitmaps[i]); 18 tmpAngle+=sweepAngle; 19 } 20 mStartAngle+=mSpeed; 21 //判断是否点击停止按钮 22 if (isShouldEnd) { 23 mSpeed-=1; 24 } 25 if (mSpeed<=0) 26 { 27 mSpeed=0; 28 isShouldEnd=false; 29 } 30 } 31 } 32 catch (Exception e) { 33 34 } 35 finally 36 { 37 if (mCanvas!=null) { 38 mHolder.unlockCanvasAndPost(mCanvas); 39 } 40 } 41 }
四、抽奖转盘的秘密

1 /** 2 * 电机启动旋转 3 * 控制指定盘快停止范围 4 * @param index 5 */ 6 public void luckyStart(int index) 7 { 8 //计算每一项的角度 9 float angle=360/mItemCount; 10 //计算每一项的中奖范围 11 //1->150~210 12 //0->210~270 13 float from=270-(index+1)*angle; 14 float end=from+angle; 15 // 设置停下来需要旋转的距离 16 float targetFrom=4*360+from; 17 float targetEnd=4*360+end; 18 /** 19 * <pre> 20 * v1->0 且每次-1 21 * (v1+0)*(v1+1)/2=targetFrom 等差数列求和公式; 22 * v1*v1+v1-2*targetFrom=0; 23 * v1=(-1+Math.sqrt(1+8*targetFrom))/2 一元二次方程求根公式 24 * </pre> 25 */ 26 float v1=(float) ((-1+Math.sqrt(1+8*targetFrom))/2); 27 float v2=(float) ((-1+Math.sqrt(1+8*targetEnd))/2); 28 mSpeed=v1+Math.random()*(v2-v1); 29 // mSpeed=v2; 30 isShouldEnd=false; 31 }