在Android画布上如何在图片上绘制透明圆形

64
我正在创建一个像素猎人游戏,我的活动显示一个ImageView。我想创建一个提示“展示物体的位置”。为此,我需要模糊整个图像,除了物体所在点周围的圆形区域外。而我可以显示一个半透明的黑色背景来代替模糊效果。 在Canvas上绘制半透明矩形没有问题,但是我不知道如何从中裁剪出透明的圆形。 结果应该看起来像这样:enter image description here 请帮助我在Android SDK上实现相同的结果。
11个回答

55

最终我设法完成了这个任务。

首先,我在整个视图上绘制了一个半透明黑色矩形。然后使用 PorterDuff.Mode.CLEAR 剪切透明圆以显示猫的位置。

我遇到了 PorterDuff.Mode.CLEAR 的问题:一开始我得到的是黑色圆而不是透明的。

多亏了 Romain Guy 在这里的评论:评论区,我明白了我的窗口是不透明的,我应该在另一个位图上绘制。只有在绘制完成后,再绘制在 View 的画布上。

以下是我的 onDraw 方法:

private Canvas temp;
private Paint paint;
private Paint p = new Paint();
private Paint transparentPaint;

private void init(){
    Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
    temp = new Canvas(bitmap);
    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);
}

14
由于onDraw方法可能会被重复调用,因此应避免在其中分配所有内容。参见:https://dev59.com/B2Yr5IYBdhLWcg3wn7gc - marmor
1
+1,请通过删除所有这些分配来更正代码! - joe1806772
1
哇,每次 onDraw 时分配一整个画布大小的 Bitmap,这是绝对不要做的典型例子。 - rupps
4
Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight()) 会抛出 NPE 异常,因为此时 canvas 为 null。 - Pedro Oliveira
1
魔术位图实例是什么,init()方法在哪里调用?请提供完整的工作代码。 - RevakoOA
显示剩余6条评论

44

我找到了一种无需位图绘制和创建的解决方案。以下是我的实现结果: Example of overlay drawing

你需要创建一个自定义的 FrameLayout,并使用 Clear 画笔绘制圆形:

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

同时不要忘记禁用硬件加速并调用setWillNotDraw(false),因为我们将覆盖onDraw方法。

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

完整的示例在这里:

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

提示:这个实现只是在自己内部绘制出一个洞,你需要在布局中放置背景并将这个TutorialView放在最上层。


9
在我的视图构造函数中调用setLayerType(LAYER_TYPE_HARDWARE, null)之前,我的橡皮擦轨迹都是黑色的。嗯。 - homerman
嗨@Alexandr,感谢您的回答。您能在这个问题中再帮我一下吗?我试图实现与此问题中相同的图像,但我无法像图片中显示的那样模糊图像,我的去模糊图像也不清晰。我使用了您的解决方案来实现我的要求,但仍然失败了。提前感谢您。 :) - Rucha Bhatt Joshi
1
这太棒了。 - Madona wambua
行得通!谢谢@Oleksandr - Victor Gomes

44
我通过创建自定义的LinearLayout来实现这种方式:
请查看截图:

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>

希望这能帮到您。

1
谢谢,它对我非常完美地运作。正是我在寻找的东西。 - RevakoOA
1
在API 15(4.0.4)上,我已经设置了setWillNotDraw(false),因为outerRectangle被绘制成完全透明。 - Petr Daňa
你能告诉我为什么自定义视图要继承自LinearLayout而不是View类吗? - Susheel

4

@Robert的回答实际上向我展示了如何解决这个问题,但他的代码不起作用。因此,我更新了他的解决方案并让它起作用:

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

现在可以像这样在任何布局中使用这个视图:

<?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>

我希望在SurfaceView上创建一个半透明层,并在中心创建一个透明的圆形。

P.S. 这段代码并不是最优化的,因为它在onDraw方法中创建了位图。这是因为我无法在init方法中获取父视图的宽度和高度,所以我只能在onDraw方法中获取。


p Paint 对象未被分配,它是什么? - Shruti

4

一个简单的2020 Kotlin解决方案,没有任何警告。


1. 具有孔的自定义视图的代码

class HolePosition(var x: Float, var y: Float, var r: Float)

class HoleView @JvmOverloads constructor(
    context: Context?,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
): View(context, attrs, defStyleAttr) {

    private val paint: Paint = Paint()
    private var holePaint: Paint = Paint()
    private var bitmap: Bitmap? = null
    private var layer: Canvas? = null

    //position of hole
    var holePosition: HolePosition = HolePosition(0.0f, 0.0f, 0.0f)
        set(value) {
            field = value
            //redraw
            this.invalidate()
        }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (bitmap == null) { configureBitmap() }

        //draw background
        layer?.drawRect(0.0f, 0.0f, width.toFloat(), height.toFloat(), paint)
        //draw hole
        layer?.drawCircle(holePosition.x, holePosition.y, holePosition.r, holePaint);
        //draw bitmap
        canvas.drawBitmap(bitmap!!, 0.0f, 0.0f, paint);
    }

    private fun configureBitmap() {
        //create bitmap and layer
        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        layer = Canvas(bitmap!!)
    }

    init {
        //configure background color
        val backgroundAlpha = 0.8
        paint.color = ColorUtils.setAlphaComponent(resources.getColor(R.color.mainDark, null), (255 * backgroundAlpha).toInt() )

        //configure hole color & mode
        holePaint.color = resources.getColor(android.R.color.transparent, null)
        holePaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
    }
}

2. 布局示例

<com.your_company.package.HoleView
    android:id="@+id/hole_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</com.your_company.package.HoleView>

3. 洞集使用

val holeView = findViewById<HoleView>(R.id.hole_view)
holeView.holePosition = HolePosition(x = 350.0f, y = 350.0f, r = 180.0f)

4. 结果

结果


1
我没有太多可以补充你的答案,但如果有人感兴趣,我将位图分配和所有内容都移动到onSizeChanged中,这样在性能方面更好。
在这里,您可以找到一个带有中间“孔”的FrameLayout ;)
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;

    }

}

顺便提一下,虽然这种方法比原来的答案更有效率,但在draw()方法中仍然是一个相对繁重的任务,所以如果你像我一样在动画中使用这种技术,请不要期望60.0fps的流畅效果。


1

可以使用Path对象以更高效的方式、并且用更少的代码来实现。

enter image description here

代码: 在一个视图叠加类的构造函数中:

    path.addCircle(600,1000, 200, Path.Direction.CW);
    path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
    paint.setColor(0x55_00_00_00);

(上面的x、y和半径数字应替换为您需要的任何内容)。

并覆盖视图的onDraw(Canvas)方法:

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawPath(path,paint);
}

就是这样...


1
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

如果您在具有不透明背景的视图上剪切透明圆形时遇到问题,请参阅此答案。为了使其正常工作,我在XML中将自定义布局视图设置为具有透明背景,然后使用线条绘制了我想要的布局背景颜色。

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

上面链接中的完整OnDraw方法:

@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
这对我有用:
canvas.drawCircle(x,y,radius,new Paint(Color.TRANSPARENT))

4
这并不会在已经画好的部分上再作修改,它只是画了一个看不见的圆。 - rjr-apps

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接