如何使用带有圆边的对角线渐变创建自定义绘图?

时间:2017-10-12 07:40:12

标签: android xml android-layout gradient

尝试使用xml制作自定义绘图,例如附加图像enter image description here

以下是我所做的两种方法,

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="30dp" />
<solid android:color="@color/colorAccent" />
<stroke
    android:width="2dp"
    android:color="@color/colorAccent" />
<gradient
    android:angle="135"
    android:endColor="#000"
    android:startColor="#ffff"
    android:type="linear" />
</shape>

通过这样做我可以得到正确的效果,但颜色似乎合并,我想要两种颜色没有任何合并效果,然后我尝试这样,

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white">
    <shape android:shape="oval">
        <corners android:radius="@dimen/size_30dp" />
    </shape>
</item>
<item>
    <rotate
        android:fromDegrees="35"
        android:pivotX="0%"
        android:pivotY="100%">
        <shape android:shape="rectangle">
            <solid android:color="@color/textColorGreyExtraLight" />
        </shape>
    </rotate>
</item>
</layer-list>

这种方法实际上搞砸了用户界面,我也在圆角上妥协,

所以我有什么方法可以使用XML绘制这种效果?

任何形式的帮助将不胜感激。

FYI 可绘制的宽度和高度会有所不同,因此对角线应始终从左下边缘到右上边缘。

由于

5 个答案:

答案 0 :(得分:8)

试试这个:

@media screen and (-webkit-min-device-pixel-ratio:0) {
  #your-id {
    font-size: 13px;
  }
}

you will get output like this

答案 1 :(得分:1)

当您要求提供基于XML的解决方案时,我很有兴趣构建自定义Drawable来解决您的问题。它基本上只使用自定义路径和围绕它们的边框绘制两个形状。如果为颜色添加setter或使其成为构造函数参数,您将获得应用程序主题更改时所需的灵活性。如果XML是你的方式,请随意忽略这一点,我很乐意构建它。 :)

看起来像这样:

Screenshot of custom drawable

public class CustomDrawable extends Drawable {

    private final int mBorderWidth;
    private final int mCornerRadius;

    private final Paint mBorderPaint = new Paint();
    private final Paint mTopLeftShapePaint = new Paint();
    private final Paint mBottomRightShapePaint = new Paint();

    private final RectF mBorderRect = new RectF();

    private final Path mTopLeftShapePath = new Path();
    private final Path mBottomRightShapePath = new Path();

    public CustomDrawable() {
        mBorderWidth = 8;
        mCornerRadius = 64;

        mTopLeftShapePaint.setColor(Color.parseColor("#00a2e8"));
        mTopLeftShapePaint.setStyle(Paint.Style.FILL);
        mTopLeftShapePaint.setAntiAlias(true);

        mBottomRightShapePaint.setColor(Color.parseColor("#3f48cc"));
        mBottomRightShapePaint.setStyle(Paint.Style.FILL);
        mBottomRightShapePaint.setAntiAlias(true);

        mBorderPaint.setColor(Color.parseColor("#3f48cc"));
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setStrokeWidth(mBorderWidth);
        mBorderPaint.setAntiAlias(true);
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);

        mBorderRect.set(bounds);
        mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);

        calculatePaths();
    }

    private void calculatePaths() {
        // Calculate position of the four corners
        RectF topLeftCorner = new RectF(mBorderRect.left, mBorderRect.top, mBorderRect.left + 2 * mCornerRadius, mBorderRect.top + 2 * mCornerRadius);
        RectF topRightCorner = new RectF(mBorderRect.right - 2 * mCornerRadius, mBorderRect.top, mBorderRect.right, mBorderRect.top + 2 * mCornerRadius);
        RectF bottomLeftCorner = new RectF(mBorderRect.left, mBorderRect.bottom - 2 * mCornerRadius, mBorderRect.left + 2 * mCornerRadius, mBorderRect.bottom);
        RectF bottomRightCorner = new RectF(mBorderRect.right - 2 * mCornerRadius, mBorderRect.bottom - 2 * mCornerRadius, mBorderRect.right, mBorderRect.bottom);

        // Calculate position of intersections of diagonal line and top-right / bottom-left corner
        PointF topRightCornerIntersection = calculateCircleCoordinate(topRightCorner.centerX(), topRightCorner.centerY(), 315);
        PointF bottomLeftCornerIntersection = calculateCircleCoordinate(bottomLeftCorner.centerX(), bottomLeftCorner.centerY(), 135);

        // Build top left shape
        mTopLeftShapePath.reset();
        mTopLeftShapePath.moveTo(topLeftCorner.left, topLeftCorner.centerY());
        mTopLeftShapePath.lineTo(bottomLeftCorner.left, bottomLeftCorner.centerY());
        mTopLeftShapePath.arcTo(bottomLeftCorner, -180, -45, false);
        mTopLeftShapePath.lineTo(topRightCornerIntersection.x, topRightCornerIntersection.y);
        mTopLeftShapePath.arcTo(topRightCorner, -45, -45, false);
        mTopLeftShapePath.lineTo(topLeftCorner.centerX(), topLeftCorner.top);
        mTopLeftShapePath.arcTo(topLeftCorner, -90, -90, false);

        // Build bottom right shape
        mBottomRightShapePath.reset();
        mBottomRightShapePath.moveTo(bottomLeftCorner.centerX(), bottomLeftCorner.bottom);
        mBottomRightShapePath.lineTo(bottomRightCorner.centerX(), bottomRightCorner.bottom);
        mBottomRightShapePath.arcTo(bottomRightCorner, 90, -90, false);
        mBottomRightShapePath.lineTo(topRightCorner.right, topRightCorner.centerY());
        mBottomRightShapePath.arcTo(topRightCorner, 0, -45, false);
        mBottomRightShapePath.lineTo(bottomLeftCornerIntersection.x, bottomLeftCornerIntersection.y);
        mBottomRightShapePath.arcTo(bottomLeftCorner, 135, -45, false);
    }

    private PointF calculateCircleCoordinate(float centerX, float centerY, double angdeg) {
        double angle = Math.toRadians(angdeg);
        double x = centerX + mCornerRadius * Math.cos(angle);
        double y = centerY + mCornerRadius * Math.sin(angle);
        return new PointF((float) x, (float) y);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        canvas.drawPath(mTopLeftShapePath, mTopLeftShapePaint);
        canvas.drawPath(mBottomRightShapePath, mBottomRightShapePaint);
        canvas.drawRoundRect(mBorderRect, mCornerRadius, mCornerRadius, mBorderPaint);
    }

    @Override
    public void setAlpha(int alpha) {
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
    }

    @Override
    public int getOpacity() {
        return PixelFormat.UNKNOWN;
    }
}

免责声明:此快速草案不包括边缘案例,例如:角半径对于给定的视图边界来说太大了。

答案 2 :(得分:1)

似乎,最简单的方法是以SVG格式绘制背景并直接将其用作背景,或者,如果您想以编程方式更改颜色,则可以将SVG转换为矢量绘图(例如, this工具)而不是用作背景。对于您的示例VectorDrawabletwo_colored_roundrect.xml)可以是这样的:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="550dp"
        android:height="390dp"
        android:viewportWidth="550"
        android:viewportHeight="390">

    <path
        android:fillColor="#00bfbf"
        android:strokeColor="#003f7f"
        android:strokeWidth="5"
        android:pathData="M1.50016,66.028801l0,0c0,-35.411501 28.03274,-64.118481
62.611443,-64.118481l28.463097,0l0,0l136.615303,0l256.152985,0c16.605011,0
32.529999,6.75751 44.274994,18.780581c11.744995,12.0231 18.338013,28.3319
18.338013,45.3379l0,160.295204l0,0l0,96.177002l0,0c0,35.410004
-28.03302,64.11499
-62.613007,64.11499l-256.152985,0l-136.615303,0l-28.463097,0c-34.578703,0
-62.611443,-28.704987 -62.611443,-64.11499l0,0l0,-96.177002l0,0l0,-160.295204z" />
    <path
        android:strokeColor="#003f7f"
        android:strokeWidth="5"
        android:pathData="M 19.5 368.149994 L 529.5 21.149994" />
    <path
        android:fillColor="#003f7f"
        android:strokeColor="#003f7f"
        android:strokeWidth="5"
        android:pathData="M529.617981,20.690901c11.744995,12.0231 18.338013,28.3319
18.338013,45.3379l0,160.295204l0,0l0,96.177002l0,0c0,35.410004
-28.03302,64.11499
-62.613007,64.11499l-256.152985,0l-136.615303,0l-28.463097,0c-17.289352,0
-32.942213,-7.176239 -44.272736,-18.778748l509.779114,-347.146349z" />
</vector>

和布局xml文件一样:

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

    <View
        android:layout_width="300dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:background="@drawable/two_colored_roundrect"
        />

</RelativeLayout>

View的位置和尺寸 - 仅举例),因此你应该得到这样的结果:

Two-colored round rect background

您可以更改路径描边和填充的颜色,例如this问题的答案。

答案 3 :(得分:1)

我有一个库可以做到这一点。 Basicilly我绘制了2个三角形,您可以指定左角到右角或右角到左角的方向。

Library

答案 4 :(得分:0)

所以,根据this post,现在可以使用带有矢量绘图的颜色资源。 所以你要做的是:

  1. 使用您喜欢的软件(例如Illustrator)创建所需的形状
  2. 将其导出为SVG文件
  3. 直接使用this one或Android Studio等工具将其转换为XML。
  4. 现在你有了drawable,用你想要使用的资源替换所有颜色。 (例如:android:fillColor="#0000"成为android:fillColor="@color/custom_black")。

    然后在ImageView中,而不是android:src=""使用app:srcCompat来设置drawable。

    不要忘记在您的gradle文件中添加vectorDrawables.useSupportLibrary = true下的defaultConfig

    我已经在API 19和24上进行了测试,但它确实有效。

    编辑:我正在使用支持库版本26.0.2