wake-up-neo.com

Android-Leinwand: Zeichne einen transparenten Kreis auf das Bild

Ich erschaffe ein Pixel-Jagdspiel. Meine Aktivität zeigt also eine ImageView. Und ich möchte einen Hinweis erstellen "Zeigen Sie mir, wo das Objekt ist". Dafür muss ich das gesamte Bild mit Ausnahme eines Kreises um einen Punkt verwischen, an dem sich das Objekt befindet. Statt Unschärfe kann ich nur einen halbtransparenten schwarzen Hintergrund anzeigen. Es gibt kein Problem, ein halbtransparentes Rechteck auf die Leinwand zu zeichnen. Das Ergebnis sollte folgendermaßen aussehen: enter image description here

Bitte helfen Sie mir, das gleiche Ergebnis mit Android SDK zu erzielen.

40
Robert

Endlich habe ich es geschafft. 

Zuerst zeichne ich ein halbtransparentes schwarzes Rechteck auf die gesamte Ansicht ..__ Danach schneide ich mit PorterDuff.Mode.CLEAR einen transparenten Kreis, um die Position der Katze anzuzeigen.

Ich hatte Probleme mit PorterDuff.Mode.CLEAR: Zuerst bekam ich einen schwarzen Kreis anstelle eines transparenten Kreises.

Dank der Kommentare von Romain Guy hier: Kommentar hier Ich habe verstanden, dass mein Fenster undurchsichtig ist, und ich sollte auf eine andere Bitmap zurückgreifen. Und erst nach dem Zeichnen auf der Leinwand von View.

Hier ist meine onDraw Methode:

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);
}
44
Robert

Auf diese Weise habe ich benutzerdefinierte LinearLayout erstellt:

Überprüfen Sie den Screenshot:

 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:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/rlParent"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">


    <ImageView
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:background="@drawable/lighthouse"
        Android:scaleType="fitXY" />

    <common.customview.CircleOverlayView
        Android:id="@+id/cicleOverlay"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

    </common.customview.CircleOverlayView>


</RelativeLayout>

colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
</resources>

dimens.xml:

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="nav_header_vertical_spacing">16dp</dimen>
    <dimen name="nav_header_height">160dp</dimen>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
    <dimen name="fab_margin">16dp</dimen>

    <dimen name="radius">50dp</dimen>
</resources>

Hoffe, das wird dir helfen.

33
Hiren Patel

Ich habe eine Lösung ohne Zeichnen und Erstellen von Bitmaps gefunden. Hier ist das Ergebnis meiner Implementierung:  Example of overlay drawing

Sie müssen ein benutzerdefiniertes FrameLayout und drawCircle mit Clear Paint erstellen:

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

Vergessen Sie nicht, die Hardwarebeschleunigung zu deaktivieren und setWillNotDraw(false) aufzurufen, da wir die onDraw-Methode überschreiben

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

Das vollständige Beispiel ist hier:

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);
        }
    }
}

PS: Diese Implementierung zieht nur ein Loch in sich, Sie müssen Hintergrund in Ihr Layout einfügen und dieses _ TutorialView oben hinzufügen.

30
Oleksandr

@ Roberts Antwort hat mir tatsächlich gezeigt, wie ich dieses Problem lösen kann, aber sein Code funktioniert nicht. Also habe ich seine Lösung aktualisiert und es funktioniert:

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);
  }
}

Und jetzt benutzen Sie diese Ansicht in einem beliebigen Layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

<SurfaceView
    Android:id="@+id/surfaceView"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="center">
</SurfaceView>

<com.example.myapp.CaptureLayerView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />

<Button
    Android:id="@+id/btnTakePicture"
    Android:layout_width="match_parent"
    Android:layout_height="80dp"
    Android:onClick="onClickPicture"
    Android:text="@string/take_picture">
</Button>

Hier wollte ich eine halbtransparente Schicht auf dem SurfaceView mit einem transparenten Kreis in der Mitte .. /. P.S. Dieser Code ist nicht optimiert, da Bitmap in der onDraw-Methode erstellt wird. Dies liegt daran, dass ich die Breite und Höhe der übergeordneten Ansicht in der init-Methode nicht ermitteln konnte. Daher konnte ich sie nur in onDraw kennen.

4
Yelnar

Ich habe nicht viel zu Ihrer Antwort hinzuzufügen, aber wenn jemand interessiert ist, habe ich die Bitmapzuweisung und alle auf onSizeChanged verschoben, damit es eine bessere Leistung darstellt. 

Hier findest du ein FrameLayout mit einem "Loch" in der Mitte;)

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;

    }

}

p.s: Obwohl es viel effizienter ist als die ursprüngliche Antwort, ist es immer noch eine relativ schwere Aufgabe in einer draw () -Methode. Wenn Sie also diese Technik in einer Animation wie mir verwenden, sollten Sie keine glatte 60,0 fps erwarten 

1
Iman Akbari

Wenn Sie Probleme haben, einen transparenten Kreisausschnitt über einer Ansicht mit undurchsichtigem Hintergrund zu erhalten, sehen Sie diese Antwort . Damit dies funktioniert, habe ich für meine benutzerdefinierte Layoutansicht einen transparenten Hintergrund in XML festgelegt und dann die gewünschte Hintergrundfarbe für das Layout mit der Linie gezeichnet

cv.drawColor(Color.BLUE); //replace with your desired background color

Die vollständige OnDraw-Methode aus der Antwort, die ich oben verlinkt habe:

@Override
protected void onDraw(Canvas canvas) {

    int w = getWidth();
    int h = getHeight();
    int radius = w > h ? h / 2 : w / 2;

    bm.eraseColor(Color.TRANSPARENT);
    cv.drawColor(Color.BLUE);
    cv.drawCircle(w / 2, h / 2, radius, eraser);
    canvas.drawBitmap(bm, 0, 0, null);
    super.onDraw(canvas);
}
0
Jacob Hatwell
public class CircleBlur extends Activity implements View.OnTouchListener {
SeekBar seekBar;
ImageView image,image1;

private Paint paint;
Bitmap circle,blurimg;

private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
private PointF start = new PointF();
private PointF mid = new PointF();
private float oldDist = 1f;
float newRot = 0f;
private float d = 0f;
private float[] lastEvent = null;
private float radius=12;
Bitmap blurbitmap;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.activity_blurimage);
    image=findViewById(R.id.image);
    seekBar=findViewById(R.id.seekbar);
    image1=findViewById(R.id.image1);

    Paint = new Paint(Paint.ANTI_ALIAS_FLAG);

    //here your image bind to Imageview
    image.setImageResource(R.drawable.nas1);

    image1.setOnTouchListener(this);
    seekBar.setProgress(12);

    seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            radius = (float) CircleBlur.this.seekBar.getProgress();
            blurbitmap=createBlurBitmap(blurimg, radius);
            CircleBlur();
        }
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }
    });

}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private Bitmap createBlurBitmap(Bitmap src, float r) {
    if (r <= 0) {
        r = 0.1f;
    } else if (r > 25) {
        r = 25.0f;
    }
    Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
    RenderScript renderScript = RenderScript.create(this);

    Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
    Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

    ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
    blur.setInput(blurInput);
    blur.setRadius(r);
    blur.forEach(blurOutput);

    blurOutput.copyTo(bitmap);
    renderScript.destroy();

    return bitmap;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public void CircleBlur()
{
    Bitmap result;
    // your circle image
    circle = BitmapFactory.decodeResource(getResources(),R.drawable.cicleouter);
        result = Bitmap.createBitmap(blurimg.getWidth(), blurimg.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas mCanvas = new Canvas(result);
        Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        Paint.setDither(true);
        mCanvas.drawBitmap(blurimg,0,0, null);
        mCanvas.drawBitmap(circle, matrix, Paint);
        Paint.setXfermode(null);
    image1.setImageBitmap(result);
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public boolean onTouch(View v, MotionEvent event) {
        image1 = (ImageView) v;
        float x = event.getX(), y = event.getY();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                start.set(x, y);
                mode = DRAG;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                d = rotation(event);
                break;
            // case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG) {
                    matrix.set(savedMatrix);
                    float dx = x - start.x;
                    float dy = y - start.y;
                    matrix.postTranslate(dx, dy);
                } else if (mode == ZOOM) {
                    float newDist = spacing(event);
                    if (newDist > 10f) {
                        matrix.set(savedMatrix);
                        float scale = (newDist / oldDist);
                        matrix.postScale(scale, scale, mid.x, mid.y);
                    }
                    if (lastEvent != null && event.getPointerCount() == 2 || event.getPointerCount() == 3) {
                        newRot = rotation(event);
                        float r = newRot - d;
                        float[] values = new float[9];
                        matrix.getValues(values);
                        float tx = values[2];
                        float ty = values[5];
                        float sx = values[0];
                        float xc = (image.getWidth() / 2) * sx;
                        float yc = (image.getHeight() / 2) * sx;
                        matrix.postRotate(r, tx + xc, ty + yc);
                    }
                }
                break;
        }
    CircleBlur();
        return true;
}
private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    float s=x * x + y * y;
    return (float)Math.sqrt(s);
}
private void midPoint(PointF point, MotionEvent event) {
    float x = event.getX(0) + event.getX(1);
    float y = event.getY(0) + event.getY(1);
    point.set(x / 2, y / 2);
}
private float rotation(MotionEvent event) {
    double delta_x = (event.getX(0) - event.getX(1));
    double delta_y = (event.getY(0) - event.getY(1));
    double radians = Math.atan2(delta_y, delta_x);
    return (float) Math.toDegrees(radians);
}

}

0
Purushotham

Das hat für mich funktioniert:

canvas.drawCircle(x,y,radius,new Paint(Color.TRANSPARENT))
0
max