How to create a 'transparent circle inside rectangle' shape in XML in Android?

后端 未结 3 783
臣服心动
臣服心动 2020-12-02 21:55

I\'m trying to create the following design in my app.

Design Mockup

Its an overlay on top of the main UI. Trying to create this using a layout on top o

相关标签:
3条回答
  • 2020-12-02 22:16

    I've been playing recently with something similar, and adapted it for you. All the magic is happening in the onDraw :

    public class FocusView extends View {
      private Paint mTransparentPaint;
      private Paint mSemiBlackPaint;
      private Path mPath = new Path();
    
      public FocusView(Context context) {
        super(context);
        initPaints();
      }
    
      public FocusView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaints();
      }
    
      public FocusView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaints();
      }
    
      private void initPaints() {
        mTransparentPaint = new Paint();
        mTransparentPaint.setColor(Color.TRANSPARENT);
        mTransparentPaint.setStrokeWidth(10);
    
        mSemiBlackPaint = new Paint();
        mSemiBlackPaint.setColor(Color.TRANSPARENT);
        mSemiBlackPaint.setStrokeWidth(10);
      }
    
      @Override
      protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    
        mPath.reset();
    
        mPath.addCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, 550, Path.Direction.CW);
        mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
    
        canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, 550, mTransparentPaint);
    
        canvas.drawPath(mPath, mSemiBlackPaint);
        canvas.clipPath(mPath);
        canvas.drawColor(Color.parseColor("#A6000000"));
      }
     }
    

    The trick here is to create a Path (the transparent circle) so that we can set the drawing method of the path to be "outside of the path" instead of "inside of the path". Finally we can simply clip the canvas to that path, and fill in the black color.

    For you, you'll just need to change Color.BLACK to your color, as well as change the desired radius.

    EDIT : Oh and simply add it programmatically : FocusView view = new FocusView(context) your_layout.addView(view)

    Or by XML :

    <package_path_to_.FocusView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

    EDIT2 : I just saw you wanted this for the onboarding of your app. You might consider having a look at https://github.com/iammert/MaterialIntroView then

    0 讨论(0)
  • 2020-12-02 22:18

    You can use PorterDuffXferMode and custom view for that.

    Good example of different modes provided at this picture (see A Out B): AlphaCompositing

    The idea is to create custom view, with opaque black rectangle and circle over it. When you apply PorterDuffXferMode.SRC_OUT, it will "erase" the circle from rectangle, so you wil have result what you want.

    In your customview you should override dispatchDraw(Canvas canvas) method, and draw resulting bitmap on your frame.

    Then you can put MapView and your custom view in FrameLayout and enjoy result.

    0 讨论(0)
  • 2020-12-02 22:18

    I ran into such a problem that code does not work on api lvl 16 from NSimon. I fixed the code and now it supports api 16+.

    public class FocusView extends View {
        private Paint mPaint;
        private Paint mStrokePaint;
        private Path mPath = new Path();
    
        public FocusView(Context context) {
            super(context);
            initPaints();
        }
    
        public FocusView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initPaints();
        }
    
        public FocusView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initPaints();
        }
    
        private void initPaints() {
            mPaint = new Paint();
            mPaint.setColor(Color.parseColor("#A6000000"));
    
            mStrokePaint = new Paint();
            mStrokePaint.setColor(Color.YELLOW);
            mStrokePaint.setStrokeWidth(2);
            mStrokePaint.setStyle(Paint.Style.STROKE);
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPath.reset();
    
            float radius = 0;
            float strokeWidth = 0;
            if (canvas.getWidth() < canvas.getHeight()) {
                radius = canvas.getWidth() / 2 - 10;
                strokeWidth = (canvas.getHeight() - canvas.getWidth())/2;
            } else {
                radius = canvas.getHeight() / 2 - 10;
                strokeWidth = (canvas.getWidth() - canvas.getHeight())/2;
            }
    
            mPaint.setStrokeWidth(strokeWidth);
    
            mPath.addCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, radius, Path.Direction.CW);
            mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
    
            canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, radius, mStrokePaint);
    
            canvas.drawPath(mPath, mPaint);
        }
    }
    
    0 讨论(0)
提交回复
热议问题