Android 在画布上绘制-PorterDuff.Mode.CLEAR绘制黑色!为什么?

Android 在画布上绘制-PorterDuff.Mode.CLEAR绘制黑色!为什么?,android,canvas,drawing,porter-duff,Android,Canvas,Drawing,Porter Duff,我正在尝试创建一个工作简单的自定义视图:有一个位图,由弧路径显示-从0度到360度。度数随着一些FPS而变化 因此,我使用重写的onDraw()方法创建了一个自定义视图: @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); arcPaint.setXfermode(new PorterDuffXfermode(Po

我正在尝试创建一个工作简单的自定义视图:有一个位图,由弧路径显示-从0度到360度。度数随着一些FPS而变化

因此,我使用重写的
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
初始化如下:

arcPaint = new Paint();
arcPaint.setAntiAlias(true);
arcPaint.setColor(Color.RED); // Color doesn't matter
现在,一切都很好,但是。。。背景在整个视图中是黑色的

如果我设置了
canvas.drawColor(…,PorterDuff.Mode.DST)
并省略了
canvas.drawBitmap()
,则圆弧将正确地绘制在透明背景上

我的问题是-如何设置
PorterDuff
模式以使其透明工作


当然,
bitmap
是带有alpha通道的32位PNG。

代码中的一切都正常,除了一件事:因为窗口不透明,所以背景是黑色的。要获得透明效果,您应该在另一个位图上绘制。在您的onDraw方法中,请创建一个新位图,并对其执行所有操作。然后在画布上绘制此位图


有关详细信息和示例代码,请阅读:

如果您有纯色背景,则只需将Paint color设置为背景色。例如,如果您有白色背景,则可以执行以下操作:

paint.setColor(Color.WHITE);
但是,如果需要删除具有透明背景的线条,请尝试以下操作: 为了使用透明颜色进行绘制,必须使用Paint setXfermode,只有在画布上设置位图时,它才会起作用。如果您按照以下步骤操作,您应该会得到所需的结果

创建画布并设置其位图

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);
}
现在,您应该能够使用以下工具使用透明颜色进行绘制:

mCanvas.drawPath(path, mPaint);

在视图初始化期间使用此语句

setLayerType(LAYER_TYPE_HARDWARE, null);

PorterDuff.Mode.CLEAR
不适用于硬件加速。刚定

view.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 

非常适合我。

解决不需要的
PorterDuff
效果
首先使用最简单的方法,就像OP的问题一样,一个
Path.arcTo(*,*,*,*,*,false)
就足够了——注意它是
arcTo
,而不是
addArc
,而
false
意味着在添加arc之前
无需强制移动到
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);
}
如果您确实需要PorterDuff,主要用于复杂的颜色变形,如混合渐变,请不要将具有PorterDuff过滤效果的颜色、形状或位图直接绘制到
onDraw(canvas)
中提供的默认画布上,请使用一些带有alpha通道的缓冲/dest位图和
setHasAlpha(true)
--要存储PorterDuff过滤的结果,最后将位图绘制到默认画布,而不应用除矩阵更改以外的任何过滤。
下面是创建边界模糊圆形图像的工作示例:

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);
    }
 }
一些注意事项: 1,此视图扩展了
图像视图
而不是
视图
,存在一些差异。
2、为什么
drawWithLayers
-
saveLayer
saveLayerPha
--未被推荐:a、它们不确定,有时不能正常工作(显示为黑色透明),尤其是对于
视图
的用户,他们的
onDraw(画布)
是空的,而
ImageView.onDraw(画布)
使用
可绘制的
绘制一些;b、 它们价格昂贵,它们分配
屏幕外位图
来存储临时绘图结果,并且没有任何资源回收机制的明确线索。
3、使用自己的位图,更适合定制资源回收。

有人说,如果不在每张图纸上分配位图,就不可能使用PorterDuff,因为在绘制/布局/测量之前无法确定位图的宽度、高度。
您可以对PorterDuff绘图使用缓冲位图:
首先,分配一些足够大的位图。
然后,用一些矩阵绘制位图。
然后,使用一些矩阵将位图绘制成dest位图。
最后,使用一些矩阵将dest位图绘制到画布中。

有些人推荐setLayerType(View.LAYER\u TYPE\u SOFTWARE,null),这对我来说不是一个选项,因为它会导致在循环中调用onDraw(Canvas)


只需保存画布,然后恢复即可

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

在本例中,第一个矩形将作为目标,第二个矩形将作为源

现在想象您在每个onDraw()上创建一个新位图并在其上绘制。听起来像是杀掉了应用程序。最后,很遗憾地说,但我想要实现的是不可能的!有一些技巧,比如在窗口顶部设置视图绘制(使画布透明),但我使用的视图与另一个视图重叠,所以它不适合。@cadavre不可能?尼特什·塔拉尼的回答对我有用me@cadavre现在我看到他4年后回答了。你应该把他的答案记为已被接受的答案,但你找到解决办法了吗?我已经半途而废了,但当我试图保存位图时,会在删除区域显示黑色部分。如果不涉及OpenGL或更改SDK,就没有解决方案。我终于决定不画位图了。从上两天开始,我就一直有黑色的问题。谢谢你,伙计。它的工作原理类似于charm:)
setLayerType(LAYER\u TYPE\u HARDWARE,null)