1、Paint.setsetXfermode(PorterDuffXfermode xfermode)
PorterDuffXfermode的构造函数如下:
public PorterDuffXfermode(PorterDuff.Mode mode)
PorterDuff.Mode表示混合模式,枚举值有18个,这里使用Mode.SRC_IN
来实现圆形。代码很简单:
private void init(Context context) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(0xffffffff);
porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.width = w;
this.height = h;
}
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
super.onDraw(canvas);
return;
}
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
int layerId = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
//画目标图像
canvas.drawCircle(width / 2, height / 2, width / 2, mPaint);
//设置混合模式
mPaint.setXfermode(porterDuffXfermode);
//画源图像
canvas.drawBitmap(bitmap, 0, 0, mPaint);
canvas.restoreToCount(layerId);
}
混合模式设置成SRC_IN时,会根据目标图像的透明度来决定源图像的显示与否。在这里目标图像是一个圆心在控件中心,直径为控件的宽(由于是圆形,控件需设置成宽高一致)的圆形,所以在圆形之外的目标图像的透明度为0,则最终显示为圆形图片。
需要注意的是:在使用setXfermode需要调用saveLayer,saveLayer()会生成一个全新的透明的Bitmap,这个Bitmap大小就是我们指定保存的区域大小,在调用saveLayer后所有操作都在这个bitmap上进行的。
在画源图像时,会把之前画布上所有内容都作为目标图像,而在saveLayer新生成的bitmap上,只有圆形,所以除了与圆形相交之外的位置都是空像素。
如果没有调用saveLayer,那么在画源图像时,目标图像是原始画布上所有图像,此时与圆形相交之外的位置不再是空像素了。这样也无法实现圆形的效果了。
如果调用canvas.save()
是不是也可以呢?并不行:save()并不会生成一个新的透明像素的bitmap,它只是保存了画布的状态。
2、Paint.setShader(BitmapShader shader)
BitmapShader,它的构造函数如下:
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY)
这个就相当于PhotoShop中的图案印章工具,bitmap用来指定图案,tileX用来指定当X轴超出单个图片大小时时所使用的重复策略,同样tileY用于指定当Y轴超出单个图片大小时时所使用的重复策略
其中TileMode的取值有:
- TileMode.CLAMP:用边缘色彩填充多余空间
- TileMode.REPEAT:重复原图像来填充多余空间
- TileMode.MIRROR:重复使用镜像模式的图像来填充多余空间
简单了解了BitmapShader之后,直接上代码
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
super.onDraw(canvas);
return;
}
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
mPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
canvas.drawCircle(width / 2, height / 2, width / 2, mPaint);
mPaint.setShader(null);
}
代码很简单就这几行,不过使用Shader需要注意的就是:无论使用绘图函数绘制多大一块,在哪绘制,都和Shader无关。因为Shader总是在控件的左上角开始,而你绘制的部分只是显示出来的部分而已。没有显示出来的部分,虽然已经生成但是没有显示而已。
3 View.setOutlineProvider(ViewOutlineProvider provider)
在5.0以后Android提供了ViewOutlineProvider,可以动态的为View设置轮廓,具体使用如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
circleImageView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0,0,view.getWidth(),view.getHeight());
}
});
circleImageView.setClipToOutline(true);
}
网友评论