Lienzo de Android: dibuja un círculo transparente en la imagen

Estoy creando un juego de búsqueda de píxeles. Entonces mi actividad muestra un ImageView. Y quiero crear una pista “muéstrame dónde está el objeto”. Para esto necesito desenfocar toda la imagen excepto un círculo alrededor de un punto donde se encuentra el objeto. En lugar de desenfoque, puedo mostrar un fondo negro semitransparente. No hay problema para dibujar un rectángulo semitransparente en el canvas. Pero no sé cómo cortar un círculo transparente de él. El resultado debería verse así: enter image description here

Ayúdame a lograr el mismo resultado en Android SDK.

Así que finalmente logré hacer esto.

Primero dibujo un rectángulo negro semitransparente en toda la vista. Después de eso, usando PorterDuff.Mode.CLEAR corté un círculo transparente para mostrar la posición del gato.

Tuve un problema con PorterDuff.Mode.CLEAR : primero obtuve un círculo negro en lugar de uno transparente.

Gracias a los comentarios de Romain Guy aquí: comente aquí Entendí que mi ventana es opaca y debería dibujar en otro bitmap. Y solo después de dibujar en el canvas de View .

Aquí está mi método onDraw :

 private Canvas temp; private Paint paint; private Paint p = new Paint(); private Paint transparentPaint; private void init(){ temp = new Canvas(bitmap); Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888); paint = new Paint(); paint.setColor(0xcc000000); transparentPaint = new Paint(); transparentPaint.setColor(getResources().getColor(android.R.color.transparent)); transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); } protected void onDraw(Canvas canvas) { temp.drawRect(0, 0, temp.getWidth(), temp.getHeight(), paint); temp.drawCircle(catPosition.x + radius / 2, catPosition.y + radius / 2, radius, transparentPaint); canvas.drawBitmap(bitmap, 0, 0, p); } 

Lo hice de esta manera creando LinearLayout personalizado:

Verifique la captura de pantalla :

enter image description here

CircleOverlayView.java

 import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.os.Build; import android.util.AttributeSet; import android.widget.LinearLayout; /** * Created by hiren on 10/01/16. */ public class CircleOverlayView extends LinearLayout { private Bitmap bitmap; public CircleOverlayView(Context context) { super(context); } public CircleOverlayView(Context context, AttributeSet attrs) { super(context, attrs); } public CircleOverlayView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CircleOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (bitmap == null) { createWindowFrame(); } canvas.drawBitmap(bitmap, 0, 0, null); } protected void createWindowFrame() { bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas osCanvas = new Canvas(bitmap); RectF outerRectangle = new RectF(0, 0, getWidth(), getHeight()); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(getResources().getColor(R.color.colorPrimary)); paint.setAlpha(99); osCanvas.drawRect(outerRectangle, paint); paint.setColor(Color.TRANSPARENT); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); float centerX = getWidth() / 2; float centerY = getHeight() / 2; float radius = getResources().getDimensionPixelSize(R.dimen.radius); osCanvas.drawCircle(centerX, centerY, radius, paint); } @Override public boolean isInEditMode() { return true; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); bitmap = null; } } 

CircleDrawActivity.java :

 public class CircleDrawActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_circle_draw); } } 

activity_circle_draw.xml :

       

colors.xml :

   #3F51B5 #303F9F #FF4081  

dimens.xml :

   16dp 160dp  16dp 16dp 16dp 50dp  

Espero que esto te ayudará.

Encontré una solución sin dibujar y crear mapas de bits. Este es el resultado de mi implementación: Ejemplo de dibujo superpuesto

Necesita crear un FrameLayout personalizado y drawCircle con Clear paint:

  mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 

Además, no olvide desactivar la aceleración de hardware y llamar a setWillNotDraw(false) porque onDraw método onDraw

  setWillNotDraw(false); setLayerType(LAYER_TYPE_HARDWARE, null); 

El ejemplo completo está aquí:

 public class TutorialView extends FrameLayout { private static final float RADIUS = 200; private Paint mBackgroundPaint; private float mCx = -1; private float mCy = -1; private int mTutorialColor = Color.parseColor("#D20E0F02"); public TutorialView(Context context) { super(context); init(); } public TutorialView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TutorialView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public TutorialView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { setWillNotDraw(false); setLayerType(LAYER_TYPE_HARDWARE, null); mBackgroundPaint = new Paint(); mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); } @Override public boolean onTouchEvent(MotionEvent event) { mCx = event.getX(); mCy = event.getY(); invalidate(); return true; } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(mTutorialColor); if (mCx >= 0 && mCy >= 0) { canvas.drawCircle(mCx, mCy, RADIUS, mBackgroundPaint); } } } 

PD: Esta implementación simplemente dibuja un agujero en su interior, necesita poner fondo en su diseño y agregar este TutorialView en la parte superior.

La respuesta de @Robert realmente me mostró cómo resolver este problema, pero su código no funciona. Así que actualicé su solución y la hice funcionar:

 public class CaptureLayerView extends View { private Bitmap bitmap; private Canvas cnvs; private Paint p = new Paint(); private Paint transparentPaint = new Paint();; private Paint semiTransparentPaint = new Paint();; private int parentWidth; private int parentHeight; private int radius = 100; public CaptureLayerView(Context context) { super(context); init(); } public CaptureLayerView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { transparentPaint.setColor(getResources().getColor(android.R.color.transparent)); transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); semiTransparentPaint.setColor(getResources().getColor(R.color.colorAccent)); semiTransparentPaint.setAlpha(70); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); bitmap = Bitmap.createBitmap(parentWidth, parentHeight, Bitmap.Config.ARGB_8888); cnvs = new Canvas(bitmap); cnvs.drawRect(0, 0, cnvs.getWidth(), cnvs.getHeight(), semiTransparentPaint); cnvs.drawCircle(parentWidth / 2, parentHeight / 2, radius, transparentPaint); canvas.drawBitmap(bitmap, 0, 0, p); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { parentWidth = MeasureSpec.getSize(widthMeasureSpec); parentHeight = MeasureSpec.getSize(heightMeasureSpec); this.setMeasuredDimension(parentWidth, parentHeight); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } 

Y ahora usa esta vista en cualquier diseño como este:

       

Aquí quería una capa semi transparente en el SurfaceView con un círculo transparente en el centro. PD: Este código no está optimizado porque crea Bitmap en el método onDraw, es porque no pude obtener el ancho y alto de la vista padre en el método init, así que solo pude conocerlos en onDraw.

No tengo mucho que agregar a su respuesta, pero si a alguien le interesa, moví la asignación de bitmap y todo a onSizeChanged, por lo que es mejor para el rendimiento.

Aquí puede encontrar un FrameLayout con un “Hoyo” en el medio;

 import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.widget.FrameLayout; /** * Created by blackvvine on 1/1/16. */ public class SteroidFrameLayout extends FrameLayout { private Paint transPaint; private Paint defaultPaint; private Bitmap bitmap; private Canvas temp; public SteroidFrameLayout(Context context) { super(context); __init__(); } public SteroidFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); __init__(); } public SteroidFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); __init__(); } private void __init__() { transPaint = new Paint(); defaultPaint = new Paint(); transPaint.setColor(Color.TRANSPARENT); transPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); setWillNotDraw(false); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); temp = new Canvas(bitmap); } @Override protected void dispatchDraw(Canvas canvas) { temp.drawColor(Color.TRANSPARENT); super.dispatchDraw(temp); temp.drawCircle(cx, cy, getWidth()/4, transPaint); canvas.drawBitmap(bitmap, 0, 0, defaultPaint); if (p < 1) invalidate(); else animRunning = false; } } 

PD: aunque es mucho más eficiente que la respuesta original, todavía es una tarea relativamente pesada en un método draw (), por lo que si estás utilizando esta técnica en una animación como yo, no esperes un 60.0fps liso

Esto funcionó para mí:

 canvas.drawCircle(x,y,radius,new Paint(Color.TRANSPARENT)) 
    Intereting Posts