You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android CameraX扫码界面如何绘制居中透明矩形遮罩?

现有代码核心问题

  • Paint属性配置错误:mSemiBlackPaint未设置填充样式Paint.Style.FILL,导致半透明背景绘制异常;单独绘制透明矩形的逻辑完全无效,透明色绘制到Canvas上不会产生任何效果,属于冗余代码。
  • 坐标计算错误:onDraw方法中使用getLeft()/getRight()/getTop()/getBottom()计算矩形坐标是错误的,这几个值是View相对于父布局的位置,Canvas的绘制坐标是View自身的内部坐标,应该使用0、getWidth()、getHeight()来计算。
  • 绘制逻辑冗余:同时调用了canvas.drawPath(mPath, mSemiBlackPaint)和canvas.drawColor()两次绘制半透黑色,属于多余操作,clipPath后的绘制完全没必要。
  • 矩形不符合需求:当前计算的矩形不是正方形,也不支持自定义边长或内边距。

修复后完整代码

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class QROverlay extends View {
    private final Paint mSemiBlackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Path mPath = new Path();
    private RectF mBoxRect = new RectF();

    // 两种配置模式:优先使用内边距,内边距为0时使用指定边长
    private int mBoxPadding = 0;
    private int mBoxSideLength = dp2px(240); // 默认边长240dp

    public QROverlay(Context context) {
        super(context);
        init();
    }

    public QROverlay(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public QROverlay(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mSemiBlackPaint.setStyle(Paint.Style.FILL);
        mSemiBlackPaint.setColor(Color.parseColor("#A6000000")); // 65%不透明度的黑色
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 计算居中的正方形扫码框
        int sideLength;
        if (mBoxPadding > 0) {
            sideLength = Math.min(w, h) - 2 * mBoxPadding;
        } else {
            sideLength = mBoxSideLength;
        }
        // 居中计算坐标
        float left = (w - sideLength) / 2f;
        float top = (h - sideLength) / 2f;
        float right = left + sideLength;
        float bottom = top + sideLength;
        mBoxRect.set(left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPath.reset();
        mPath.addRect(mBoxRect, Path.Direction.CW);
        mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
        canvas.drawPath(mPath, mSemiBlackPaint);
    }

    // 公开方法:设置扫码框内边距(基于View短边计算)
    public void setBoxPadding(int paddingPx) {
        this.mBoxPadding = paddingPx;
        this.mBoxSideLength = 0;
        requestLayout();
    }

    // 公开方法:设置扫码框边长
    public void setBoxSideLength(int sideLengthPx) {
        this.mBoxSideLength = sideLengthPx;
        this.mBoxPadding = 0;
        requestLayout();
    }

    private int dp2px(float dp) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * density + 0.5f);
    }
}

用法说明

  • 如需指定固定边长:调用setBoxSideLength方法传入边长像素值即可,代码中默认提供了dp转px的工具方法,也可自己传入适配后的像素值。
  • 如需按内边距计算边长:调用setBoxPadding方法传入内边距像素值,会自动基于View的短边计算居中正方形的边长。
  • 叠加到CameraX的预览View上时,需要保证QROverlay和预览View大小一致,放在同一个帧布局即可。

内容的提问来源于stack exchange,提问作者Shuba. . S hetty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 11:42:00