Adding borders for image rounded image android

前端 未结 4 1066
夕颜
夕颜 2020-12-02 23:33

What i have:: I have a Imageview for which i am making image as a circle using picassso \"enter

相关标签:
4条回答
  • 2020-12-03 00:14

    Final transformation class, thanks to blackbelt

    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";
        }
    }
    
    0 讨论(0)
  • 2020-12-03 00:15

    This is solution for circle and rectangle shapes. Also it's useful not only for Picasso usage, but for general android Canvas-tasks.

    I've created them very voluminous and detailed only for yours understanding of processes, shorten as you want.

    But I want to clarify a main problem that many people faced with. In android there no possibilities to create inner or outer border - only centered:

    And this is the reason why you receive border elements cut off like these:

    So there only a single way to recalculate position of border: in case of

    • circle you have to decrease circle radius by HALF OF BORDER WIDTH
    • rectangle you have to 1) shift border lines "inside" by HALF OF BORDER WIDTH; 2) decrease corner radius by HALF OF BORDER WIDTH

    These actions will provide you an expected results:

    If you're need only code for you border - pick only correspondent drawBorder() method. Here is an example for empty Fragment with two images:

    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>
    
    0 讨论(0)
  • 2020-12-03 00:28

    Answers of BlackBelt and Devrath are great. And if you are looking for a Kotlin version of this class, here it is:

    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"
    }
    

    }

    0 讨论(0)
  • 2020-12-03 00:29

    you can use drawCircle with another Paint object. For instance:

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

    Also, instead of using a drawRoundRect to draw a circle, you can use drawCircle

    0 讨论(0)
提交回复
热议问题