android - significado - tutomarte
Dibujo sobre lienzo-PorterDuff.Mode.CLEAR dibuja negro! ¿Por qué? (6)
Estoy tratando de crear una vista personalizada que funciona de manera simple: hay un mapa de bits que se revela mediante la ruta del arco, desde 0deg hasta 360deg. Los grados están cambiando con algunos FPS.
Así que hice una vista personalizada con el método onDraw()
invalidado:
@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
se inicializa de la siguiente manera:
arcPaint = new Paint();
arcPaint.setAntiAlias(true);
arcPaint.setColor(Color.RED); // Color doesn''t matter
Ahora, todo se ve genial, pero ... el fondo es NEGRO en toda la vista.
Si configuro canvas.drawColor(..., PorterDuff.Mode.DST)
y canvas.drawBitmap()
, el arco se dibuja correctamente en un fondo transparente.
Mi pregunta es: ¿cómo configurar los modos PorterDuff
para que funcionen con transparencia?
Por supuesto, el bitmap
es PNG de 32 bits con canal alfa.
Si tiene un fondo de color sólido, todo lo que necesita hacer es establecer el color de pintura en el color de fondo. Por ejemplo, si tienes un fondo blanco puedes hacer:
paint.setColor(Color.WHITE);
Sin embargo, si necesita borrar una línea con un fondo transparente, intente esto: para dibujar con un color transparente, debe usar Paint setXfermode, que solo funcionará si establece un mapa de bits en su lienzo. Si sigue los pasos a continuación, debe obtener el resultado deseado.
Crea un lienzo y establece su mapa de bits.
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);
}
Ahora deberías poder dibujar con un color transparente usando:
mCanvas.drawPath(path, mPaint);
Todo está bien en tu código, excepto una cosa: obtienes un fondo negro porque tu ventana es opaca. Para lograr un resultado transparente debe dibujar en otro mapa de bits. En su método onDraw, cree un nuevo mapa de bits y haga todo el personal en él. Después de eso dibuja este mapa de bits en tu lienzo.
Para detalles y código de muestra por favor lea esto mi respuesta :
Utilice esta declaración durante la inicialización de la vista
setLayerType(LAYER_TYPE_HARDWARE, null);
para resolver el efecto PorterDuff
no deseado
use el método más simple al principio, como el problema del OP, un Path.arcTo(*, *, *, *, false)
es suficiente - tenga en cuenta que es arcTo
, no addArc
, y que false
significa que no forceMoveTo
antes de agregar arco - existe No hay necesidad de 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);
}
Si realmente necesita PorterDuff, principalmente para la transformación compleja de colores, como la combinación de degradados, no dibuje colores, formas o mapas de bits con el efecto de filtrado de PorterDuff directamente en el lienzo predeterminado proporcionado en onDraw(Canvas)
, use algunos mapas de bits de almacenamiento intermedio / destino. con canal alfa - y setHasAlpha(true)
- para almacenar el resultado del filtrado de PorterDuff, al final dibuje el mapa de bits al lienzo predeterminado sin aplicar ningún filtrado, excepto el cambio de matriz.
Aquí hay un ejemplo de trabajo para crear un borde borroso alrededor de la imagen:
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);
}
}
Algunas notas: 1, esta vista amplía ImageView
no View
, hay algunas diferencias.
2, por qué no se drawWithLayers
saveLayer
drawWithLayers
- saveLayer
o saveLayerAlpha
: a, son inciertos, a veces no funcionan bien (se muestra transparente como negro), especialmente para View
quién está en onDraw(Canvas)
está vacío, mientras que ImageView.onDraw(Canvas)
utiliza un Drawable
para dibujar algunos; b, son caros, asignan off-screen bitmap
de off-screen bitmap
para almacenar el resultado del dibujo temporal y no hay pistas claras de ningún mecanismo de reciclaje de recursos.
3, usar su propio mapa de bits [s], es mejor para el reciclaje de recursos personalizado.
Algunas personas dijeron que es imposible usar PorterDuff sin asignación de mapa de bits [s] en cada dibujo, ya que el ancho del mapa de bits, la altura no se puede determinar antes del dibujo / diseño / medida.
PUEDE usar un mapa de bits de búfer [s] para el dibujo PorterDuff:
al principio, asigne algunos mapas de bits lo suficientemente grandes [s].
luego, dibuja en el mapa de bits [s] con alguna matriz.
y, dibujar el mapa de bits [s] en un mapa de bits de destino con alguna matriz.
por fin, dibuje el mapa de bits de dest en lienzo con alguna matriz.
algunas personas recomiendan setLayerType (View.LAYER_TYPE_SOFTWARE, null), que no es una opción para mí, porque hará que se llame aDraw (Canvas) en un bucle.
simplemente guarda el lienzo y restaura después
canvas.saveLayer(clipContainer, null, Canvas.ALL_SAVE_FLAG);
canvas.drawRoundRect(rectTopGrey, roundCorners, roundCorners, greyPaint);
canvas.drawRoundRect(rectTopGreyClip, roundCorners, roundCorners, clipPaint);
canvas.restore();
en este caso, el primer rectángulo será el destino de https://developer.android.com/reference/android/graphics/PorterDuff.Mode y el segundo rectángulo como fuente
PorterDuff.Mode.CLEAR
no funciona con la aceleración de hardware. Acaba de establecer
view.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
Funciona perfectamente para mi.