Android View背景自定义区域透明设置

背景

最近遇到一个新功能引导的需求,需要在整个页面上添加黑色的覆盖层,并且新增功能位置无覆盖,示意图如下:


示意图

思路

开始想能否在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

你可能感兴趣的:(Android View背景自定义区域透明设置)