Сделать круглые углы сверху слева сверху справа снизу слева снизу справа через пути и RectF в Android


Сделав пользовательский ImageView и переопределив метод onDraw следующим образом, ImageView будет иметь скругленные углы. Ссылка

@Override
protected void onDraw(Canvas canvas) {
    float radius = getContext().getResources().getDimension(R.dimen.round_corner_radius);
    Path path = new Path();
    RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
    path.addRoundRect(rect, radius, radius, Path.Direction.CW);
    canvas.clipPath(path);
    super.onDraw(canvas);
}

Как я могу выборочно сделать круглые углы вместо того, чтобы сделать все четыре угла круглыми? Например, только сделать верхний левый и верхний правый углы круглыми и оставить нижние углы нетронутыми. Вот решение , которое нужно сделать с помощью растрового изображения. Я ищу, чтобы сделать это в этом методе onDraw и ТОЛЬКО используя путь и RectF.

1 3

1 ответ:

Существует перегрузка Path#addRoundRect(), которая принимает массив из восьми значений float, в котором мы можем указать радиус x и y для каждого из четырех углов. Эти значения находятся в парах [x, y], начиная с верхнего левого угла и по часовой стрелке вокруг остальных. Для тех углов, которые мы хотим округлить, мы устанавливаем оба значения пары на значение радиуса и оставляем их на нуле для тех, которые мы не делаем.

В качестве наглядного примера, простой метод, который вернет Path, который может быть использован в вашем фрагмент:

private Path getPath(float radius, boolean topLeft, boolean topRight,
                     boolean bottomRight, boolean bottomLeft) {

    final Path path = new Path();
    final float[] radii = new float[8];

    if (topLeft) {
        radii[0] = radius;
        radii[1] = radius;
    }

    if (topRight) {
        radii[2] = radius;
        radii[3] = radius;
    }

    if (bottomRight) {
        radii[4] = radius;
        radii[5] = radius;
    }

    if (bottomLeft) {
        radii[6] = radius;
        radii[7] = radius;
    }

    path.addRoundRect(new RectF(0, 0, getWidth(), getHeight()),
                      radii, Path.Direction.CW);

    return path;
}

В соответствии с вашим описанием примера, округляя верхний левый и верхний правый углы:

@Override
protected void onDraw(Canvas canvas) {
    float radius = getContext().getResources().getDimension(R.dimen.round_corner_radius);
    Path path = getPath(radius, true, true, false, false);
    canvas.clipPath(path);
    super.onDraw(canvas);
}
Как всегда, я бы рекомендовал держать метод onDraw() как можно более плотным, перемещая все, что не обязательно должно быть в другом месте. Например, значение ресурса для radius может быть извлечено в конструкторе и сохранено в поле. Кроме того, Path может быть построен только в случае необходимости, т. е. при изменении размера View или при изменении радиуса или выбранные углы меняются.

Поскольку я собрал простой обычай ImageView, чтобы проверить это, я включу его сюда, поскольку он демонстрирует вышеупомянутые пункты. Этот пользовательский View также предлагает XML-атрибуты, которые позволяют задать радиус угла и закругленные углы в макете.

public class RoundishImageView extends ImageView {

    public static final int CORNER_NONE = 0;
    public static final int CORNER_TOP_LEFT = 1;
    public static final int CORNER_TOP_RIGHT = 2;
    public static final int CORNER_BOTTOM_RIGHT = 4;
    public static final int CORNER_BOTTOM_LEFT = 8;
    public static final int CORNER_ALL = 15;

    private static final int[] CORNERS = {CORNER_TOP_LEFT,
                                          CORNER_TOP_RIGHT,
                                          CORNER_BOTTOM_RIGHT,
                                          CORNER_BOTTOM_LEFT};

    private final Path path = new Path();
    private int cornerRadius;
    private int roundedCorners;

    public RoundishImageView(Context context) {
        this(context, null);
    }

    public RoundishImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundishImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundishImageView);
        cornerRadius = a.getDimensionPixelSize(R.styleable.RoundishImageView_cornerRadius, 0);
        roundedCorners = a.getInt(R.styleable.RoundishImageView_roundedCorners, CORNER_NONE);
        a.recycle();
    }

    public void setCornerRadius(int radius) {
        if (cornerRadius != radius) {
            cornerRadius = radius;
            setPath();
            invalidate();
        }
    }

    public int getCornerRadius() {
        return cornerRadius;
    }

    public void setRoundedCorners(int corners) {
        if (roundedCorners != corners) {
            roundedCorners = corners;
            setPath();
            invalidate();
        }
    }

    public boolean isCornerRounded(int corner) {
        return (roundedCorners & corner) == corner;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (!path.isEmpty()) {
            canvas.clipPath(path);
        }

        super.onDraw(canvas);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        setPath();
    }

    private void setPath() {
        path.rewind();

        if (cornerRadius >= 1f && roundedCorners != CORNER_NONE) {
            final float[] radii = new float[8];

            for (int i = 0; i < 4; i++) {
                if (isCornerRounded(CORNERS[i])) {
                    radii[2 * i] = cornerRadius;
                    radii[2 * i + 1] = cornerRadius;
                }
            }

            path.addRoundRect(new RectF(0, 0, getWidth(), getHeight()),
                              radii, Path.Direction.CW);
        }
    }
}

Чтобы атрибуты XML работали, в вашем <resources> должно быть следующее, что вы можете сделать, поместив этот файл в папку вашего проекта res/values/, или добавив в ту, которая уже может быть там.

attrs.xml

<resources>
    <declare-styleable name="RoundishImageView">
        <attr name="cornerRadius" format="dimension" />
        <attr name="roundedCorners">
            <flag name="topLeft" value="1" />
            <flag name="topRight" value="2" />
            <flag name="bottomRight" value="4" />
            <flag name="bottomLeft" value="8" />
            <flag name="all" value="15" />
        </attr>
    </declare-styleable>
</resources>

cornerRadius является атрибутом измерения и должен быть указан как dp или px значение. roundedCorners является атрибутом флага, и несколько углов могут быть выбраны с помощью символа трубы, |. Например:

<com.mycompany.myapp.RoundishImageView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/riv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitXY"
    android:src="@drawable/magritte"
    app:cornerRadius="@dimen/round_corner_radius"
    app:roundedCorners="topLeft|topRight" />

скриншот