Ответ 1
PorterDuff.Mode.CLEAR
не работает с аппаратным ускорением. Просто установите
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Отлично работает для меня
Я пытаюсь создать пользовательский вид, который работает просто: есть растровое изображение, которое обнаруживается путем дуги - от 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
Теперь все рисует отлично, но... фон BLACK в целом View.
Если я устанавливаю canvas.drawColor(..., PorterDuff.Mode.DST)
и опускаю canvas.drawBitmap()
- дуга рисуется правильно на прозрачном фоне.
Мой вопрос: как установить PorterDuff
режимы, чтобы он работал с прозрачностью?
Конечно, bitmap
- это 32-разрядный PNG с альфа-каналом.
PorterDuff.Mode.CLEAR
не работает с аппаратным ускорением. Просто установите
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Отлично работает для меня
Используйте этот оператор во время инициализации представления
setLayerType(LAYER_TYPE_HARDWARE, null);
Все в порядке, кроме одного: вы получаете черный фон, потому что ваше окно непрозрачно. Чтобы добиться прозрачного результата, вы должны использовать другое растровое изображение. В методе onDraw, пожалуйста, создайте новое растровое изображение и сделайте все на нем. После этого нарисуйте это растровое изображение на вашем холсте.
Подробнее и пример кода читайте этот мой ответ:
для устранения нежелательного эффекта PorterDuff
сначала используйте простейший метод, как проблема OP, a Path.arcTo(*, *, *, *, false)
достаточно - обратите внимание на arcTo
, а не addArc
, а false
означает no 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, в основном для сложного морфинга цвета, например, для градиентов смешивания, не рисуйте цвет или форму или растровое изображение с эффектом фильтрации PorterDuff непосредственно на холст по умолчанию, указанный в onDraw(Canvas)
, используйте некоторую буферизацию /dest bitmap [ s] с альфа-каналом - и 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, этот вид расширяет ImageView
not View
, есть некоторые отличия.
2, почему drawWithLayers
- saveLayer
или saveLayerAlpha
- не рекомендуется: a, они неопределенные, иногда не работают правильно (показывают прозрачность как черный), особенно для View
whoes onDraw(Canvas)
пуст, а ImageView.onDraw(Canvas)
использовал a Drawable
для рисования некоторых; b, они дороги, они выделяют off-screen bitmap
для хранения временного результата рисования, и нет четких подсказок любого механизма утилизации ресурсов.
3, используя ваш собственный растровый рисунок [s], лучше для индивидуальной утилизации ресурсов.
Некоторые люди сказали, что невозможно использовать PorterDuff без растрового изображения выделения [s] для каждого чертежа, потому что ширина и высота растрового изображения не могут быть определены до рисования/макета/меры.
вы можете использовать буферизацию растрового изображения [s] для рисунка PorterDuff:
сначала выделите достаточно большой битмап [s].
затем нарисуйте растровое изображение с некоторой матрицей.
и, нарисуйте растровое изображение [s] в растровое изображение с некоторой матрицей.
наконец, нарисуем растровое изображение dest в холст с помощью некоторой матрицы.
некоторые люди рекомендуют setLayerType (View.LAYER_TYPE_SOFTWARE, null), что для меня не является вариантом, потому что это вызовет вызов onDraw (Canvas) в цикле.
Если у вас есть сплошной цветной фон, все, что вам нужно сделать, это установить цвет краски на цвет фона. Например, если у вас есть белый фон, вы можете сделать:
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);
просто сохранить холст и восстановить после
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, а второй прямоугольник - источником.