图片的圆形效果

建议先阅读:自定义圆形的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图,可以利用这个,想绘制一个圆角的图,然后设置绘制模式,接着绘制一个矩形的图,两者一叠加,正好是用圆角图去截取矩形图,矩形图也就是我们的原图片了。

示例代码如下所示:

activity_main.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">

<ImageView
android:id="@+id/iv_round_image_one"
android:layout_margin="5dp"
android:layout_width="70dp"
android:layout_height="70dp"
/>

</LinearLayout>

dimens.xml:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="round_bitmap_width">70dp</dimen>
</resources>

MainActivity.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class MainActivity extends AppCompatActivity {

private ImageView mImageViewOne;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mImageViewOne = findViewById(R.id.iv_round_image_one);

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.logo);

mImageViewOne.setImageBitmap(RoundImageTools.toRoundCorner(bitmap,(int)getResources().getDimension(R.dimen.round_bitmap_width)));

}
}

实现圆形效果的代码:

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
public class RoundImageTools {

//对图片裁剪成圆形
public static Bitmap toRoundCorner(Bitmap bitmap, int widgetWidth) {
//裁剪除方形的Bitmap
Bitmap rawBitmap = dealRawBitmap(bitmap);
//对Bitmap进行缩放
Bitmap newBitmap = scaleBitmap(rawBitmap, widgetWidth);

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

Paint paint = new Paint();

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

//将原始图像裁剪成正方形
private static 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 static Bitmap scaleBitmap(Bitmap bitmap,int widgetWidth){
//一定要强转成float 不然有可能因为精度不够 出现 scale为0 的错误
float scale = (float)widgetWidth/(float)bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}

最终的效果图:
此处输入图片的描述

通过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
public class RoundImageTools {

....

//对图片裁剪成圆形
public static Bitmap toRoundCorner(Bitmap abimap, int widgetWidth) {

//裁剪除方形的Bitmap
Bitmap rawBitmap = dealRawBitmap(abimap);
//对Bitmap进行缩放
Bitmap newBitmap = scaleBitmap(rawBitmap, widgetWidth);

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

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

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

// 利用画笔将纹理图绘制到画布上面
canvas.drawCircle(newBitmap.getWidth()/2,newBitmap.getWidth()/2,newBitmap.getWidth()/2,paint);

return output;
}

....

}

通过画布裁剪的方式实现

关于画布裁剪的知识,建议阅读: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
public class RoundImageTools {

....

//对图片裁剪成圆形
public static Bitmap toRoundCornerC(Bitmap abimap, int widgetWidth) {

//裁剪除方形的Bitmap
Bitmap rawBitmap = dealRawBitmap(abimap);
//对Bitmap进行缩放
Bitmap newBitmap = scaleBitmap(rawBitmap, widgetWidth);

//指定为 ARGB_4444 可以减小图片大小
Bitmap output = Bitmap.createBitmap(newBitmap.getWidth(), newBitmap.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, newBitmap.getWidth(), newBitmap.getHeight());

Path path = new Path();
path.addCircle(newBitmap.getWidth()/2, newBitmap.getWidth()/2,newBitmap.getWidth()/2, Path.Direction.CCW);
canvas.clipPath(path, Region.Op.INTERSECT);

// 利用画笔将纹理图绘制到画布上面
canvas.drawBitmap(newBitmap,rect,rect,paint);

return output;
}

....
}

Demo链接:ImageViews

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