1package com.android.ex.chips;
2
3import android.content.Context;
4import android.graphics.Bitmap;
5import android.graphics.BitmapShader;
6import android.graphics.Canvas;
7import android.graphics.Color;
8import android.graphics.Matrix;
9import android.graphics.Paint;
10import android.graphics.RectF;
11import android.graphics.Shader;
12import android.graphics.drawable.BitmapDrawable;
13import android.graphics.drawable.Drawable;
14import android.graphics.drawable.StateListDrawable;
15import android.util.AttributeSet;
16import android.widget.ImageView;
17
18/**
19 * An ImageView class with a circle mask so that all images are drawn in a
20 * circle instead of a square.
21 */
22public class CircularImageView extends ImageView {
23    private static float circularImageBorder = 1f;
24
25    private final Matrix matrix;
26    private final RectF source;
27    private final RectF destination;
28    private final Paint bitmapPaint;
29    private final Paint borderPaint;
30
31    public CircularImageView(Context context) {
32        this(context, null, 0);
33    }
34
35    public CircularImageView(Context context, AttributeSet attrs) {
36        this(context, attrs, 0);
37    }
38
39    public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
40        super(context, attrs, defStyle);
41
42        matrix = new Matrix();
43        source = new RectF();
44        destination = new RectF();
45
46        bitmapPaint = new Paint();
47        bitmapPaint.setAntiAlias(true);
48        bitmapPaint.setFilterBitmap(true);
49        bitmapPaint.setDither(true);
50
51        borderPaint = new Paint();
52        borderPaint.setColor(Color.TRANSPARENT);
53        borderPaint.setStyle(Paint.Style.STROKE);
54        borderPaint.setStrokeWidth(circularImageBorder);
55        borderPaint.setAntiAlias(true);
56    }
57
58    @Override
59    protected void onDraw(Canvas canvas) {
60        Drawable drawable = getDrawable();
61        BitmapDrawable bitmapDrawable = null;
62        // support state list drawable by getting the current state
63        if (drawable instanceof StateListDrawable) {
64            if (((StateListDrawable) drawable).getCurrent() != null) {
65                bitmapDrawable = (BitmapDrawable) drawable.getCurrent();
66            }
67        } else {
68            bitmapDrawable = (BitmapDrawable) drawable;
69        }
70
71        if (bitmapDrawable == null) {
72            return;
73        }
74        Bitmap bitmap = bitmapDrawable.getBitmap();
75        if (bitmap == null) {
76            return;
77        }
78
79        source.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
80        destination.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(),
81                getHeight() - getPaddingBottom());
82
83        drawBitmapWithCircleOnCanvas(bitmap, canvas, source, destination);
84    }
85
86    /**
87     * Given the source bitmap and a canvas, draws the bitmap through a circular
88     * mask. Only draws a circle with diameter equal to the destination width.
89     *
90     * @param bitmap The source bitmap to draw.
91     * @param canvas The canvas to draw it on.
92     * @param source The source bound of the bitmap.
93     * @param dest The destination bound on the canvas.
94     */
95    public void drawBitmapWithCircleOnCanvas(Bitmap bitmap, Canvas canvas,
96                                             RectF source, RectF dest) {
97        // Draw bitmap through shader first.
98        BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
99                Shader.TileMode.CLAMP);
100        matrix.reset();
101
102        // Fit bitmap to bounds.
103        matrix.setRectToRect(source, dest, Matrix.ScaleToFit.FILL);
104
105        shader.setLocalMatrix(matrix);
106        bitmapPaint.setShader(shader);
107        canvas.drawCircle(dest.centerX(), dest.centerY(), dest.width() / 2f,
108                bitmapPaint);
109
110        // Then draw the border.
111        canvas.drawCircle(dest.centerX(), dest.centerY(),
112                dest.width() / 2f - circularImageBorder / 2, borderPaint);
113    }
114}