有人说SurfaceView是View的孪生兄弟,其实SurfaceView也是继承自View的,不过View的绘制只能在主线程,而SurfaceView却可以在子线程中进行绘制。本文我们不介绍SurfaceView的基础用法,只介绍如何使用SurfaceView来制作一个简易写字板。
public class PaintSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
public static final String TAG = PaintSurfaceView.class.getSimpleName();
private SurfaceHolder mHolder;
private Paint mPaint;
// 是否绘制
private boolean isDrawing;
private Canvas mCanvas;
private Path mPath;
public PaintSurfaceView(Context context) {
this(context, null);
}
public PaintSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PaintSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mHolder = getHolder();
mHolder.addCallback(this);
mPaint = new Paint();
//设置抗锯齿
mPaint.setAntiAlias(true);
//设置画笔的风格
mPaint.setStyle(Paint.Style.STROKE);
//设置边线的宽度
mPaint.setStrokeWidth(10);
//设置画笔的颜色
mPaint.setColor(Color.BLACK);
mPath = new Path();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isDrawing = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged: ");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isDrawing = false;
Log.d(TAG, "surfaceDestroyed: ");
}
}
可以看到这里我们只是做了简单的初始化工作。
@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);
return true;
case MotionEvent.ACTION_MOVE:
//记录所有划过的点
mPath.lineTo(x, y);
return true;
}
invalidate();
return super.onTouchEvent(event);
}
在手指落下时,记录手指滑动的起点,move的时候记录滑动路劲。所以这里我们只需要处理ACTION_DOWN和ACTION_MOVE事件即可。
@Override
public void run() {
while (isDrawing) {
try {
// 锁定画布
mCanvas = mHolder.lockCanvas();
myDraw();
} catch (Exception e) {
Log.d(TAG, "run: " + e.getMessage());
} finally {
if (mCanvas != null) {
// 释放画布
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
public void myDraw() {
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath, mPaint);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isDrawing = true;
new Thread(this).start();
}
在surface创建的时候我们开始绘制,熟悉Java线程的朋友应该知道创建新线程的时候构造需要传一个Runnable对象,所以我们传this即可。
这时候在xml文件引用我们的PaintSurfaceView,运行就可以开始写字了。这时要改变颜色和擦除路径只需要调用mPaint.setColor和mPath.reset()即可。
/**
* 我们需要用到的是Path和color值
*/
public class DrawPath {
private Path mPath;
private int mColor;
public DrawPath() {
}
public DrawPath(Path path, int color) {
mPath = path;
mColor = color;
}
public Path getPath() {
return mPath;
}
public void setPath(Path path) {
mPath = path;
}
public int getColor() {
return mColor;
}
public void setColor(int color) {
mColor = color;
}
}
此时在PaintSurfaceView中初始化一个List集合,通过它来管理我们的绘制路径
/**
* 改变颜色
*
* @param color
*/
public void changeColor(int color) {
mPaint.setColor(color);
mPath = new Path();
DrawPath path = new DrawPath(mPath, color);
mPathList.add(path);
}
/**
* 清除所有
*/
public void clear() {
int size = mPathList.size();
DrawPath drawPath = mPathList.get(size - 1);
mPathList.clear();
mPath = drawPath.getPath();
mPath.reset();
mPathList.add(drawPath);
}
每次改变颜色时,我们创建一个新的DrawPath对象来保存之前的绘制路劲和颜色,并将其放入list集合。再创建了一个擦除方法,用于擦除所有绘制的路径,并保留擦除之前最后选中的颜色。
public void myDraw() {
mCanvas.drawColor(Color.WHITE);
for (DrawPath drawPath : mPathList) {
mPaint.setColor(drawPath.getColor());
mCanvas.drawPath(drawPath.getPath(), mPaint);
}
}
因为所有的路径信息都保存在mPathList种,所以只需要迭代mPathList,即可实现我们想要的效果。