背景
最近遇到一个新功能引导的需求,需要在整个页面上添加黑色的覆盖层,并且新增功能位置无覆盖,示意图如下:
思路
开始想能否在onDraw过程修改canvas,将指定区域去掉,感觉可以通过clipPath实现,最终未果。看了众多资料找到可以从View的背景下手,自定义一个Drawable支持指定区域设置为透明
实现
自定义Drawable代码实现
class CustomDrawable extends Drawable {
private final Paint srcPaint;
private final Drawable innerDrawable;
/**
* 使用时需要自定义path
*/
private Path srcPath = new Path();
public CustomDrawable(Drawable innerDrawable) {
this.innerDrawable = innerDrawable;
// path默认实现
srcPath.addRect(100, 100, 200, 200, Path.Direction.CW);
srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
srcPaint.setColor(0xffffffff);
}
/**
* 设置内部透明的部分
*/
public void setSrcPath(Path srcPath) {
this.srcPath = srcPath;
}
@Override
public void draw(@NonNull Canvas canvas) {
innerDrawable.setBounds(getBounds());
if (srcPath == null || srcPath.isEmpty()) {
innerDrawable.draw(canvas);
} else {
// 将绘制操作保存到新的图层
int saveCount = canvas.saveLayer(0, 0, getBounds().width(), getBounds().height(), srcPaint,
Canvas.ALL_SAVE_FLAG);
// 绘制目标图
innerDrawable.draw(canvas);
// 设置混合模式
srcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// src 绘制源图
canvas.drawPath(srcPath, srcPaint);
// 清除混合模式
srcPaint.setXfermode(null);
// 还原画布
canvas.restoreToCount(saveCount);
}
}
@Override
public void setAlpha(int alpha) {
innerDrawable.setAlpha(alpha);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
innerDrawable.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return innerDrawable.getOpacity();
}
}
使用
方式1:
自定义View,在构造函数中设置Drawable,onLayout时设置自定义的透明区域
public class CustomFL extends FrameLayout {
private CustomDrawable background;
public CustomFL(@NonNull Context context) {
super(context);
init();
}
public CustomFL(@NonNull Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomFL(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
background = new CustomDrawable(getBackground());
setBackground(background);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
resetBackgroundHoleArea();
}
private void resetBackgroundHoleArea() {
Path path = new Path();
// 自定义透明区域
path.addRect(0, 0, 200, 200, Path.Direction.CW);
background.setSrcPath(path);
}
}
方法2:
动态设置
FrameLayout frameLayout = findViewById(R.id.fl_layout);
CustomDrawable drawable = new CustomDrawable(frameLayout.getBackground());
Path path = new Path();
path.addRoundRect(left, top, right, bottom, Path.Direction.CW);
drawable.setSrcPath(path);
frameLayout.setBackground(drawable);
参考文章
https://cloud.tencent.com/developer/article/1743017