[关闭]
@946898963 2019-10-04T08:38:04.000000Z 字数 16925 阅读 1359

自定义圆形的ImageView

Android自定义控件 Android绘图


实现圆形的ImageView最简单的方式可以使用裁剪CardView来实现,但是裁剪的API只支持在Android 5.0及其以上的设备,所以适配起来可能会有问题,为了省去适配的麻烦,我们可以使用自定义控件的方式来实现圆形的ImageView。

通过Xfermode方式实现

使用画笔Paint去绘制东西,当绘制多个图层叠加的时候,有16中模式。效果如下图。

此处输入图片的描述

模式 说明
PorterDuff.Mode.CLEAR 所有绘制不会绘制到画布上
PorterDuff.Mode.SRC 显示上层绘制图形
PorterDuff.Mode.DST 显示下层绘制图形
PorterDuff.Mode.SRC_OVER 图形叠加,上层盖住下层
PorterDuff.Mode.DST_OVER 图形叠加,下层盖住上层
PorterDuff.Mode.SRC_IN 显示上层交集部分
PorterDuff.Mode.DST_IN 显示下层交集部分
PorterDuff.Mode.SRC_OUT 显示上层非交集部分
PorterDuff.Mode.DST_OUT 显示下层非交集部分
PorterDuff.Mode.SRC_ATOP 显示下层非交集部分和上层交集部分
PorterDuff.Mode.DST_ATOP 显示下层交集部分与上层非交集部分
PorterDuff.Mode.XOR 去除交集部分
PorterDuff.Mode.DARKEN 交集部分颜色加深
PorterDuff.Mode.LIGHTEN 交集部分颜色变亮
PorterDuff.Mode.MULTIPLY 显示交集部分,颜色混合叠加
PorterDuff.Mode.SCREEN 取两图层全部区域,交集部分变为透明色

官方demo中主要绘制代码如下:

  1. // mDstB是黄色的圆形图bitmap
  2. // mSrcB是蓝色的矩形图bitmap
  3. canvas.drawBitmap(mDstB, 0, 0, paint);
  4. paint.setXfermode(sModes[i]);
  5. canvas.drawBitmap(mSrcB, 0, 0, paint)

更详细的内容,建议阅读:Paint Xfermode 学习小结

可以看到在两个绘制图形过程中,添加Xfermode绘制模式,能够改变两个图的叠加效果,我们主要关注一下SrcIn模式,可以看见,用图层叠加的交集去截取mSrcB图,可以利用这个,先绘制一个圆角的图,然后设置绘制模式,接着绘制一个矩形的图,两者一叠加,正好是用圆角图去截取矩形图,矩形图也就是我们的原图片了。

代码如下所示:

  1. public class RoundImageViewA extends ImageView {
  2. private Paint paint = new Paint();
  3. public RoundImageViewA(Context context) {
  4. super(context);
  5. }
  6. public RoundImageViewA(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9. public RoundImageViewA(Context context, AttributeSet attrs, int defStyle) {
  10. super(context, attrs, defStyle);
  11. }
  12. @Override
  13. protected void onDraw(Canvas canvas) {
  14. Drawable drawable = getDrawable();
  15. if (null != drawable) {
  16. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  17. //处理Bitmap 转成正方形
  18. Bitmap newBitmap = dealRawBitmap(rawBitmap);
  19. //将newBitmap 转换成圆形
  20. Bitmap circleBitmap = toRoundCorner(newBitmap, 14);
  21. final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  22. // 重置画笔,不然会留下黑色区域
  23. paint.reset();
  24. //绘制到画布上
  25. canvas.drawBitmap(circleBitmap, rect, rect, paint);
  26. } else {
  27. super.onDraw(canvas);
  28. }
  29. }
  30. //将原始图像裁剪成正方形
  31. private Bitmap dealRawBitmap(Bitmap bitmap){
  32. int width = bitmap.getWidth();
  33. int height = bitmap.getHeight();
  34. //获取宽度
  35. int minWidth = width > height ? height:width ;
  36. //计算正方形的范围
  37. int leftTopX = (width - minWidth)/2;
  38. int leftTopY = (height - minWidth)/2;
  39. //裁剪成正方形
  40. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  41. //裁剪后也可以不进行缩放操作,而是将缩放操作放在最后去做。详情请阅读:https://blog.csdn.net/ydxlt/article/details/80307516
  42. return scaleBitmap(newBitmap);
  43. }
  44. //将头像按比例缩放
  45. private Bitmap scaleBitmap(Bitmap bitmap){
  46. int width = getWidth();
  47. //一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
  48. float scale = (float)width/(float)bitmap.getWidth();
  49. Matrix matrix = new Matrix();
  50. matrix.postScale(scale, scale);
  51. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  52. }
  53. private Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
  54. //指定为 ARGB_4444 可以减小图片大小
  55. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
  56. Canvas canvas = new Canvas(output);
  57. final int color = 0xff424242;
  58. final Rect rect = new Rect(0, 0,bitmap.getWidth(), bitmap.getHeight());
  59. paint.setAntiAlias(true);
  60. canvas.drawARGB(0, 0, 0, 0);
  61. paint.setColor(color);
  62. int x = bitmap.getWidth();
  63. canvas.drawCircle(x / 2, x / 2, x / 2, paint);
  64. paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
  65. canvas.drawBitmap(bitmap, rect, rect, paint);
  66. return output;
  67. }
  68. }

大多数情况下,要展示图片的控件(ImageView)的长宽和图片的长宽并不是一致的,甚至长宽比都不一致,所以在拿到一张图片时候,大多数情况下需要根据控件的大小对图片进行拉伸缩放处理,有人会问为什么不直接使用ImageView属性scaleType去控制拉伸缩放,这是因为当我们将一个Bitmap绘制成圆角后,再去进行拉伸缩放,圆角可能会变形,所以在Bitmap设置到控件之前就需要对Bitmap进行一下拉伸缩放处理,直接看下面代码。

  1. // 图片根据控件大小等比例缩放拉伸
  2. float widthScale = imageViewWidth * 1.0f / bitmap.getWidth();
  3. float heightScale = imageViewHeight * 1.0f / bitmap.getHeight();
  4. // 设置长宽拉伸缩放比
  5. Matrix matrix = new Matrix();
  6. matrix.setScale(widthScale, heightScale);
  7. // 拉伸缩放图片
  8. Bitmap newBt = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

拉伸缩放也可以在

  1. Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)

方法中控制。

利用这种缩放方式实现了RoundImageViewB,代码如下所示:

  1. public class RoundImageViewB extends ImageView {
  2. private Paint paint = new Paint();
  3. public RoundImageViewB(Context context) {
  4. super(context);
  5. }
  6. public RoundImageViewB(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9. public RoundImageViewB(Context context, AttributeSet attrs, int defStyle) {
  10. super(context, attrs, defStyle);
  11. }
  12. @Override
  13. protected void onDraw(Canvas canvas) {
  14. Drawable drawable = getDrawable();
  15. if (null != drawable) {
  16. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  17. //处理Bitmap 转成正方形
  18. Bitmap newBitmap = dealRawBitmap(rawBitmap);
  19. //将newBitmap 转换成圆形
  20. Bitmap circleBitmap = toRoundCorner(newBitmap, 14);
  21. final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  22. final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());
  23. paint.reset();
  24. //绘制到画布上
  25. canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);
  26. } else {
  27. super.onDraw(canvas);
  28. }
  29. }
  30. //将原始图像裁剪成正方形
  31. private Bitmap dealRawBitmap(Bitmap bitmap){
  32. int width = bitmap.getWidth();
  33. int height = bitmap.getHeight();
  34. //获取宽度
  35. int minWidth = width > height ? height:width ;
  36. //计算正方形的范围
  37. int leftTopX = (width - minWidth)/2;
  38. int leftTopY = (height - minWidth)/2;
  39. //裁剪成正方形
  40. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  41. return newBitmap;
  42. }
  43. private Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
  44. //指定为 ARGB_4444 可以减小图片大小
  45. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
  46. Canvas canvas = new Canvas(output);
  47. final int color = 0xff424242;
  48. final Rect rect = new Rect(0, 0,bitmap.getWidth(), bitmap.getHeight());
  49. paint.setAntiAlias(true);
  50. canvas.drawARGB(0, 0, 0, 0);
  51. paint.setColor(color);
  52. int x = bitmap.getWidth();
  53. canvas.drawCircle(x / 2, x / 2, x / 2, paint);
  54. paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
  55. canvas.drawBitmap(bitmap, rect, rect, paint);
  56. return output;
  57. }
  58. }

通过BitmapShader方式实现

所有的绘制圆角的实现,推荐使用这个方法,不仅仅可以帮助我们实现圆角,连部分圆角都可以实现,比如顶部是两个圆角,底部是两个直角的图片。

首先介绍一下BitmapShader这个类,它作为纹理用于绘制一张图。新图可以是纹理图重复/镜像/边缘像素拉伸而绘制成的新图。这个类构造函数很简单,BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY),第一个参数是Bitmap,作为纹理图传入,tileX是指在水平方向上的绘制方式,tileY是指在竖直方向上的绘制方式。TileMode有三种属性,拉伸、重复、镜像。

使用BitmapShader绘制图的时候,是从画布的左上角开始绘制的。我们是使用拉伸的绘制模式,直接来看一下代码,了解处理过程。

  1. /**
  2. * 圆形控件
  3. *
  4. * @author ljnalex
  5. *
  6. */
  7. public class RoundImageViewC extends ImageView {
  8. private Paint paint = new Paint();
  9. public RoundImageViewC(Context context) {
  10. super(context);
  11. }
  12. public RoundImageViewC(Context context, AttributeSet attrs) {
  13. super(context, attrs);
  14. }
  15. public RoundImageViewC(Context context, AttributeSet attrs, int defStyle) {
  16. super(context, attrs, defStyle);
  17. }
  18. @Override
  19. protected void onDraw(Canvas canvas) {
  20. Drawable drawable = getDrawable();
  21. if (null != drawable) {
  22. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  23. //处理Bitmap 转成正方形
  24. Bitmap newBitmap = dealRawBitmap(rawBitmap);
  25. //将newBitmap 转换成圆形
  26. Bitmap circleBitmap = toRoundCorner(newBitmap, 14);
  27. final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  28. paint.reset();
  29. //绘制到画布上
  30. canvas.drawBitmap(circleBitmap, rect, rect, paint);
  31. } else {
  32. super.onDraw(canvas);
  33. }
  34. }
  35. //将原始图像裁剪成正方形
  36. private Bitmap dealRawBitmap(Bitmap bitmap){
  37. int width = bitmap.getWidth();
  38. int height = bitmap.getHeight();
  39. //获取宽度
  40. int minWidth = width > height ? height:width ;
  41. //计算正方形的范围
  42. int leftTopX = (width - minWidth)/2;
  43. int leftTopY = (height - minWidth)/2;
  44. //裁剪成正方形
  45. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  46. return scaleBitmap(newBitmap);
  47. }
  48. //将头像按比例缩放
  49. private Bitmap scaleBitmap(Bitmap bitmap){
  50. int width = getWidth();
  51. //一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
  52. float scale = (float)width/(float)bitmap.getWidth();
  53. Matrix matrix = new Matrix();
  54. matrix.postScale(scale, scale);
  55. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  56. }
  57. private Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
  58. // 初始化绘制纹理图
  59. BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  60. //指定为 ARGB_4444 可以减小图片大小
  61. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
  62. Canvas canvas = new Canvas(output);
  63. // 初始化画笔
  64. Paint paint = new Paint();
  65. paint.setAntiAlias(true);
  66. paint.setShader(bitmapShader);
  67. // 利用画笔将纹理图绘制到画布上面
  68. canvas.drawCircle(bitmap.getWidth()/2,bitmap.getWidth()/2,bitmap.getWidth()/2,paint);
  69. return output;
  70. }
  71. }

首先初始化了绘制的纹理图。然后初始化了画布和画笔,设置画笔绘制的纹理图,画笔在绘制图形时候就不是使用单纯的颜色绘制了。最后在利用画笔在画布上面绘制出圆形图片。

利用Rect实现缩放的RoundImageViewD

  1. /**
  2. * 圆形控件
  3. * @author ljnalex
  4. */
  5. public class RoundImageViewD extends ImageView {
  6. private Paint paint = new Paint();
  7. public RoundImageViewD(Context context) {
  8. super(context);
  9. }
  10. public RoundImageViewD(Context context, AttributeSet attrs) {
  11. super(context, attrs);
  12. }
  13. public RoundImageViewD(Context context, AttributeSet attrs, int defStyle) {
  14. super(context, attrs, defStyle);
  15. }
  16. @Override
  17. protected void onDraw(Canvas canvas) {
  18. Drawable drawable = getDrawable();
  19. if (null != drawable) {
  20. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  21. //处理Bitmap 转成正方形
  22. Bitmap newBitmap = dealRawBitmap(rawBitmap);
  23. //将newBitmap 转换成圆形
  24. Bitmap circleBitmap = toRoundCorner(newBitmap, 14);
  25. final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  26. final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());
  27. paint.reset();
  28. //绘制到画布上
  29. canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);
  30. } else {
  31. super.onDraw(canvas);
  32. }
  33. }
  34. //将原始图像裁剪成正方形
  35. private Bitmap dealRawBitmap(Bitmap bitmap){
  36. int width = bitmap.getWidth();
  37. int height = bitmap.getHeight();
  38. //获取宽度
  39. int minWidth = width > height ? height:width ;
  40. //计算正方形的范围
  41. int leftTopX = (width - minWidth)/2;
  42. int leftTopY = (height - minWidth)/2;
  43. //裁剪成正方形
  44. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  45. return newBitmap;
  46. }
  47. private Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
  48. // 初始化绘制纹理图
  49. BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  50. //指定为 ARGB_4444 可以减小图片大小
  51. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
  52. Canvas canvas = new Canvas(output);
  53. // 初始化画笔
  54. Paint paint = new Paint();
  55. paint.setAntiAlias(true);
  56. paint.setShader(bitmapShader);
  57. // 利用画笔将纹理图绘制到画布上面
  58. canvas.drawCircle(bitmap.getWidth()/2,bitmap.getWidth()/2,bitmap.getWidth()/2,paint);
  59. return output;
  60. }
  61. }

利用BitmapShader还可以实现很多其他样式的效果,有下面几种:

具体请参考:Android圆角图片和圆形图片实现总结

通过画布裁剪的方式实现

关于画布裁剪的知识,建议阅读:Canvas的裁剪

  1. /**
  2. * 圆形控件
  3. *
  4. * @author zhangyan
  5. *
  6. */
  7. public class RoundImageViewE extends ImageView {
  8. private Paint paint = new Paint();
  9. public RoundImageViewE(Context context) {
  10. super(context);
  11. }
  12. public RoundImageViewE(Context context, AttributeSet attrs) {
  13. super(context, attrs);
  14. }
  15. public RoundImageViewE(Context context, AttributeSet attrs, int defStyle) {
  16. super(context, attrs, defStyle);
  17. }
  18. @Override
  19. protected void onDraw(Canvas canvas) {
  20. Drawable drawable = getDrawable();
  21. if (null != drawable) {
  22. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  23. //处理Bitmap 转成正方形
  24. Bitmap newBitmap = dealRawBitmap(rawBitmap);
  25. //将newBitmap 转换成圆形
  26. Bitmap circleBitmap = toRoundCorner(newBitmap);
  27. final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  28. paint.reset();
  29. //绘制到画布上
  30. canvas.drawBitmap(circleBitmap, rect, rect, paint);
  31. } else {
  32. super.onDraw(canvas);
  33. }
  34. }
  35. //将原始图像裁剪成正方形
  36. private Bitmap dealRawBitmap(Bitmap bitmap){
  37. int width = bitmap.getWidth();
  38. int height = bitmap.getHeight();
  39. //获取宽度
  40. int minWidth = width > height ? height:width ;
  41. //计算正方形的范围
  42. int leftTopX = (width - minWidth)/2;
  43. int leftTopY = (height - minWidth)/2;
  44. //裁剪成正方形
  45. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  46. return scaleBitmap(newBitmap);
  47. }
  48. //将头像按比例缩放
  49. private Bitmap scaleBitmap(Bitmap bitmap){
  50. int width = getWidth();
  51. //一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
  52. float scale = (float)width/(float)bitmap.getWidth();
  53. Matrix matrix = new Matrix();
  54. matrix.postScale(scale, scale);
  55. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  56. }
  57. private Bitmap toRoundCorner(Bitmap bitmap) {
  58. //指定为 ARGB_4444 可以减小图片大小
  59. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
  60. Canvas canvas = new Canvas(output);
  61. // 初始化画笔
  62. Paint paint = new Paint();
  63. paint.setAntiAlias(true);
  64. paint.setFilterBitmap(true);
  65. Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
  66. Path path = new Path();
  67. path.addCircle(bitmap.getWidth()/2, bitmap.getWidth()/2,bitmap.getWidth()/2, Path.Direction.CCW);
  68. canvas.clipPath(path, Region.Op.INTERSECT);
  69. // 利用画笔将纹理图绘制到画布上面
  70. canvas.drawBitmap(bitmap,rect,rect,paint);
  71. return output;
  72. }
  73. }

利用Rect实现缩放的RoundImageViewF。

  1. public class RoundImageViewF extends ImageView {
  2. private Paint paint = new Paint();
  3. public RoundImageViewF(Context context) {
  4. super(context);
  5. }
  6. public RoundImageViewF(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9. public RoundImageViewF(Context context, AttributeSet attrs, int defStyle) {
  10. super(context, attrs, defStyle);
  11. }
  12. @Override
  13. protected void onDraw(Canvas canvas) {
  14. Drawable drawable = getDrawable();
  15. if (null != drawable) {
  16. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  17. //处理Bitmap 转成正方形
  18. Bitmap newBitmap = dealRawBitmap(rawBitmap);
  19. Bitmap circleBitmap = toRoundCorner(newBitmap);
  20. final Rect rectSrc = new Rect(0, 0, newBitmap.getWidth(), newBitmap.getHeight());
  21. final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());
  22. paint.reset();
  23. //绘制到画布上
  24. canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);
  25. } else {
  26. super.onDraw(canvas);
  27. }
  28. }
  29. //将原始图像裁剪成正方形
  30. private Bitmap dealRawBitmap(Bitmap bitmap){
  31. int width = bitmap.getWidth();
  32. int height = bitmap.getHeight();
  33. //获取宽度
  34. int minWidth = width > height ? height:width ;
  35. //计算正方形的范围
  36. int leftTopX = (width - minWidth)/2;
  37. int leftTopY = (height - minWidth)/2;
  38. //裁剪成正方形
  39. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  40. return newBitmap;
  41. }
  42. private Bitmap toRoundCorner(Bitmap bitmap) {
  43. //指定为 ARGB_4444 可以减小图片大小
  44. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
  45. Canvas canvas = new Canvas(output);
  46. // 初始化画笔
  47. Paint paint = new Paint();
  48. paint.setAntiAlias(true);
  49. paint.setFilterBitmap(true);
  50. Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
  51. Path path = new Path();
  52. path.addCircle(bitmap.getWidth()/2, bitmap.getWidth()/2,bitmap.getWidth()/2, Path.Direction.CCW);
  53. canvas.clipPath(path, Region.Op.INTERSECT);
  54. // 利用画笔将纹理图绘制到画布上面
  55. canvas.drawBitmap(bitmap,rect,rect,paint);
  56. return output;
  57. }
  58. }

在面试的过程中阿里的面试官说这种方案会新建一个Bitmap,所以可能会导致内存占用率过高。我们可以改进上面的裁剪的实现方式,直接在原来的画布上进行裁剪,避免创建新的的Bitmap,改进后的代码如下所示:

  1. public class RoundImageViewE extends ImageView {
  2. private Paint paint = new Paint();
  3. public RoundImageViewE(Context context) {
  4. super(context);
  5. }
  6. public RoundImageViewE(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9. public RoundImageViewE(Context context, AttributeSet attrs, int defStyle) {
  10. super(context, attrs, defStyle);
  11. }
  12. @Override
  13. protected void onDraw(Canvas canvas) {
  14. Drawable drawable = getDrawable();
  15. if (null != drawable) {
  16. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  17. //处理Bitmap 转成正方形
  18. Bitmap circleBitmap = dealRawBitmap(rawBitmap);
  19. Path path = new Path();
  20. path.addCircle(getWidth()/2, getHeight()/2,getWidth()/2, Path.Direction.CCW);
  21. canvas.clipPath(path, Region.Op.INTERSECT);
  22. final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  23. paint.reset();
  24. //绘制到画布上
  25. canvas.drawBitmap(circleBitmap, rect, rect, paint);
  26. } else {
  27. super.onDraw(canvas);
  28. }
  29. }
  30. //将原始图像裁剪成正方形
  31. private Bitmap dealRawBitmap(Bitmap bitmap){
  32. int width = bitmap.getWidth();
  33. int height = bitmap.getHeight();
  34. //获取宽度
  35. int minWidth = width > height ? height:width ;
  36. //计算正方形的范围
  37. int leftTopX = (width - minWidth)/2;
  38. int leftTopY = (height - minWidth)/2;
  39. //裁剪成正方形
  40. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  41. return scaleBitmap(newBitmap);
  42. }
  43. //将头像按比例缩放
  44. private Bitmap scaleBitmap(Bitmap bitmap){
  45. int width = getWidth();
  46. //一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
  47. float scale = (float)width/(float)bitmap.getWidth();
  48. Matrix matrix = new Matrix();
  49. matrix.postScale(scale, scale);
  50. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  51. }
  52. }
  1. public class RoundImageViewF extends ImageView {
  2. private Paint paint = new Paint();
  3. public RoundImageViewF(Context context) {
  4. super(context);
  5. }
  6. public RoundImageViewF(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9. public RoundImageViewF(Context context, AttributeSet attrs, int defStyle) {
  10. super(context, attrs, defStyle);
  11. }
  12. @Override
  13. protected void onDraw(Canvas canvas) {
  14. Drawable drawable = getDrawable();
  15. if (null != drawable) {
  16. Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();
  17. //处理Bitmap 转成正方形
  18. Bitmap circleBitmap = dealRawBitmap(rawBitmap);
  19. Path path = new Path();
  20. path.addCircle(getWidth()/2, getHeight()/2,getWidth()/2, Path.Direction.CCW);
  21. canvas.clipPath(path, Region.Op.INTERSECT);
  22. final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
  23. final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());
  24. paint.reset();
  25. //绘制到画布上
  26. canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);
  27. } else {
  28. super.onDraw(canvas);
  29. }
  30. }
  31. //将原始图像裁剪成正方形
  32. private Bitmap dealRawBitmap(Bitmap bitmap){
  33. int width = bitmap.getWidth();
  34. int height = bitmap.getHeight();
  35. //获取宽度
  36. int minWidth = width > height ? height:width ;
  37. //计算正方形的范围
  38. int leftTopX = (width - minWidth)/2;
  39. int leftTopY = (height - minWidth)/2;
  40. //裁剪成正方形
  41. Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
  42. return newBitmap;
  43. }
  44. }

Demo链接:ImageViews

参考链接:

【Android】自定义圆形ImageView(圆形头像 可指定大小)(XferMode方式主要参考自这篇文章)

Android自定义圆角矩形ImageView,支持Glide加载图片及颜色填充&&Android圆角图片和圆形图片实现总结(利用Rect对Bitmap进行缩放,参考自这两篇文章)

Android圆角图片和圆形图片实现总结(BitmapShader方式主要参考自这篇文章,这篇文章也详细的介绍了Xfermode的相关知识)

各个击破搞明白PorterDuff.Mode(推荐阅读这个)

Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解

细数PorterDuffXferMode的几个坑, PorterDuffXferMode不正确的真正原因

android中对Canvas.drawCircle()方法的理解

  1. mPaint.setStyle(Paint.Style.STROKE);//空心效果 只会画出圆弧

如果不设置的话,画圆是实心的圆

android canvas void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)

Bitmap createBitmap参数(三)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注