2013-08-22 4 views
15

J'essaye de créer une vue faite sur commande qui fonctionne simple: il y a un bitmap qui est révélé par le chemin d'arc - de 0deg à 360deg. Les degrés changent avec certains FPS.Dessin sur toile - PorterDuff.Mode.CLEAR dessine du noir! Pourquoi?

Je fait une vue personnalisée avec la méthode surchargée onDraw():

@Override 
protected void onDraw(Canvas canvas) { 

    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 
    arcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 
    canvas.drawArc(arcRectF, -90, currentAngleSweep, true, arcPaint); 
    arcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 
    canvas.drawBitmap(bitmap, circleSourceRect, circleDestRect, arcPaint); 
} 

arcPaint est initialisé comme suit:

arcPaint = new Paint(); 
arcPaint.setAntiAlias(true); 
arcPaint.setColor(Color.RED); // Color doesn't matter 

Maintenant, tout tire très bien, mais ... le fond est noir dans vue entière.

Si je règle canvas.drawColor(..., PorterDuff.Mode.DST) et omet canvas.drawBitmap() - l'arc est dessiné correctement sur fond transparent.

Ma question est - comment régler PorterDuff modes pour le faire fonctionner avec la transparence?

Bien sûr bitmap est un format PNG 32 bits avec canal alpha.

+0

avez-vous trouvé la solution? Je suis à mi-chemin mais quand j'essaie de sauver bitmap qui me donne une partie noire à la zone de suppression. –

+0

Il n'y a pas de solution pour cela sans impliquer OpenGL ou changer de SDK. J'ai finalement décidé de ne pas dessiner de bitmap du tout. – cadavre

Répondre

2

Tout est OK dans votre code sauf une chose: vous obtenez un fond noir parce que votre fenêtre est opaque. Pour obtenir un résultat transparent, vous devez utiliser un autre bitmap. Dans votre méthode onDraw, créez un nouveau bitmap et faites-y tout le personnel. Après cela, dessinez ce bitmap sur votre toile.

Pour plus de détails et des exemples de code s'il vous plaît lire this my answer:

+1

Imaginez maintenant que vous créez sur chaque onDraw() un nouveau bitmap et dessinez dessus. On dirait que tuer l'application. Enfin c'est triste à dire - mais ce que je voulais accomplir est impossible! Il y avait quelques trucs comme le fait de dessiner un dessin en haut de la fenêtre (ce qui rendait Canvas transparent) mais j'ai utilisé View qui chevauchait une autre vue, donc ça ne rentrait pas. – cadavre

0

Si vous avez un fond de couleur solide tout ce que vous devez faire est de définir la couleur de la peinture à votre couleur de fond. Par exemple, si vous avez un fond blanc que vous pouvez faire:

paint.setColor(Color.WHITE); 

Cependant, si vous avez besoin d'effacer une ligne avec un fond transparent vous essayez ceci: Pour dessiner avec une couleur transparente, vous devez utiliser la peinture setXfermode qui ne fonctionnera que si vous définissez un bitmap sur votre canevas. Si vous suivez les étapes ci-dessous, vous devriez obtenir le résultat souhaité.

Créez un canevas et définissez son bitmap.

mCanvas = new Canvas(); 
mBitmap= Bitmap.createBitmap(scrw, scrh, Config.ARGB_8888); 
mCanvas.setBitmap(mBitmap); 
When you want to erase something you just need to use setXfermode. 

public void onClickEraser() 
{ 
    if (isEraserOn) 
     mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 
    else 
     mPaint.setXfermode(null); 
} 

Maintenant, vous devriez être capables d'exploiter avec une couleur transparente en utilisant:

mCanvas.drawPath(path, mPaint); 
4

Utilisez cette instruction lors de l'initialisation de la vue

setLayerType(LAYER_TYPE_HARDWARE, null); 
+0

Il a résolu le problème de changement de couleur, mais maintenant ma ligne s'est déformée – Godwin

11

PorterDuff.Mode.CLEAR ne fonctionne pas avec l'accélération matérielle. Réglez simplement

view.setLayerType(View.LAYER_TYPE_SOFTWARE,null); 

Fonctionne parfaitement pour moi.

+0

J'avais ce problème de couleur noire depuis deux jours. Merci mec. ça fonctionne comme du charme :) –

+0

'setLayerType (LAYER_TYPE_HARDWARE, null);' Dans mon cas, cette instruction a fonctionné. Mais après avoir défini cela, les lignes et les bitmaps sont détruits. – Godwin

1

pour résoudre indésirable PorterDuff effet
utiliser la méthode la plus simple d'abord, comme le problème de l'OP, un Path.arcTo(*, *, *, *, false) suffit - notez qu'il est arcTo, pas addArc et le false signifie no forceMoveTo avant d'ajouter à l'arc - il n'y a pas besoin pour PorterDuff.

Path arcPath = new Path(); 
@Override 
protected void onDraw(Canvas canvas) { 
    arcPath.rewind(); 
    arcPath.moveTo(arcRectF.centerX, arcRectF.centerY); 
    arcPath.arcTo(arcRectF, -90, currentAngleSweep, false); 
    arcPath.close(); 
    canvas.clipPath(arcPath, Region.Op.DIFFERENCE); 
    canvas.drawBitmap(bitmap, circleSourceRect, circleDestRect, arcPaint); 
} 

si vous avez vraiment besoin PORTERDUFF, principalement pour morphing de couleurs complexes, comme les gradients de mélange, ne tirez pas de couleur ou de forme ou bitmap avec effet de filtrage PORTERDUFF directement sur la toile par défaut fourni dans onDraw(Canvas), utilisez un tampon/dest bitmap [s] avec canal alpha - et setHasAlpha(true) - pour stocker le résultat du filtrage PorterDuff, enfin dessiner le bitmap sur le canevas par défaut sans appliquer de filtrage sauf le changement de matrice.
est ici un exemple de travail pour créer une image floue autour de la frontière:

import android.annotation.SuppressLint; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.PorterDuff; 
import android.graphics.PorterDuffXfermode; 
import android.graphics.RadialGradient; 
import android.graphics.Rect; 
import android.graphics.RectF; 
import android.graphics.Shader; 
import android.support.annotation.Nullable; 
import android.util.AttributeSet; 
import android.widget.ImageView; 

/** 
* Created by zdave on 6/22/17. 
*/ 

public class BlurredCircleImageViewShader extends ImageView { 
private Canvas mCanvas; 
private Paint mPaint; 
private Matrix matrix; 
private static final float GRADIENT_RADIUS = 600f; //any value you like, but should be big enough for better resolution. 
private Shader gradientShader; 
private Bitmap bitmapGradient; 
private Bitmap bitmapDest; 
private Canvas canvasDest; 
public BlurredCircleImageViewShader(Context context) { 
    this(context, null); 
} 

public BlurredCircleImageViewShader(Context context, @Nullable AttributeSet attrs) { 
    this(context, attrs, 0); 
} 

public BlurredCircleImageViewShader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    matrix = new Matrix(); 
    int[] colors = new int[]{Color.BLACK, Color.BLACK, Color.TRANSPARENT}; 
    float[] colorStops = new float[]{0f, 0.5f, 1f}; 
    gradientShader = new RadialGradient(GRADIENT_RADIUS, GRADIENT_RADIUS, GRADIENT_RADIUS, colors, colorStops, Shader.TileMode.CLAMP); 
    mPaint.setShader(gradientShader); 

    bitmapGradient = Bitmap.createBitmap((int)(GRADIENT_RADIUS * 2), (int)(GRADIENT_RADIUS * 2), Bitmap.Config.ARGB_8888); 
    bitmapDest = bitmapGradient.copy(Bitmap.Config.ARGB_8888, true); 

    Canvas canvas = new Canvas(bitmapGradient); 
    canvas.drawRect(0, 0, GRADIENT_RADIUS * 2, GRADIENT_RADIUS * 2, mPaint); 

    canvasDest = new Canvas(bitmapDest); 
} 

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    int width = getMeasuredWidth(); 
    setMeasuredDimension(width, width); 
} 

@Override 
protected void onDraw(Canvas canvas){ 
    /*uncomment each of them to show the effect, the first and the third one worked, the second show the same problem as OP's*/ 
    //drawWithLayers(canvas); //unrecommended. 
    //drawWithBitmap(canvas); //this shows transparent as black 
    drawWithBitmapS(canvas); //recommended. 
} 
@SuppressLint("WrongCall") 
private void drawWithLayers(Canvas canvas){ 
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 
    float width = canvas.getWidth(); 
    float hWidth = width/2; 
    //both saveLayerAlpha saveLayer worked here, and if without either of them, 
    //the transparent area will be black. 
    //int count = canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 255, Canvas.ALL_SAVE_FLAG); 
    int count = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG); 
    super.onDraw(canvas); 
    float scale = hWidth/GRADIENT_RADIUS; 
    matrix.setTranslate(hWidth - GRADIENT_RADIUS, hWidth - GRADIENT_RADIUS); 
    matrix.postScale(scale, scale, hWidth, hWidth); 
    gradientShader.setLocalMatrix(matrix); 

    canvas.drawRect(0, 0, width, width, mPaint); 

    canvas.restoreToCount(count); 
} 
@SuppressLint("WrongCall") 
private void drawWithBitmap(Canvas canvas){ 
    super.onDraw(canvas); 
    float scale = canvas.getWidth()/(GRADIENT_RADIUS * 2); 
    matrix.setScale(scale, scale); 
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 
    canvas.drawBitmap(bitmapGradient, matrix, mPaint); //transparent area is still black. 
} 
@SuppressLint("WrongCall") 
private void drawWithBitmapS(Canvas canvas){ 
    float scale = canvas.getWidth()/(GRADIENT_RADIUS * 2); 
    int count = canvasDest.save(); 
    canvasDest.scale(1/scale, 1/scale); //tell super to draw in 1/scale. 
    super.onDraw(canvasDest); 
    canvasDest.restoreToCount(count); 

    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 
    canvasDest.drawBitmap(bitmapGradient, 0, 0, mPaint); 

    matrix.setScale(scale, scale); //to scale bitmapDest to canvas. 
    canvas.drawBitmap(bitmapDest, matrix, null); 
    } 
} 

quelques notes: 1, ce point de vue ne ViewImageView étend, il y a quelques différences.
2, pourquoi drawWithLayers - saveLayer ou saveLayerAlpha - est unrecommended: un, ils sont incertains, parfois ne fonctionne pas correctement (montrer transparent noir), en particulier pour View whoes onDraw(Canvas) est vide, alors que ImageView.onDraw(Canvas) utilisé un Drawable pour dessiner certains; b, ils sont chers, ils allouent off-screen bitmap pour stocker le résultat du dessin temporaire, et il n'y a pas d'indices clairs de tout mécanisme de recyclage des ressources.
3, en utilisant votre propre bitmap [s], est mieux pour le recyclage des ressources personnalisées. Certaines personnes ont dit, il est impossible d'utiliser PorterDuff sans allocation bitmap [s] chaque dessin, parce que la largeur du bitmap, la hauteur ne peut être déterminée avant le dessin/mise en page/mesure.
vous pouvez utiliser des tampons bitmap [s] pour le dessin PorterDuff:
dans un premier temps, allouer quelques bitmap assez grand [s].
puis, dessinez sur le bitmap [s] avec une matrice. Le, dessine le (s) bitmap (s) dans un bitmap de destination avec une matrice.
enfin, dessinez l'image bitmap dest dans canvas avec une matrice.

Certaines personnes recommandent setLayerType (View.LAYER_TYPE_SOFTWARE, null), ce qui n'est pas une option pour moi, car cela entraînera l'appel d'onDraw (Canvas) dans une boucle.

result image source image