2

I'm trying to mimic something from the iPhone version of my app. I have a square image and I want to show it in a circle with a white border around it. like this

enter image description here

Is there a way I can do this?

birdy
  • 9,286
  • 24
  • 107
  • 171

3 Answers3

0

You can achieve this effect, or something very close to it, using a custom Drawable class, containing a Paint object with a BitmapShader that renders the image as a texture. This is the code I'm using (slightly adapted from Romain's Guy post, which uses the same technique to draw images with rounded corners).

class CircularDrawable extends Drawable
{
    private float mCircleRadius;
    private final RectF mBackgroundRect = new RectF();
    private final Paint mBackgroundPaint;
    private final BitmapShader mBitmapShader;
    private final Paint mPaint;
    private final int mMargin;

    CircularDrawable(Bitmap bitmap, int margin, int backgroundColor)
    {
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(mBitmapShader);

        mMargin = margin;
        mBackgroundPaint = new Paint();
        mBackgroundPaint.setColor(backgroundColor);
    }

    @Override
    protected void onBoundsChange(Rect bounds)
    {
        super.onBoundsChange(bounds);
        mBackgroundRect.set(bounds);
        mCircleRadius = Math.min(bounds.width() / 2 - mMargin, bounds.height() / 2 - mMargin);
    }

    @Override
    public void draw(Canvas canvas)
    {
        canvas.drawRect(mBackgroundRect, mBackgroundPaint);
        canvas.drawCircle(mBackgroundRect.width() / 2, mBackgroundRect.height() / 2, mCircleRadius, mPaint);
    }

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

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

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

Having the bitmap you want to draw, just build a CircularDrawable from it with

new CircularDrawable(bitmap, margin, Color.WHITE);
matiash
  • 54,791
  • 16
  • 125
  • 154
  • Thanks but I'm confused as to where does the png/jpeg image go in that ? item.mBitmap part? – birdy May 10 '14 at 03:17
  • Yes. You must build this drawable from a Bitmap instance (e.g. extracted from BitmapDrawable, downloaded from the network, &c). Suggestion: Download Romain's full project, it's really easy to understand. Then substitute its StreamDrawable class by this one to see the effect. – matiash May 10 '14 at 03:20
0

Try this.

public class CircularImageView extends ImageView {
private int borderWidth;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private BitmapShader shader;

public CircularImageView(final Context context) {
    this(context, null);
}

public CircularImageView(Context context, AttributeSet attrs) {
    this(context, attrs, R.attr.circularImageViewStyle);
}

public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    // init paint
    paint = new Paint();
    paint.setAntiAlias(true);

    paintBorder = new Paint();
    paintBorder.setAntiAlias(true);

    // load the styled attributes and set their properties
    TypedArray attributes = context.obtainStyledAttributes(attrs,
            R.styleable.CircularImageView, defStyle, 0);

    if (attributes.getBoolean(R.styleable.CircularImageView_border, true)) {
        setBorderWidth(attributes.getColor(
                R.styleable.CircularImageView_border_width, 4));
        setBorderColor(attributes.getInt(
                R.styleable.CircularImageView_border_color, Color.WHITE));
    }

    if (attributes.getBoolean(R.styleable.CircularImageView_shadow, false))
        addShadow();
}

public void setBorderWidth(int borderWidth) {
    this.borderWidth = borderWidth;
    this.invalidate();
}

public void setBorderColor(int borderColor) {
    if (paintBorder != null)
        paintBorder.setColor(borderColor);
    this.invalidate();
}

public void addShadow() {
    setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
    paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
}

@SuppressLint("DrawAllocation")
@Override
public void onDraw(Canvas canvas) {
    // load the bitmap
    BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
    if (bitmapDrawable != null)
        image = bitmapDrawable.getBitmap();

    // 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);
}
}
Vikrant_Dev
  • 430
  • 2
  • 15
-1

I'd make a custom view and just draw what you want to the canvas- draw the border, then the white circle, then the image.It's a couple of easy canvas calls. If you need to clip the image to a circular area, just set a clipping Region before doing the image draw.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127