8

我正在创建一个使用下面的 DrawingSurfaceView 类的绘图应用程序。在那个类中,我有一个名为 eraserPaint 的油漆,用户可以打开和关闭它。当在那个油漆上时,假设要擦除它路径中的任何东西。但它只是画了一条黑线..

当我将画布保存为透明 png 时,橡皮擦是正确的,但在屏幕上显示为黑色。

EraserPaint 手机屏幕截图用于在 blob 上写“Erik”

在此处输入图像描述

从画布中保存 PNG 在此处输入图像描述

橡皮擦看起来像这样:

eraserPaint = new Paint();
        eraserPaint.setAlpha(0);
        eraserPaint.setColor(Color.TRANSPARENT);
        eraserPaint.setStrokeWidth(60);
        eraserPaint.setStyle(Style.STROKE);
        eraserPaint.setMaskFilter(null);
        eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        eraserPaint.setAntiAlias(true);

整个班级

     public KNDrawingSurfaceView(Context c, float width, float height, KNSketchBookActivity parent) {

        super(c);

        myWidth = width;
        myHeight = height;

        mBitmap = Bitmap.createBitmap((int) myWidth, (int) myHeight, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);

        _parent = parent;


        mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);


        tile = new Paint();

        tileImage = BitmapFactory.decodeResource(getResources(), R.drawable.checkerpattern);
        shader = new BitmapShader(tileImage, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        tile.setShader(shader);


        mPath = new Path();
        eraserPaint = new Paint();
        eraserPaint.setAlpha(0x00);
        eraserPaint.setColor(Color.TRANSPARENT);
        eraserPaint.setStrokeWidth(60);
        eraserPaint.setStyle(Style.STROKE);
        //eraserPaint.setMaskFilter(null);
        eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        eraserPaint.setAntiAlias(true);

        mBitmapPaint = new Paint(Paint.DITHER_FLAG);



        mCanvas.drawRect(0, 0, myWidth, myHeight, tile);

        mCanvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    }

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

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


    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (!_parent.isDrawerOpen()&&mPaint!=null) {
            Log.v("onDraw:", "curent paths size:" + paths.size());

            //mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            //canvas.drawPath(mPath, mPaint);
            for (int i=0;i< paths.size();i++) {
                tempPaint =  paints.get(i);
                eraserPaint.setStrokeWidth(tempPaint.getStrokeWidth());
                if(fills.get(i)){
                    tempPaint.setStyle(Style.FILL_AND_STROKE);
                    eraserPaint.setStyle(Style.FILL_AND_STROKE);
                }else{
                    tempPaint.setStyle(Style.STROKE);
                    eraserPaint.setStyle(Style.STROKE);
                }
                if(erasers.get(i)){
                    //tempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
                    canvas.drawPath(paths.get(i), eraserPaint);
                }else{
                    //tempPaint.setXfermode(null);
                    canvas.drawPath(paths.get(i), tempPaint);
                }
                //canvas.drawPath(paths.get(i), tempPaint);
            }
            if(_parent.toggleFill.isChecked()){
               mPaint.setStyle(Style.FILL_AND_STROKE); 
               eraserPaint.setStyle(Style.FILL_AND_STROKE);

            }else{
               mPaint.setStyle(Style.STROKE);
               eraserPaint.setStyle(Style.STROKE);
            }
            if(_parent.toggleErase.isChecked()){
               //mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
                 canvas.drawPath(mPath, eraserPaint);
            }else{
                //mPaint.setXfermode(null);
                canvas.drawPath(mPath, mPaint);
            }
            //canvas.drawPath(mPath, mPaint);
        }
    }

    public void onClickUndo() {

        if (paths.size() > 0) {
            undonePaths.add(paths.remove(paths.size() - 1));
            undonePaints.add(paints.remove(paints.size() - 1));
            undoneFills.add(fills.remove(fills.size() - 1));
            undoneErasers.add(erasers.remove(erasers.size() - 1));
            clearCanvasCache();
            invalidate();
        } else {

        }
        _parent.checkButtonStates();
    }

    public void onClickRedo() {

        if (undonePaths.size() > 0) {
            paths.add(undonePaths.remove(undonePaths.size() - 1));
            paints.add(undonePaints.remove(undonePaints.size() - 1));
            fills.add(undoneFills.remove(undoneFills.size() - 1));
            erasers.add(undoneErasers.remove(undoneErasers.size() - 1));
            clearCanvasCache();
            invalidate();
        } else {

        }
        _parent.checkButtonStates();
    }

    public void onClickClear() {

        paths.clear();
        paints.clear();
        fills.clear();
        erasers.clear();
        undoneFills.clear();
        undonePaths.clear();
        undonePaints.clear();
        undoneErasers.clear();
        clearCanvasCache();
        invalidate();
        _parent.checkButtonStates();
    }

    public void saveDrawing() {

        FileOutputStream outStream = null;
        String fileName = "tempTag";
        try {

            outStream = new FileOutputStream("/sdcard/" + fileName + ".png");

            mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
            outStream.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }

    }

    private float mX, mY;

    private static final float TOUCH_TOLERANCE = 4;

    private void touch_start(float x, float y) {

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

        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y) {

        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }

    private void touch_up() {

        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        if(_parent.toggleErase.isChecked()){
            mCanvas.drawPath(mPath, eraserPaint);
            erasers.add(true);
            paints.add(eraserPaint);
        }else{
            mCanvas.drawPath(mPath, mPaint);
            erasers.add(false);
            paints.add(mPaint);
        }

        // kill this so we don't double draw

        paths.add(mPath);


        if(_parent.toggleFill.isChecked()){
            fills.add(true);
        }else{
            fills.add(false);
        }
        if(_parent.toggleErase.isChecked()){
            erasers.add(true);
        }else{
            erasers.add(false);
        }


        _parent.checkButtonStates();
        mPath = new Path();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(mPaint==null &&!_parent._showingAlert){
            _parent.showNoPaintAlert();
        }

        if (!_parent.isDrawerOpen()&&mPaint!=null) {
            float x = event.getX();
            float y = event.getY();
            if (x > myWidth) {
                x = myWidth;

            }
            if (y > myHeight) {
                y = myHeight;

            }
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
            }
            return true;
        } else {
            return true;
        }
    }

    public void clearCanvasCache() {

        mBitmap = Bitmap.createBitmap((int) myWidth, (int) myHeight, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
    }
}

我应该补充一点,我正在将此自定义视图添加到具有该方格图案作为背景图像的相对布局中。

请帮助..我需要在使用橡皮擦油漆后预览图像不显示黑色..我需要它来显示后面的方格图案..我知道橡皮擦正在工作,因为那些黑色橡皮擦标记保存为透明。

新注

我在玩,发现了一些奇怪的东西。进行实验时,我尝试从绘图切换到canvas传递给方法的onDraw方法,然后直接切换到我在调用的构造函数中设置的画布,mCanvas 并注意到它没有绘制到我所能看到的范围内..所以我添加了一个日志,onDraw 如下所示:

 protected void onDraw(Canvas canvas) {
       Log.v("DRAWING SURFACE", "canvas:"+canvas+" mCanvas:"+mCanvas);

吐出来

06-21 11:10:43.994: V/DRAWING SURFACE(4532): canvas:android.view.Surface$CompatibleCanvas@42a8c030 mCanvas:android.graphics.Canvas@431df180
4

5 回答 5

7

我的应用程序也有同样的问题。我什至尝试了“手指画”示例代码,但仍然遇到同样的问题。我永远无法让橡皮擦作为路径,但我能够找到解决方法。我不是在擦除时绘制路径,而是在用户放下手指或发生“移动”事件时绘制一个圆圈(可以是任何形状):

case MotionEvent.ACTION_DOWN:
mPaint.setStrokeWidth(25);
            mPaint.setXfermode(new PorterDuffXfermode(
                    PorterDuff.Mode.CLEAR));
            mCanvas.drawCircle(x, y, 10, mPaint);
            isErase = true;
            invalidate();
        }
        touch_start(x, y);
        invalidate();
        break;
case MotionEvent.ACTION_MOVE:   
        if(isErase)
        {
            mCanvas.drawCircle(x, y, 20, mPaint);
        }
        else{
            touch_move(x, y);
        }invalidate();
        break;

将其合并到您的代码中需要一些时间,但我向您保证,它所花费的时间将少于您尝试解决此问题所花费的时间。如果您认为有帮助,我可以向您发送更多我的 PaintView。

于 2013-06-21T15:16:43.590 回答
1

遇到同样的问题,尝试了所有其他解决方案,没有运气。

但我有一个解决方法。您可以添加位图来存储笔画。

    public void init(int width, int height) {
    Log.i(TAG,"init with "+width+"x"+height);
    foreground = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    cacheCanvas = new Canvas();
    cacheCanvas.setBitmap(foreground);
}

每当有任何触摸时,用当前的绘画和当前的笔画宽度记录笔画。(油漆可以是任何颜色,包括橡皮擦油漆)

然后重写 onDraw(Canvas) 方法。由于位图支持橡皮擦,而画布不支持,我们可以先在位图上绘制生成的图像,然后再将位图绘制到画布上。

    @Override
protected void onDraw(Canvas canvas) {
    // Log.i(TAG,"onDraw called");
    synchronized (strokes) {
        if (strokes.size() > 0) {
            for (Stroke s : strokes) {
                cacheCanvas.drawPath(s.path, s.color);
            }
            canvas.drawBitmap(foreground, 0, 0, null);
            strokes.clear();
        }
    }
}

仅供参考,如果前景位图非常大,性能会很低。为了解决这个问题,我们应该只使最近手指触摸改变的区域无效。

于 2013-12-06T06:20:27.903 回答
0

这只是一个猜测:它可能与硬件加速有关。尝试禁用硬件加速。如果这有帮助,您可以创建一个视图大小的位图,将所有内容绘制到该位图,然后将位图绘制到视图的画布中。

于 2013-06-28T15:49:19.823 回答
0

要使画布擦除和失效,您必须将画布的 setXfermode 设置为 null。检查代码的最后一行。

if(view.getId()==R.id.erase_btn) {

        erase_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onDraw.setErase(true);
            }
        }
}

public void setErase(boolean isErase){
    erase=isErase;
    if(erase) drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    else drawPaint.setXfermode(null);
}
于 2015-11-17T06:35:50.510 回答
-2

isEraser = true您可以在选择橡皮擦(即。 )时使用布尔变量,onDraw()如果不是橡皮擦,您可以绘制路径。

@Override
protected void onDraw(Canvas canvas) {
  if(!isEraser ){
   canvas.drawPath(mPath, mPaint);
  }
}
于 2015-09-14T10:09:21.817 回答