圆形图片CircleImageView的使用和分析 在项目开发中,我们经常需要用到圆形图片效果,典型案例是用户头像的显示。

在项目开发中,我们经常需要用到圆形图片效果,典型案例是用户头像的显示。

如图所示。


下面我们使用开源控件CircleImageView来实现该效果。

CircleImageView项目下载地址:
https://github.com/hdodenhof/CircleImageView

(1).CircleImageView的使用

首先我们将CircleImageView添加到gradle。

  1. dependencies {
  2. compile 'de.hdodenhof:circleimageview:2.1.0'
  3. }

然后看一下自定义属性attrs:

  1. <declare-styleable name="CircleImageView">
  2. <attr name="civ_border_width" format="dimension" />
  3. <attr name="civ_border_color" format="color" />
  4. <attr name="civ_border_overlay" format="boolean" />
  5. <attr name="civ_fill_color" format="color" />
  6. </declare-styleable>


属性介绍:civ_border_width:   设置边框的宽度,默认为0,即无边框。
civ_border_color:    设置边框的颜色,默认为黑色。
civ_border_overlay:设置边框是否覆盖在图片上,默认为false,即边框在图片外圈。
civ_fill_color:           设置图片的底色,默认透明。

接下来在布局文件中引入CircleImageView。
  1. <de.hdodenhof.circleimageview.CircleImageView
  2. xmlns:circleimageview= "http://schemas.android.com/apk/res-auto"
  3. android:id= "@+id/imageview"
  4. android:layout_width= "wrap_content"
  5. android:layout_height= "wrap_content"
  6. android:src= "@drawable/profile"
  7. circleimageview:civ_border_color= "@android:color/holo_red_light"
  8. circleimageview:civ_border_overlay= "false"
  9. circleimageview:civ_border_width= "2dp"
  10. circleimageview:civ_fill_color= "@android:color/holo_blue_light"/>

注意:CircleImageView的默认ScaleType为CENTER_CROP,且只能为CENTER_CROP。

(2).CircleImageView的源码分析

通过查看源码可以看到,内部核心方法主要是对Paint、Canvas、BitmapShader、Matrix、RectF类的使用。

我们分析一下代码的执行流程。首先可以通过布局文件中的src属性或者java代码的setImageXxx()方法来设置图片,控件在构造方法中获取到自定义属性的参数值,然后会执行到setup()这个方法。

我们来分析一下setup()方法,该方法是CircleImageView的核心。


  1. private void setup() {
  2. // mReady和mSetupPending属性,在构造方法和setup()方法都有引用和赋值
  3. // 目的是确保setup()内部逻辑的执行是在构造方法执行完毕之后,即获取到自定义属性参数之后
  4. if (!mReady) {
  5. mSetupPending = true;
  6. return;
  7. }
  8. // 图片宽高为0,不往下执行
  9. if (getWidth() == 0 && getHeight() == 0) {
  10. return;
  11. }
  12. // 只有传入了图片,才会往下执行
  13. if (mBitmap == null) {
  14. invalidate();
  15. return;
  16. }
  17. // BitmapShader类用来渲染头像
  18. // 参数1:要处理的bitmap对象,参数2和3:指定模式为对图片的边缘进行拉伸
  19. mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  20. // 图片画笔:设置抗锯齿
  21. mBitmapPaint.setAntiAlias( true);
  22. // 图片画笔:设置渲染器
  23. mBitmapPaint.setShader(mBitmapShader);
  24. // 边框画笔:设置样式为描边
  25. mBorderPaint.setStyle(Paint.Style.STROKE);
  26. // 边框画笔:设置抗锯齿
  27. mBorderPaint.setAntiAlias( true);
  28. // 边框画笔:设置颜色值
  29. mBorderPaint.setColor(mBorderColor);
  30. // 边框画笔:设置宽度
  31. mBorderPaint.setStrokeWidth(mBorderWidth);
  32. // 底色画笔:设置样式为填充
  33. mFillPaint.setStyle(Paint.Style.FILL);
  34. // 底色画笔:设置抗锯齿
  35. mFillPaint.setAntiAlias( true);
  36. // 底色画笔:设置颜色
  37. mFillPaint.setColor(mFillColor);
  38. // 获取原图的高度
  39. mBitmapHeight = mBitmap.getHeight();
  40. // 获取原图的宽度
  41. mBitmapWidth = mBitmap.getWidth();
  42. // 设置边框的显示区域
  43. mBorderRect.set(calculateBounds());
  44. // 计算边框的半径
  45. mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
  46. // 设置图片的显示区域为上面计算出来的mBorderRect
  47. mDrawableRect.set(mBorderRect);
  48. // 如果边框不是覆盖在图片之上,并且边框宽度大于0,扩大图片的显示区域,增加mBorderWidth-1
  49. if (!mBorderOverlay && mBorderWidth > 0) {
  50. mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
  51. }
  52. // 计算图片的半径
  53. mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
  54. // 该方法内部只有一行代码,mBitmapPaint.setColorFilter(mColorFilter),给bitmap添加滤镜
  55. applyColorFilter();
  56. updateShaderMatrix(); // 下文会介绍
  57. invalidate(); // 下文会介绍
  58. }

上述setup()方法中使用到了updateShaderMatrix()方法。
  1. private void updateShaderMatrix() {
  2. float scale;
  3. float dx = 0;
  4. float dy = 0;
  5. mShaderMatrix.set( null);
  6. // 这里取最小的缩放比例,以尽量保证图片的质量
  7. // 判断如果宽度的比例大于高度的比例,取高度的缩放比,平移x轴方向
  8. // 否则取宽度的缩放比,平移y轴方向
  9. if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
  10. scale = mDrawableRect.height() / ( float) mBitmapHeight;
  11. dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
  12. } else {
  13. scale = mDrawableRect.width() / ( float) mBitmapWidth;
  14. dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
  15. }
  16. // 设置mShaderMatrix:缩放
  17. mShaderMatrix.setScale(scale, scale);
  18. // 设置mShaderMatrix:平移
  19. mShaderMatrix.postTranslate(( int) (dx + 0.5f) + mDrawableRect.left, ( int) (dy + 0.5f) + mDrawableRect.top);
  20. // 将mShaderMatrix设置到mBitmapShader
  21. mBitmapShader.setLocalMatrix(mShaderMatrix);
  22. }

该方法的作用是设置mBitmapShader变量的mShaderMatrix参数,对图片进行缩放setScale()和平移postTranslate(),使图片的显示区域缩放到与mDrawableRect一致,并通过平移确保显示图片的中心位置。

在setup()方法中最后一行执行invalidate(),触发View的onDraw()方法。


  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. // 如果禁止显示圆形(setDisableCircularTransformation()方法可设置),则不往下执行。
  4. if (mDisableCircularTransformation) {
  5. super.onDraw(canvas);
  6. return;
  7. }
  8. // 未设置图片,不往下执行
  9. if (mBitmap == null) {
  10. return;
  11. }
  12. // 如果底色不是透明,绘制底色
  13. if (mFillColor != Color.TRANSPARENT) {
  14. canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);
  15. }
  16. // 使用上面设置好的mDrawableRect、mDrawableRadius、mBitmapPaint,绘制圆形图片,即内圆
  17. canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
  18. // 如果边框宽度大于0,使用上面设置好的mBorderRect、mBorderRadius、mBorderPaint,绘制圆形边框,即外圆
  19. if (mBorderWidth > 0) {
  20. canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
  21. }
  22. }

在onDraw()方法中,使用setup()中设置好的各项参数,完成图片、底色和边框的绘制。到这里,流程执行结束。


版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/ruancoder/article/details/51889505

如图所示。


下面我们使用开源控件CircleImageView来实现该效果。

CircleImageView项目下载地址:
https://github.com/hdodenhof/CircleImageView

(1).CircleImageView的使用

首先我们将CircleImageView添加到gradle。

  1. dependencies {
  2. compile 'de.hdodenhof:circleimageview:2.1.0'
  3. }

然后看一下自定义属性attrs:

  1. <declare-styleable name="CircleImageView">
  2. <attr name="civ_border_width" format="dimension" />
  3. <attr name="civ_border_color" format="color" />
  4. <attr name="civ_border_overlay" format="boolean" />
  5. <attr name="civ_fill_color" format="color" />
  6. </declare-styleable>


属性介绍:civ_border_width:   设置边框的宽度,默认为0,即无边框。
civ_border_color:    设置边框的颜色,默认为黑色。
civ_border_overlay:设置边框是否覆盖在图片上,默认为false,即边框在图片外圈。
civ_fill_color:           设置图片的底色,默认透明。

接下来在布局文件中引入CircleImageView。
  1. <de.hdodenhof.circleimageview.CircleImageView
  2. xmlns:circleimageview= "http://schemas.android.com/apk/res-auto"
  3. android:id= "@+id/imageview"
  4. android:layout_width= "wrap_content"
  5. android:layout_height= "wrap_content"
  6. android:src= "@drawable/profile"
  7. circleimageview:civ_border_color= "@android:color/holo_red_light"
  8. circleimageview:civ_border_overlay= "false"
  9. circleimageview:civ_border_width= "2dp"
  10. circleimageview:civ_fill_color= "@android:color/holo_blue_light"/>

注意:CircleImageView的默认ScaleType为CENTER_CROP,且只能为CENTER_CROP。

(2).CircleImageView的源码分析

通过查看源码可以看到,内部核心方法主要是对Paint、Canvas、BitmapShader、Matrix、RectF类的使用。

我们分析一下代码的执行流程。首先可以通过布局文件中的src属性或者java代码的setImageXxx()方法来设置图片,控件在构造方法中获取到自定义属性的参数值,然后会执行到setup()这个方法。

我们来分析一下setup()方法,该方法是CircleImageView的核心。


  1. private void setup() {
  2. // mReady和mSetupPending属性,在构造方法和setup()方法都有引用和赋值
  3. // 目的是确保setup()内部逻辑的执行是在构造方法执行完毕之后,即获取到自定义属性参数之后
  4. if (!mReady) {
  5. mSetupPending = true;
  6. return;
  7. }
  8. // 图片宽高为0,不往下执行
  9. if (getWidth() == 0 && getHeight() == 0) {
  10. return;
  11. }
  12. // 只有传入了图片,才会往下执行
  13. if (mBitmap == null) {
  14. invalidate();
  15. return;
  16. }
  17. // BitmapShader类用来渲染头像
  18. // 参数1:要处理的bitmap对象,参数2和3:指定模式为对图片的边缘进行拉伸
  19. mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  20. // 图片画笔:设置抗锯齿
  21. mBitmapPaint.setAntiAlias( true);
  22. // 图片画笔:设置渲染器
  23. mBitmapPaint.setShader(mBitmapShader);
  24. // 边框画笔:设置样式为描边
  25. mBorderPaint.setStyle(Paint.Style.STROKE);
  26. // 边框画笔:设置抗锯齿
  27. mBorderPaint.setAntiAlias( true);
  28. // 边框画笔:设置颜色值
  29. mBorderPaint.setColor(mBorderColor);
  30. // 边框画笔:设置宽度
  31. mBorderPaint.setStrokeWidth(mBorderWidth);
  32. // 底色画笔:设置样式为填充
  33. mFillPaint.setStyle(Paint.Style.FILL);
  34. // 底色画笔:设置抗锯齿
  35. mFillPaint.setAntiAlias( true);
  36. // 底色画笔:设置颜色
  37. mFillPaint.setColor(mFillColor);
  38. // 获取原图的高度
  39. mBitmapHeight = mBitmap.getHeight();
  40. // 获取原图的宽度
  41. mBitmapWidth = mBitmap.getWidth();
  42. // 设置边框的显示区域
  43. mBorderRect.set(calculateBounds());
  44. // 计算边框的半径
  45. mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
  46. // 设置图片的显示区域为上面计算出来的mBorderRect
  47. mDrawableRect.set(mBorderRect);
  48. // 如果边框不是覆盖在图片之上,并且边框宽度大于0,扩大图片的显示区域,增加mBorderWidth-1
  49. if (!mBorderOverlay && mBorderWidth > 0) {
  50. mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
  51. }
  52. // 计算图片的半径
  53. mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
  54. // 该方法内部只有一行代码,mBitmapPaint.setColorFilter(mColorFilter),给bitmap添加滤镜
  55. applyColorFilter();
  56. updateShaderMatrix(); // 下文会介绍
  57. invalidate(); // 下文会介绍
  58. }

上述setup()方法中使用到了updateShaderMatrix()方法。
  1. private void updateShaderMatrix() {
  2. float scale;
  3. float dx = 0;
  4. float dy = 0;
  5. mShaderMatrix.set( null);
  6. // 这里取最小的缩放比例,以尽量保证图片的质量
  7. // 判断如果宽度的比例大于高度的比例,取高度的缩放比,平移x轴方向
  8. // 否则取宽度的缩放比,平移y轴方向
  9. if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
  10. scale = mDrawableRect.height() / ( float) mBitmapHeight;
  11. dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
  12. } else {
  13. scale = mDrawableRect.width() / ( float) mBitmapWidth;
  14. dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
  15. }
  16. // 设置mShaderMatrix:缩放
  17. mShaderMatrix.setScale(scale, scale);
  18. // 设置mShaderMatrix:平移
  19. mShaderMatrix.postTranslate(( int) (dx + 0.5f) + mDrawableRect.left, ( int) (dy + 0.5f) + mDrawableRect.top);
  20. // 将mShaderMatrix设置到mBitmapShader
  21. mBitmapShader.setLocalMatrix(mShaderMatrix);
  22. }

该方法的作用是设置mBitmapShader变量的mShaderMatrix参数,对图片进行缩放setScale()和平移postTranslate(),使图片的显示区域缩放到与mDrawableRect一致,并通过平移确保显示图片的中心位置。

在setup()方法中最后一行执行invalidate(),触发View的onDraw()方法。


  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. // 如果禁止显示圆形(setDisableCircularTransformation()方法可设置),则不往下执行。
  4. if (mDisableCircularTransformation) {
  5. super.onDraw(canvas);
  6. return;
  7. }
  8. // 未设置图片,不往下执行
  9. if (mBitmap == null) {
  10. return;
  11. }
  12. // 如果底色不是透明,绘制底色
  13. if (mFillColor != Color.TRANSPARENT) {
  14. canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);
  15. }
  16. // 使用上面设置好的mDrawableRect、mDrawableRadius、mBitmapPaint,绘制圆形图片,即内圆
  17. canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
  18. // 如果边框宽度大于0,使用上面设置好的mBorderRect、mBorderRadius、mBorderPaint,绘制圆形边框,即外圆
  19. if (mBorderWidth > 0) {
  20. canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
  21. }
  22. }

在onDraw()方法中,使用setup()中设置好的各项参数,完成图片、底色和边框的绘制。到这里,流程执行结束。


猜你喜欢

转载自blog.csdn.net/daimengs/article/details/80943850