SurfaceView和View最本质的区别?
surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞,将无法响应按键,触屏等消息。当使用surfaceView,由于是在新的线程中更新画面,所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,按照预先设计,画面需要被改变,那么你就需要在surfaceView的thread中改变画面,这就需要一个event queue来保存touch event,并及时处理这些touch event,这会稍稍复杂一点,因为涉及到线程同步。
何时该使用SurfaceView?
当需要快速地更新View的UI,或者当渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。例如,使用3D图形,创建游戏,或者实时预览摄像头。
实际案例:
- 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
- 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。
使用方法:
步骤
- 继承SurfaceView并实现SurfaceHolder.Callback接口
- SurfaceView.getHolder()获得SurfaceHolder对象
- SurfaceHolder.addCallback(callback)添加回调函数
- SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布
- Canvas绘画
- SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示
ClockSurfaceView
public class ClockSurfaceView extends SurfaceView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;//控件的宽度
private int height = 200;//控件的高度
private int padding = 5;
private Calendar mCalendar;
private int mHour;//小时
private int mMinuate;//分钟
private int mSecond;//秒
private float mDegrees;//因为圆是360度 我们有12个刻度 所以就是360/12
private int mHourLineLen;//时指针 线
private int mMinuateLine;//分钟 线
private int mSecondLine;//表钟线
SurfaceHolder holder;
private boolean loop = true;
public ClockSurfaceView(Context context) {
this(context, null);
}
public ClockSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ClockSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
//开启硬件加速
// this.setLayerType(View.LAYER_TYPE_HARDWARE, null);
holder = getHolder();
setBackTransfluent(); //设置背景色为透明色
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(new ClockThread()).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
loop = false;
}
});
}
class ClockThread implements Runnable {//内部定义异步线程来完成绘画操作...
@Override
public void run() {
// TODO Auto-generated method stub
while (loop) {
Canvas canvas = holder.lockCanvas(null);//锁定画布...
clear(canvas);
drawCircle(canvas);
drawScale(canvas);
canvasCenterCircle(canvas);
drawPointer(canvas);
drawStr(canvas);
holder.unlockCanvasAndPost(canvas);//解除锁定
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth, height);
mHourLineLen = (int) (widhth / 2 * 0.6);
mMinuateLine = (int) (widhth / 2 * 0.7);
mSecondLine = (int) (widhth / 2 * 0.8);
}
private void setBackTransfluent() {
this.setZOrderOnTop(true);
//this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
holder.setFormat(PixelFormat.TRANSLUCENT);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#FF4081"));
mPaint.setAntiAlias(true);
}
private void clear(Canvas canvas) {
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);//抗锯齿
// canvas 清除画布内容。
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(mPaint);
}
/**
* 绘制文字
*
* @param canvas
*/
private void drawStr(Canvas canvas) {
mPaint.setTextSize(24);
StringBuffer sb = new StringBuffer();
if (mHour < 10) {
sb.append("0").append(String.valueOf(mHour)).append(":");
} else {
sb.append(String.valueOf(mHour)).append(":");
}
if (mMinuate < 10) {
sb.append("0").append(String.valueOf(mMinuate)).append(":");
} else {
sb.append(String.valueOf(mMinuate)).append(":");
}
if (mSecond < 10) {
sb.append("0").append(String.valueOf(mSecond));
} else {
sb.append(String.valueOf(mSecond));
}
String str = sb.toString();
int strW = (int) mPaint.measureText(str);
canvas.drawText(str, widhth / 2 - strW / 2, widhth / 2 + 30, mPaint);
}
/**
* 在 圆中心绘制一个点
*
* @param canvas
*/
private void canvasCenterCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(widhth / 2, height / 2, 5, mPaint);
}
/**
* 绘制圆
*
* @param canvas
*/
private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth / 2, height / 2, widhth / 2 - padding, mPaint);
}
/**
* 绘制刻度
*
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i < 12; i++) {
if (i % 3 == 0) {// 12 3 6 9对应的线长点
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);
} else {
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);
}
canvas.rotate(30, widhth / 2, widhth / 2);
}
}
/**
* 绘制时 分 表 指针
*
* @param canvas
*/
private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance();
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
//小时的旋转度
mDegrees = mHour * 30 + mMinuate / 2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
canvas.restore();
//分钟
mPaint.setColor(Color.parseColor("#666666"));
mPaint.setStrokeWidth(5);
mDegrees = mMinuate * 6 + mSecond / 10;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mMinuateLine, mPaint);
canvas.restore();
//绘制表针
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.parseColor("#666666"));
mDegrees = mSecond * 6;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mSecondLine, mPaint);
canvas.restore();
}
}
在布局文件xml中引用即可。
效果:
网友评论