计算两个形状的差值两个新形状(布尔代数) [英] Calculate difference of two shapes two form new shape (boolean algebra)

查看:51
本文介绍了计算两个形状的差值两个新形状(布尔代数)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

查看提供的示例图片。我想要实现的是从卡片视图背景(或任何其他图像)中剪切出一个随机形式(在本例中为半圆形)。

这样,最后我就得到了带有裁剪形状的背景图像。最终结果将是图3,其中操作按钮周围是透明的。

备注:

  • 立面与阴影不能迷失
  • 我不想使用静态背景,因此它必须是真正的剪贴,而不是示例中带有背景颜色的叠加
  • 用户界面必须动态调整和调整大小-裁剪也是如此

如何实现这一点?

推荐答案

此半圆裁剪可以使用MaterialShapeDrawable和自定义ShapeAppearanceModel来实现。此裁切与BottomAppBar中使用的行为类似,BottomAppBar将此半圆放入其顶边边。BottomAppBar在其ShapeAppearanceModel中仅对顶部使用BottomAppBarTopEdgeTreatment。您需要的是类似的行为,但不是顶部边缘侧位于底部边缘侧。

在下面的示例中,我使用了与BottomAppBarTopEdgeTreatment中相同的代码,唯一的区别是您可以使用CutoutCircleEdgeTreatment的构造函数中使用的cutoutEndSpacingDp将半圆移动到特定的x位置。

1.获取最新的材料设计库('com.google.android.material:material:1.4.0')并将其添加到您的高级应用程序中。

2.创建名为CutoutCircleEdgeTreatmentEdgeTreatment子类,如下所示:

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);
    }
}
从上面的构造函数中,您可以传递fabDiameterDpcutoutEndSpacingDpfabDiameterDp是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>

结果:

这篇关于计算两个形状的差值两个新形状(布尔代数)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆