如何在Android画布中实现撤消,重做和擦除

时间:2017-04-26 12:33:20

标签: android android-canvas

我想创建类似绘图的项目,这是支持绘图,撤消,重做和橡皮擦所需的。橡皮擦必须仅删除图纸视图而不删除背景。下面的代码实现了撤消和重做功能。我想添加橡皮擦选项,但它没有完成。如何使用下面的代码实现橡皮擦选项?

        import android.content.Context;
        import android.graphics.Bitmap;
        import android.graphics.Canvas;
        import android.graphics.Color;
        import android.graphics.Paint;
        import android.graphics.Path;
        import android.util.AttributeSet;
        import android.view.MotionEvent;


        import java.util.ArrayList;


        public class CanvasView extends View {


        private Paint mPenPainter;
        public int width;

        public int height;


        private Bitmap mBitmap;
        private Canvas mCanvas;

        private Path mPath;

        Context context;

        private Paint mPaint;

        private float mX, mY;

        private static final float TOLERANCE = 5;
        private ArrayList<Path> paths = new ArrayList<Path>();
        private ArrayList<Path> undonePaths = new ArrayList<Path>();


        private int paintColor = 0xFF000000;

        public CanvasView(Context c, AttributeSet attrs) {

        super(c, attrs);

        context = c;


        // we set a new Path

        mPath = new Path();


        // and we set a new Paint with the desired attributes

        mPaint = new Paint();

        mPaint.setAntiAlias(true);

        mPaint.setColor(paintColor);

        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setStrokeJoin(Paint.Join.ROUND);

        mPaint.setStrokeWidth(4f);


        //float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
        mPenPainter = new Paint();
        mPenPainter.setColor(Color.BLUE);

        }


        // override onSizeChanged
        @Override

        protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        super.onSizeChanged(w, h, oldw, oldh);


        // your Canvas will draw onto the defined Bitmap

        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        mCanvas = new Canvas(mBitmap);

        }


        // override onDraw

        @Override

        protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        // draw the mPath with the mPaint on the canvas when onDraw
        for (Path p : paths) {
        canvas.drawPath(p, mPaint);
        }
        canvas.drawPath(mPath, mPaint);
        // paths.add(mPath);

        }

        private void startTouch(float x, float y) {

        undonePaths.clear();
        mPath.reset();
        mPath.moveTo(x, y);

        mX = x;

        mY = y;
        }

        public void onClickUndo() {
        if (paths.size() > 0) {
        undonePaths.add(paths.remove(paths.size() - 1));
        invalidate();
        } else {
        //Util.Imageview_undo_redum_Status=false;
        }
        //toast the user
        }

        public void onClickRedo() {
        if (undonePaths.size() > 0) {
        paths.add(undonePaths.remove(undonePaths.size() - 1));
        invalidate();
        } else {
        // Util.Imageview_undo_redum_Status=false;
        }
        //toast the user
        }

        // when ACTION_MOVE move touch according to the x,y values

        private void moveTouch(float x, float y) {

        float dx = Math.abs(x - mX);

        float dy = Math.abs(y - mY);

        if (dx >= TOLERANCE || dy >= TOLERANCE) {

        mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);

        mX = x;

        mY = y;

        }
        }


        private void upTouch() {


        mPath.lineTo(mX, mY);

        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, mPaint);
        // kill this so we don't double draw
        paths.add(mPath);
        mPath = new Path();
        }


        //override the onTouchEvent

        @Override

        public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();

        float y = event.getY();
        float mCurX;
        float mCurY;

        switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:


        mX = event.getX();

        mY = event.getY();


        startTouch(x, y);
        invalidate();

        break;

        case MotionEvent.ACTION_MOVE:


        moveTouch(x, y);

        invalidate();

        break;

        case MotionEvent.ACTION_UP:

        upTouch();
        invalidate();

        break;
        }

        return true;

        }

3 个答案:

答案 0 :(得分:0)

我认为你应该使用这种模式: https://sourcemaking.com/design_patterns/command

我将它用于我的应用程序以使用undo / redo

答案 1 :(得分:0)

要删除,您需要找出当前选择和绘制路径之间的交叉。参考下面的代码

   package opensourcecode.com.paginationwebview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

/**
 * Created by damodhar.meshram on 4/26/2017.
 */

public class CanvasView extends View {
    private Paint mPenPainter;
    public int width;

    public int height;


    private Bitmap mBitmap;
    private Canvas mCanvas;

    private Path mPath;

    Context context;

    private Paint mPaint;

    private float mX, mY;

    private static final float TOLERANCE = 5;
    private ArrayList<Path> paths = new ArrayList<Path>();
    private ArrayList<Path> undonePaths = new ArrayList<Path>();

    private boolean isErasemode = false;

    private int paintColor = 0xFF000000;

    public CanvasView(Context c, AttributeSet attrs) {

        super(c, attrs);

        context = c;


        // we set a new Path

        mPath = new Path();


        // and we set a new Paint with the desired attributes

        mPaint = new Paint();

        mPaint.setAntiAlias(true);

        mPaint.setColor(paintColor);

        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setStrokeJoin(Paint.Join.ROUND);

        mPaint.setStrokeWidth(4f);


        //float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
        mPenPainter = new Paint();
        mPenPainter.setColor(Color.BLUE);

    }

    public CanvasView(Context c) {

        super(c);

        context = c;


        // we set a new Path

        mPath = new Path();


        // and we set a new Paint with the desired attributes

        mPaint = new Paint();

        mPaint.setAntiAlias(true);

        mPaint.setColor(paintColor);

        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setStrokeJoin(Paint.Join.ROUND);

        mPaint.setStrokeWidth(4f);


        //float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
        mPenPainter = new Paint();
        mPenPainter.setColor(Color.BLUE);

    }


    // override onSizeChanged
    @Override

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        super.onSizeChanged(w, h, oldw, oldh);


        // your Canvas will draw onto the defined Bitmap

        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        mCanvas = new Canvas(mBitmap);

    }


    // override onDraw

    @Override

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        // draw the mPath with the mPaint on the canvas when onDraw
        for (Path p : paths) {
            canvas.drawPath(p, mPaint);
        }
        canvas.drawPath(mPath, mPaint);
        // paths.add(mPath);

    }

    private void startTouch(float x, float y) {

        undonePaths.clear();
        mPath.reset();
        mPath.moveTo(x, y);

        mX = x;

        mY = y;
    }

    public void onClickUndo() {
        if (paths.size() > 0) {
            undonePaths.add(paths.remove(paths.size() - 1));
            invalidate();
        } else {
            //Util.Imageview_undo_redum_Status=false;
        }
        //toast the user
    }

    public void onEraser(){
        if(!isErasemode){
            isErasemode = true;
        }else{
            isErasemode = false;
        }
    }

    private void remove(int index){
        paths.remove(index);
        invalidate();
    }

    public void onClickRedo() {
        if (undonePaths.size() > 0) {
            paths.add(undonePaths.remove(undonePaths.size() - 1));
            invalidate();
        } else {
            // Util.Imageview_undo_redum_Status=false;
        }
        //toast the user
    }

    // when ACTION_MOVE move touch according to the x,y values

    private void moveTouch(float x, float y) {

        float dx = Math.abs(x - mX);

        float dy = Math.abs(y - mY);

        if (dx >= TOLERANCE || dy >= TOLERANCE) {

            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);

            mX = x;

            mY = y;

        }
    }


    private void upTouch() {


        mPath.lineTo(mX, mY);

        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, mPaint);
        // kill this so we don't double draw
        paths.add(mPath);
        mPath = new Path();
    }


    //override the onTouchEvent

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();

        float y = event.getY();
        float mCurX;
        float mCurY;

        if(isErasemode){

            for(int i = 0;i<paths.size();i++){
                RectF r = new RectF();
                Point pComp = new Point((int) (event.getX()), (int) (event.getY() ));

                Path mPath = paths.get(i);
                mPath.computeBounds(r, true);
                if (r.contains(pComp.x, pComp.y)) {
                    Log.i("need to remove","need to remove");
                    remove(i);
                    break;
                }
            }
            return false;
        }else {

            switch (event.getAction()) {

                case MotionEvent.ACTION_DOWN:


                    mX = event.getX();

                    mY = event.getY();


                    startTouch(x, y);
                    invalidate();

                    break;

                case MotionEvent.ACTION_MOVE:


                    moveTouch(x, y);

                    invalidate();

                    break;

                case MotionEvent.ACTION_UP:

                    upTouch();
                    invalidate();

                    break;
            }

            return true;
        }

    }
}

答案 2 :(得分:0)

答案似乎有点晚,但经过两天的努力,我终于提出了解决方案。

public class DrawView extends View {

    public int width;
    public  int height;
    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Paint   mBitmapPaint;
    Context context;
    private Paint circlePaint;
    private Path circlePath;
    Boolean eraserOn = false;
    Boolean newAdded = false;
    Boolean allClear = false;
    private Path drawPath;

    private ArrayList<Bitmap> bitmap = new ArrayList<>();
    private ArrayList<Bitmap> undoBitmap = new ArrayList<>();

    public DrawView(Context c) {
        super(c);
        context=c;
        drawPath = new Path();
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);
        circlePaint = new Paint();
        circlePath = new Path();
        circlePaint.setAntiAlias(true);
        circlePaint.setColor(Color.BLUE);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeJoin(Paint.Join.MITER);
        circlePaint.setStrokeWidth(4f);
        drawPaint = new Paint();
        drawPaint.setAntiAlias(true);
        drawPaint.setDither(true);
        drawPaint.setColor(Color.BLACK);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        drawPaint.setStrokeWidth(20);
     //   drawPaint.setAlpha(80);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        if(mBitmap==null) {
            mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBitmap);

        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // if(!eraserOn)
        canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);

        canvas.drawPath(drawPath, drawPaint);
        canvas.drawPath( circlePath,  circlePaint);
    }

    public void onClickEraser(boolean isEraserOn)
    {
        if (isEraserOn) {
            eraserOn = true;
            drawPaint.setColor(getResources().getColor(android.R.color.transparent));
            drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        }
        else {
            eraserOn = false;
            drawPaint.setColor(mPaint.getColor());
            drawPaint.setXfermode(null);
        }
    }



    public void onClickUndo () {
        if(newAdded) {
            bitmap.add(mBitmap.copy(mBitmap.getConfig(), mBitmap.isMutable()));
            newAdded=false;
        }
        if (bitmap.size()>1)
        {
            undoBitmap.add(bitmap.remove(bitmap.size()-1));
            mBitmap= bitmap.get(bitmap.size()-1).copy(mBitmap.getConfig(),mBitmap.isMutable());
            mCanvas = new Canvas(mBitmap);
            invalidate();
            if(bitmap.size()==1)
                allClear=true;
        }
        else
        {

        }
        //toast the user
    }

    public void onClickRedo (){
        if (undoBitmap.size()>0)
        {
            bitmap.add(undoBitmap.remove(undoBitmap.size()-1));
            mBitmap= bitmap.get(bitmap.size()-1).copy(mBitmap.getConfig(),mBitmap.isMutable());
            mCanvas = new Canvas(mBitmap);
            invalidate();
        }
        else
        {

        }
        //toast the user
    }

    @Override
    public boolean performClick() {
        return super.performClick();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(penSelected || eraserSelected) {
            float touchX = event.getX();
            float touchY = event.getY();

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    newAdded = true;
                    if(!allClear)
                    bitmap.add(mBitmap.copy(mBitmap.getConfig(),mBitmap.isMutable()));
                    else allClear=false;

                    drawPath.moveTo(touchX, touchY);
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (eraserOn) {
                        drawPath.lineTo(touchX, touchY);
                        mCanvas.drawPath(drawPath, drawPaint);
                        drawPath.reset();
                        drawPath.moveTo(touchX, touchY);
                    } else {
                        drawPath.lineTo(touchX, touchY);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    mCanvas.drawPath(drawPath, drawPaint);
                     drawPath.reset();
                    break;
                case MotionEvent.ACTION_CANCEL:
                    return false;

                default:
                    return false;
            }

            invalidate();
            return true;
        }
        return false;
    }
}