Tratando de crear una pintura de borrador para lienzo

Estoy creando una aplicación de dibujo que utiliza la clase DrawingSurfaceView a continuación. En esa clase tengo una Paint Called eraserPaint que el usuario puede activar y desactivar .. Cuando en esa pintura se supone que borrar lo que nunca está en su camino. Pero en su lugar sólo está dibujando una línea negra ..

Cuando guardo el lienzo como un png transparente el borrador es correcto pero en la pantalla aparece negro ..

Captura de pantalla desde el teléfono de EraserPaint utilizado para escribir "Erik" en blob

Introduzca aquí la descripción de la imagen

Guardado PNG de lienzo Introduzca aquí la descripción de la imagen

EraserPaint se ve así:

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); 

toda la clase

  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); } } 

Debo agregar que estoy agregando esta vista personalizada a un diseño relativo que tiene ese patrón a cuadros como la imagen de fondo.

POR FAVOR POR FAVOR, POR FAVOR ayuda .. necesito que la imagen de vista previa para no mostrar negro después de una pintura de borrador se utilizó .. lo necesito para mostrar el patrón a cuadros detrás .. Sé que el borrador está trabajando como las marcas de borrador negro salvo como transparente.

NUEVA NOTA

Yo estaba jugando y descubierto algo más que curioso. Experimentando, traté de cambiar de dibujo a la canvas como pasó al método onDraw y directamente a la tela que se estableció en el constructor llamado mCanvas y noté que no dibujar en la medida de lo que podía ver .. por lo que agregó un registro a la onDraw como así:

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

Que escupe

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

Tuve este mismo problema con mi aplicación. Incluso probé el código de ejemplo de "pintura con dedos" y todavía tenía el mismo problema. Nunca pude tener el borrador de trabajo como un camino, pero pude encontrar una solución. En lugar de dibujar un camino cuando borro, dibujo un círculo (podría ser de cualquier forma) cuando el usuario pone su dedo hacia abajo o hay un evento "mover":

 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; 

Tomará un cierto tiempo incorporar esto en su código, pero le garantizo que tomará menos tiempo que la cantidad de tiempo que usted ha pasado el intentar solucionar este problema. Puedo enviarte más de mi PaintView si crees que sería útil.

El mismo problema encontrado, probado todas las otras soluciones encontradas, no hay suerte.

Pero tengo una solución. Puede agregar un mapa de bits para almacenar los trazos.

  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); } 

Siempre que haya algún toque, registre la carrera con la pintura actual y el ancho de carrera actual. (La pintura podría ser cualquier color, incluyendo la pintura del borrador)

Y luego anular el método onDraw (Canvas). Como el mapa de bits admite el borrador mientras el lienzo no, podemos dibujar primero la imagen resultante en el mapa de bits primero y, a continuación, dibujar el mapa de bits en el lienzo.

  @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(); } } } 

FYI, si el mapa de bits en primer plano es muy grande, el rendimiento será bajo. Para resolver esto, deberíamos invalidar sólo el área que el último dedo toca alterado.

Esto es sólo una suposición: podría estar relacionado con la aceleración de hardware. Intente desactivar la aceleración de hardware. Si eso ayuda, puede crear un mapa de bits del tamaño de la vista, dibujar todas sus cosas a ese mapa de bits y, a continuación, dibujar el mapa de bits en el lienzo de la vista.

Para que el lienzo se borre e invalide, debe establecer el setXfermode de su lienzo como null. Compruebe la última línea del código.

 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); } 

Puede usar una variable booleana mientras elige borrador (es decir, isEraser = true ) y en onDraw() , puede dibujar ruta si no es borrador.

 @Override protected void onDraw(Canvas canvas) { if(!isEraser ){ canvas.drawPath(mPath, mPaint); } } 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.