为了解决不需要的PorterDuff效果,首先使用最简单的方法,就像OP的问题一样,使用Path.arcTo(*, *, *, *, false)就足够了--请注意是arcTo而不是addArc,并且false表示在添加弧之前不需要forceMoveTo--没有必要使用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,主要用于复杂的颜色变换,比如混合渐变,不要直接在
onDraw(Canvas)
提供的默认画布上使用PorterDuff过滤效果绘制颜色、形状或位图,而是使用一些带有alpha通道的缓冲/目标位图来存储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;
public class BlurredCircleImageViewShader extends ImageView {
private Canvas mCanvas;
private Paint mPaint;
private Matrix matrix;
private static final float GRADIENT_RADIUS = 600f;
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){
drawWithBitmapS(canvas);
}
@SuppressLint("WrongCall")
private void drawWithLayers(Canvas canvas){
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
float width = canvas.getWidth();
float hWidth = width / 2;
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);
}
@SuppressLint("WrongCall")
private void drawWithBitmapS(Canvas canvas){
float scale = canvas.getWidth() / (GRADIENT_RADIUS * 2);
int count = canvasDest.save();
canvasDest.scale(1/scale, 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);
canvas.drawBitmap(bitmapDest, matrix, null);
}
}
一些注意事项:
1、该视图继承自ImageView
而不是View
,有一些区别。
2、为什么不推荐使用drawWithLayers
——saveLayer
或saveLayerAlpha
——a、它们是不确定的,有时无法正确工作(将透明显示为黑色),特别是对于onDraw(Canvas)
为空的View
,而ImageView.onDraw(Canvas)
则使用一个Drawable
来绘制一些内容;b、它们很昂贵,它们分配离屏位图
来存储临时绘制结果,并且没有任何资源回收机制的明确线索。
3、使用自己的位图更好地实现自定义资源回收。
有些人说,如果不为每次绘制分配位图,则无法使用PorterDuff,因为在绘制/布局/测量之前无法确定位图的宽度和高度。
您可以使用缓冲位图进行PorterDuff绘制:
首先,分配一些足够大的位图。
然后,使用某些矩阵在位图上绘制。
接下来,使用某些矩阵将位图绘制到目标位图中。
最后,使用某些矩阵将目标位图绘制到画布中。
有些人建议设置LayerType(View.LAYER_TYPE_SOFTWARE, null),但这对我不是一个选项,因为它会导致onDraw(Canvas)被循环调用。
结果图像
源图像