圆形图片(或者给头像加个描边)CircleImageView的使用和分析

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

如图所示。


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

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

(1).CircleImageView的使用

首先我们将CircleImageView添加到gradle。

[html]  view plain  copy
  1. dependencies {  
  2.     compile 'de.hdodenhof:circleimageview:2.1.0'  
  3. }  

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

[html]  view plain  copy
  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。(给头像加描边使用circleimageview:civ_border_color和circleimageview:civ_border_width即可
[html]  view plain  copy
  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的核心。


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

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

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

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


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

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


转自:https://blog.csdn.net/ruancoder/article/details/51889505

猜你喜欢

转载自blog.csdn.net/LI_YU_CSDN/article/details/80337246