问题汇总:
- SurfaceView是什么
- SurfaceView和View的区别
- SurfaceView怎么用
SurfaceView是什么
View绘制时,是通过VSYNC信号进行刷新,也就是16ms刷新一次。如果需要大量的操作导致16ms内无法绘制完成就会出现卡顿。在这种情况下SurfaceView
就诞生了。
SurfaceView和View的区别
SufaceView适合处理逻辑多,刷新频繁的情况。View适用于一般大部分需求。
SufaceView | View |
---|---|
被动刷新(适用于频繁刷新) | 主动刷新 |
底层实现双缓冲机制 | 无双缓冲 |
子线程中刷新画面 | 主线程中刷新 |
SurfaceView模板
SurfaceView有一套模板,可以直接套用,具体意义直接看注释,是很详细的。
/**-----------------------------------------
* surfaceview模板:
* 1.需要继承SurfaceView
* 2.实现SurfaceHolder.Callback, Runnable
* -----------------------------------------*/
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable{
//SurfaceHolder
protected SurfaceHolder mHolder;
//Canvas:用于绘图
protected Canvas mCanvas;
//子线程标志位
protected boolean mIsDrawing;
public SurfaceViewTemplate(Context context){
super(context);
initView(); //初始化
}
public SurfaceViewTemplate(Context context, AttributeSet attrs){
super(context, attrs);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initView();
}
/*
* 初始化
* */
private void initView(){
mHolder = getHolder();
mHolder.addCallback(this); //注册SurfaceHolder的回调方法
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
//SurfaceView创建
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true; //开启子线程
new Thread(this).start(); //在子线程中通过while不断绘制
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false; //停止绘制(退出子线程循环)
}
/*-----------------------------------------------
* 子线程中不断绘制(while):
* 因为画面不需要过多刷新,浪费性能。所以适当进行sleep以此节省性能。
*----------------------------------------------------*/
@Override
public void run() {
long startTime;
long endTime;
while(mIsDrawing){
startTime = System.currentTimeMillis();
draw();//绘制
endTime = System.currentTimeMillis();
//测量一次draw的时间,并以此sleep一定时长
//取值范围一般是50~100ms
if(endTime - startTime < 50){
try {
Thread.sleep((50-(endTime-startTime))); //睡满直至100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*------------------------------------
* 绘制:
* 1.lockCanvas()获得当前canvas对象, 每次获得的都是上次的Canvas对象, 绘制操作会保留。
* 需要擦除,再回之前通过drawColor清屏
* 2.unlockCanvasAndPost(mCanvas); 对画布内容进行提交
* 3.将unlockCanvasAndPost放入finally确保每次都会提交
*-----------------------------------*/
private void draw(){
try {
mCanvas = mHolder.lockCanvas(); //获得当前canvas对象
//draw something
}catch (Exception e){
}finally {
if(mCanvas != null){
mHolder.unlockCanvasAndPost(mCanvas); //对画布内容进行提交
}
}
}
}
实例:写字板
写字板:用手在屏幕上写字,并且显示出来,就像电子黑板一样。
思路是用path在onTouchEvent中记录用户点击路径,并用paint按照path在canvas上进行绘制。这里直接继承SurfaceView模板,并且重新实现run()和draw()。
public class DrawBoardView extends SurfaceViewTemplate {
Path mPath = new Path();
public DrawBoardView(Context context) {
super(context);
}
public DrawBoardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawBoardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
public void run() {
long startTime;
long endTime;
while(mIsDrawing){
startTime = System.currentTimeMillis();
draw();
endTime = System.currentTimeMillis();
//取值范围一般是50~100ms
if(endTime - startTime < 50){
try {
Thread.sleep((50-(endTime-startTime))); //睡满直至100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas(); //获得当前canvas对象
mCanvas.drawColor(Color.BLACK);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE); //绘制一定宽度的实线
paint.setStrokeWidth(8);
mCanvas.drawPath(mPath, paint);
//draw something
} catch (Exception e) {
} finally {
if (mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas); //对画布内容进行提交
}
}
}
}