自定义圆角矩形的ImageView

实现圆角矩形的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
2
3
4
5
// mDstB是黄色的圆形图bitmap
// mSrcB是蓝色的矩形图bitmap
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB, 0, 0, paint)

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

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class RoundRectImageViewA extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewA(Context context) {
super(context);
}

public RoundRectImageViewA(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewA(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

//处理Bitmap 转成正方形
Bitmap newBitmap = scaleBitmap(rawBitmap);
//将newBitmap 转换成圆形
Bitmap circleBitmap = toRoundCorner(newBitmap, 30);

final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
paint.reset();
//绘制到画布上
canvas.drawBitmap(circleBitmap, rect, rect, paint);
} else {
super.onDraw(canvas);
}
}


//将头像按比例缩放
private Bitmap scaleBitmap(Bitmap bitmap){
int width = getWidth();
int height = getHeight();
//一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
float scaleX = (float)width/(float)bitmap.getWidth();
float scaleY = (float) height / (float) bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleX, scaleY);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

}

private Bitmap toRoundCorner(Bitmap bitmap, int radius) {

//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

final int color = 0xff424242;
final Rect rect = new Rect(0, 0,bitmap.getWidth(), bitmap.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
int x = bitmap.getWidth();
canvas.drawRoundRect(new RectF(0,0,bitmap.getWidth(),bitmap.getHeight()), radius,radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}

}

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

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

拉伸缩放也可以在

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

方法中控制。

利用这种缩放方式实现了RoundRectImageViewB(有问题,还未找到原因),代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class RoundRectImageViewB extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewB(Context context) {
super(context);
}

public RoundRectImageViewB(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewB(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();


//将newBitmap 转换成圆形
Bitmap circleBitmap = toRoundCorner(rawBitmap, 30);

final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());

paint.reset();
//绘制到画布上
canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);

} else {
super.onDraw(canvas);
}
}


//将原始图像裁剪成正方形
private Bitmap dealRawBitmap(Bitmap bitmap){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//获取宽度
int minWidth = width > height ? height:width ;
//计算正方形的范围
int leftTopX = (width - minWidth)/2;
int leftTopY = (height - minWidth)/2;
//裁剪成正方形
Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
return newBitmap;
}

//将头像按比例缩放
private Bitmap scaleBitmap(Bitmap bitmap){
int width = getWidth();
//一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
float scale = (float)width/(float)bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

}

private Bitmap toRoundCorner(Bitmap bitmap, int radius) {

//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

final int color = 0xff424242;
final Rect rect = new Rect(0, 0,bitmap.getWidth(), bitmap.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
int x = bitmap.getWidth();
canvas.drawRoundRect(new RectF(0,0,bitmap.getWidth(),bitmap.getHeight()), radius,radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
}

通过BitmapShader方式实现

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

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

  • TileMode.CLAMP 拉伸绘制,并不是指图片拉伸,而是指图片最后一个像素不断绘制,纹理图水平或者竖直方向最后一个像素不断绘制
  • TileMode.REPEAT 重复绘制,在水平或者竖直方向上不断重复绘制纹理图
  • TileMode.MIRROR 镜像绘制,水平或者竖直方向不断的绘制翻转纹理图

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class RoundRectImageViewC extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewC(Context context) {
super(context);
}

public RoundRectImageViewC(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewC(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

Bitmap newBitmap = scaleBitmap(rawBitmap);

Bitmap circleBitmap = toRoundCorner(newBitmap, 30);

final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());

paint.reset();

//绘制到画布上
canvas.drawBitmap(circleBitmap, rect, rect, paint);
} else {
super.onDraw(canvas);
}
}


//将头像按比例缩放
private Bitmap scaleBitmap(Bitmap bitmap){
int width = getWidth();
int height = getHeight();
//一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
float scaleX = (float)width/(float)bitmap.getWidth();
float scaleY = (float) height / (float) bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleX, scaleY);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

private Bitmap toRoundCorner(Bitmap bitmap, int radius) {

// 初始化绘制纹理图
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

// 初始化画笔
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(bitmapShader);

// 利用画笔将纹理图绘制到画布上面
canvas.drawRoundRect(new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()), radius, radius, paint);

return output;

}
}

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

利用Rect实现缩放的RoundImageViewD(有问题,还未找到原因)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class RoundRectImageViewD extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewD(Context context) {
super(context);
}

public RoundRectImageViewD(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewD(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

Bitmap circleBitmap = toRoundCorner(rawBitmap, 30);

final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());

final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());

paint.reset();

//绘制到画布上
canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);
} else {
super.onDraw(canvas);
}
}



private Bitmap toRoundCorner(Bitmap bitmap, int radius) {

// 初始化绘制纹理图
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

// 初始化画笔
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(bitmapShader);

// 利用画笔将纹理图绘制到画布上面
canvas.drawRoundRect(new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()), radius, radius, paint);

return output;

}
}

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

  • 底部两个圆角,顶部两个直角,
  • 四角都是圆角
  • 同边圆角,底部圆角/顶部圆角/左边圆角/右边圆角
  • 对角线圆角,左上右下圆角/左下右上圆角
  • 单个圆角,左上圆角/左下圆角/右上圆角/右下圆角
  • 三个圆角,左上非圆角/左下非圆角/右上非圆角/右下非圆角

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

通过画布裁剪的方式实现

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class RoundRectImageViewE extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewE(Context context) {
super(context);
}

public RoundRectImageViewE(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewE(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

//处理Bitmap 转成正方形
Bitmap newBitmap = dealRawBitmap(rawBitmap);
//将newBitmap 转换成圆形
Bitmap circleBitmap = toRoundCorner(newBitmap,30);

final Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
paint.reset();
//绘制到画布上
canvas.drawBitmap(circleBitmap, rect, rect, paint);
} else {
super.onDraw(canvas);
}
}


//将原始图像裁剪成正方形
private Bitmap dealRawBitmap(Bitmap bitmap){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//获取宽度
int minWidth = width > height ? height:width ;
//计算正方形的范围
int leftTopX = (width - minWidth)/2;
int leftTopY = (height - minWidth)/2;
//裁剪成正方形
Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
return scaleBitmap(newBitmap);
}

//将头像按比例缩放
private Bitmap scaleBitmap(Bitmap bitmap){
int width = getWidth();
int height = getHeight();
//一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
float scaleX = (float)width/(float)bitmap.getWidth();
float scaleY = (float) height / (float) bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleX, scaleY);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

}
private Bitmap toRoundCorner(Bitmap bitmap,int radius) {


//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

// 初始化画笔
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);

Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

Path path = new Path();
path.addRoundRect(new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()), radius, radius, Path.Direction.CW);
canvas.clipPath(path, Region.Op.INTERSECT);

canvas.drawBitmap(bitmap,rect,rect,paint);

return output;
}
}

通过Rect缩放实现的RoundRectImageViewF。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* 圆形控件
*
* @author zhangyan
*
*/
public class RoundRectImageViewF extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewF(Context context) {
super(context);
}

public RoundRectImageViewF(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewF(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

//处理Bitmap 转成正方形
Bitmap newBitmap = dealRawBitmap(rawBitmap);
//将newBitmap 转换成圆形
Bitmap circleBitmap = toRoundCorner(newBitmap,30);

final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());

paint.reset();
//绘制到画布上
canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);
} else {
super.onDraw(canvas);
}
}


//将原始图像裁剪成正方形
private Bitmap dealRawBitmap(Bitmap bitmap){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//获取宽度
int minWidth = width > height ? height:width ;
//计算正方形的范围
int leftTopX = (width - minWidth)/2;
int leftTopY = (height - minWidth)/2;
//裁剪成正方形
Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
return newBitmap;
}


private Bitmap toRoundCorner(Bitmap bitmap,int radius) {


//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

// 初始化画笔
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);

Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

Path path = new Path();
path.addRoundRect(new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()), radius, radius, Path.Direction.CW);
canvas.clipPath(path, Region.Op.INTERSECT);

canvas.drawBitmap(bitmap,rect,rect,paint);

return output;
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* 圆形控件
*
* @author zhangyan
*
*/
public class RoundRectImageViewE extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewE(Context context) {
super(context);
}

public RoundRectImageViewE(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewE(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

//处理Bitmap 转成正方形
Bitmap circleBitmap = dealRawBitmap(rawBitmap);

Path path = new Path();
path.addRoundRect(new RectF(0, 0, getWidth(), getHeight()), 30, 30, Path.Direction.CW);
canvas.clipPath(path, Region.Op.INTERSECT);

Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());

paint.reset();
//绘制到画布上
canvas.drawBitmap(circleBitmap, rect, rect, paint);

} else {
super.onDraw(canvas);
}
}


//将原始图像裁剪成正方形
private Bitmap dealRawBitmap(Bitmap bitmap){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//获取宽度
int minWidth = width > height ? height:width ;
//计算正方形的范围
int leftTopX = (width - minWidth)/2;
int leftTopY = (height - minWidth)/2;
//裁剪成正方形
Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
return scaleBitmap(newBitmap);
}

//将头像按比例缩放
private Bitmap scaleBitmap(Bitmap bitmap){
int width = getWidth();
int height = getHeight();
//一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
float scaleX = (float)width/(float)bitmap.getWidth();
float scaleY = (float) height / (float) bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleX, scaleY);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

}

}

(实现的圆角矩形没有任何问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class RoundRectImageViewF extends ImageView {

private Paint paint = new Paint();

public RoundRectImageViewF(Context context) {
super(context);
}

public RoundRectImageViewF(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RoundRectImageViewF(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap rawBitmap =((BitmapDrawable)drawable).getBitmap();

//处理Bitmap 转成正方形
Bitmap circleBitmap = dealRawBitmap(rawBitmap);

Path path = new Path();
path.addRoundRect(new RectF(0, 0, getWidth(), getHeight()), 30, 30, Path.Direction.CW);
canvas.clipPath(path, Region.Op.INTERSECT);

final Rect rectSrc = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());
final Rect rectDes = new Rect(0, 0, getWidth(), getHeight());
paint.reset();
//绘制到画布上
canvas.drawBitmap(circleBitmap, rectSrc, rectDes, paint);


} else {
super.onDraw(canvas);
}
}

//将原始图像裁剪成正方形
private Bitmap dealRawBitmap(Bitmap bitmap){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//获取宽度
int minWidth = width > height ? height:width ;
//计算正方形的范围
int leftTopX = (width - minWidth)/2;
int leftTopY = (height - minWidth)/2;
//裁剪成正方形
Bitmap newBitmap = Bitmap.createBitmap(bitmap,leftTopX,leftTopY,minWidth,minWidth,null,false);
return newBitmap;
}

}

Demo链接:ImageViews

参考链接:

Android自定义圆角矩形ImageView,支持Glide加载图片及颜色填充(XferMode方式主要参考自这篇文章)

【Android】自定义圆形ImageView(圆形头像 可指定大小)

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

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

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

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

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

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

Bitmap createBitmap参数(三)

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器