我正在尝试创建一个简单的自定义视图:有一个由弧路径显示的位图 - 从0deg到360deg。学位正在随着一些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。
答案 0 :(得分:28)
PorterDuff.Mode.CLEAR
不支持硬件加速。只需设置
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
完美适合我。
答案 1 :(得分:8)
在视图初始化期间使用此语句
setLayerType(LAYER_TYPE_HARDWARE, null);
答案 2 :(得分:2)
除了一件事,你的代码中的一切都还可以:因为你的窗口是不透明的,你会得到黑色背景。要获得透明结果,您应该绘制另一个位图。在你的onDraw方法中,请创建一个新的位图并完成所有工作人员。之后在画布上绘制这个位图。
有关详细信息和示例代码,请阅读this my answer:
答案 3 :(得分:1)
解决不受欢迎的PorterDuff
效果
首先使用最简单的方法,就像OP的问题一样,Path.arcTo(*, *, *, *, false)
就够了 - 注意它是arcTo
,而不是addArc
,而{{1}在添加弧之前表示false
- 不需要no forceMoveTo
。
PorterDuff
如果你真的需要PorterDuff,主要用于复杂的颜色变形,比如混合渐变,不要将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);
}
中提供的默认画布,请使用一些缓冲/ dest位图[s]与alpha通道 - 和onDraw(Canvas)
- 存储来自PorterDuff过滤的结果,最后将位图绘制到默认画布,而不应用除矩阵更改之外的任何过滤。
这是创建边框模糊圆形图像的工作示例:
setHasAlpha(true)
一些说明:
1,此视图扩展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);
}
}
而不是ImageView
,存在一些差异
2,为什么View
- drawWithLayers
或saveLayer
- 未被推荐:a,它们不确定,有时无法正常工作(显示透明为黑色),尤其是{ {1}}谁saveLayerAlpha
为空,而View
使用onDraw(Canvas)
绘制一些人; b,它们价格昂贵,它们分配ImageView.onDraw(Canvas)
来存储临时绘图结果,并且没有明确的任何资源回收机制的线索。
3,使用自己的位图[s],更适合自定义资源回收。
有些人说,在没有分配位图[s]的情况下使用PorterDuff是不可能的,因为在绘制/布局/测量之前不能确定位图的宽度,高度。登记/>
你可以使用缓冲位图[s]进行PorterDuff绘图:
首先,分配一些足够大的位图[s]
然后,用一些矩阵绘制位图[s]
然后,将位图[s]绘制成带有一些矩阵的dest位图
最后,用一些矩阵将dest位图绘制到画布中
有些人推荐使用setLayerType(View.LAYER_TYPE_SOFTWARE,null),这对我来说不是一个选项,因为它会导致在循环中调用onDraw(Canvas)。
答案 4 :(得分:0)
如果你有纯色背景,你需要做的就是将Paint颜色设置为背景颜色。例如,如果您有白色背景,则可以执行以下操作:
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);
答案 5 :(得分:0)
只需保存画布,然后还原
canvas.saveLayer(clipContainer, null, Canvas.ALL_SAVE_FLAG);
canvas.drawRoundRect(rectTopGrey, roundCorners, roundCorners, greyPaint);
canvas.drawRoundRect(rectTopGreyClip, roundCorners, roundCorners, clipPaint);
canvas.restore();
在这种情况下,第一个矩形将作为https://developer.android.com/reference/android/graphics/PorterDuff.Mode的目标,第二个矩形将作为源