Высота леденца на вогнутом контуре

У меня есть пользовательский вид, который отображает форму звезды с помощью пути. Это представление работает, как и ожидалось, но теперь я хочу перенести его реализацию на новую рекомендацию Google Material.

К сожалению, elevation зависит от выпуклого контура, и я пока не нашел решения.

Есть ли какие-либо известные обходные пути или любые другие творческие решения, которые кто-либо из вас знает?

введите здесь описание изображения

Это мой вогнутый путь:

    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)

Любая идея, как достичь этой цели?


person rnrneverdies    schedule 20.12.2014    source источник
comment
Вогнутые контуры не поддерживаются. Вы можете использовать предварительно сгенерированную тень (например, PNG или растровое изображение) или использовать выпуклое приближение (например, круг).   -  person alanv    schedule 22.12.2014
comment
@alanv да, по умолчанию не поддерживаются, но невозможно ли этого добиться?   -  person rnrneverdies    schedule 22.12.2014
comment
Код фреймворка, отвечающий за создание объемных/точечных теней, не может работать с невыпуклыми контурами. Вы не сможете использовать высота/перевод Z с невыпуклым контуром. Использование нескольких выпуклых контуров даст вам перекрывающиеся тени. Как я уже упоминал, существуют обходные пути, не связанные с невыпуклым контуром или генерируемыми фреймворком тенями.   -  person alanv    schedule 22.12.2014
comment
@alanv другая возможность - нарисовать (используя onDraw) форму с тенью...   -  person User    schedule 21.07.2017


Ответы (2)


Как отмечают некоторые комментарии и ответы, собственная тень Android работает только с выпуклыми контурами.

Таким образом, вам остается либо рисовать фальшивую тень вручную самостоятельно (через холст, растровое изображение и т. д.), либо полагаться на чью-либо библиотеку, чтобы нарисовать фальшивую тень для вас (библиотека Google Material Components и т. д.).

Есть ли какие-либо известные обходные пути или любые другие творческие решения, которые кто-либо из вас знает?

Если вы должны полагаться на собственную тень Android, вы можете попытаться разбить форму на несколько выпуклых форм и нарисовать их по отдельности.

Вот пример:

Я разбил форму звезды на 1 пятиугольник и 5 треугольных многоугольников (все они имеют выпуклый контур) и нарисовал их по отдельности.

Треугольный вид:

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);
        }
    }
}

ПентагонВью:

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)
    }
}
person Weizhi    schedule 31.01.2020

В пакете AndroidX есть новый drawable под названием MaterialShapeDrawable. Учитывая путь, он может отображать тень как для вогнутых, так и для выпуклых форм.

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

Вот как вы могли бы создать тень для своей вогнутой формы БЕЗ MaterialShapeDrawable:

  • Создать новое растровое изображение
  • Измените растровое изображение (нарисуйте на нем путь в форме звезды, используя новый объект Canvas)
  • Размойте растровое изображение, чтобы оно выглядело как тень. (Размытие следует выполнять с помощью RenderScript по соображениям производительности)
  • Нарисуйте растровое изображение на представлении Canvas.
person Nezih Yılmaz    schedule 18.05.2019