Как создать частично округленные углы-прямоугольники с возможностью центровки и без создания нового растрового изображения?

Фон

Я уже видел, как создать вытачиваемый из кругового кружка, а также как добавить вокруг него контур (AKA), здесь.

Проблема

Я не могу узнать, как выполнить аналогичную задачу для округления только некоторых из углов растрового изображения внутри рисованного, без создания нового растрового изображения, и по-прежнему делать это для централизованного ImageView.

Что я нашел

Это то, что я нашел, но он создает новое растровое изображение и при использовании его в imageView с центрированием (источник здесь):

/**
 * Create rounded corner bitmap from original bitmap.
 *
 * @param input                               Original bitmap.
 * @param cornerRadius                        Corner radius in pixel.
 * @param squareTL,squareTR,squareBL,squareBR where to use square corners instead of rounded ones.
 */
public static Bitmap getRoundedCornerBitmap(final Bitmap input, final float cornerRadius, final int w, final int h,
                                            final boolean squareTL, final boolean squareTR, final boolean squareBL, final boolean squareBR) {
    final Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
    final Canvas canvas = new Canvas(output);
    final int color = 0xff424242;
    final Rect rect = new Rect(0, 0, w, h);
    final RectF rectF = new RectF(rect);
    // make sure that our rounded corner is scaled appropriately
    Paint paint = new Paint();
    paint.setXfermode(null);
    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
    // draw rectangles over the corners we want to be square
    if (squareTL) 
        canvas.drawRect(0, 0, w / 2, h / 2, paint);
    if (squareTR) 
        canvas.drawRect(w / 2, 0, w, h / 2, paint);
    if (squareBL) 
        canvas.drawRect(0, h / 2, w / 2, h, paint);
    if (squareBR) 
        canvas.drawRect(w / 2, h / 2, w, h, paint);
    paint.setXfermode(PORTER_DUFF_XFERMODE_SRC_IN);
    canvas.drawBitmap(input, 0, 0, paint);
    return output;
}

И вот что я нашел для создания округлых углов, которые можно использовать во всех углах:

public static class RoundedCornersDrawable extends Drawable {
    private final float mCornerRadius;
    private final RectF mRect = new RectF();
    private final BitmapShader mBitmapShader;
    private final Paint mPaint;

    public RoundedCornersDrawable(final Bitmap bitmap, final float cornerRadius) {
        mCornerRadius = cornerRadius;
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(false);
        mPaint.setShader(mBitmapShader);
        mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
    }

    @Override
    protected void onBoundsChange(final Rect bounds) {
        super.onBoundsChange(bounds);
        mRect.set(0, 0, bounds.width(), bounds.height());
    }

    @Override
    public void draw(final Canvas canvas) {
        canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
    }

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

    @Override
    public void setAlpha(final int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(final ColorFilter cf) {
        mPaint.setColorFilter(cf);
    }
}

Но это решение работает только хорошо, если изображениеView отображает контент, сохраняя при этом тот же формат изображения, что и растровое изображение, и также имеет свой размер, предварительно определенный.

Вопрос

Как создать вырезаемый с помощью центра, который показывает растровое изображение, имеет закругленные углы для определенных углов, а также может отображать контур/ход вокруг него?

Я хочу сделать это без создания нового растрового изображения или расширения ImageView. Используйте только drawable, который имеет растровое изображение в качестве входа.

Ответ 1

Способ SMART - использовать режим смешивания PorterDuff. Это действительно простой и гладкий способ создания любого причудливого затенения, смешивания, эффекта "урожая". вы можете найти много хорошего учебника о PorterDuff. здесь хороший.

Ответ 2

Я всегда использую эту библиотеку для достижения того, что вы ищете. вы можете обойти любой желаемый угол, а также добавить штрих.

https://github.com/vinc3m1/RoundedImageView

Вы можете использовать его или просмотреть исходные коды только для вдохновения.

ИЗМЕНИТЬ

нет необходимости использовать Image View и сделать растровое изображение или вытащить себя и показать его в Image View. Просто замените Image View на Rounded Image View, и он будет обрабатывать все для вас без дополнительной работы в коде! Вот пример:

<com.makeramen.roundedimageview.RoundedImageView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/imageView1"
    android:scaleType="centerCrop"
    app:riv_corner_radius="8dp"
    app:riv_border_width="2dp"
    app:riv_border_color="#333333"
    app:riv_oval="false" />

В коде просто передайте ему какой-либо ресурс изображения или используйте с ним любой Image Loader.

RoundedImageView myRoundedImage=(RoundedImageView)findViewById(R.id.imageView1);
myRoundedImage.setImageResource(R.drawable.MY_DRAWABLE);
// OR
ImageLoader.getInstance().displayImage(YOUR_IMAGE_URL, myRoundedImage);

если вы хотите просто сделать определенные углы округленными, попробуйте следующее:

 <com.makeramen.roundedimageview.RoundedImageView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/imageView1"
    android:scaleType="centerCrop"
    app:riv_corner_radius_top_right="8dp"
    app:riv_corner_radius_bottom_right="8dp"
    app:riv_border_width="2dp"
    app:riv_border_color="#333333"
    app:riv_oval="false" />

Ответ 3

Ну, вы можете создать новый .xml drawable с именем my_background и вставить этот код ниже:

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle"
    >
    <solid android:color="#00000000"/>
    <corners
        android:bottomLeftRadius="12dp"
        android:bottomRightRadius="12dp"
        android:topLeftRadius="12dp"
        android:topRightRadius="12dp" />
    <stroke
        android:width="1dp"
        android:color="#000000"
        />
</shape>

Затем вы установите для своего ImageButton, ImageView фон свой новый способ рисования, например:

android:background="@drawable/my_background"
android:scaleType="centerCrop"

или программно:

myView.setBackground(R.drawable.my_background);

Надеюсь, что это поможет!

EDIT:

Чтобы программно создать аналогичную возможность, вы можете ее использовать:

GradientDrawable shape = new GradientDrawable();
    shape.setShape(GradientDrawable.RECTANGLE);
    shape.setCornerRadii(new float[] { 8, 8, 8, 8, 0, 0, 0, 0 });
    shape.setColor(Color.TRANSPARENT);
    shape.setStroke(3, Color.BLACK);
    v.setBackgroundDrawable(shape);

Ответ 4

Хорошо, здесь моя попытка. Единственная проблема заключается в том, что "int corner" означает набор флагов. Например, 0b1111, где каждый 1 представляет угол, который нужно закруглить, а 0 - противоположное. Приказ: TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT.

пример использования сначала, отформатированный для удобочитаемости:

((ImageView) findViewById(R.id.imageView)).setImageDrawable(
    new RoundedRectDrawable(
        getResources(),
        bitmap,
        (float) .15,
        0b1101,
        8,
        Color.YELLOW
    )
);

код:

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.SystemClock;

/**
 * Created by mliu on 4/15/16.
 */
public class RoundedRectDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
private final float outlineBorderRadius;
private final int w;
private final int h;
private final int corners;
private final int border;
private final int bordercolor;

public RoundedRectDrawable(final Resources resources, final Bitmap bitmap, float borderRadiusSeed, int corners, int borderPX, int borderColor) {
    super(resources, bitmap);
    bitmapShader = new BitmapShader(getBitmap(),
            BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
    final Bitmap b = getBitmap();
    p = getPaint();
    p.setAntiAlias(true);
    p.setShader(bitmapShader);
    w = b.getWidth();
    h = b.getHeight();
    rect = new RectF(0,0,w,h);
    borderRadius = borderRadiusSeed * Math.min(w, h);
    border = borderPX;
    this.corners = corners;
    this.bordercolor = borderColor;
    outlineBorderRadius = borderRadiusSeed * Math.min(w+border,h+border);
}

@Override
public void draw(final Canvas canvas) {
    if ((corners&0b1111)==0){
        if (border>0) {
            Paint border = new Paint();
            border.setColor(bordercolor);
            canvas.drawRect(rect, border);
        }
        canvas.drawRect(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border, p);
    } else {
        if (border >0) {
            Paint border = new Paint();
            border.setColor(bordercolor);
            canvas.drawRoundRect(rect, outlineBorderRadius, outlineBorderRadius, border);

            if ((corners & 0b1000) == 0) {
                //top left
                canvas.drawRect(rect.left, rect.top, rect.width() / 2, rect.height() / 2, border);
            }
            if ((corners & 0b0100) == 0) {
                //top right
                canvas.drawRect(rect.width() / 2, rect.top, rect.width(), rect.height() / 2, border);
            }
            if ((corners & 0b0010) == 0) {
                //bottom left
                canvas.drawRect(rect.left, rect.height() / 2, rect.width() / 2, rect.height(), border);
            }
            if ((corners & 0b0001) == 0) {
                //bottom right
                canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width(), rect.height(), border);
            }
        }
        canvas.drawRoundRect(new RectF(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border), borderRadius, borderRadius, p);
        if ((corners & 0b1000) == 0) {
            //top left
            canvas.drawRect(rect.left + border, rect.top + border, rect.width() / 2, rect.height() / 2, p);
        }
        if ((corners & 0b0100) == 0) {
            //top right
            canvas.drawRect(rect.width() / 2, rect.top + border, rect.width() - border, rect.height() / 2, p);
        }
        if ((corners & 0b0010) == 0) {
            //bottom left
            canvas.drawRect(rect.left + border, rect.height() / 2, rect.width() / 2, rect.height() - border, p);
        }
        if ((corners & 0b0001) == 0) {
            //bottom right
            canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width() - border, rect.height() - border, p);
        }
    }
}

}

Таким образом, сначала обрабатывает контур, если необходимо, а затем растровое изображение. Сначала он отмечает холст с закругленным прямоугольником, затем "квадратизирует" каждый угол, который вы не хотите крутить. Кажется крайне неэффективным и, вероятно, является, но средним временем выполнения до минимальной оптимизации (углы = 0b0000, 10 canvas.draw) берет ~ 200us на S7. И это время SUPER несовместимо на основе использования телефона. Я достиг 80% и достигает 1,5 мс.

ПРИМЕЧАНИЯ/ПРЕДУПРЕЖДЕНИЕ: Я ПЛОХО. Это не оптимально. Там, вероятно, лучше ответы уже на SO. Тема немного необычна и сложна для поиска. Я изначально не собирался публиковать это, но на момент написания этого ответа все еще не было отмечено, и библиотека OP не хотела использовать из-за проблем с их Drawable, фактически использовать очень похожий подход, как мое страшное решение. Итак, теперь я смущен, чтобы поделиться этим. Кроме того, хотя то, что я опубликовал сегодня, было написано вчера на 95%, я знаю, что получил часть этого кода из учебника или сообщения SO, но я не могу вспомнить, кого это атрибут, потому что я не использовал его в своем проекте, Извините, кто бы вы ни были.