Las respuestas ya dadas a su pregunta apuntan en la dirección correcta: es necesario separar el bloque de color de fondo y el plano de primer plano en capas separadas, luego unirlas antes de guardar todo en un archivo .png . Así es como se diseñó el flujo de trabajo de Adobe Photoshop ... Tiene sentido, si lo pensamos bien: tomemos por ejemplo un software como MsPaint: como no usa capas, tiene que depender de algoritmos de inundación para lograr (aunque de manera incompleta) algo remotamente similar a un cambio de fondo ...
Una forma de implementar tal cosa sería instanciar 2 objetos Canvas respaldados por 2 mapas de bits diferentes. El primer par Canvas-Bitmap se usará para su dibujo en primer plano, y el segundo par Canvas-Bitmap se usará para el dibujo de capas fusionadas (es decir, dibujo en primer plano + bloque de color de fondo). Entonces, el 2.º Bitmap es lo que se guardará en un archivo .png cuando lo necesite para guardarlo. De esta forma, nuestro primer par Canvas-Bitmap almacena su información de primer plano, que no se destruye si es necesario realizar un cambio de color de fondo. Cada vez que se realiza una operación, las capas se pueden fusionar en el segundo par Canvas-Bitmap para que siempre haya un mapa de bits con el contenido correcto que esté listo para guardarse a su antojo.
Aquí hay una vista personalizada que hice para aclarar esta metodología. Implementa una vista simple utilizada para pintar una línea azul en la pantalla táctil con un dedo, con un color de fondo que cambia dependiendo de la posición XY de dicho dedo para demostrar un cambio de color de fondo sin una complejidad de código innecesaria inherente a una implementación completa con una rueda de color/menús/ entre otras cosas:
package com.epichorns.basicpaint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Paint.Style;
import android.view.View;
public class PaintView extends View{
Bitmap mMergedLayersBitmap=null; //Note: this bitmap here contains the whole of the drawing (background+foreground) to be saved.
Canvas mMergedLayersCanvas=null;
Bitmap mBitmap = null; //bitmap onto which we draw our stuff
Canvas mCanvas = null; //Main canvas. Will be linked to a .bmp file
int mBackgroundColor = 0xFF000000; //default background color
Paint mDefaultPaint = new Paint();
Paint mDrawPaint = new Paint(); //used for painting example foreground stuff... We draw line segments.
Point mDrawCoor = new Point(); //used to store last location on our PaintView that was finger-touched
//Constructor: we instantiate 2 Canvas-Bitmap pairs
public PaintView(Context context, int width, int height) {
super(context);
mMergedLayersBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mMergedLayersCanvas = new Canvas(mMergedLayersBitmap);
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
//Change background color
public void changeColor(int newColor){
mBackgroundColor = newColor;
invalidate(); //refresh view: this will indirectly invoke onDraw soon afterwards
}
//Called by user of PaintView in order to start a painting "stroke" (finger touching touch-screen): stores the
//location of the finger when it first touched the screen
public void startDraw(int x, int y, int radius, int color){
mDrawPaint.setColor(color);
mDrawPaint.setStyle(Style.STROKE);
mDrawPaint.setStrokeWidth(radius);
mDrawCoor.x = x;
mDrawCoor.y = y;
}
//Called by user of PaintView when finger touching touch-screen is moving (must be called after a startDraw,
//as the latter initializes a couple of necessary things)
public void continueDraw(int x, int y){
mCanvas.drawLine(mDrawCoor.x, mDrawCoor.y, x, y, mDrawPaint);
mDrawCoor.x = x;
mDrawCoor.y = y;
invalidate(); //refresh view: this will indirectly invoke onDraw soon afterwards
}
//Merge the foreground Canvas-Bitmap with a solid background color, then stores this in the 2nd Canvas-Bitmap pair.
private void mergeLayers(){
mMergedLayersCanvas.drawColor(mBackgroundColor);
mMergedLayersCanvas.drawBitmap(mBitmap, 0, 0, mDefaultPaint);
}
@Override
public void onDraw(Canvas canvas){
mergeLayers();
canvas.drawBitmap(mMergedLayersBitmap, 0, 0, mDefaultPaint);
}
}
para probar este punto de vista, aquí es una actividad de prueba que utiliza la clase PaintView
. Ambos de estos archivos son autosuficientes en un proyecto de Android, por lo que se puede probar en el dispositivo real sin complicaciones:
package com.epichorns.basicpaint;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import com.epichorns.basicpaint.PaintView;
public class BasicPaintActivity extends Activity {
PaintView mPaintView=null;
LinearLayout mL = null;
boolean mIsDrawing=false;
int mBackgroundColor = 0xFF000000;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
final float dispWidth = (float)display.getWidth();
final float dispHeight = (float)display.getHeight();
mPaintView = new PaintView(this, display.getWidth(), display.getHeight());
mPaintView.changeColor(mBackgroundColor);
mPaintView.setOnTouchListener(new View.OnTouchListener(){
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
mPaintView.startDraw((int)event.getX(), (int)event.getY(), 6, 0x806060FF);
mIsDrawing=true;
return true;
}
else if(event.getAction()==MotionEvent.ACTION_UP){
mIsDrawing=false;
return true;
}
else if(event.getAction()==MotionEvent.ACTION_MOVE){
if(mIsDrawing){
//To demonstrate background change, change background color depending on X-Y position
int r = (int)(255f*event.getX()/dispWidth);
int g = (int)(255f*event.getY()/dispHeight);
mBackgroundColor = Color.argb(0xFF, r,g, 0x00);
Log.d("DEBUG1", "Color channels: (r, g) = ("+String.valueOf(r)+", "+String.valueOf(g)+")");
mPaintView.changeColor(mBackgroundColor);
//now, draw stuff where finger was dragging...
mPaintView.continueDraw((int)event.getX(), (int)event.getY());
return true;
}
else{
return false;
}
}
return false;
}
});
setContentView(mPaintView);
}
}
Debe mostrarnos el método onDraw .. – Ronnie
Hola, he adjuntado una implementación de solución a continuación. Hace más o menos lo que quiere que haga, como se describe en su pregunta. – epichorns