Добавление границ для закругленного изображения Android

Что у меня есть:: У меня есть Imageview, для которого я делаю изображение в виде круга, используя picassso введите описание изображения здесь

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

Picasso.with(this)
.load("http://i.imgur.com/DvpvklR.png")
.transform(new RoundedTransformation(50, 4))
.resize(100, 100)
.centerCrop().into(imageView1);

RoundedTransformation.java

// enables hardware accelerated rounded corners
// original idea here : http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
public class RoundedTransformation implements com.squareup.picasso.Transformation {
    private final int radius;
    private final int margin;  // dp

    // radius is corner radii in dp
    // margin is the board in dp
    public RoundedTransformation(final int radius, final int margin) {
        this.radius = radius;
        this.margin = margin;
    }

    @Override
    public Bitmap transform(final Bitmap source) {
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));

        Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);

        if (source != output) {
            source.recycle();
        }

        return output;
    }

    @Override
    public String key() {
        return "rounded";
    }
}

РЕДАКТИРОВАТЬ

public class RoundedTransformation implements com.squareup.picasso.Transformation {
    private final int radius;
    private final int margin;  // dp

    // radius is corner radii in dp
    // margin is the board in dp
    public RoundedTransformation(final int radius, final int margin) {
        this.radius = radius;
        this.margin = margin;
    }

    @Override
    public Bitmap transform(final Bitmap source) {


        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));

        Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);

        if (source != output) {
            source.recycle();
        }

        Paint paint1 = new Paint();      
        paint1.setColor(Color.RED);
        paint1.setStyle(Style.STROKE);
        paint1.setAntiAlias(true);
        paint1.setStrokeWidth(2);
        canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint1);


        return output;
    }

    @Override
    public String key() {
        return "rounded";
    }
}

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


person Devrath    schedule 01.12.2014    source источник
comment
я использую эту версию ... красная рамка не такая большая, как изображение, и установка 50 в качестве первого параметра изображения не округляется   -  person user3528466    schedule 21.02.2016


Ответы (4)


вы можете использовать drawCircle с другим Paint object. Например:

Paint paint = new Paint();      
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint);

Кроме того, вместо использования drawRoundRect для рисования круга вы можете использовать drawCircle

person Blackbelt    schedule 01.12.2014
comment
это почти правильно. Вы должны использовать paint1 вместо paint, и это radius-2 не margin-2 - person Blackbelt; 01.12.2014
comment
[+1] но вижу изображение в редактировании ... если я использую paint1 ... мое изображение полностью исчезает, но я вижу маленький кружок, но не на границе, а в центре - person Devrath; 01.12.2014
comment
ты поменял margin-2 на radius-2? - person Blackbelt; 01.12.2014
comment
Почти идеально ... но он не покрывает небольшую часть сверху, как видно в редактировании ... можно ли его улучшить? - person Devrath; 01.12.2014
comment
это, вероятно, RoundRect. Можете ли вы использовать drawCircle вместо него? с радиусом вместо радиуса-2. В любом случае, теперь это просто вопрос настройки - person Blackbelt; 01.12.2014
comment
Спасибо ! ... работает отлично .... Я разместил полный класс, если кто-то ищет что-то подобное - person Devrath; 01.12.2014
comment
Ширина штриха различна для разных размеров изображений. Как сделать фиксированным? - person shurrok; 09.11.2018

Окончательный класс трансформации благодаря черному поясу

public class RoundedTransformation implements com.squareup.picasso.Transformation {
    private final int radius;
    private final int margin;  // dp

    // radius is corner radii in dp
    // margin is the board in dp
    public RoundedTransformation(final int radius, final int margin) {
        this.radius = radius;
        this.margin = margin;
    }

    @Override
    public Bitmap transform(final Bitmap source) {


        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));

        Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint);

        if (source != output) {
            source.recycle();
        }

        Paint paint1 = new Paint();      
        paint1.setColor(Color.RED);
        paint1.setStyle(Style.STROKE);
        paint1.setAntiAlias(true);
        paint1.setStrokeWidth(2);
        canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint1);


        return output;
    }

    @Override
    public String key() {
        return "rounded";
    }
}
person Devrath    schedule 01.12.2014
comment
Это не работает для прямоугольных изображений. Предварительно их нужно сделать квадратными. - person Michael Katkov; 17.04.2016

Ответы BlackBelt и Devrath великолепны. И если вы ищете Kotlin-версию этого класса, вот она:

class RoundedBorderTransform(private val radius: Int, private val margin: Int) : com.squareup.picasso.Transformation {

override fun transform(source: Bitmap?): Bitmap {
    val paint = Paint()
    paint.isAntiAlias = true
    paint.shader = BitmapShader(source!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)

    val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(output)
    canvas.drawCircle((source.width - margin) / 2f, (source.height - margin) / 2f, radius - 2f, paint)

    if (source != output) {
        source.recycle()
    }

    val borderPaint = Paint()
    borderPaint.color = Color.RED
    borderPaint.style = Paint.Style.STROKE
    borderPaint.isAntiAlias = true
    borderPaint.strokeWidth = 2f
    canvas.drawCircle((source.width - margin) / 2f, (source.height - margin) / 2f, radius - 2f, borderPaint)

    return output
}

override fun key(): String {
    return "roundedBorder"
}

}

person Tô Minh Tiến    schedule 25.08.2019

Это решение для круглых и прямоугольных форм. Также это полезно не только для использования Picasso, но и для общих Android Canvas-задач.

Я сделал их очень объемными и подробными только для вашего понимания процессов, сокращайте как хотите.

Но я хочу прояснить основную проблему, с которой сталкиваются многие люди. В Android нет возможности создать внутреннюю или внешнюю границу - только по центру:

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

И это причина, по которой вы получаете элементы границы, обрезанные следующим образом:

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

Так что есть только один способ пересчитать положение границы: в случае

  • круг необходимо уменьшить радиус круга на ПОЛОВИНУ ШИРИНЫ ГРАНИЦЫ
  • прямоугольник вам необходимо 1) сдвинуть линии границ "внутрь" на ПОЛОВИНУ ШИРИНЫ ГРАНИЦЫ; 2) уменьшить радиус угла на ПОЛОВИНУ ШИРИНЫ ГРАНИЦЫ

Эти действия дадут вам ожидаемые результаты:

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

Если вам нужен только код для вашей границы - выберите только соответствующий drawBorder() метод. Вот пример пустого фрагмента с двумя изображениями:

ViewsCroppingAndBorderingTestFragment.kt

class ViewsCroppingAndBorderingTestFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_test_views_cropping_and_bordering, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val urlCircle      = "https://images-na.ssl-images-amazon.com/images/I/71zLQIfmTlL._AC_SX466_.jpg"
        val urlRectRounded = "https://www.gardendesign.com/pictures/images/675x529Max/site_3/helianthus-yellow-flower-pixabay_11863.jpg"

        val ivCircle       = view.findViewById<ImageView>(R.id.ivCircle)
        val ivRectRounded  = view.findViewById<ImageView>(R.id.ivRectRounded)

        val trCircle       = CircleTransformation()
        val trRectRounded  = RectRoundedTransformation()

        Picasso.get().load(urlCircle).transform(trCircle).into(ivCircle)
        Picasso.get().load(urlRectRounded).transform(trRectRounded).into(ivRectRounded)
    }


    class CircleTransformation() : Transformation {

        override fun transform(source: Bitmap): Bitmap {

            val size = Math.min(source.width, source.height)
            val x = (source.width  - size) / 2
            val y = (source.height - size) / 2
            val w = size
            val h = size
            val squaredBitmap = Bitmap.createBitmap(source, x, y, w, h)

            if (squaredBitmap != source) {
                source.recycle()
            }

            val bitmap = Bitmap.createBitmap(w, h, source.config)

            val canvas = Canvas(bitmap)

//          >>  Draw original rectangle image
//            val paint = Paint(Paint.ANTI_ALIAS_FLAG)
//            canvas.drawBitmap(squaredBitmap, 0F, 0F, paint)

            val rImage = w / 2f

            cropWithCircle(squaredBitmap, canvas, rImage)
            squaredBitmap.recycle()


            drawBorder(canvas, rImage)


            return bitmap
        }

        private fun cropWithCircle(bitmapSource: Bitmap, canvas: Canvas, rImage: Float) {

            val shader = BitmapShader(bitmapSource, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)

            val paint         = Paint(Paint.ANTI_ALIAS_FLAG)
            paint.shader      = shader
            paint.isAntiAlias = true

            val xCenter = rImage
            val yCenter = rImage

            canvas.drawCircle(xCenter, yCenter, rImage, paint)
        }

        private fun drawBorder(canvas: Canvas, rImage: Float) {

            val borderWidth = 30F

            val paintBorder         = Paint()
            paintBorder.strokeWidth = borderWidth
            paintBorder.style       = Paint.Style.STROKE
            paintBorder.color       = Color.GREEN
            paintBorder.isAntiAlias = true


            val cCenter = rImage
            val yCenter = rImage

            // ANDROID'S BORDER IS ALWAYS CENTERED.
            //  So have to reduce radius by half of border's width

            val rBorder = rImage - borderWidth / 2

            canvas.drawCircle(cCenter, yCenter, rBorder, paintBorder)
        }

        override fun key(): String {
            return "circle"
        }
    }

    class RectRoundedTransformation() : Transformation {

        override fun transform(source: Bitmap): Bitmap {

            val x = 0
            val y = 0
            val w = source.width
            val h = source.height
            val squaredBitmap = Bitmap.createBitmap(source, x, y, w, h)

            if (squaredBitmap != source) {
                source.recycle()
            }

            val bitmap = Bitmap.createBitmap(w, h, source.config)

            val canvas = Canvas(bitmap)

//          >>  Draw original rectangle image
//            val paint = Paint(Paint.ANTI_ALIAS_FLAG)
//            canvas.drawBitmap(squaredBitmap, 0F, 0F, paint)

            val rCorner = 80F

            cropCorners(squaredBitmap, canvas, rCorner)
            squaredBitmap.recycle()


            drawBorder(canvas, rCorner)


            return bitmap
        }

        private fun cropCorners(bitmapSource: Bitmap, canvas: Canvas, rCorner: Float) {

            val width  = canvas.width
            val height = canvas.height

            val maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
            val maskCanvas = Canvas(maskBitmap)
            val maskPaint  = Paint(Paint.ANTI_ALIAS_FLAG)
            val maskRect   = RectF(0F, 0F, width.toFloat(), height.toFloat())

            // 1. draw base rect
            maskCanvas.drawRect(maskRect, maskPaint)

            // 2. cut off inner rounded rect, leave corners
            maskPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
            maskCanvas.drawRoundRect(maskRect, rCorner, rCorner, maskPaint)

            // 3. draw original rectangle image
            val paintSource = Paint(Paint.ANTI_ALIAS_FLAG)

            canvas.drawBitmap(bitmapSource, 0f, 0f, paintSource)

            // 4.cut off corners
            val paintCropped = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
            paintCropped!!.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)

            canvas.drawBitmap(maskBitmap!!, 0f, 0f, paintCropped)
        }

        private fun drawBorder(canvas: Canvas, rCorner: Float) {

            val borderWidth  = 30F

            val paintBorder         = Paint()
            paintBorder.strokeWidth = borderWidth
            paintBorder.style       = Paint.Style.STROKE
            paintBorder.color       = Color.RED
            paintBorder.isAntiAlias = true


//            // ANDROID'S BORDER IS ALWAYS CENTERED.
//            //  So have to shift every side by half of border's width

            val rectLeft   = 0 + borderWidth / 2
            val rectTop    = 0 + borderWidth / 2
            val rectRight  = canvas.width.toFloat() - borderWidth / 2
            val rectBottom = canvas.height.toFloat() - borderWidth / 2
            val rectF      = RectF(rectLeft, rectTop, rectRight, rectBottom)

//            //  So have to corner reduce radius by half of border's width
            val rectRCorner = rCorner - borderWidth / 2

            canvas.drawRoundRect(rectF, rectRCorner, rectRCorner, paintBorder)
        }

        override fun key(): String {
            return "rectRounded"
        }
    }
}

fragment_test_views_cropping_and_bordering.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/temp"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:background="@android:color/darker_gray">


    <ImageView
        android:id="@+id/ivCircle"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_margin="10dp"/>


    <ImageView
        android:id="@+id/ivRectRounded"
        android:layout_width="200dp"
        android:layout_height="160dp"
        android:layout_margin="10dp"/>


</LinearLayout>
person chatlanin    schedule 14.06.2020
comment
Я не уверен, что картина политики здесь хорошо подходит. Не могли бы использовать что-то еще, чтобы показать результаты вашего кода? - person β.εηοιτ.βε; 15.06.2020