自定义圆角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>