How to add a shadow and a border on circular imageView android?

JavaAndroidImageview

Java Problem Overview


I created a CircularImageView with this question: Create circular image view in android

Download project on GitHub

  1. This is the CircularImageView class :

    public class CircularImageView extends ImageView { public CircularImageView(Context context) { super(context); }

     public CircularImageView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
    
     public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
    
     @Override
     protected void onDraw(Canvas canvas) {
         Drawable drawable = getDrawable();
         if (drawable == null) {
             return;
         }
    
         if (getWidth() == 0 || getHeight() == 0) {
             return; 
         }
         Bitmap b =  ((BitmapDrawable)drawable).getBitmap() ;
         Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);		
    
         Bitmap roundBitmap =  getCroppedBitmap(bitmap, getWidth());
         canvas.drawBitmap(roundBitmap, 0, 0, null);
     }
    
     public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
         Bitmap sbmp;
         if(bmp.getWidth() != radius || bmp.getHeight() != radius)
             sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
         else
             sbmp = bmp;
     
         Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(), Bitmap.Config.ARGB_8888);
         final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());
    
         Paint paint = new Paint();
         paint.setAntiAlias(true);
         paint.setFilterBitmap(true);
         paint.setDither(true);      
         paint.setColor(Color.parseColor("#BAB399"));
    
         Canvas c = new Canvas(output);        
         c.drawARGB(0, 0, 0, 0);
         c.drawCircle(sbmp.getWidth() / 2+0.7f, sbmp.getHeight() / 2+0.7f, sbmp.getWidth() / 2+0.1f, paint);
         paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
         c.drawBitmap(sbmp, rect, rect, paint);
    
         return output;
     }
    

    }

  2. I use in my layout like this :

    http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#cccccc" android:gravity="center" android:orientation="vertical" android:padding="10dp" >

     <com.mikhaellopez.circularimageview.CircularImageView
         android:id="@+id/imageViewCircular"
         android:layout_width="@dimen/image_view_size"
         android:layout_height="@dimen/image_view_size"
         android:layout_gravity="center"
         android:background="@drawable/border"
         android:src="@drawable/image" />
    

  3. Current result in picture :

Current result

How do I change this code to have a shadow and a circular border around my imageView?

Objectif result :

Objectif result


Edit 10/15/2015 :

You can used or download my GitHub library CircularImageView with all the fixes by using gradle dependency :

compile 'com.mikhaellopez:circularimageview:2.0.1'

Java Solutions


Solution 1 - Java

I modified the CircularImageView found here to achieve what you want.

To create a shadow around the border, I simply used these two lines:

this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);

You need setLayerType due to hardware acceleration on HoneyComb and up. It didn't work without it when I tried it.

Here is the full code:

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class CircularImageView extends ImageView
{
    private int borderWidth = 4;
    private int viewWidth;
    private int viewHeight;
    private Bitmap image;
    private Paint paint;
    private Paint paintBorder;
    private BitmapShader shader;
    
    public CircularImageView(Context context)
    {
	    super(context);
	    setup();
    }
    
    public CircularImageView(Context context, AttributeSet attrs)
    {
	    super(context, attrs);
	    setup();
    }
    
    public CircularImageView(Context context, AttributeSet attrs, int defStyle)
    {
	    super(context, attrs, defStyle);
	    setup();
    }
    
    private void setup()
    {
	    // init paint
	    paint = new Paint();
	    paint.setAntiAlias(true);
	    
	    paintBorder = new Paint();
	    setBorderColor(Color.WHITE);
	    paintBorder.setAntiAlias(true);
	    this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
        paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
    }
    
    public void setBorderWidth(int borderWidth)
    {
	    this.borderWidth = borderWidth;
	    this.invalidate();
    }
    
    public void setBorderColor(int borderColor)
    {
	    if (paintBorder != null)
	       	paintBorder.setColor(borderColor);
	    
	    this.invalidate();
    }
    
    private void loadBitmap()
    {
	    BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
	    
	    if (bitmapDrawable != null)
	    	image = bitmapDrawable.getBitmap();
    }

    @SuppressLint("DrawAllocation")
    @Override
    public void onDraw(Canvas canvas)
    {
	    // load the bitmap
	    loadBitmap();
	    
	    // init shader
	    if (image != null)
	    {
		    shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
		    paint.setShader(shader);
		    int circleCenter = viewWidth / 2;
		    
		    // circleCenter is the x or y of the view's center
		    // radius is the radius in pixels of the cirle to be drawn
		    // paint contains the shader that will texture the shape
		    canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter + borderWidth - 4.0f, paintBorder);
		    canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter - 4.0f, paint);
	    }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
	    int width = measureWidth(widthMeasureSpec);
	    int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
	    
	    viewWidth = width - (borderWidth * 2);
	    viewHeight = height - (borderWidth * 2);
	
	    setMeasuredDimension(width, height);
    }
    
    private int measureWidth(int measureSpec)
    {
	    int result = 0;
	    int specMode = MeasureSpec.getMode(measureSpec);
	    int specSize = MeasureSpec.getSize(measureSpec);
	    
	    if (specMode == MeasureSpec.EXACTLY)
	    {
		    // We were told how big to be
		    result = specSize;
	    }
	    else
	    {
		    // Measure the text
		    result = viewWidth;
	    }
	    
	    return result;
    }
    
    private int measureHeight(int measureSpecHeight, int measureSpecWidth)
    {
	    int result = 0;
	    int specMode = MeasureSpec.getMode(measureSpecHeight);
	    int specSize = MeasureSpec.getSize(measureSpecHeight);
	    
	    if (specMode == MeasureSpec.EXACTLY)
	    {
		    // We were told how big to be
		    result = specSize;
	    }
	    else
	    {
		    // Measure the text (beware: ascent is a negative number)
		    result = viewHeight;
	    }
	    
	    return (result + 2);
    }
}

I hope it helps!

.

EDIT

I forked your CircularImageView and added support for selector overlays. I also improved drawing performance significantly...

https://github.com/Pkmmte/CircularImageView

Solution 2 - Java

To add a border by making ImageView as a circle, i've done a simple thing, I used this class to make my image as a circle

package com.fidenz.fexceller.fexceller;

/**
 * Created by Chathu Hettiarachchi on 5/18/2015.
 */
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;

public class RoundedImg extends Drawable {
    private final Bitmap mBitmap;
    private final Paint mPaint;
    private final RectF mRectF;
    private final int mBitmapWidth;
    private final int mBitmapHeight;

    public RoundedImg(Bitmap bitmap) {
        mBitmap = bitmap;
        mRectF = new RectF();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint.setShader(shader);

        mBitmapWidth = mBitmap.getWidth();
        mBitmapHeight = mBitmap.getHeight();
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.drawOval(mRectF, mPaint);
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);
        mRectF.set(bounds);
    }

    @Override
    public void setAlpha(int alpha) {
        if (mPaint.getAlpha() != alpha) {
            mPaint.setAlpha(alpha);
            invalidateSelf();
        }
    }

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

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

    @Override
    public int getIntrinsicWidth() {
        return mBitmapWidth;
    }

    @Override
    public int getIntrinsicHeight() {
        return mBitmapHeight;
    }

    public void setAntiAlias(boolean aa) {
        mPaint.setAntiAlias(aa);
        invalidateSelf();
    }

    @Override
    public void setFilterBitmap(boolean filter) {
        mPaint.setFilterBitmap(filter);
        invalidateSelf();
    }

    @Override
    public void setDither(boolean dither) {
        mPaint.setDither(dither);
        invalidateSelf();
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }

}

and by using this on onCreate i have call the image for setting it,

profilePic = (ImageView)findViewById(R.id.img_home_profile_pic);

Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.no_image);
roundedImage = new RoundedImg(bm);
profilePic.setImageDrawable(roundedImage);

to add a border i created a circle shape XML like this,

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
    <gradient android:startColor="@color/ring_color" android:endColor="@color/ring_color"
        android:angle="270"/>
</shape>

then using layouts i added a RelativeLayout with ImageView inside of it, by using padding and background drawable with wrap_content i set my RelativeLayout like this

<RelativeLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/lay_rel_img"
    android:layout_gravity="center"
    android:padding="5dp"
    android:background="@drawable/circle">

    <ImageView
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center"
        android:id="@+id/img_home_profile_pic"
        android:src="@drawable/no_image"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>

now it show like this, i don't know to add the shadow, sorry for that too

enter image description here

Solution 3 - Java

Add canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, paint); before canvas.drawBitmap(roundBitmap, 0, 0, null);

Change c.drawCircle(sbmp.getWidth() / 2, sbmp.getHeight() / 2, sbmp.getWidth() / 2, paint); to c.drawCircle(sbmp.getWidth() / 2, sbmp.getHeight() / 2, sbmp.getWidth() / 2 - "the border with you prefer", paint);

Hope it helps.

Maybe a better solution here.

Solution 4 - Java

Create a custom drawable, and use that to define your the background attribute of your ImageView. You can use a LayeredDrawable, to create as many different components for the component as you would like.

Checkout this answer, which creates a custom Rectangle (but is exactly the same with an Oval\Circle): https://stackoverflow.com/questions/17571118/how-to-create-google-cards-ui-in-a-list-view/17571277#17571277

Solution 5 - Java

I found a library that doing exactly what you wish, worked fine for me. Check it out. https://android-arsenal.com/details/1/932

Solution 6 - Java

just use drawCircle() method with more width and height before drawing the actual image. Increase the width and height of in that new method call according to your wish, and set some another color of you want on paint

Solution 7 - Java

This Class is Custom Circular Imageview with shadow, Stroke,saturation and using this Custom Circular ImageView you can make your image in Circular Shape with Radius. Guys for Circular Shadow ImageView No need Github this class is enough.

Adding CircularImageView to your layout

// Bitmap myimage=BitmapFactory.decodeResource(getResources(),R.drawable.pic); CircularImageView c=new CircularImageView(this,screen width,screen height,Bitmap myimage); yourLayout.addView(c);**

public class CircularImageView extends android.support.v7.widget.AppCompatImageView  
{
    private final Context context;
    private final int width, height;
    private final Paint paint;
    private final Paint paintBorder,imagePaint;
    private final Bitmap bitmap2;
    private final Paint paint3;
    private Bitmap bitmap;
    private BitmapShader shader;
    private float radius = 4.0f;
    float x = 0.0f;
    float y = 8.0f;
    private float stroke;
    private float strokeWidth = 0.0f;
    private Bitmap bitmap3;
    private int corner_radius=50;

    
    public CircularImageView(Context context, int width, int height, Bitmap bitmap)     {
        super(context);
        this.context = context;
        this.width = width;
        this.height = height;

   //here "bitmap" is the square shape(width* width) scaled bitmap ..

        this.bitmap = bitmap;


        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);


        paint3=new Paint();
        paint3.setStyle(Paint.Style.STROKE);
        paint3.setColor(Color.WHITE);
        paint3.setAntiAlias(true);

        paintBorder = new Paint();
        imagePaint= new Paint();

        paintBorder.setColor(Color.WHITE);
        paintBorder.setAntiAlias(true);
        this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);


        this.bitmap2 = Bitmap.createScaledBitmap(bitmap, (bitmap.getWidth() - 40), (bitmap.getHeight() - 40), true);


        imagePaint.setAntiAlias(true);




        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) 
    {
        super.onDraw(canvas);
        Shader b;
         if (bitmap3 != null)
            b = new BitmapShader(bitmap3, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
         else
            b = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        imagePaint.setShader(b);
        canvas.drawBitmap(maskedBitmap(), 20, 20, null);
    }

    private Bitmap maskedBitmap()
    {
        Bitmap l1 = Bitmap.createBitmap(width,width, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(l1);
        paintBorder.setShadowLayer(radius, x, y, Color.parseColor("#454645"));
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        final RectF rect = new RectF();
        rect.set(20, 20, bitmap2.getWidth(), bitmap2.getHeight());

        canvas.drawRoundRect(rect, corner_radius, corner_radius, paintBorder);

        canvas.drawRoundRect(rect, corner_radius, corner_radius, imagePaint);

        if (strokeWidth!=0.0f)
        {
            paint3.setStrokeWidth(strokeWidth);
            canvas.drawRoundRect(rect, corner_radius, corner_radius, paint3);
        }

         paint.setXfermode(null);
        return l1;
    }

       


     // use seekbar here, here you have to pass  "0 -- 250"  here corner radius will change 

    public void setCornerRadius(int corner_radius)
    {
        this.corner_radius = corner_radius;
        invalidate();
    }



    -------->use seekbar here, here you have to pass  "0 -- 10.0f"  here shadow radius will change 

    public void setShadow(float radius)
    {
        this.radius = radius;
        invalidate();
    }

   // use seekbar here, here you have to pass  "0 -- 10.0f"  here stroke size  will change 

    public void setStroke(float stroke)
    {
        this.strokeWidth = stroke;
        invalidate();
    }

    private Bitmap updateSat(Bitmap src, float settingSat)
    {

        int w = src.getWidth();
        int h = src.getHeight();

        Bitmap bitmapResult =
                Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvasResult = new Canvas(bitmapResult);
        Paint paint = new Paint();
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.setSaturation(settingSat);
        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
        paint.setColorFilter(filter);
        canvasResult.drawBitmap(src, 0, 0, paint);

        return bitmapResult;
    }




  // use seekbar here, here you have to pass  "0 -- 2.0f"  here saturation  will change 

    public void setSaturation(float sat)
    {
        System.out.println("qqqqqqqqqq            "+sat);
        bitmap3=updateSat(bitmap2, sat);

        invalidate();
    } 


}




   

        // Seekbar to change radius
            
                  radius_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
                        {
                            text_radius.setText(""+progress);
                            circularImageView.setCornerRadius(progress);
                        }
    
                        @Override
                        public void onStartTrackingTouch(SeekBar seekBar) {
    
                        }
    
                        @Override
                        public void onStopTrackingTouch(SeekBar seekBar) {
    
                        }
                    });
    
    
     // Seekbar to change shadow
    
                    shadow_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
                        {
                            float f= 4+progress/10.0f;
                            text_shadow.setText(""+progress);
                            circularImageView.setShadow(f);
                        }
    
                        @Override
                        public void onStartTrackingTouch(SeekBar seekBar) {
    
                        }
    
                        @Override
                        public void onStopTrackingTouch(SeekBar seekBar) {
    
                        }
                    });
    
    
           // Seekbar to change saturation
    
                    saturation_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
                        {
                            int progressSat = saturation_seekbar.getProgress();
                            float sat = (float) ((progressSat*4 / 100.0f)-1.0f);
                            circularImageView.setSaturation(sat);
    
                            text_saturation.setText(""+progressSat);
                        }
    
                        @Override
                        public void onStartTrackingTouch(SeekBar seekBar) {
    
                        }
    
                        @Override
                        public void onStopTrackingTouch(SeekBar seekBar) {
    
                        }
                    });
    
    
    // Seekbar to change stroke
    
                    stroke_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
                        {
                            if (progress==0)
                            {
                                float f=(progress*10.0f/100.0f);
                                circularImageView.setStroke(f);
                            }
                            else
                            {
                                float f=(progress*10.0f/100.0f);
                                circularImageView.setStroke(f);
                            }
    
                            text_stroke.setText(""+progress);
                        }
    
                        @Override
                        public void onStartTrackingTouch(SeekBar seekBar) {
    
                        }
    
                        @Override
                        public void onStopTrackingTouch(SeekBar seekBar) {
    
                        }
                    });
    
    
    
    
             //radius seekbar in xml file
    
             <SeekBar
                android:layout_width="match_parent"
                android:layout_gravity="center" 
                android:progress="50"
                android:max="250"
                android:id="@+id/radius_seekbar"
                android:layout_height="wrap_content" />
    
    
    
    
    
          //saturation seekbar in xml file
    
             <SeekBar
                android:layout_width="match_parent"
                android:layout_gravity="center" 
                android:progress="50"
                android:max="100"
                android:id="@+id/saturation_seekbar"
                android:layout_height="wrap_content" />
    
    
    
    
    
    //shadow seekbar in xml file
    
             <SeekBar
                android:layout_width="match_parent"
                android:layout_gravity="center" 
                android:progress="0"
                android:max="100"
                android:id="@+id/shadow_seekbar"
                android:layout_height="wrap_content" />
    
    
    
    
         //stroke seekbar in xml file
    
             <SeekBar
                android:layout_width="match_parent"
                android:layout_gravity="center" 
                android:progress="0"
                android:max="100"
                android:id="@+id/stroke _seekbar"
                android:layout_height="wrap_content" />

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionlopez.mikhaelView Question on Stackoverflow
Solution 1 - JavaPkmmteView Answer on Stackoverflow
Solution 2 - JavaChathura JayanathView Answer on Stackoverflow
Solution 3 - JavacycDroidView Answer on Stackoverflow
Solution 4 - JavaBoogerView Answer on Stackoverflow
Solution 5 - Javadaniel kilinskasView Answer on Stackoverflow
Solution 6 - JavaOnur A.View Answer on Stackoverflow
Solution 7 - Javadileep krishnanView Answer on Stackoverflow