SurfaceView的使用
surfaceView在子线程里绘制图形
一般的创建流程
1.先写构造方法
2.获得SurfaceHolder //getHolder()
3.holder.addcallback(this);
4.实现3个callback的回调方法
4.1 surfaceCreated() //设置线程标示为true,开启线程
4.2 surfacesurfaceChanged()
4.3 surfaceDestroyed()//设置线程标示为false
5.在线程里run()方法里绘制图形( draw() )
6.在draw()方法里获得 Canvas (带有try catch)
代码模板
package com.example.luckyturnplate;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class SurfaceViewTurnplate extends SurfaceView implements Callback, Runnable{
private SurfaceHolder mHolder;
private Canvas mCanvas;
/**
* 用于绘制的线程
*/
private Thread t;
/**
* 用于控制线程的开关
*/
private boolean isRunning;
public SurfaceViewTurnplate(Context context) {
//调用自身2个参数的构造方法
this(context, null);
}
public SurfaceViewTurnplate(Context context, AttributeSet attrs) {
super(context, attrs);
//初始化
mHolder = getHolder();
mHolder.addCallback(this);
//设置可获得焦点
setFocusable(true);
setFocusableInTouchMode(true);
//设置常量
setKeepScreenOn(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
t = new Thread(this);
t.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
}
@Override
public void run() {
while(isRunning) {
draw();
}
}
private void draw() {
//1.为什么判空?
//当按home或者back键时,surfaceView会销毁,这时候可能进入draw(),所以要判空
//2.为什么要try catch?
//同上情况, 这时候可能正在执行线程,通常线程有很多异常抛出,所以try catch包住比较安全
try {
mCanvas = mHolder.lockCanvas();
if (mCanvas!=null) {
//draw something
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (mCanvas!=null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
效果:
具体实现
SurfaceViewTurnplate
package com.example.luckyturnplate;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class SurfaceViewTurnplate extends SurfaceView implements Callback, Runnable{
private SurfaceHolder mHolder;
private Canvas mCanvas;
/**
* 用于绘制的线程
*/
private Thread t;
/**
* 用于控制线程的开关
*/
private boolean isRunning;
//画笔
private Paint mArcPaint,mTextPaint;
//盘块的文字
private String[] strs = new String[] {"单反相机","IPAD","IPHONE","谢谢","衣服一套","谢谢"};
//
private float mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics());
//盘块的图片
private int[] imgs = new int[] {R.drawable.danfan,R.drawable.ipad,R.drawable.iphone,R.drawable.f040,R.drawable.meizi,R.drawable.f040};
private Bitmap[] bitmaps ;
//盘块的背景颜色
private int[] colors = new int[] {0xffffc300,0xfff17e01,0xffffc300,0xfff17e01,0xffffc300,0xfff17e01};
//盘块的数量
private int itemCount = 6;
//整个盘块的
private Rect mRect = new Rect();
//盘块的半径
private int mRadius;
//盘块滚动的速度
private double mSpeed = 0;
private volatile float mStartAngle = 0;
//是否点击停止按钮
private boolean isShouldEnd;
//转盘的中心位置
private int mCenter;
//padding以padding-left为准
private int mPadding;
//背景图
private Bitmap mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg2);
public SurfaceViewTurnplate(Context context) {
//调用自身2个参数的构造方法
this(context, null);
}
public SurfaceViewTurnplate(Context context, AttributeSet attrs) {
super(context, attrs);
//初始化
mHolder = getHolder();
mHolder.addCallback(this);
//设置可获得焦点
setFocusable(true);
setFocusableInTouchMode(true);
//设置常量
setKeepScreenOn(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//初始化画笔
mArcPaint = new Paint();
mArcPaint.setAntiAlias(true);
mArcPaint.setDither(true);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setDither(true);
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTextSize(mTextSize);
//初始化盘块绘制的范围
mRect = new Rect(mPadding, mPadding, mPadding+2*mRadius, mPadding+2*mRadius);
//实例化bitmap
bitmaps = new Bitmap[itemCount];
for (int i=0;i<itemCount;i++) {
bitmaps[i] = BitmapFactory.decodeResource(getResources(), imgs[i]);
}
isRunning = true;
t = new Thread(this);
t.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
}
@Override
public void run() {
while(isRunning) {
long start = System.currentTimeMillis();
draw();
long end = System.currentTimeMillis();
//保持50毫秒绘制一次
if (end-start<50) {
try {
Thread.sleep(50-(end-start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void draw() {
//1.为什么判空?
//当按home或者back键时,surfaceView会销毁,这时候可能进入draw(),所以要判空
//2.为什么要try catch?
//同上情况, 这时候可能正在执行线程,通常线程有很多异常抛出,所以try catch包住比较安全
try {
mCanvas = mHolder.lockCanvas();
if (mCanvas!=null) {
//画布抗锯齿
mCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
//绘制背景
drawBg();
//绘制盘块
drawPlate();
mStartAngle += mSpeed;
//是否点击停止按钮
if(isShouldEnd) {
mSpeed -= 1;
if (mSpeed<=0) {
mSpeed = 0;
isShouldEnd = false;
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (mCanvas!=null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
/**
* 绘制盘块
*/
private void drawPlate() {
float tmpAngle = mStartAngle;
float sweepAngle = 360/itemCount;
for (int i =0;i<itemCount;i++) {
mArcPaint.setColor(colors[i]);
//绘制圆弧,弧度0是水平线的右边,然后顺时针绘制
mCanvas.drawArc(new RectF(mRect), tmpAngle, sweepAngle, true, mArcPaint);
//绘制文本
drawText(tmpAngle, sweepAngle,i);
//绘制图片
drawIcon(tmpAngle,i);
tmpAngle+=sweepAngle;
}
}
private void drawIcon(float tmpAngle, int i) {
//图片长, 宽
int width = mRadius/4;
float angle = (float) ((tmpAngle+360/itemCount/2) * Math.PI/180);// (* Math.PI/180)是转弧度
int x = (int) (mCenter+mRadius/2*Math.cos(angle));
int y = (int) (mCenter+mRadius/2*Math.sin(angle));
mCanvas.drawBitmap(bitmaps[i], null, new Rect(x-width/2, y-width/2, x+width/2, y+width/2), null);
}
/**
* 绘制文本
* @param tmpAngle
* @param sweepAngle
* @param i
*/
private void drawText(float tmpAngle, float sweepAngle, int i) {
Path path = new Path();
path.addArc(new RectF(mRect), tmpAngle, sweepAngle);
float textWidth = mTextPaint.measureText(strs[i]);
int hOffset =(int) ((2*mRadius*Math.PI/itemCount/2)-textWidth/2);
int vOffset = mRadius/6;
mCanvas.drawTextOnPath(strs[i], path, hOffset, vOffset, mTextPaint);
//mCanvas.drawTextOnPath(text, path, hOffset, vOffset, paint)
//hOffset水平偏移量
//vOffset垂直偏移量
}
/**
* 绘制背景
*/
private void drawBg() {
mCanvas.drawColor(Color.WHITE);
mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding/2, mPadding/2, getMeasuredWidth()-mPadding/2, getMeasuredWidth()-mPadding/2), null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(width, width);
mPadding = getPaddingLeft();
mRadius = (width-mPadding*2)/2;
//
mCenter = width/2;
}
public boolean isStart() {
return mSpeed != 0;
}
public void start() {
mSpeed = 20;
isShouldEnd = false;
}
/**
* 作弊,index代表停的位置
*/
public void start(int index) {
//计算每一项的角度
float angle = 360 / itemCount;
//计算每一项中奖范围(当前index)
//0->210~270
//1->150~210
float from = 270 - (index + 1) * angle;
float end = from + angle;
//设置停下来需要旋转的距离
float targetFrom = 3 * 360 + from;
float targetEnd = 3 * 360 + end;
//旋转的距离是一个等差数列的和(mSpeed的和)
//套用公式可以获得速度
float v1 = (float) ((-1 +Math.sqrt(1+8*targetFrom)) /2);
float v2 = (float) ((-1 +Math.sqrt(1+8*targetEnd)) /2);
mSpeed = v1 + Math.random()*(v2-v1) ;
isShouldEnd = false;
}
public void stop() {
isShouldEnd = true;
}
/**
* 作弊
*/
public void stop(String str) {
mStartAngle = 0;
isShouldEnd = true;
}
public boolean isShouldEnd() {
// TODO Auto-generated method stub
return isShouldEnd;
}
}
xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
>
<com.example.luckyturnplate.SurfaceViewTurnplate
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/sv_turnplater"
android:layout_centerInParent="true"
android:padding="30dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bt"
android:src="@drawable/start"
android:layout_centerInParent="true"/>
</RelativeLayout>
MainActivity
...
surfaceViewTurnplate = (SurfaceViewTurnplate) findViewById(R.id.sv_turnplater);
button = (ImageView) findViewById(R.id.bt);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!surfaceViewTurnplate.isStart()) {
// surfaceViewTurnplate.start();
//指定停在某一地方,index代表奖项位置
surfaceViewTurnplate.start(1);
button.setImageResource(R.drawable.stop);
} else {
if (!surfaceViewTurnplate.isShouldEnd()) {
// surfaceViewTurnplate.stop();
//指定停在某一地方, 搭配使用
surfaceViewTurnplate.stop("bad");
button.setImageResource(R.drawable.start);
}
}
}
});
...