自定义圆角ImageView控件

时间:2022-05-17 20:35:10

这个就当工具类用吧,因为直接是继承的ImageView.所以也具备了ImageView所有的特点,不同的是,可以自动裁剪成圆角图片。看效果吧。

自定义圆角ImageView控件

效果还是不错的。使用方式:

直接在配置中添加依赖

1 compile 'de.hdodenhof:circleimageview:2.1.0'

并且查看其源码,思路也非常简单清晰:

  1 /*
  2  * Copyright 2014 - 2016 Henning Dodenhof
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *     http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 package de.hdodenhof.circleimageview;
 17 
 18 import android.content.Context;
 19 import android.content.res.TypedArray;
 20 import android.graphics.Bitmap;
 21 import android.graphics.BitmapShader;
 22 import android.graphics.Canvas;
 23 import android.graphics.Color;
 24 import android.graphics.ColorFilter;
 25 import android.graphics.Matrix;
 26 import android.graphics.Paint;
 27 import android.graphics.RectF;
 28 import android.graphics.Shader;
 29 import android.graphics.drawable.BitmapDrawable;
 30 import android.graphics.drawable.ColorDrawable;
 31 import android.graphics.drawable.Drawable;
 32 import android.net.Uri;
 33 import android.support.annotation.ColorInt;
 34 import android.support.annotation.ColorRes;
 35 import android.support.annotation.DrawableRes;
 36 import android.util.AttributeSet;
 37 import android.widget.ImageView;
 38 
 39 public class CircleImageView extends ImageView {
 40 
 41     private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
 42 
 43     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
 44     private static final int COLORDRAWABLE_DIMENSION = 2;
 45 
 46     private static final int DEFAULT_BORDER_WIDTH = 0;
 47     private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
 48     private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
 49     private static final boolean DEFAULT_BORDER_OVERLAY = false;
 50 
 51     private final RectF mDrawableRect = new RectF();
 52     private final RectF mBorderRect = new RectF();
 53 
 54     private final Matrix mShaderMatrix = new Matrix();
 55     private final Paint mBitmapPaint = new Paint();
 56     private final Paint mBorderPaint = new Paint();
 57     private final Paint mFillPaint = new Paint();
 58 
 59     private int mBorderColor = DEFAULT_BORDER_COLOR;
 60     private int mBorderWidth = DEFAULT_BORDER_WIDTH;
 61     private int mFillColor = DEFAULT_FILL_COLOR;
 62 
 63     private Bitmap mBitmap;
 64     private BitmapShader mBitmapShader;
 65     private int mBitmapWidth;
 66     private int mBitmapHeight;
 67 
 68     private float mDrawableRadius;
 69     private float mBorderRadius;
 70 
 71     private ColorFilter mColorFilter;
 72 
 73     private boolean mReady;
 74     private boolean mSetupPending;
 75     private boolean mBorderOverlay;
 76     private boolean mDisableCircularTransformation;
 77 
 78     public CircleImageView(Context context) {
 79         super(context);
 80 
 81         init();
 82     }
 83 
 84     public CircleImageView(Context context, AttributeSet attrs) {
 85         this(context, attrs, 0);
 86     }
 87 
 88     public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
 89         super(context, attrs, defStyle);
 90 
 91         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
 92 
 93         mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
 94         mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
 95         mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
 96         mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);
 97 
 98         a.recycle();
 99 
100         init();
101     }
102 
103     private void init() {
104         super.setScaleType(SCALE_TYPE);
105         mReady = true;
106 
107         if (mSetupPending) {
108             setup();
109             mSetupPending = false;
110         }
111     }
112 
113     @Override
114     public ScaleType getScaleType() {
115         return SCALE_TYPE;
116     }
117 
118     @Override
119     public void setScaleType(ScaleType scaleType) {
120         if (scaleType != SCALE_TYPE) {
121             throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
122         }
123     }
124 
125     @Override
126     public void setAdjustViewBounds(boolean adjustViewBounds) {
127         if (adjustViewBounds) {
128             throw new IllegalArgumentException("adjustViewBounds not supported.");
129         }
130     }
131 
132     @Override
133     protected void onDraw(Canvas canvas) {
134         if (mDisableCircularTransformation) {
135             super.onDraw(canvas);
136             return;
137         }
138 
139         if (mBitmap == null) {
140             return;
141         }
142 
143         if (mFillColor != Color.TRANSPARENT) {
144             canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);
145         }
146         canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
147         if (mBorderWidth > 0) {
148             canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
149         }
150     }
151 
152     @Override
153     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
154         super.onSizeChanged(w, h, oldw, oldh);
155         setup();
156     }
157 
158     @Override
159     public void setPadding(int left, int top, int right, int bottom) {
160         super.setPadding(left, top, right, bottom);
161         setup();
162     }
163 
164     @Override
165     public void setPaddingRelative(int start, int top, int end, int bottom) {
166         super.setPaddingRelative(start, top, end, bottom);
167         setup();
168     }
169 
170     public int getBorderColor() {
171         return mBorderColor;
172     }
173 
174     public void setBorderColor(@ColorInt int borderColor) {
175         if (borderColor == mBorderColor) {
176             return;
177         }
178 
179         mBorderColor = borderColor;
180         mBorderPaint.setColor(mBorderColor);
181         invalidate();
182     }
183 
184     /**
185      * @deprecated Use {@link #setBorderColor(int)} instead
186      */
187     @Deprecated
188     public void setBorderColorResource(@ColorRes int borderColorRes) {
189         setBorderColor(getContext().getResources().getColor(borderColorRes));
190     }
191 
192     /**
193      * Return the color drawn behind the circle-shaped drawable.
194      *
195      * @return The color drawn behind the drawable
196      *
197      * @deprecated Fill color support is going to be removed in the future
198      */
199     @Deprecated
200     public int getFillColor() {
201         return mFillColor;
202     }
203 
204     /**
205      * Set a color to be drawn behind the circle-shaped drawable. Note that
206      * this has no effect if the drawable is opaque or no drawable is set.
207      *
208      * @param fillColor The color to be drawn behind the drawable
209      *
210      * @deprecated Fill color support is going to be removed in the future
211      */
212     @Deprecated
213     public void setFillColor(@ColorInt int fillColor) {
214         if (fillColor == mFillColor) {
215             return;
216         }
217 
218         mFillColor = fillColor;
219         mFillPaint.setColor(fillColor);
220         invalidate();
221     }
222 
223     /**
224      * Set a color to be drawn behind the circle-shaped drawable. Note that
225      * this has no effect if the drawable is opaque or no drawable is set.
226      *
227      * @param fillColorRes The color resource to be resolved to a color and
228      *                     drawn behind the drawable
229      *
230      * @deprecated Fill color support is going to be removed in the future
231      */
232     @Deprecated
233     public void setFillColorResource(@ColorRes int fillColorRes) {
234         setFillColor(getContext().getResources().getColor(fillColorRes));
235     }
236 
237     public int getBorderWidth() {
238         return mBorderWidth;
239     }
240 
241     public void setBorderWidth(int borderWidth) {
242         if (borderWidth == mBorderWidth) {
243             return;
244         }
245 
246         mBorderWidth = borderWidth;
247         setup();
248     }
249 
250     public boolean isBorderOverlay() {
251         return mBorderOverlay;
252     }
253 
254     public void setBorderOverlay(boolean borderOverlay) {
255         if (borderOverlay == mBorderOverlay) {
256             return;
257         }
258 
259         mBorderOverlay = borderOverlay;
260         setup();
261     }
262 
263     public boolean isDisableCircularTransformation() {
264         return mDisableCircularTransformation;
265     }
266 
267     public void setDisableCircularTransformation(boolean disableCircularTransformation) {
268         if (mDisableCircularTransformation == disableCircularTransformation) {
269             return;
270         }
271 
272         mDisableCircularTransformation = disableCircularTransformation;
273         initializeBitmap();
274     }
275 
276     @Override
277     public void setImageBitmap(Bitmap bm) {
278         super.setImageBitmap(bm);
279         initializeBitmap();
280     }
281 
282     @Override
283     public void setImageDrawable(Drawable drawable) {
284         super.setImageDrawable(drawable);
285         initializeBitmap();
286     }
287 
288     @Override
289     public void setImageResource(@DrawableRes int resId) {
290         super.setImageResource(resId);
291         initializeBitmap();
292     }
293 
294     @Override
295     public void setImageURI(Uri uri) {
296         super.setImageURI(uri);
297         initializeBitmap();
298     }
299 
300     @Override
301     public void setColorFilter(ColorFilter cf) {
302         if (cf == mColorFilter) {
303             return;
304         }
305 
306         mColorFilter = cf;
307         applyColorFilter();
308         invalidate();
309     }
310 
311     @Override
312     public ColorFilter getColorFilter() {
313         return mColorFilter;
314     }
315 
316     private void applyColorFilter() {
317         if (mBitmapPaint != null) {
318             mBitmapPaint.setColorFilter(mColorFilter);
319         }
320     }
321 
322     private Bitmap getBitmapFromDrawable(Drawable drawable) {
323         if (drawable == null) {
324             return null;
325         }
326 
327         if (drawable instanceof BitmapDrawable) {
328             return ((BitmapDrawable) drawable).getBitmap();
329         }
330 
331         try {
332             Bitmap bitmap;
333 
334             if (drawable instanceof ColorDrawable) {
335                 bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
336             } else {
337                 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
338             }
339 
340             Canvas canvas = new Canvas(bitmap);
341             drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
342             drawable.draw(canvas);
343             return bitmap;
344         } catch (Exception e) {
345             e.printStackTrace();
346             return null;
347         }
348     }
349 
350     private void initializeBitmap() {
351         if (mDisableCircularTransformation) {
352             mBitmap = null;
353         } else {
354             mBitmap = getBitmapFromDrawable(getDrawable());
355         }
356         setup();
357     }
358 
359     private void setup() {
360         if (!mReady) {
361             mSetupPending = true;
362             return;
363         }
364 
365         if (getWidth() == 0 && getHeight() == 0) {
366             return;
367         }
368 
369         if (mBitmap == null) {
370             invalidate();
371             return;
372         }
373 
374         mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
375 
376         mBitmapPaint.setAntiAlias(true);
377         mBitmapPaint.setShader(mBitmapShader);
378 
379         mBorderPaint.setStyle(Paint.Style.STROKE);
380         mBorderPaint.setAntiAlias(true);
381         mBorderPaint.setColor(mBorderColor);
382         mBorderPaint.setStrokeWidth(mBorderWidth);
383 
384         mFillPaint.setStyle(Paint.Style.FILL);
385         mFillPaint.setAntiAlias(true);
386         mFillPaint.setColor(mFillColor);
387 
388         mBitmapHeight = mBitmap.getHeight();
389         mBitmapWidth = mBitmap.getWidth();
390 
391         mBorderRect.set(calculateBounds());
392         mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
393 
394         mDrawableRect.set(mBorderRect);
395         if (!mBorderOverlay && mBorderWidth > 0) {
396             mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
397         }
398         mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
399 
400         applyColorFilter();
401         updateShaderMatrix();
402         invalidate();
403     }
404 
405     private RectF calculateBounds() {
406         int availableWidth  = getWidth() - getPaddingLeft() - getPaddingRight();
407         int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
408 
409         int sideLength = Math.min(availableWidth, availableHeight);
410 
411         float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
412         float top = getPaddingTop() + (availableHeight - sideLength) / 2f;
413 
414         return new RectF(left, top, left + sideLength, top + sideLength);
415     }
416 
417     private void updateShaderMatrix() {
418         float scale;
419         float dx = 0;
420         float dy = 0;
421 
422         mShaderMatrix.set(null);
423 
424         if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
425             scale = mDrawableRect.height() / (float) mBitmapHeight;
426             dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
427         } else {
428             scale = mDrawableRect.width() / (float) mBitmapWidth;
429             dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
430         }
431 
432         mShaderMatrix.setScale(scale, scale);
433         mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
434 
435         mBitmapShader.setLocalMatrix(mShaderMatrix);
436     }
437 
438 }

 

xml使用方式实例:

 1 <LinearLayout
 2         android:layout_width="match_parent"
 3         android:layout_height="200dp"
 4         android:background="@color/color_main"
 5         android:gravity="center"
 6         android:orientation="vertical">
 7 
 8         <de.hdodenhof.circleimageview.CircleImageView
 9             android:id="@+id/profile_image"
10             android:layout_width="96dp"
11             android:layout_height="96dp"
12             android:src="@mipmap/timg"
13             app:civ_border_color="@color/color_white"
14             app:civ_border_width="2dp" />
15 
16 </LinearLayout>