在 android 中撤消在自定义图像视图上的绘制

Undo drawing on the custom image view in android

我正在开发绘图应用程序,到目前为止我可以在图像上画线,问题是我发现它无法在自定义图像视图中集成撤消操作。

我采用了“一条条存储绘制路径,并在onDraw语句上绘制”的逻辑,但代码中似乎存在一些缺失/缺陷。

如果您想了解有关代码的更多详细信息,欢迎提出任何问题。屏幕截图是我的应用程序的样子(在图像上绘制)

感谢您的帮助。

主Activity中:

// set image

    bitmap = downScale(view.getTag().toString(),1280,1024);
    altered_bitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
    draw_view.setNewImage(altered_bitmap,bitmap);

undo.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View arg0) {
                if (pencil.getVisibility() == View.VISIBLE || pen.getVisibility() == View.VISIBLE) {
                    draw_view.onClickUndo();
                }
            }
        });

并且在自定义图像视图中:

private ArrayList<Path> paths = new ArrayList<Path>();
    private Path mPath;

  public ScaleImageView(Context context) {
            super(context);
            sharedConstructing(context);
        }

    public void sharedConstructing(Context context) {
            super.setClickable(true);
            this.context = context;

            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
            matrix = new Matrix();
            m = new float[9];
            setImageMatrix(matrix);
            setScaleType(ScaleType.MATRIX);

            paint = new Paint();

            paint.setAntiAlias(true);
            paint.setStrokeWidth(width);
            paint.setColor(color);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setStrokeCap(Paint.Cap.ROUND);
            paint.setAlpha(alpha);


            drawListener = new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (getDrawable() != null) {
                        int action = event.getAction();
                        switch (action) {
                            case MotionEvent.ACTION_DOWN:
                                downx = getPointerCoords(event)[0];// event.getX();
                                downy = getPointerCoords(event)[1];// event.getY();
                                break;
                            case MotionEvent.ACTION_MOVE:
                                upx = getPointerCoords(event)[0];// event.getX();
                                upy = getPointerCoords(event)[1];// event.getY();
                                canvas.drawLine(downx, downy, upx, upy, paint);
                                mPath = new Path();
                                paths.add(mPath);
                                invalidate();
                                downx = upx;
                                downy = upy;
                                break;
                            case MotionEvent.ACTION_UP:
                                upx = getPointerCoords(event)[0];// event.getX();
                                upy = getPointerCoords(event)[1];// event.getY();
                                canvas.drawLine(downx, downy, upx, upy, paint);
                                mPath = new Path();
                                paths.add(mPath);
                                invalidate();
                                break;
                            case MotionEvent.ACTION_CANCEL:
                                break;
                            default:
                                break;
                        }
                    }
                    return true;
                }
            };

            setOnTouchListener(drawListener);
        }

        //draw view start
        public void setNewImage(Bitmap alteredBitmap, Bitmap bmp) {
            canvas = new Canvas(alteredBitmap);
            matrix_draw = new Matrix();
            canvas.drawBitmap(bmp, matrix_draw, paint);
            setImageBitmap(alteredBitmap);  
            mPath = new Path();
            paths.add(mPath);
        }

        public void setBrushColor(int color) {
            this.color = color;
            paint.setColor(color);
            paint.setAlpha(alpha);
        }

        public void setAlpha(int alpha) {
            this.alpha = alpha;
            paint.setAlpha(alpha);
        }

        public void setWidth(float width) {
            this.width = width;
            paint.setStrokeWidth(width);
        }

        final float[] getPointerCoords(MotionEvent e) {
            final int index = e.getActionIndex();
            final float[] coords = new float[] { e.getX(index), e.getY(index) };
            Matrix matrix = new Matrix();
            getImageMatrix().invert(matrix);
            matrix.postTranslate(getScrollX(), getScrollY());
            matrix.mapPoints(coords);
            return coords;
        }

        public void setIsScale() {
            isScale = !isScale;
            setOnTouchListener(isScale ? zoomListener : drawListener);
        }

    @Override
    protected void onDraw(Canvas canvas) {  
        for (Path p : paths){
            canvas.drawPath(p, paint_line);
        }
    }

    public void onClickUndo () {
        if (paths.size()>0){
            paths.remove(paths.size()-1);
            invalidate();
        }
    }
    //draw view end

更新:测试结果

经过一段时间的测试,发现应用程序运行但所选图像不是像这样绘制在自定义图像视图中:

如果你有空闲时间,我已经上传了项目(<1 mb), 这是一个小型绘图工具,首先将一个包含一些图像的文件夹复制到您设备

中的文件夹"HistoryTool"

路径,例如:

sd card root/ HistoryTool/ folder1 / a.jpg 

,然后你就可以在上面画画了,仅此而已,但是现在撤消操作不起作用,需要修复。

https://drive.google.com/file/d/0B9mELZtUJp0LLVh0b1Q0a3VTcG8/view?usp=sharing

非常感谢

实际上我想我已经找到问题的根源:每次捕获 MotionEvent.ACTION_MOVE 时都会添加一条新路径,但是触摸传感器非常嘈杂并且您会收到很多这样的事件。您可能会考虑编辑您在检测到 MotionEvent.ACTION_DOWN 时添加的现有路径。我会考虑做这样的事情

Path currentPath = null;
drawListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
    if (getDrawable() != null) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:                               
                downx = getPointerCoords(event)[0];// event.getX();
                downy = getPointerCoords(event)[1];// event.getY();
                currentPath = new Path();
                currentPath.moveTo(downx, downy);
                paths.add(currentPath);
                break;
            case MotionEvent.ACTION_MOVE:
                upx = getPointerCoords(event)[0];// event.getX();
                upy = getPointerCoords(event)[1];// event.getY();
                currentPath.lineTo(upx, upy);                               
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                upx = getPointerCoords(event)[0];// event.getX();
                upy = getPointerCoords(event)[1];// event.getY();
                currentPath.lineTo(upx, upy);
                invalidate();
                currentPath = null;
                break;
            case MotionEvent.ACTION_CANCEL:
                currentPath = null;
                break;
            default:
                break;
        }
    }
    return true;
  }
};

只需确保将 currentPath 声明为自定义视图的私有字段 class。对不起,我现在不能测试代码,所以如果你对这段代码有什么问题,请告诉我,晚上我会给你一个测试过的代码。

编辑:抱歉,我在 ACTION_MOVE 处理中添加了额外的 moveTo,这将阻止正确的工作代码