Android View背景自定義區(qū)域透明設置

背景

最近遇到一個新功能引導的需求,需要在整個頁面上添加黑色的覆蓋層,并且新增功能位置無覆蓋,示意圖如下:


示意圖

思路

開始想能否在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);

參考文章

https://cloud.tencent.com/developer/article/1743017

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容