webentwicklung-frage-antwort-db.com.de

Zeichnen auf Leinwand - PorterDuff.Mode.CLEAR zeichnet schwarz! Warum?

Ich versuche, eine benutzerdefinierte Ansicht zu erstellen, die einfach funktioniert: Es gibt eine Bitmap, die durch einen Bogenpfad angezeigt wird - von 0 Grad bis 360 Grad. Die Grade ändern sich mit einigen FPS.

Also habe ich eine benutzerdefinierte Ansicht mit der überschriebenen Methode onDraw() erstellt:

@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 wird wie folgt initialisiert:

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

Jetzt zeichnet alles großartig, aber ... der Hintergrund ist in der gesamten Ansicht SCHWARZ.

Wenn ich canvas.drawColor(..., PorterDuff.Mode.DST) setze und canvas.drawBitmap() weglasse, wird der Bogen ordnungsgemäß auf transparentem Hintergrund gezeichnet.

Meine Frage ist - wie man PorterDuff Modi einstellt, damit es mit Transparenz funktioniert?

Natürlich ist bitmap 32-Bit-PNG mit Alphakanal.

20
cadavre

PorterDuff.Mode.CLEAR funktioniert nicht mit Hardwarebeschleunigung. Einfach eingestellt 

view.setLayerType(View.LAYER_TYPE_SOFTWARE,null); 

Funktioniert perfekt für mich.

22
Nitesh Tarani

Verwenden Sie diese Anweisung während der Initialisierung der Ansicht

setLayerType(LAYER_TYPE_HARDWARE, null);
6
Mukesh Kumar

In Ihrem Code ist alles in Ordnung, mit einer Ausnahme: Sie erhalten schwarzen Hintergrund, weil Ihr Fenster undurchsichtig ist. Um ein transparentes Ergebnis zu erzielen, sollten Sie eine andere Bitmap verwenden. Erstellen Sie in Ihrer onDraw-Methode eine neue Bitmap und führen Sie alle Mitarbeiter aus. Danach zeichnen Sie dieses Bitmap auf Ihrer Leinwand. 

Für Details und Beispielcode lesen Sie bitte diese meine Antwort :

2
Robert

unerwünschten PorterDuff-Effekt lösen
verwenden zuerst die einfachste Methode, wie das Problem des OPs, eine Path.arcTo(*, *, *, *, false) reicht aus - beachten Sie, dass es arcTo ist, nicht addArc, und false bedeutet no forceMoveTo, bevor Sie arc hinzufügen - es ist keine PorterDuff-Angabe erforderlich.

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

wenn Sie PorterDuff wirklich benötigen, vor allem für komplexe Farbmorphings, z. B. für das Mischen von Farbverläufen, zeichnen Sie keine Farbe oder Form oder Bitmap mit dem Filtereffekt von PorterDuff direkt in die in onDraw(Canvas) bereitgestellte Standard-Zeichenfläche --und setHasAlpha(true)-- Um das Ergebnis der PorterDuff-Filterung zu speichern, zeichnen Sie die Bitmap zuletzt in den Standard-Zeichenbereich, ohne eine Filterung vorzunehmen, mit Ausnahme der Matrixänderung.
Hier ist ein Arbeitsbeispiel zum Erstellen eines unscharfen runden Bildes. 

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

einige Anmerkungen: 1, diese Ansicht erweitert ImageView nicht View, es gibt einige Unterschiede.
2, warum drawWithLayers --saveLayer oder saveLayerAlpha-- nicht empfohlen wird: a, sie sind unsicher, funktionieren manchmal nicht richtig (transparent als schwarz anzeigen), insbesondere für View whoes onDraw(Canvas) ist leer, während ImageView.onDraw(Canvas) ein Drawable um einige zu zeichnen; b, sie sind teuer, sie weisen off-screen bitmap zu, um temporäre Zeichnungsergebnisse zu speichern, und es gibt keine klaren Hinweise auf einen Ressourcenrecyclingmechanismus.
3 verwendet Ihre eigenen Bitmap [s] und eignet sich besser für das maßgeschneiderte Ressourcenrecycling.

Einige Leute sagten, es ist unmöglich, PorterDuff ohne Zuweisung von Bitmaps jeder Zeichnung zu verwenden, da die Breite und Höhe des Bitmaps nicht vor dem Zeichnen/Layout/Messen bestimmt werden kann.
Sie können Buffering-Bitmap [s] für die PorterDuff-Zeichnung verwenden: 
weisen Sie zunächst eine ausreichend große Bitmap [s] zu.
zeichnen Sie dann die Bitmap [s] mit etwas Matrix.
und die Bitmap [s] mit einer Matrix in eine dest-Bitmap zeichnen.
Zum Schluss zeichnen Sie die dest-Bitmap mit einer Matrix in die Leinwand.

Einige Leute empfehlen setLayerType (View.LAYER_TYPE_SOFTWARE, null), was für mich keine Option ist, da onDraw (Canvas) in einer Schleife aufgerufen wird.

ErgebnisbildQuellbild

1
zdave

speichern Sie einfach Leinwand und stellen Sie danach wieder her

canvas.saveLayer(clipContainer, null, Canvas.ALL_SAVE_FLAG);
canvas.drawRoundRect(rectTopGrey, roundCorners, roundCorners, greyPaint);
canvas.drawRoundRect(rectTopGreyClip, roundCorners, roundCorners, clipPaint);
canvas.restore();

in diesem Fall wird das erste Rechteck als Ziel für https://developer.Android.com/reference/Android/graphics/PorterDuff.Mode und das zweite Rechteck als Quelle verwendet

0
Mykola Tychyna

Wenn Sie einen einfarbigen Hintergrund haben, müssen Sie nur die Farbe für die Hintergrundfarbe festlegen. Wenn Sie beispielsweise einen weißen Hintergrund haben, können Sie Folgendes tun:

Paint.setColor(Color.WHITE);

Wenn Sie jedoch eine Zeile mit transparentem Hintergrund löschen müssen, versuchen Sie Folgendes: Um mit einer transparenten Farbe zeichnen zu können, müssen Sie Paint setXfermode verwenden. Dies funktioniert nur, wenn Sie eine Bitmap auf Ihrer Leinwand festlegen. Wenn Sie die folgenden Schritte ausführen, sollten Sie das gewünschte Ergebnis erhalten.

Erstellen Sie eine Leinwand und legen Sie ihre Bitmap fest.

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

Jetzt sollten Sie mit einer transparenten Farbe zeichnen können:

mCanvas.drawPath(path, mPaint);
0
Ishan