简体   繁体   English

如何在Android中用XML创建“矩形内部透明圆”形状?

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

I'm trying to create the following design in my app. 我正在尝试在我的应用中创建以下设计。

Design Mockup 设计样机
(https://photos.google.com/share/AF1QipPhCGTgf9zi7MetWJYm0NQ14c3wqzjqnEwxsajCFHEjqzt5R29qYvIjZ2C71q7EnQ?key=WUZKSXA1WVVwSlI2LVdTQy1IRjdUdzVuQlpCY0Rn)

Its an overlay on top of the main UI. 它是主UI上面的叠加层。 Trying to create this using a layout on top of the main UI with its background as a translucent shape created in XML. 尝试使用主UI顶部的布局创建它,其背景为以XML创建的半透明形状。 However, even after reading multiple posts, I'm not able to figure it out. 然而,即使阅读了多篇文章,我也无法弄明白。

I tried the following approach, but it didn't work. 我尝试了以下方法,但它没有用。 Created a ring shape with 200dp stroke and set it as source for a imageview and then set the scaletype to centerCrop but the shape does not scale as a bitmap does. 创建一个200dp笔划的环形,并将其设置为imageview的源,然后将scaletype设置为centerCrop,但形状不像位图那样缩放。

Shape XML : 形状XML:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:innerRadius="0dp"
    android:shape="ring"
    android:thicknessRatio="2"
    android:useLevel="false" >

    <solid android:color="@android:color/transparent" />

    <stroke
        android:width="200dp"
        android:color="#80000000" />
</shape>

Overlay layout : 叠加布局:

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

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/onboarding_background"
        android:scaleType="centerCrop"/>

</RelativeLayout>

Any pointers on how to do this or code would be really helpful. 有关如何执行此操作或代码的任何指示都非常有用。

I've been playing recently with something similar, and adapted it for you. 我最近一直在玩类似的东西,并为你量身定做。 All the magic is happening in the onDraw : 所有的魔力都发生在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". 这里的技巧是创建一个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. 对于您来说,您只需要将Color.BLACK更改为您的颜色,以及更改所需的半径。

EDIT : Oh and simply add it programmatically : FocusView view = new FocusView(context) your_layout.addView(view) 编辑:哦,只需以编程方式添加它: FocusView view = new FocusView(context) your_layout.addView(view) FocusView view = new FocusView(context) your_layout.addView(view)

Or by XML : 或者通过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. 编辑2:我刚刚看到你想要这个用于你的应用程序的入职。 You might consider having a look at https://github.com/iammert/MaterialIntroView then 您可以考虑查看https://github.com/iammert/MaterialIntroView

You can use PorterDuffXferMode and custom view for that. 您可以使用PorterDuffXferMode和自定义视图。

Good example of different modes provided at this picture (see A Out B): AlphaCompositing 这张照片提供了不同模式的好例子(参见A出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. 当您应用PorterDuffXferMode.SRC_OUT时,它将从矩形“擦除”圆圈,因此您将得到您想要的结果。

In your customview you should override dispatchDraw(Canvas canvas) method, and draw resulting bitmap on your frame. 在自定义视图中,您应该覆盖dispatchDraw(Canvas canvas)方法,并在帧上绘制结果位图。

Then you can put MapView and your custom view in FrameLayout and enjoy result. 然后,您可以将MapView和自定义视图放在FrameLayout中并享受结果。

I ran into such a problem that code does not work on api lvl 16 from NSimon. 我遇到了这样一个问题,即代码无法在NSimon的api lvl 16上运行。 I fixed the code and now it supports api 16+. 我修复了代码,现在它支持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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM