【问题标题】:Android canvas: draw transparent circle on imageAndroid画布:在图像上绘制透明圆圈
【发布时间】:2013-11-25 16:52:46
【问题描述】:

我正在创建一个像素狩猎游戏。所以我的活动显示了一个 ImageView。我想创建一个提示“告诉我对象在哪里”。为此,我需要模糊整个图像,除了围绕对象所在点的圆圈。我可以显示一个半透明的黑色背景,而不是模糊。 在 Canvas 上绘制一个半透明的矩形是没有问题的。 但我不知道如何从中裁剪出一个透明的圆圈。 结果应如下所示:

请帮助我在 Android SDK 上实现相同的结果。

【问题讨论】:

    标签: android image android-canvas


    【解决方案1】:

    一个简单的 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.结果

    【讨论】:

      【解决方案2】:

      没有位图分配的 Kotlin 版本。但这里绘制的不是圆形,而是圆角矩形。我相信你可以自己调整它来循环。

          private val scanAreaBorder = Paint().apply {
          color = ContextCompat.getColor(context, R.color.postyellow)
          style = Paint.Style.STROKE
          strokeWidth = dpToPx(context, resources.getDimension(R.dimen.scan_area_border_stroke_width).toInt())
      }
      
      private val scanArea = Paint().apply {
          color = ContextCompat.getColor(context, android.R.color.transparent)
          xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
      }
      
      private val background = Paint().apply {
          color = ContextCompat.getColor(context, R.color.scanner_area)
      }
      
       init {
          setLayerType(LAYER_TYPE_HARDWARE, null)
      }
      
          override fun onDraw(canvas: Canvas) {
          super.onDraw(canvas)
          canvas.run {
              drawRect(0f, 0f, width.toFloat(), height.toFloat(), background)
              val top = (height / 2f) - dpToPx(context, resources.getDimension(R.dimen.scan_area_height).toInt())
              val bottom = (height / 2f) + dpToPx(context, resources.getDimension(R.dimen.scan_area_height).toInt())
              val margin = dpToPx(context, resources.getDimension(R.dimen.scan_area_margin).toInt())
              val cornerRadius = dpToPx(context, resources.getDimension(R.dimen.scan_area_corner_radius).toInt())
              drawRoundRect(margin, top, width.toFloat() - margin, bottom, cornerRadius, cornerRadius, scanArea)
              drawRoundRect(margin, top, width.toFloat() - margin, bottom, cornerRadius, cornerRadius, scanAreaBorder)
          }
      }
      

      【讨论】:

        【解决方案3】:

        所以最后我设法做到了。

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

        PorterDuff.Mode.CLEAR 有问题:首先我得到的是黑色圆圈而不是透明圆圈。

        感谢 Romain Guy 的 cmets:comment here 我知道我的窗口是不透明的,我应该在另一个位图上绘制。只有在 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);
        }
        

        【讨论】:

        • 您应该避免在 onDraw 中进行所有这些分配,因为该方法可能会被重复调用。见:stackoverflow.com/questions/13528550/…
        • +1,请通过删除所有这些分配来更正代码!
        • 哇,为每个 onDraw 分配一个与整个画布大小相同的位图,这是不该做的完美示例。
        • Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight() 将抛出 NPE,因为此时 canvas 为空。
        • 什么是魔法位图实例,方法 init() 在哪里调用?请提供完整的工作代码。
        【解决方案4】:
        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);
        }
        

        }

        【讨论】:

          【解决方案5】:

          如果您在使用不透明背景的视图上获得透明圆形切口时遇到问题,see this answer。为了让它工作,我将我的自定义布局视图设置为在 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);
          }
          

          【讨论】:

            【解决方案6】:

            我找到了一个没有位图绘制和创建的解决方案。这是我的实施结果:

            您需要使用Clear 绘制自定义FrameLayoutdrawCircle

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

            PS:这个实现只是在自己内部画了一个洞,你需要在你的布局中放置背景并在顶部添加这个TutorialView

            【讨论】:

            • 我的橡皮擦痕迹全是黑色的,直到我在视图的构造函数中设置了setLayerType(LAYER_TYPE_HARDWARE, null)。嗯。
            • Hii @Alexandr 谢谢你的回答。你能在this 问题中帮助我更多吗,我试图获得与这个问题相同的图像,但我无法模糊图像,如图所示在图片中,我的模糊图像也不清楚..我使用你的解决方案来满足我的要求,但仍然失败,提前谢谢你。 :)
            • 这太棒了。
            • 为我工作!!谢谢@Oleksandr
            【解决方案7】:

            @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 上有一个半透明层,中间有一个透明圆圈。 PS这段代码没有优化,因为它在onDraw方法中创建了Bitmap,这是因为我无法在init方法中获取父视图的宽度和高度,所以我只能在onDraw中知道它们。

            【讨论】:

            • Paint的p对象没有赋值,是什么?
            【解决方案8】:

            我通过创建自定义 LinearLayout 来做到这一点:

            查看屏幕截图

            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>
            

            希望这会对你有所帮助。

            【讨论】:

            • 谢谢,它非常适合我。正是我想要的。
            • 在 API 15 (4.0.4) 上我设置了 setWillNotDraw(false) 因为 outerRectangle 被绘制为全透明。
            • 你能告诉我为什么自定义视图是从 linearalyout 扩展而来的,而不仅仅是视图类吗?
            【解决方案9】:

            这对我有用:

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

            【讨论】:

            • 这不会在已经绘制的东西上画一个洞,它只是画一个不可见的圆圈。
            【解决方案10】:

            我对您的回答没有什么要补充的,但是如果有人感兴趣,我将位图分配和所有内容都移到了 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;
            
                }
            
            }
            

            ps:虽然它比原来的答案效率更高,但在 draw() 方法中仍然是一项相对繁重的任务,所以如果你在像我这样的动画中使用这种技术,不要指望 60.0fps平滑一个

            【讨论】:

              猜你喜欢
              • 2014-01-02
              • 1970-01-01
              • 2012-02-19
              • 2022-01-26
              • 1970-01-01
              • 2013-03-13
              • 1970-01-01
              • 1970-01-01
              • 2015-10-20
              相关资源
              最近更新 更多