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

基于Camera2 API在SurfaceView绘图后内容消失问题求助

嘿,我帮你捋捋这个问题哈!你遇到的情况其实是Camera2 API的一个常见坑——当相机启动预览后,会持续往目标Surface(也就是你的mOverlay SurfaceView)输出帧数据,每一张预览帧都会把你之前画的内容覆盖掉。而你只在setUpCameraOutputs()里调用了一次drawonSurfaceView(),所以画的矩形刚出来就被新的预览帧冲没了😅

下面给你两个靠谱的解决思路,你可以根据自己的需求选:

方案一:分离预览层和绘图层(最推荐)

最稳妥的方式是把相机预览和自定义绘图拆成两个独立的View层,彻底避免互相干扰:

  1. 先调整布局,用FrameLayout把相机预览View(推荐用TextureView,比SurfaceView更灵活)和自定义绘图View叠在一起:
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 底层:相机预览 -->
    <TextureView
        android:id="@+id/camera_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <!-- 上层:自定义绘图,覆盖在预览上面 -->
    <com.yourpackage.OverlayDrawView
        android:id="@+id/overlay_draw"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>
  1. 写一个自定义的OverlayDrawView,把绘图逻辑放到onDraw()方法里:
public class OverlayDrawView extends View {
    private Paint mPaint;

    public OverlayDrawView(Context context) {
        super(context);
        initPaint();
    }

    public OverlayDrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.GREEN);
        mPaint.setStrokeWidth(3); // 给线条加个宽度,看得更清楚
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 在这里画你需要的矩形
        int viewWidth = getWidth();
        int viewHeight = getHeight();
        Rect rect = new Rect(50, 50, viewWidth - 300, viewHeight - 300);
        canvas.drawRect(rect, mPaint);
    }

    // 如果需要动态更新绘图(比如改变矩形位置),调用这个方法触发重绘
    public void refreshDraw() {
        invalidate();
    }
}
  1. 之后不管相机预览怎么刷新,你的绘图都在独立的上层View里,不会被覆盖。要是需要更新绘图,只要调用overlayDrawView.refreshDraw()就行。

方案二:在预览帧回调中重复绘图(适合必须用同一个SurfaceView的场景)

如果你一定要在相机预览的SurfaceView上绘图,那得让绘图逻辑跟着预览帧走,每来一帧就重新画一次:

  1. 首先,初始化ImageReader来监听预览帧的输出,然后在回调里触发绘图:
// 注意这里的previewSize要和相机预览的尺寸一致
ImageReader mPreviewReader = ImageReader.newInstance(
        previewSize.getWidth(), previewSize.getHeight(),
        ImageFormat.YUV_420_888, 2);

mPreviewReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
        // 一定要及时关闭Image,不然会内存泄漏!
        Image image = reader.acquireNextImage();
        if (image != null) {
            image.close();
        }
        // 每收到一帧预览,就重新画一次
        drawonSurfaceView();
    }
}, mBackgroundHandler); // mBackgroundHandler是你处理相机回调的后台Handler
  1. 调整你的drawonSurfaceView()方法,注意不要清空Canvas(不然会把预览帧也清掉):
private void drawonSurfaceView(){
    SurfaceHolder surfaceHolder = mOverlay.getHolder();
    if (!surfaceHolder.getSurface().isValid()) {
        return;
    }

    // 锁定Canvas时指定区域,避免清屏整个Surface
    Canvas canvas = surfaceHolder.lockCanvas();
    if (canvas == null) {
        return;
    }

    Paint paint = new Paint();
    paint.setStyle(Paint.Style.STROKE);
    paint.setColor(Color.GREEN);
    paint.setStrokeWidth(3);

    int canvasWidth = canvas.getWidth();
    int canvasHeight = canvas.getHeight();
    Rect rect = new Rect(50, 50, canvasWidth - 300, canvasHeight - 300);
    canvas.drawRect(rect, paint );

    surfaceHolder.unlockCanvasAndPost(canvas);
}

不过这个方案有个小缺点:绘图和预览帧的同步可能有延迟,而且频繁绘制可能影响性能,所以优先选方案一哈。

最后再提一句:千万别只在setUpCameraOutputs()里调用一次绘图,这个方法只是初始化相机参数,之后相机开始预览就会疯狂刷新Surface,必须让绘图逻辑重复执行才能保持显示~

内容的提问来源于stack exchange,提问作者Monica Das

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:43:56