1 import android.content.Context;
2 import android.content.res.TypedArray;
3 import android.graphics.Bitmap;
4 import android.graphics.BitmapShader;
5 import android.graphics.Canvas;
6 import android.graphics.Color;
7 import android.graphics.Matrix;
8 import android.graphics.Paint;
9 import android.graphics.RectF;
10 import android.graphics.Shader;
11 import android.graphics.drawable.BitmapDrawable;
12 import android.graphics.drawable.ColorDrawable;
13 import android.graphics.drawable.Drawable;
14 import android.util.AttributeSet;
15 import android.widget.ImageView;
16
17
18 public class CircleImageView extends ImageView {
19
20 private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
21
22 private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
23 private static final int COLORDRAWABLE_DIMENSION = 1;
24
25 private static final int DEFAULT_BORDER_WIDTH = 0;
26 private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
27
28 private final RectF mDrawableRect = new RectF();
29 private final RectF mBorderRect = new RectF();
30
31 private final Matrix mShaderMatrix = new Matrix();
32 private final Paint mBitmapPaint = new Paint();
33 private final Paint mBorderPaint = new Paint();
34
35 private int mBorderColor = DEFAULT_BORDER_COLOR;
36 private int mBorderWidth = DEFAULT_BORDER_WIDTH;
37
38 private Bitmap mBitmap;
39 private BitmapShader mBitmapShader;
40 private int mBitmapWidth;
41 private int mBitmapHeight;
42
43 private float mDrawableRadius;
44 private float mBorderRadius;
45
46 private boolean mReady;
47 private boolean mSetupPending;
48
49 public CircleImageView(Context context) {
50 super(context);
51 }
52
53 public CircleImageView(Context context, AttributeSet attrs) {
54 this(context, attrs, 0);
55 }
56
57 public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
58 super(context, attrs, defStyle);
59 super.setScaleType(SCALE_TYPE);
60
61 TypedArray a = context.obtainStyledAttributes(attrs,
62 R.styleable.CircleImageView, defStyle, 0);
63
64 mBorderWidth = a.getDimensionPixelSize(
65 R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
66 mBorderColor = a.getColor(R.styleable.CircleImageView_border_color,
67 DEFAULT_BORDER_COLOR);
68
69 a.recycle();
70
71 mReady = true;
72
73 if (mSetupPending) {
74 setup();
75 mSetupPending = false;
76 }
77 }
78
79 @Override
80 public ScaleType getScaleType() {
81 return SCALE_TYPE;
82 }
83
84 @Override
85 public void setScaleType(ScaleType scaleType) {
86 if (scaleType != SCALE_TYPE) {
87 throw new IllegalArgumentException(String.format(
88 "ScaleType %s not supported.", scaleType));
89 }
90 }
91
92 @Override
93 protected void onDraw(Canvas canvas) {
94 if (getDrawable() == null) {
95 return;
96 }
97
98 canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius,
99 mBitmapPaint);
100 if (mBorderWidth != 0) {
101 canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius,
102 mBorderPaint);
103 }
104 }
105
106 @Override
107 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
108 super.onSizeChanged(w, h, oldw, oldh);
109 setup();
110 }
111
112 public int getBorderColor() {
113 return mBorderColor;
114 }
115
116 public void setBorderColor(int borderColor) {
117 if (borderColor == mBorderColor) {
118 return;
119 }
120
121 mBorderColor = borderColor;
122 mBorderPaint.setColor(mBorderColor);
123 invalidate();
124 }
125
126 public int getBorderWidth() {
127 return mBorderWidth;
128 }
129
130 public void setBorderWidth(int borderWidth) {
131 if (borderWidth == mBorderWidth) {
132 return;
133 }
134
135 mBorderWidth = borderWidth;
136 setup();
137 }
138
139 @Override
140 public void setImageBitmap(Bitmap bm) {
141 super.setImageBitmap(bm);
142 mBitmap = bm;
143 setup();
144 }
145
146 @Override
147 public void setImageDrawable(Drawable drawable) {
148 super.setImageDrawable(drawable);
149 mBitmap = getBitmapFromDrawable(drawable);
150 setup();
151 }
152
153 @Override
154 public void setImageResource(int resId) {
155 super.setImageResource(resId);
156 mBitmap = getBitmapFromDrawable(getDrawable());
157 setup();
158 }
159
160 private Bitmap getBitmapFromDrawable(Drawable drawable) {
161 if (drawable == null) {
162 return null;
163 }
164
165 if (drawable instanceof BitmapDrawable) {
166 return ((BitmapDrawable) drawable).getBitmap();
167 }
168
169 try {
170 Bitmap bitmap;
171
172 if (drawable instanceof ColorDrawable) {
173 bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION,
174 COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
175 } else {
176 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
177 drawable.getIntrinsicHeight(), BITMAP_CONFIG);
178 }
179
180 Canvas canvas = new Canvas(bitmap);
181 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
182 drawable.draw(canvas);
183 return bitmap;
184 } catch (OutOfMemoryError e) {
185 return null;
186 }
187 }
188
189 private void setup() {
190 if (!mReady) {
191 mSetupPending = true;
192 return;
193 }
194
195 if (mBitmap == null) {
196 return;
197 }
198
199 mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
200 Shader.TileMode.CLAMP);
201
202 mBitmapPaint.setAntiAlias(true);
203 mBitmapPaint.setShader(mBitmapShader);
204
205 mBorderPaint.setStyle(Paint.Style.STROKE);
206 mBorderPaint.setAntiAlias(true);
207 mBorderPaint.setColor(mBorderColor);
208 mBorderPaint.setStrokeWidth(mBorderWidth);
209
210 mBitmapHeight = mBitmap.getHeight();
211 mBitmapWidth = mBitmap.getWidth();
212
213 mBorderRect.set(0, 0, getWidth(), getHeight());
214 mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2,
215 (mBorderRect.width() - mBorderWidth) / 2);
216
217 mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width()
218 - mBorderWidth, mBorderRect.height() - mBorderWidth);
219 mDrawableRadius = Math.min(mDrawableRect.height() / 2,
220 mDrawableRect.width() / 2);
221
222 updateShaderMatrix();
223 invalidate();
224 }
225
226 private void updateShaderMatrix() {
227 float scale;
228 float dx = 0;
229 float dy = 0;
230
231 mShaderMatrix.set(null);
232
233 if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width()
234 * mBitmapHeight) {
235 scale = mDrawableRect.height() / (float) mBitmapHeight;
236 dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
237 } else {
238 scale = mDrawableRect.width() / (float) mBitmapWidth;
239 dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
240 }
241
242 mShaderMatrix.setScale(scale, scale);
243 mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth,
244 (int) (dy + 0.5f) + mBorderWidth);
245
246 mBitmapShader.setLocalMatrix(mShaderMatrix);
247 }
248
249 }
1 <com.sample.CircleImageView
2 android:layout_width="80dp"
3 android:layout_height="80dp"
4 android:layout_marginLeft="40dp"
5 android:scaleType="centerCrop"
6 android:src="@drawable/charming" />