2012-04-22 5 views
8

Ich versuche, einen Weg zu finden, Hintergrund der Leinwand mit einer Farbe von benutzerdefinierten Farbauswahl abgeholt, ohne irgendwelche Zeichnungen darauf zu entfernen. Ich versuche, eine Anwendung zu erstellen, die auf Leinwand zeichnen und als PNG speichern kann. Aber wenn ich der aktuellen Leinwand einen neuen Hintergrund hinzufüge, sind alle Zeichnungen weg. Ich verwende etwas wie folgt aus:Android ändern Leinwand Hintergrundfarbe ohne irgendwelche Zeichnungen davon zu verlieren

mCanvas.drawColor(picker.getColor());

Irgendwelche Ideen, wie ich die Dinge können zu arbeiten?

+0

Sie müssen uns die OnDraw-Methode zeigen .. – Ronnie

+0

Hallo, ich habe eine Lösung Implementierung unten angehängt. Es tut ziemlich genau das, was Sie wollen, wie es in Ihrer Frage beschrieben ist. – epichorns

Antwort

4

Wenn Sie die Farbe zeichnen, wird sie über Ihre Zeichnungen gezeichnet. Sie müssen die Farbe zeichnen und dann alles andere wieder zeichnen.

+0

Ja, aber wenn ich die alte Bitmap speichere und danach wieder zeichne ... ist der Hintergrund der alte und die neue Farbe wird nicht im Vordergrund sein ... also wie kann ich das beheben? –

+0

Er sagt, dass die Reihenfolge draw_background-> draw_image jedes Mal sein sollte, wenn sich die Hintergrundfarbe ändert. Es ist schnell, so weit das Auge nur den Hintergrund ändert. – DeeV

0

Solange Ihr Hintergrund ist und in einer anderen Farbe sein wird, können Sie tun:

for (x...) 
    for (y...) 
    if (bitmap.getPixel(x,y) == oldBackgroundColor) 
     bitmap.setPixel(x,y,newBackgroundColor) 

Oder Sie Ihre Inhalte auf einem Off-Screen-Bitmap zeichnen können, und den Hintergrund ziehen und dann dem Off-Screen auf dem tatsächliche Bitmap Auf diese Weise können Sie die Hintergrundfarbe ändern, die bei der nächsten zweistufigen Zeichnung verwendet wird.

+0

Tatsächlich ... möchte der OP wahrscheinlich, dass die Vordergrundzeichnung ihre Integrität beibehält, obwohl sie die gleiche oder eine andere Farbe hat als der Hintergrund ... Ein * Farbwiedergabefilter * ist eine destruktive Operation, während der OP wahrscheinlich etwas wollte ähnlich wie Photoshop-Ebenen ... – epichorns

+0

vereinbart, das ist nur ein Workaround für Sonderfälle, keine generische Lösung! – Bondax

7

Die Antworten auf Ihre Frage weisen alle in die richtige Richtung: Sie müssen Ihren Hintergrundfarbblock und Ihre Vordergrundzeichnung in separaten Ebenen trennen und dann zusammenführen, bevor Sie die gesamte Datei in einer PNG-Datei speichern . So gestaltet sich auch der Adobe Photoshop Workflow ... Wenn wir darüber nachdenken, ergibt das Sinn: Nehmen wir zum Beispiel eine Software wie MsPaint: Da sie keine Ebenen verwendet, muss sie sich auf Dinge wie Floodfill-Algorithmen verlassen erreichen (wenn auch auf eine unvollständige Weise) etwas entfernt ähnlich wie eine Hintergrundänderung ...

Eine Möglichkeit, so etwas zu implementieren wäre, 2 Canvas-Objekte zu instanziieren, die von 2 verschiedenen Bitmaps unterstützt werden. Das erste Canvas-Bitmap-Paar wird für Ihre Zeichnung im Vordergrund verwendet, und das zweite Canvas-Bitmap-Paar wird für die Zeichnung mit verbundenen Layern verwendet (d. H. Vordergrundzeichnung + Hintergrundfarbblock). Dann wird das 2. Bitmap in einer .png-Datei gespeichert, wenn Sie es zum Speichern benötigen. Auf diese Weise speichert unser erstes Canvas-Bitmap-Paar Ihre Vordergrundinformationen, die nicht zerstört werden, wenn eine Hintergrundfarbe geändert werden muss. Jedes Mal, wenn eine Operation ausgeführt wird, können die Layer in das zweite Canvas-Bitmap-Paar zusammengeführt werden, sodass immer ein Bitmap mit dem richtigen Inhalt vorhanden ist, der nach Belieben gespeichert werden kann.

Hier ist eine benutzerdefinierte Ansicht, die ich gemacht habe, um diese Methodik zu klären. Es implementiert eine einfache Ansicht, die verwendet wird, um eine blaue Linie auf dem Berührungsbildschirm unter Verwendung eines Fingers zu malen, wobei sich eine Hintergrundfarbe abhängig von der XY-Position des Fingers ändert, um eine Hintergrundfarbänderung ohne unnötige Codekomplexität einer vollständigen Implementierung zu demonstrieren mit einem Farbrad/menus/unter anderem:

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

} 

um diese Ansicht zu testen, hier ist ein Test, der die Aktivität PaintView-Klasse verwendet. Diese beiden Dateien sind autarke in einem Android-Projekt, so dass Sie es auf Ihrem realen Gerät ohne großen Aufwand testen:

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




} 
+0

Er muss den Hintergrund von Bildern ändern können, die bereits im Speicher gespeichert sind. Zum Beispiel, um das Bild abzurufen und die Ebenen zu trennen (Vordergrund vom Hintergrund) und dann die Farbe der Hintergrundebene zu ändern und die Ebenen wieder wie zuvor zusammenzuführen. – Ronnie

+0

Hmmm. Das habe ich von seiner Frage nicht bekommen. Wenn Sie Recht haben, gibt es keine saubere Lösung, die nicht in einem anderen Format wie .psd ... gespeichert wird. Da jede auf Floodfill basierende Lösung oder Farbaustauschlösung fehlschlägt, da keine Software den Alphakanal kennen kann von halbtransparenten Vordergrundzeichnungen, unter anderem. – epichorns

+0

... Es sei denn, er darf separate Ebenen in verschiedenen Bildern speichern, z. B. nur die transparente Vordergrundebene (Farbmodus 8888) in einem .psd speichern und, wenn ein Hintergrund benötigt wird, entweder in einem anderen Bild speichern oder serialize die Farbinformation in einem Format seiner Wahl ... PSD oder gleichwertig wäre in diesem Fall wahrscheinlich am besten. – epichorns

1

Schauen Sie, wenn Sie wollen in Leinwand ändern, dann müssen Sie rufen entkräften diese anzuwenden ändert sich Ihr Bildschirm.Und wenn Sie Ungültig anrufen, wird Ihre onDraw() Methode aufrufen.

Wenn Sie nur die Hintergrundfarbe des Canvas vom Farbwähler ändern möchten, dann speichern Sie den Farbwert in der Variable und rufen Sie nach dem Speichern der Variable invalidate auf. Jetzt ruft Ihr onDraw() auf.Jetzt Hintergrund der Leinwand ändern, indem setBackgroundColor(color variable) in onDraw() Aufruf und zeichnen alles andere wollen Sie

1

Verwendung canvas.drawARGB (a, r, g, b) und es wird für bestimmte

+0

Die einfachste und korrekte Antwort. Verwenden Sie '' 'canvas.drawARGB (200, 0, 0, 0);' '', um die Ansicht dunkler zu machen. – oxied

0

@ Android-Droid

arbeiten

Diese beiden Codezeilen arbeiteten wie Charme für mich. Wenn jemals Benutzer klickt auf eine beliebige Farbe (zB: rot) eingestellt, dass Farbe wie

 mPaint.setColor(Color.RED); 

und wann mPaint immer Sie wollen Leinwandfarbe

dv.setBackgroundColor(mPaint.getColor()); 

wo dv ist das Objekt der Klasse ändern, die erweitert Ansicht (Benutzerdefinierte Ansicht). Versuchen Sie es und lassen Sie mich wissen, wenn Sie irgendwelche Probleme haben.

0

Vielleicht ist es eine alte Frage, aber ich will mit dieser Lösung beizutragen, falls Sie das Bitmap aus einer Quelle nehmen und dann eine ziehbar mit Leinwand tun, vielleicht kann dies u passen:

@Override 
public Bitmap transform(final Bitmap source) { 
    //Background for transparent images 
    Bitmap backg = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); 
    backg.eraseColor(Color.WHITE); // Any color you want... 
    Paint back = new Paint(); 
    BitmapShader backshader = new BitmapShader(backg, 
    BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); 
    back.setShader(backshader); 
    back.setAntiAlias(true); 

    // Image for the draw 
    final Paint paint = new Paint(); 
    paint.setAntiAlias(true); 
    paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, 
      Shader.TileMode.CLAMP)); 
    Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig()); 
    Canvas canvas = new Canvas(output); 

    // IMPORTANT THING 
    canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() 
      - margin, source.getHeight() - margin), radius, radius, back); // Draw the background first... 
    canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() 
      - margin, source.getHeight() - margin), radius, radius, paint); // And then Draw the image, so it draws on top of the background 

    if (source != output) { 
     source.recycle(); 
    } 

    // This is for if i want to put a border in the drawable, its optional 
    Paint paint1 = new Paint();  
    paint1.setColor(Color.parseColor("#CC6C7B8B")); 
    paint1.setStyle(Style.STROKE); 
    paint1.setAntiAlias(true); 
    paint1.setStrokeWidth(2); 
    canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() 
      - margin, source.getHeight() - margin), radius, radius, paint1); 

    // and then, return the final drawable... 
    return output; 
} 

Hoffe, es hilft ...

Verwandte Themen