圆形图片CircleImageView的使用和分析

xiaoxiao2021-02-28  100

在项目开发中,我们经常需要用到圆形图片效果,典型案例是用户头像的显示。 如图所示。 下面我们使用开源控件CircleImageView来实现该效果。 CircleImageView项目下载地址: https://github.com/hdodenhof/CircleImageView (1).CircleImageView的使用 首先我们将CircleImageView添加到gradle。 [html] view plain copy print ? dependencies {      compile 'de.hdodenhof:circleimageview:2.1.0'  }   dependencies { compile 'de.hdodenhof:circleimageview:2.1.0' }

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

[html] view plain copy print ? <declare-styleable name="CircleImageView">          <attr name="civ_border_width" format="dimension" />          <attr name="civ_border_color" format="color" />          <attr name="civ_border_overlay" format="boolean" />          <attr name="civ_fill_color" format="color" />      </declare-styleable>   <declare-styleable name="CircleImageView"> <attr name="civ_border_width" format="dimension" /> <attr name="civ_border_color" format="color" /> <attr name="civ_border_overlay" format="boolean" /> <attr name="civ_fill_color" format="color" /> </declare-styleable>

属性介绍:civ_border_width:   设置边框的宽度,默认为0,即无边框。 civ_border_color:    设置边框的颜色,默认为黑色。 civ_border_overlay:设置边框是否覆盖在图片上,默认为false,即边框在图片外圈。 civ_fill_color:           设置图片的底色,默认透明。 接下来在布局文件中引入CircleImageView。 [html] view plain copy print ? <de.hdodenhof.circleimageview.CircleImageView          xmlns:circleimageview="http://schemas.android.com/apk/res-auto"          android:id="@+id/imageview"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:src="@drawable/profile"          circleimageview:civ_border_color="@android:color/holo_red_light"          circleimageview:civ_border_overlay="false"          circleimageview:civ_border_width="2dp"          circleimageview:civ_fill_color="@android:color/holo_blue_light"/>   <de.hdodenhof.circleimageview.CircleImageView xmlns:circleimageview="http://schemas.android.com/apk/res-auto" android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/profile" circleimageview:civ_border_color="@android:color/holo_red_light" circleimageview:civ_border_overlay="false" circleimageview:civ_border_width="2dp" 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 print ? private void setup() {          // mReady和mSetupPending属性,在构造方法和setup()方法都有引用和赋值          // 目的是确保setup()内部逻辑的执行是在构造方法执行完毕之后,即获取到自定义属性参数之后          if (!mReady) {              mSetupPending = true;              return;          }            // 图片宽高为0,不往下执行          if (getWidth() == 0 && getHeight() == 0) {              return;          }            // 只有传入了图片,才会往下执行          if (mBitmap == null) {              invalidate();              return;          }            // BitmapShader类用来渲染头像          // 参数1:要处理的bitmap对象,参数2和3:指定模式为对图片的边缘进行拉伸          mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);            // 图片画笔:设置抗锯齿          mBitmapPaint.setAntiAlias(true);          // 图片画笔:设置渲染器          mBitmapPaint.setShader(mBitmapShader);            // 边框画笔:设置样式为描边          mBorderPaint.setStyle(Paint.Style.STROKE);          // 边框画笔:设置抗锯齿          mBorderPaint.setAntiAlias(true);          // 边框画笔:设置颜色值          mBorderPaint.setColor(mBorderColor);          // 边框画笔:设置宽度          mBorderPaint.setStrokeWidth(mBorderWidth);            // 底色画笔:设置样式为填充          mFillPaint.setStyle(Paint.Style.FILL);          // 底色画笔:设置抗锯齿          mFillPaint.setAntiAlias(true);          // 底色画笔:设置颜色          mFillPaint.setColor(mFillColor);            // 获取原图的高度          mBitmapHeight = mBitmap.getHeight();          // 获取原图的宽度          mBitmapWidth = mBitmap.getWidth();            // 设置边框的显示区域          mBorderRect.set(calculateBounds());          // 计算边框的半径          mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);            // 设置图片的显示区域为上面计算出来的mBorderRect          mDrawableRect.set(mBorderRect);          // 如果边框不是覆盖在图片之上,并且边框宽度大于0,扩大图片的显示区域,增加mBorderWidth-1          if (!mBorderOverlay && mBorderWidth > 0) {              mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);          }          // 计算图片的半径          mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);            // 该方法内部只有一行代码,mBitmapPaint.setColorFilter(mColorFilter),给bitmap添加滤镜          applyColorFilter();          updateShaderMatrix();// 下文会介绍          invalidate();// 下文会介绍      }   private void setup() { // mReady和mSetupPending属性,在构造方法和setup()方法都有引用和赋值 // 目的是确保setup()内部逻辑的执行是在构造方法执行完毕之后,即获取到自定义属性参数之后 if (!mReady) { mSetupPending = true; return; } // 图片宽高为0,不往下执行 if (getWidth() == 0 && getHeight() == 0) { return; } // 只有传入了图片,才会往下执行 if (mBitmap == null) { invalidate(); return; } // BitmapShader类用来渲染头像 // 参数1:要处理的bitmap对象,参数2和3:指定模式为对图片的边缘进行拉伸 mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); // 图片画笔:设置抗锯齿 mBitmapPaint.setAntiAlias(true); // 图片画笔:设置渲染器 mBitmapPaint.setShader(mBitmapShader); // 边框画笔:设置样式为描边 mBorderPaint.setStyle(Paint.Style.STROKE); // 边框画笔:设置抗锯齿 mBorderPaint.setAntiAlias(true); // 边框画笔:设置颜色值 mBorderPaint.setColor(mBorderColor); // 边框画笔:设置宽度 mBorderPaint.setStrokeWidth(mBorderWidth); // 底色画笔:设置样式为填充 mFillPaint.setStyle(Paint.Style.FILL); // 底色画笔:设置抗锯齿 mFillPaint.setAntiAlias(true); // 底色画笔:设置颜色 mFillPaint.setColor(mFillColor); // 获取原图的高度 mBitmapHeight = mBitmap.getHeight(); // 获取原图的宽度 mBitmapWidth = mBitmap.getWidth(); // 设置边框的显示区域 mBorderRect.set(calculateBounds()); // 计算边框的半径 mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f); // 设置图片的显示区域为上面计算出来的mBorderRect mDrawableRect.set(mBorderRect); // 如果边框不是覆盖在图片之上,并且边框宽度大于0,扩大图片的显示区域,增加mBorderWidth-1 if (!mBorderOverlay && mBorderWidth > 0) { mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f); } // 计算图片的半径 mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f); // 该方法内部只有一行代码,mBitmapPaint.setColorFilter(mColorFilter),给bitmap添加滤镜 applyColorFilter(); updateShaderMatrix();// 下文会介绍 invalidate();// 下文会介绍 } 上述setup()方法中使用到了updateShaderMatrix()方法。 [java] view plain copy print ? private void updateShaderMatrix() {          float scale;          float dx = 0;          float dy = 0;            mShaderMatrix.set(null);            // 这里取最小的缩放比例,以尽量保证图片的质量          // 判断如果宽度的比例大于高度的比例,取高度的缩放比,平移x轴方向          // 否则取宽度的缩放比,平移y轴方向          if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {              scale = mDrawableRect.height() / (float) mBitmapHeight;              dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;          } else {              scale = mDrawableRect.width() / (float) mBitmapWidth;              dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;          }            // 设置mShaderMatrix:缩放          mShaderMatrix.setScale(scale, scale);          // 设置mShaderMatrix:平移          mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);            // 将mShaderMatrix设置到mBitmapShader          mBitmapShader.setLocalMatrix(mShaderMatrix);      }   private void updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); // 这里取最小的缩放比例,以尽量保证图片的质量 // 判断如果宽度的比例大于高度的比例,取高度的缩放比,平移x轴方向 // 否则取宽度的缩放比,平移y轴方向 if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { scale = mDrawableRect.height() / (float) mBitmapHeight; dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = mDrawableRect.width() / (float) mBitmapWidth; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } // 设置mShaderMatrix:缩放 mShaderMatrix.setScale(scale, scale); // 设置mShaderMatrix:平移 mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top); // 将mShaderMatrix设置到mBitmapShader mBitmapShader.setLocalMatrix(mShaderMatrix); } 该方法的作用是设置mBitmapShader变量的mShaderMatrix参数,对图片进行缩放setScale()和平移postTranslate(),使图片的显示区域缩放到与mDrawableRect一致,并通过平移确保显示图片的中心位置。

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

[java] view plain copy print ? @Override      protected void onDraw(Canvas canvas) {          // 如果禁止显示圆形(setDisableCircularTransformation()方法可设置),则不往下执行。          if (mDisableCircularTransformation) {              super.onDraw(canvas);              return;          }            // 未设置图片,不往下执行          if (mBitmap == null) {              return;          }            // 如果底色不是透明,绘制底色          if (mFillColor != Color.TRANSPARENT) {              canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);          }          // 使用上面设置好的mDrawableRect、mDrawableRadius、mBitmapPaint,绘制圆形图片,即内圆          canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);          // 如果边框宽度大于0,使用上面设置好的mBorderRect、mBorderRadius、mBorderPaint,绘制圆形边框,即外圆          if (mBorderWidth > 0) {              canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);          }      }   @Override protected void onDraw(Canvas canvas) { // 如果禁止显示圆形(setDisableCircularTransformation()方法可设置),则不往下执行。 if (mDisableCircularTransformation) { super.onDraw(canvas); return; } // 未设置图片,不往下执行 if (mBitmap == null) { return; } // 如果底色不是透明,绘制底色 if (mFillColor != Color.TRANSPARENT) { canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint); } // 使用上面设置好的mDrawableRect、mDrawableRadius、mBitmapPaint,绘制圆形图片,即内圆 canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint); // 如果边框宽度大于0,使用上面设置好的mBorderRect、mBorderRadius、mBorderPaint,绘制圆形边框,即外圆 if (mBorderWidth > 0) { canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint); } } 在onDraw()方法中,使用setup()中设置好的各项参数,完成图片、底色和边框的绘制。到这里,流程执行结束。

转载请注明原文地址: https://www.6miu.com/read-50922.html

最新回复(0)