Android uses SurfaceView to make simple tablets

Keywords: SurfaceView Java xml

Some people say that SurfaceView is the twin brother of View. In fact, SurfaceView is also inherited from View, but View can only be drawn in the main thread, while SurfaceView can be drawn in the sub-thread. In this article, we will not introduce the basic usage of SurfaceView, but only how to use SurfaceView to make a simple writing board.

PreView(gif loads slowly, please wait patiently)

thinking

  • Create a class that inherits our SurfaceView
  • On TouchEvent to Record the Sliding Track of Fingers
  • Open a sub-thread to plot our finger trajectory

extend

  • Can change the color of the brush
  • The color of the image drawn before changing the color remains unchanged
  • Increase erase function

Create PaintSurface View

public class PaintSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    public static final String TAG = PaintSurfaceView.class.getSimpleName();

    private SurfaceHolder mHolder;
    private Paint mPaint;
    // Whether to draw
    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();
        //Setting Anti-aliasing
        mPaint.setAntiAlias(true);
        //Setting the style of the brush
        mPaint.setStyle(Paint.Style.STROKE);
        //Set the width of the sideline
        mPaint.setStrokeWidth(10);
        //Set the color of the brush
        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: ");
    }
}
As you can see here, we just did a simple initialization.

Recording finger sliding trajectory by 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:
                //Record all crossed points
                mPath.lineTo(x, y);
                return true;
        }
        invalidate();
        return super.onTouchEvent(event);
    }

When the finger falls, record the starting point of the finger sliding, and record the strength of the sliding road when moving. So here we just need to deal with ACTION_DOWN and ACTION_MOVE events.

Implementing Runable Interface

  @Override
    public void run() {
        while (isDrawing) {
            try {
                // Lock canvas
                mCanvas = mHolder.lockCanvas();
                myDraw();
            } catch (Exception e) {
                Log.d(TAG, "run: " + e.getMessage());
            } finally {
                if (mCanvas != null) {
                    // Release canvas
                    mHolder.unlockCanvasAndPost(mCanvas);
                }
            }
        }
    }

     public void myDraw() {
        mCanvas.drawColor(Color.WHITE);
        mCanvas.drawPath(mPath, mPaint);
    }

Open Drawing Thread

 @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isDrawing = true;
        new Thread(this).start();
    }

When surface was created, we started drawing. Friends familiar with Java threads should know that when creating new threads, the construction needs to pass a Runnable object, so we can pass this.

By referring to our PaintSurfaceView in the xml file, you can start writing. To change the color and erase the path, you only need to call mPaint.setColor and mPath.reset().

But here comes the problem. In this case, if the color value changes suddenly, the color of the path drawn before will also change, so how can we not change the previous color? The answer is that we can put paths and colors to be drawn in a model class (MVC's ideas are mundane.) Then define a collection to manage all rendered paths.

Define model classes

/**
 * We need to use Path and color values.
 */
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;
    }
}
    At this point, a List < DrawPath > collection is initialized in PaintSurfaceView to manage our drawing path.

Create a way to change color

 /**
     * Color change
     *
     * @param color
     */
    public void changeColor(int color) {
        mPaint.setColor(color);
        mPath = new Path();
        DrawPath path = new DrawPath(mPath, color);
        mPathList.add(path);
    }

  /**
     * Clear all
     */
    public void clear() {
        int size = mPathList.size();
        DrawPath drawPath = mPathList.get(size - 1);
        mPathList.clear();
        mPath = drawPath.getPath();
        mPath.reset();
        mPathList.add(drawPath);

    }

Each time we change the color, we create a new DrawPath object to save the previous rendering power and color, and put it into the list collection. An erase method is created to erase all drawn paths and retain the last selected color before erasing.

Start drawing

 public void myDraw() {
        mCanvas.drawColor(Color.WHITE);
        for (DrawPath drawPath : mPathList) {
            mPaint.setColor(drawPath.getColor());
            mCanvas.drawPath(drawPath.getPath(), mPaint);
        }
    }
Because all the path information is stored in mPathList, we only need to iterate mPathList to achieve the desired effect.

Source download

Posted by robin105 on Fri, 29 Mar 2019 18:36:30 -0700