概述
android系统提供了View来进行绘图处理,View可以满足大部分的绘图要求,但是在某一些时候就显得有些不足了。因为View是通过刷新来重绘视图,android系统是通过发送VSYNC信号来进行屏幕的绘制,刷新的时间间隔为16ms,如果在16ms内View没有完成重绘刷新的操作,那么用户在视觉上就会有卡顿的现象。打印日志会发现有下面的警告:"skipped 47 frames! The application may be doing too much work on its main thread"
Canvas和Paint
paint的基本用法:
Paint paint = new Paint(); paint.setAntiAlias();//设置画笔的锯齿效果 paint.setColor();//设置画笔的颜色 paint.setARGB();//设置画笔的A、R、G、B值 paint.setAlpha();//设置画笔的alpha值 paint.setTextSize();//设置字体的大小 paint.setStyle();//设置画笔的风格(空心、实心) paint.setStrokeWidth();//设置空心边框的宽度
canvas的基本用法:
//绘制点 canvas.drawPoint(x, y, paint); //绘制单条直线 canvas.drawLine(startX , startY , endX , endY , piant); //绘制多条直线 float[] opts = { startX1 , startY1 , endX1 , endY1 , ... startXn , startYn , endXn , endYn }; canvas.drawLines(); //绘制矩形 canvas.drawRect(left , top , right , bottom , paint); //绘制圆角矩形 canvas.drawRoundRect(left , top , right , bottom , radiusX , radiusY , paint); //绘制圆 canvas.drawCircle(x , y , radius , paint); //绘制扇形 canvas.drawArc(left , top , right , bottom , startAndle , sweepAngle , useCenter ,paint); //绘制椭圆 canvas.drawOval(left , top , right , bottom , paint); //绘制文本 canvas.drawText(text , x , y , paint); //绘制路径 Path path = new Path(); path.moveTo(x , y ); path.lineTo(x , y); canvas.drawPath(path , paint);
view和surfaceview的区别
1. View主要适用于主动更新的情况下,而SurfaceView主要适用于被动更新的情况下,例如频繁的刷新;
2. View在主线程中对画面进行刷新,而SurfaceView通常会通过一个子线程来进行页面刷新;
3. View在绘图的时候没有双缓存机制,而SurfaceView在底层已经实现了双缓存机制;
总之,如果自定义的View需要频繁的刷新,或者刷新的处理比较大,就建议使用SurfaceView。
SurfaceView的使用
这里以绘画板为例,记录手指画过的轨迹:
首先创建一个SurfaceView,并实现两个接口 ----- SurfaceHolder.Callback和Runnable
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{
在构造函数里面初始化
private void init(){ mHolder = getHolder();//初始化holder mHolder.addCallback(this);//注册SurfaceHolder的回调方法 setFocusable(true); setFocusableInTouchMode(true); this.setKeepScreenOn(true); }
实现SurfaceHolder的三个方法
/** * surfaceview的创建 * @param holder */ @Override public void surfaceCreated(SurfaceHolder holder) { mIsDrawing = true; new Thread(this).start();//开启线程,在子线程中绘制 } /** * surfaceview的改变 * @param holder */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * surfaceview的销毁 * @param holder */ @Override public void surfaceDestroyed(SurfaceHolder holder) { mIsDrawing = false; }
开始绘制界面
/** * 在子线程中进行绘制 */ private Path mPath = new Path(); private Paint mPaint = new Paint(); @Override public void run() { mPaint.setAntiAlias(false);//设置画笔的锯齿效果 mPaint.setAlpha(1);//设置画笔的透明度 mPaint.setColor(Color.RED);//设置画笔的颜色 mPaint.setTextSize(30); mPaint.setStyle(Paint.Style.STROKE);//设置画笔的风格(空心、实心) mPaint.setStrokeWidth(2);//设置空心边框的宽度 long start = System.currentTimeMillis(); while (mIsDrawing){ draw(); } long end = System.currentTimeMillis(); if((end - start) < 100){//这里间隔设置为100ms SystemClock.sleep(100 - (end - start)); } } /** * 重新绘制:这个时间间隔最好能控制在50ms~100ms之间 */ private void draw(){ try { mCanvas = mHolder.lockCanvas();//得到是上次的canvas mCanvas.drawColor(Color.WHITE);//进行清屏操作(如果没有清屏就会保持上次的操作) mCanvas.drawPath(mPath, mPaint); }catch (Exception e){ e.printStackTrace(); }finally { if(mCanvas != null){ mHolder.unlockCanvasAndPost(mCanvas);//对画布的内容进行提交 } } }
根据手势移动变化来记录轨迹
/** * 记录触摸轨迹 * @param event * @return */ @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; }
View的使用
这里以自定义的进度条为例说明:
先定义一个自定义的view:
public class MyView extends View {
开始绘制底框和进度
@Override protected void onDraw(Canvas canvas) { //创建一个画笔 Paint mPaint = new Paint(); //设置底框画笔的属性 mPaint.setAntiAlias(false);//设置画笔的锯齿效果 mPaint.setAlpha(1);//设置画笔的透明度 mPaint.setColor(Color.RED);//设置画笔的颜色 mPaint.setStyle(Paint.Style.FILL);//设置画笔的风格(空心、实心) //绘制底层框 canvas.drawRect(0, 100, 720, 150, mPaint); //设置进度画笔的属性 mPaint.setAntiAlias(false);//设置画笔的锯齿效果 mPaint.setAlpha(1);//设置画笔的透明度 mPaint.setColor(Color.BLACK);//设置画笔的颜色 mPaint.setStyle(Paint.Style.FILL);//设置画笔的风格(空心、实心) //绘制当前的进度 canvas.drawRect(0, 100, progress, 150, mPaint); }
刷新进度条
/** * 开启绘制更新 */ private int offset = 0; public void startDraw(){ mHnadler.postDelayed(new Runnable() { @Override public void run() { offset += 5; Message msg = Message.obtain(); msg.what = 0000; msg.obj = offset; mHnadler.sendMessage(msg); mHnadler.postDelayed(this , 500); } }, 500); } /** * handler收到message之后刷新进度条 */ public void setProgress(int progress){ this.progress = progress; invalidate();//主线程刷新 // postInvalidate();//子线程刷新 }