Android之使用SurfaceView制作简易写字板

有人说SurfaceView是View的孪生兄弟,其实SurfaceView也是继承自View的,不过View的绘制只能在主线程,而SurfaceView却可以在子线程中进行绘制。本文我们不介绍SurfaceView的基础用法,只介绍如何使用SurfaceView来制作一个简易写字板。

PreView(gif加载较慢,请耐心等待)

思路

  • 创建一个类继承我们的SurfaceView
  • 通过onTouchEvent来记录手指的滑动轨迹
  • 开启一个子线程来绘制我们手指轨迹

扩展

  • 可以改变画笔的颜色
  • 改变颜色之后之前绘制的图像颜色不变
  • 增加擦除功能

创建PaintSurfaceView


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: ");
    }
}
可以看到这里我们只是做了简单的初始化工作。

通过onTouchEvent记录手指滑动轨迹


    @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事件即可。

实现Runable接口

  @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.setColormPath.reset()即可。

但是,问题来了。这种情况下突然改变颜色值,之前绘制的路径颜色也会发生改变,那么我们如何才能不改变之前的颜色呢?答案是我们可以将要绘制的path和color放在一个模型类里面(MVC的思想有木有。。。),然后定义一个集合来管理所有绘制的路径。

定义模型类

/**
 * 我们需要用到的是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<DrawPath>集合,通过它来管理我们的绘制路径

创建一个改变颜色的方法

 /**
     * 改变颜色
     *
     * @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,即可实现我们想要的效果。

源码下载

猜你喜欢

转载自blog.csdn.net/Leavessilent/article/details/55549154
今日推荐