2012-07-05 12 views
27

L'obiettivo è semplicemente quello di disegnare una bitmap e sopra la parte superiore di essa disegnare forme che cancellano l'area sottostante della bitmap.PorterduffXfermode: cancella una sezione di una bitmap

Ho creato una semplice dimostrazione del codice di concetto per cercare di capire come esattamente dovrei andare su questo. Nelle varie discussioni qui ho trovato numerosi suggerimenti su come utilizzare:

android.graphics.PorterDuff.Mode.CLEAR 

Il codice sotto crea semplicemente uno schermo con uno sfondo blu e aggiunge una visualizzazione personalizzata. Questa vista disegna sulla sua tela uno sfondo rosa, l'immagine bitmap (con un leggero bordo per mostrare lo sfondo rosa) e cerchi gialli sovrapposti che rappresentano ciascun PorterDuffXfermode.

import android.app.Activity; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.PorterDuffXfermode; 
import android.graphics.Paint.Style; 
import android.graphics.PorterDuff.Mode; 
import android.graphics.RectF; 
import android.os.Bundle; 
import android.view.View; 
import android.view.Window; 
import android.widget.RelativeLayout; 

public class Test extends Activity { 
    Drawing d = null; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     requestWindowFeature(Window.FEATURE_NO_TITLE); 

     RelativeLayout.LayoutParams rlp = null; 

     // Create the view for the xfermode test 
     d = new Drawing(this); 
     rlp = new RelativeLayout.LayoutParams(600, 900); 
     rlp.addRule(RelativeLayout.CENTER_IN_PARENT); 
     d.setLayoutParams(rlp); 

     RelativeLayout rl = new RelativeLayout(this); 
     rl.setBackgroundColor(Color.rgb(0, 0, 255)); 
     rl.addView(d); 

     // Show the layout with the test view 
     setContentView(rl); 
    } 

    public class Drawing extends View { 
     Paint[] pDraw = null; 
     Bitmap bm = null; 

     public Drawing(Context ct) { 
      super(ct); 

      // Generate bitmap used for background 
      bm = BitmapFactory.decodeFile("mnt/sdcard/Pictures/test.jpg"); 

      // Generate array of paints 
      pDraw = new Paint[16]; 

      for (int i = 0; i<pDraw.length; i++) { 
       pDraw[i] = new Paint(); 
       pDraw[i].setARGB(255, 255, 255, 0); 
       pDraw[i].setStrokeWidth(20); 
       pDraw[i].setStyle(Style.FILL); 
      } 

      // Set all transfer modes 
      pDraw[0].setXfermode(new PorterDuffXfermode(Mode.CLEAR)); 
      pDraw[1].setXfermode(new PorterDuffXfermode(Mode.DARKEN)); 
      pDraw[2].setXfermode(new PorterDuffXfermode(Mode.DST)); 
      pDraw[3].setXfermode(new PorterDuffXfermode(Mode.DST_ATOP)); 
      pDraw[4].setXfermode(new PorterDuffXfermode(Mode.DST_IN)); 
      pDraw[5].setXfermode(new PorterDuffXfermode(Mode.DST_OUT)); 
      pDraw[6].setXfermode(new PorterDuffXfermode(Mode.DST_OVER)); 
      pDraw[7].setXfermode(new PorterDuffXfermode(Mode.LIGHTEN)); 
      pDraw[8].setXfermode(new PorterDuffXfermode(Mode.MULTIPLY)); 
      pDraw[9].setXfermode(new PorterDuffXfermode(Mode.SCREEN)); 
      pDraw[10].setXfermode(new PorterDuffXfermode(Mode.SRC)); 
      pDraw[11].setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP)); 
      pDraw[12].setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
      pDraw[13].setXfermode(new PorterDuffXfermode(Mode.SRC_OUT)); 
      pDraw[14].setXfermode(new PorterDuffXfermode(Mode.SRC_OVER)); 
      pDraw[15].setXfermode(new PorterDuffXfermode(Mode.XOR)); 
     } 

     @Override 
     public void onDraw(Canvas canv) { 
      // Background colour for canvas 
      canv.drawColor(Color.argb(255, 255, 0, 255)); 

      // Draw the bitmap leaving small outside border to see background 
      canv.drawBitmap(bm, null, new RectF(15, 15, getMeasuredWidth() - 15, getMeasuredHeight() - 15), null); 

      float w, h; 
      w = getMeasuredWidth(); 
      h = getMeasuredHeight(); 

      // Loop, draws 4 circles per row, in 4 rows on top of bitmap 
      // Drawn in the order of pDraw (alphabetical) 
      for(int i = 0; i<4; i++) { 
       for(int ii = 0; ii<4; ii++) { 
        canv.drawCircle((w/8) * (ii * 2 + 1), (h/8) * (i * 2 + 1), w/8 * 0.8f, pDraw[i*4 + ii]); 
       } 
      } 
     } 
    } 

} 

Questo è il risultato del test:

enter image description here

modo cancellazione è alto a sinistra, che mostra come il nero.

In un altro esempio in cui stavo cercando di usarlo avevo un DialogFragment in cui la modalità CLEAR cancellava l'intero DialogFragment in modo da poter vedere l'attività sottostante. Da qui il motivo per cui ho usato molti colori di sfondo diversi in questo test.

Questo potrebbe forse cancellare i pixel dell'intera attività come quell'altro esempio mi ha indotto a credere? Avrei pensato che solo i pixel della tela relativi alla vista potevano essere cancellati, ma nel mio altro esempio i pixel della vista personalizzata, della vista immagine sottostante e dello sfondo DialogFragment erano stati cancellati.

Qualcuno potrebbe aiutarmi a capire cosa sta succedendo esattamente e perché sto andando così terribilmente storto?

Grazie.

MODIFICA: Ho riprodotto un esempio che conferma i miei sospetti. Quando si aggiunge questa vista personalizzata esatta, ma in un DialogFragment, l'attività sottostante diventa visibile.

enter image description here

Questo sembra un indicatore abbastanza chiaro per me che il Mode.CLEAR è in qualche modo cancellando la tela dei punti di vista sotto come bene? La mia ipotesi sarebbe il nero nel primo esempio è quello della visualizzazione di livello superiore?

Sto pensando che sto facendo qualcosa di molto sbagliato da qualche parte: S

risposta

34

Il problema è l'accelerazione di hardware. Spegnilo per la particolare vista che stai dipingendo con CLEAR.Se stai usando una visualizzazione personalizzata, fare questo nei costruttori:

if (android.os.Build.VERSION.SDK_INT >= 11) 
{ 
    setLayerType(View.LAYER_TYPE_SOFTWARE, null); 
} 

Si può anche chiamare setLayerType su un riferimento vista.

+2

Grazie ... ha reso la mia giornata! –

+1

rallenterà il disegno, non è vero? – sandrstar

+0

Questa dovrebbe essere la risposta accettata. Super utile. Grazie! –

14

Non vedo qualcosa di inaspettato. Nel caso specifico di Mode.CLEAR, vengono cancellati sia il colore che l'alfa della destinazione, consentendo la visualizzazione dello sfondo nero. Questo utility consente di sperimentare varie modalità, colori e valori alfa, e la fonte può offrire alcune informazioni. Nell'immagine (un po 'datata) sotto, le aree CLEAR rivelano il pallido gessato grigio fornito dal delegato della piattaforma PanelUI.

image

+0

Grazie, ha molto senso. Applicando il mio problema a questo però. Come dici tu, vedi correttamente lo sfondo sottostante nell'area libera. Perché non vedo lo sfondo blu (posso capire perché non è rosa)? Il blu è una vista diversa, quindi perché i pixel non sono trasparenti nella tela della vista personalizzata? (Come l'immagine sopra). Se metto un ImageView dietro di esso, o qualsiasi altra cosa, continuo a non vederlo, solo il nero. Quando si esegue lo stesso in un DialogFragment, questo metodo ha letteralmente disegnato un foro nell'intera finestra di dialogo (oltre alla vista). Quindi sta eliminando tutti i livelli per qualche motivo? –

+0

Aka, come farei esattamente come fa l'immagine (mostra lo sfondo cancellando un'area di un disegno? –

+1

Prova ad aggiustare l'alfa del colore 'Src' nella modalità' SrcOver' per vedere l'effetto. ] (https://sites.google.com/site/drjohnbmatthews/composite/source-code) potrebbe anche offrire alcune informazioni e un esempio correlato [qui] (http://stackoverflow.com/a/7824225/230513 – trashgod

0

Come Romain Guy indica qui: google stuff è necessario scrivere sulla bitmap, non sulla tela con i cerchi che si sta tentando di cancellare, quindi impostarla sulla tela principale nella vista. Quindi, fai qualcosa come:

// Generate bitmap used for background 
      bm = BitmapFactory.decodeFile("mnt/sdcard/Pictures/test.jpg"); 

// Create the paint and set the bitmap 
Canvas temp = new Canvas(bm.getHeight(), bm.getWidth, Config.ARGB_8888); 

// in onDraw() 
temp.drawCircle((w/8) * (ii * 2 + 1), (h/8) * (i * 2 + 1), w/8 * 0.8f, pDraw[i*4 + ii]); 

// After loop 
canvas.drawBitmap(....); 

Spero che questo aiuti.

Problemi correlati