凹凸轮廓上的棒棒糖高程

时间:2014-12-20 03:20:06

标签: android android-custom-view android-5.0-lollipop material-design android-elevation

我有一个自定义视图,通过使用路径显示星形。此视图按预期工作,但现在我想将其实施转移到新的Google Material建议。

不幸的是elevation取决于凸起的轮廓,我还没有找到解决方案。

您是否知道任何已知的变通方法或任何其他创造性解决方案?

enter image description here

这是我的凹路:

    double outerSize = w / 2;
    double innerSize = w / 5;
    double delta = 2.0*Math.PI/5.0;
    double rotation = Math.toRadians(-90);
    double xpos = w/2.0;
    double ypos = h/2.0;
    mPath = new Path();

    mPath.moveTo((float)(outerSize * Math.cos(delta + rotation) + xpos),
                 (float)(outerSize * Math.sin(delta + rotation) + ypos));

    for(int point= 0;point<6;point++)
    {
        mPath.lineTo((float) (innerSize * Math.cos(delta * (point + 0.5) + rotation) + xpos),
                (float) (innerSize * Math.sin(delta * (point + 0.5) + rotation) + ypos));
        mPath.lineTo((float) (outerSize * Math.cos(delta * (point + 1.0) + rotation) + xpos),
                (float) (outerSize * Math.sin(delta * (point + 1.0) + rotation) + ypos));
    }

    mPath.close();

我已经尝试过这段代码但没有成功,这在凸视图上运行良好。

@TargetApi(21)
private class StarOutline extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
        StartView r = (StartView) view;
        // i know here say setConvexPath not setConcavePath
        outline.setConvexPath(r.mPath); 
    }
}

但正如预期的那样,我得到了例外:

java.lang.IllegalArgumentException: path must be convex
        at android.graphics.Outline.setConvexPath(Outline.java:216)

知道如何实现这个目标吗?

2 个答案:

答案 0 :(得分:5)

正如一些评论和答案所指出的那样,原生android阴影仅适用于凸轮廓。

因此,您可以自己手动绘制假阴影(通过画布,位图等),也可以依靠别人的库为您绘制假阴影(Google的Material Components库等)。

您是否知道任何已知的解决方法或任何其他创造性的解决方案?

如果必须依靠本机android阴影,则可以尝试将形状分解为多个凸形并分别绘制。

这里是一个例子:

我将星形分解为1个五边形和5个三角形多边形(都具有凸轮廓线)并分别绘制。

TriangleView:

public class TriangleView extends View {
    private final Path path = new Path();
    private final Paint paint = new Paint();

    public TriangleView(Context context) {
        super(context);
        init(context, null, 0,0);
    }

    public TriangleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0,0);
    }

    public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }

    public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.argb(255, 100, 100, 255));
        setOutlineProvider(new OutlineProvider());
    }

    public void setPoints(float x1, float y1, float x2, float y2, float x3, float y3){
        path.reset();
        path.moveTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.close();
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(path, paint);
    }

    private static class OutlineProvider extends ViewOutlineProvider{
        @Override
        public void getOutline(View view, Outline outline) {
            Path path = ((TriangleView)view).path;
            outline.setConvexPath(path);
        }
    }
}

PentagonView:

public class PentagonView extends View {
    private final Path path = new Path();
    private final Paint paint = new Paint();

    public PentagonView(Context context) {
        super(context);
        init(context, null, 0,0);
    }

    public PentagonView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0,0);
    }

    public PentagonView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }

    public PentagonView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.argb(255, 150, 150, 255));
        setOutlineProvider(new OutlineProvider());
    }

    public void setPoints(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float x5, float y5){
        path.reset();
        path.moveTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.lineTo(x5, y5);
        path.close();
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(path, paint);
    }

    private static class OutlineProvider extends ViewOutlineProvider {
        @Override
        public void getOutline(View view, Outline outline) {
            Path path = ((PentagonView)view).path;
            outline.setConvexPath(path);
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="match_parent"
    tools:context=".MainActivity">

    <app.eccweizhi.concaveshadow.PentagonView
        android:id="@+id/pentagonView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp" />

    <app.eccweizhi.concaveshadow.TriangleView
        android:id="@+id/triangle1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp" />

    <app.eccweizhi.concaveshadow.TriangleView
        android:id="@+id/triangle2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp" />

    <app.eccweizhi.concaveshadow.TriangleView
        android:id="@+id/triangle3"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp" />

    <app.eccweizhi.concaveshadow.TriangleView
        android:id="@+id/triangle4"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp" />

    <app.eccweizhi.concaveshadow.TriangleView
        android:id="@+id/triangle5"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp" />

</FrameLayout>

然后我像这样使用它们

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        pentagonView.setPoints(
            520f,
            520f,
            640f,
            520f,
            677.0818f,
            634.1266f,
            580f,
            704.6608f,
            482.9182f,
            634.1266f
        )
        triangle1.setPoints(520f, 520f, 640f, 520f, 580f, 400f)
        triangle2.setPoints(640f, 520f, 677.0818f, 634.1266f, 777f, 520f)
        triangle3.setPoints(677.0818f, 634.1266f, 580f, 704.6608f, 697f, 750f)
        triangle4.setPoints(580f, 704.6608f, 482.9182f, 634.1266f, 440f, 750f)
        triangle5.setPoints(482.9182f, 634.1266f, 520f, 520f, 400f, 520f)
    }
}

答案 1 :(得分:0)

AndroidX包中有一个名为MaterialShapeDrawable的新可绘制对象。给定路径,它可以将阴影渲染为凹凸形状。

https://developer.android.com/reference/com/google/android/material/shape/MaterialShapeDrawable

这是如何在没有MaterialShapeDrawable的情况下为凹形提供阴影的方法:

  • 创建一个新的位图
  • 修改位图(使用新的Canvas对象在其上绘制星形路径)
  • 对位图进行模糊处理,使其实际上看起来像一个阴影。 (出于性能原因,应使用RenderScript进行模糊处理)
  • 在视图Canvas上绘制位图。