Calculate difference of two shapes two form new shape (boolean algebra)(计算两个形状的差值两个新形状(布尔代数))
问题描述
查看提供的示例图片。我想要实现的是从卡片视图背景(或任何其他图像)中剪切出一个随机形式(在本例中为半圆形)。 这样,最后我就得到了带有裁剪形状的背景图像。最终结果将是图3,其中操作按钮周围是透明的。
备注:
- 立面与阴影不能迷失
- 我不想使用静态背景,因此它必须是真正的剪贴,而不是示例中带有背景颜色的叠加
- 用户界面必须动态调整和调整大小-裁剪也是如此
如何实现这一点?
推荐答案
此半圆裁剪可以使用MaterialShapeDrawable
和自定义ShapeAppearanceModel
来实现。此裁切与BottomAppBar
中使用的行为类似,BottomAppBar
将此半圆放入其顶边边。BottomAppBar
在其ShapeAppearanceModel
中仅对顶部使用BottomAppBarTopEdgeTreatment。您需要的是类似的行为,但不是顶部边缘侧位于底部边缘侧。
在下面的示例中,我使用了与BottomAppBarTopEdgeTreatment中相同的代码,唯一的区别是您可以使用CutoutCircleEdgeTreatment
的构造函数中使用的cutoutEndSpacingDp
将半圆移动到特定的x位置。
1.获取最新的材料设计库('com.google.android.material:material:1.4.0'
)并将其添加到您的高级应用程序中。
2.创建名为CutoutCircleEdgeTreatment
的EdgeTreatment
子类,如下所示:
public class CutoutCircleEdgeTreatment extends EdgeTreatment {
private static final int ARC_QUARTER = 90;
private static final int ARC_HALF = 180;
private static final int ANGLE_UP = 270;
private static final int ANGLE_LEFT = 180;
private static final float ROUNDED_CORNER_FAB_OFFSET = 1.75f;
private float roundedCornerRadius;
private float fabMargin;
private float fabDiameter;
private float cradleVerticalOffset;
private float horizontalOffset;
private float fabCornerSize = -1f;
private float cutoutEndSpacing = 0;
public CutoutCircleEdgeTreatment(Resources res, float fabDiameterDp, float cutoutEndSpacingDp){
fabMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, res.getDisplayMetrics());
roundedCornerRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, res.getDisplayMetrics());
cradleVerticalOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, res.getDisplayMetrics());
fabDiameter = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, fabDiameterDp, res.getDisplayMetrics());
cutoutEndSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, cutoutEndSpacingDp, res.getDisplayMetrics());
}
@Override
public void getEdgePath(float length, float center, float interpolation, @NonNull ShapePath shapePath) {
horizontalOffset = -center + ((fabMargin * 2 + fabDiameter)/2) + cutoutEndSpacing;
//below is the same code used in BottomAppBarTopEdgeTreatment getEdgePath()
if (fabDiameter == 0) {
// There is no cutout to draw.
shapePath.lineTo(length, 0);
return;
}
float cradleDiameter = fabMargin * 2 + fabDiameter;
float cradleRadius = cradleDiameter / 2f;
float roundedCornerOffset = interpolation * roundedCornerRadius;
float middle = center + horizontalOffset;
// The center offset of the cutout tweens between the vertical offset when attached, and the
// cradleRadius as it becomes detached.
float verticalOffset =
interpolation * cradleVerticalOffset + (1 - interpolation) * cradleRadius;
float verticalOffsetRatio = verticalOffset / cradleRadius;
if (verticalOffsetRatio >= 1.0f) {
// Vertical offset is so high that there's no curve to draw in the edge, i.e., the fab is
// actually above the edge so just draw a straight line.
shapePath.lineTo(length, 0);
return; // Early exit.
}
// Calculate the path of the cutout by calculating the location of two adjacent circles. One
// circle is for the rounded corner. If the rounded corner circle radius is 0 the corner will
// not be rounded. The other circle is the cutout.
// Calculate the X distance between the center of the two adjacent circles using pythagorean
// theorem.
float cornerSize = fabCornerSize * interpolation;
boolean useCircleCutout = fabCornerSize == -1 || java.lang.Math.abs(fabCornerSize * 2f - fabDiameter) < .1f;
float arcOffset = 0;
if (!useCircleCutout) {
verticalOffset = 0;
arcOffset = ROUNDED_CORNER_FAB_OFFSET;
}
float distanceBetweenCenters = cradleRadius + roundedCornerOffset;
float distanceBetweenCentersSquared = distanceBetweenCenters * distanceBetweenCenters;
float distanceY = verticalOffset + roundedCornerOffset;
float distanceX = (float) Math.sqrt(distanceBetweenCentersSquared - (distanceY * distanceY));
// Calculate the x position of the rounded corner circles.
float leftRoundedCornerCircleX = middle - distanceX;
float rightRoundedCornerCircleX = middle + distanceX;
// Calculate the arc between the center of the two circles.
float cornerRadiusArcLength = (float) Math.toDegrees(Math.atan(distanceX / distanceY));
float cutoutArcOffset = (ARC_QUARTER - cornerRadiusArcLength) + arcOffset;
// Draw the starting line up to the left rounded corner.
shapePath.lineTo(/* x= */ leftRoundedCornerCircleX, /* y= */ 0);
// Draw the arc for the left rounded corner circle. The bounding box is the area around the
// circle's center which is at `(leftRoundedCornerCircleX, roundedCornerOffset)`.
shapePath.addArc(
/* left= */ leftRoundedCornerCircleX - roundedCornerOffset,
/* top= */ 0,
/* right= */ leftRoundedCornerCircleX + roundedCornerOffset,
/* bottom= */ roundedCornerOffset * 2,
/* startAngle= */ ANGLE_UP,
/* sweepAngle= */ cornerRadiusArcLength);
if (useCircleCutout) {
// Draw the cutout circle.
shapePath.addArc(
/* left= */ middle - cradleRadius,
/* top= */ -cradleRadius - verticalOffset,
/* right= */ middle + cradleRadius,
/* bottom= */ cradleRadius - verticalOffset,
/* startAngle= */ ANGLE_LEFT - cutoutArcOffset,
/* sweepAngle= */ cutoutArcOffset * 2 - ARC_HALF);
} else {
float cutoutDiameter = fabMargin + cornerSize * 2f;
shapePath.addArc(
/* left= */ middle - cradleRadius,
/* top= */ -(cornerSize + fabMargin),
/* right= */ middle - cradleRadius + cutoutDiameter,
/* bottom= */ (fabMargin + cornerSize),
/* startAngle= */ ANGLE_LEFT - cutoutArcOffset,
/* sweepAngle= */ (cutoutArcOffset * 2 - ARC_HALF) / 2f);
shapePath.lineTo(middle + cradleRadius - (cornerSize + fabMargin / 2f), /* y= */
(cornerSize + fabMargin));
shapePath.addArc(
/* left= */ middle + cradleRadius - (cornerSize * 2f + fabMargin),
/* top= */ -(cornerSize + fabMargin),
/* right= */ middle + cradleRadius,
/* bottom= */ (fabMargin + cornerSize),
/* startAngle= */ 90,
/* sweepAngle= */ -90 + cutoutArcOffset);
}
// Draw an arc for the right rounded corner circle. The bounding box is the area around the
// circle's center which is at `(rightRoundedCornerCircleX, roundedCornerOffset)`.
shapePath.addArc(
/* left= */ rightRoundedCornerCircleX - roundedCornerOffset,
/* top= */ 0,
/* right= */ rightRoundedCornerCircleX + roundedCornerOffset,
/* bottom= */ roundedCornerOffset * 2,
/* startAngle= */ ANGLE_UP - cornerRadiusArcLength,
/* sweepAngle= */ cornerRadiusArcLength);
// Draw the ending line after the right rounded corner.
shapePath.lineTo(/* x= */ length, /* y= */ 0);
}
}
从上面的构造函数中,您可以传递fabDiameterDp
和cutoutEndSpacingDp
,fabDiameterDp
是DPS中的圆直径,cutoutEndSpacingDp
是将半圆从末端移动到特定x位置所需的末端间距。当然,您可以根据需要进行任何其他修改。
3.您可以使用上述CutoutCircleEdgeTreatment
,如下所示:
对于已有作为背景的MaterialShapeDrawable的材质组件,如MaterialCardView等:
MaterialCardView materialCardView = findViewById(R.id.materialCardView);
materialCardView.setShapeAppearanceModel(materialCardView.getShapeAppearanceModel()
.toBuilder()
.setBottomEdge(new CutoutCircleEdgeTreatment(getResources(), 80, 20))
.build());
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#81afdc"
android:padding="20dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/materialCardView"
android:layout_width="match_parent"
android:layout_height="200dp"
app:cardBackgroundColor="@android:color/white"
app:cardCornerRadius="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-35dp"
android:layout_marginEnd="35dp"
app:backgroundTint="@android:color/holo_orange_dark"
app:elevation="3dp"
app:fabCustomSize="70dp"
app:layout_constraintEnd_toEndOf="@+id/materialCardView"
app:layout_constraintTop_toBottomOf="@+id/materialCardView"
app:srcCompat="@android:drawable/ic_input_add"
app:tint="@android:color/white" />
</androidx.constraintlayout.widget.ConstraintLayout>
对于ConstraintLayout、RelativeLayout等没有MaterialShapeDrawable的非物质组件:
RelativeLayout relativeLayout = findViewById(R.id.relativeLayout);
ShapeAppearanceModel shapeAppearanceModel = new ShapeAppearanceModel()
.toBuilder()
.setBottomEdge(new CutoutCircleEdgeTreatment(getResources(), 80, 20))
.build();
MaterialShapeDrawable shapeDrawable = new MaterialShapeDrawable(shapeAppearanceModel);
shapeDrawable.setFillColor(ContextCompat.getColorStateList(this, android.R.color.white));
ViewCompat.setBackground(relativeLayout, shapeDrawable);
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#81afdc"
android:padding="20dp">
<RelativeLayout
android:id="@+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-35dp"
android:layout_marginEnd="25dp"
app:backgroundTint="@android:color/holo_orange_dark"
app:elevation="3dp"
app:fabCustomSize="70dp"
app:layout_constraintEnd_toEndOf="@+id/relativeLayout"
app:layout_constraintTop_toBottomOf="@+id/relativeLayout"
app:srcCompat="@android:drawable/ic_input_add"
app:tint="@android:color/white" />
</androidx.constraintlayout.widget.ConstraintLayout>
结果:
这篇关于计算两个形状的差值两个新形状(布尔代数)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:计算两个形状的差值两个新形状(布尔代数)
基础教程推荐
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01