背景
最近遇到一個新功能引導的需求,需要在整個頁面上添加黑色的覆蓋層,并且新增功能位置無覆蓋,示意圖如下:
示意圖
思路
開始想能否在onDraw過程修改canvas,將指定區(qū)域去掉,感覺可以通過clipPath實現(xiàn),最終未果??。看了眾多資料找到可以從View的背景下手,自定義一個Drawable支持指定區(qū)域設置為透明??
實現(xiàn)
自定義Drawable代碼實現(xiàn)
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默認實現(xiàn)
srcPath.addRect(100, 100, 200, 200, Path.Direction.CW);
srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
srcPaint.setColor(0xffffffff);
}
/**
* 設置內(nèi)部透明的部分
*/
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,在構造函數(shù)中設置Drawable,onLayout時設置自定義的透明區(qū)域
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();
// 自定義透明區(qū)域
path.addRect(0, 0, 200, 200, Path.Direction.CW);
background.setSrcPath(path);
}
}
方法2:
動態(tài)設置
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);
參考文章