如何在 Android 中创建 XML 中的 'transparent circle inside rectangle' 形状?
How to create a 'transparent circle inside rectangle' shape in XML in Android?
我正在尝试在我的应用程序中创建以下设计。
设计模型
它是主 UI 之上的叠加层。尝试使用主要 UI 顶部的布局来创建它,其背景是在 XML 中创建的半透明形状。但是,即使阅读了多篇文章,我也无法弄清楚。
我尝试了以下方法,但没有用。创建了一个具有 200dp 描边的环形并将其设置为 imageview 的源,然后将 scaletype 设置为 centerCrop 但该形状不像位图那样缩放。
形状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>
叠加布局:
<?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>
任何关于如何执行此操作或代码的指示都会非常有用。
我最近在玩类似的东西,给你改编了。
所有的魔法都发生在 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"));
}
}
这里的技巧是创建一个路径(透明圆圈),这样我们就可以将路径的绘制方法设置为"outside of the path"而不是"inside of the path"。最后,我们可以简单地将 canvas 剪切到该路径,并填充黑色。
对于您来说,您只需将 Color.BLACK
更改为您的颜色,并更改所需的半径。
编辑:
哦,只需以编程方式添加它:
FocusView view = new FocusView(context)
your_layout.addView(view)
或 XML :
<package_path_to_.FocusView
android:layout_width="match_parent"
android:layout_height="match_parent" />
EDIT2:我刚刚看到您想要这个来启动您的应用程序。
您可以考虑看看 https://github.com/iammert/MaterialIntroView 然后
您可以使用 PorterDuffXferMode 和自定义视图。
这张图片提供了不同模式的好例子(见 A Out B):AlphaCompositing
想法是创建自定义视图,上面有不透明的黑色矩形和圆圈。当您应用 PorterDuffXferMode.SRC_OUT 时,它将 "erase" 从矩形变成圆,因此您将得到您想要的结果。
在您的自定义视图中,您应该覆盖 dispatchDraw(Canvas canvas) 方法,并在您的框架上绘制生成的位图。
然后您可以将 MapView 和您的自定义视图放在 FrameLayout 中并享受结果。
我 运行 遇到这样一个问题,即代码无法在 NSimon 的 api lvl 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);
}
}
我正在尝试在我的应用程序中创建以下设计。
设计模型
它是主 UI 之上的叠加层。尝试使用主要 UI 顶部的布局来创建它,其背景是在 XML 中创建的半透明形状。但是,即使阅读了多篇文章,我也无法弄清楚。
我尝试了以下方法,但没有用。创建了一个具有 200dp 描边的环形并将其设置为 imageview 的源,然后将 scaletype 设置为 centerCrop 但该形状不像位图那样缩放。
形状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>
叠加布局:
<?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>
任何关于如何执行此操作或代码的指示都会非常有用。
我最近在玩类似的东西,给你改编了。 所有的魔法都发生在 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"));
}
}
这里的技巧是创建一个路径(透明圆圈),这样我们就可以将路径的绘制方法设置为"outside of the path"而不是"inside of the path"。最后,我们可以简单地将 canvas 剪切到该路径,并填充黑色。
对于您来说,您只需将 Color.BLACK
更改为您的颜色,并更改所需的半径。
编辑:
哦,只需以编程方式添加它:
FocusView view = new FocusView(context)
your_layout.addView(view)
或 XML :
<package_path_to_.FocusView
android:layout_width="match_parent"
android:layout_height="match_parent" />
EDIT2:我刚刚看到您想要这个来启动您的应用程序。 您可以考虑看看 https://github.com/iammert/MaterialIntroView 然后
您可以使用 PorterDuffXferMode 和自定义视图。
这张图片提供了不同模式的好例子(见 A Out B):AlphaCompositing
想法是创建自定义视图,上面有不透明的黑色矩形和圆圈。当您应用 PorterDuffXferMode.SRC_OUT 时,它将 "erase" 从矩形变成圆,因此您将得到您想要的结果。
在您的自定义视图中,您应该覆盖 dispatchDraw(Canvas canvas) 方法,并在您的框架上绘制生成的位图。
然后您可以将 MapView 和您的自定义视图放在 FrameLayout 中并享受结果。
我 运行 遇到这样一个问题,即代码无法在 NSimon 的 api lvl 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);
}
}