基于Camera2 API在SurfaceView绘图后内容消失问题求助
嘿,我帮你捋捋这个问题哈!你遇到的情况其实是Camera2 API的一个常见坑——当相机启动预览后,会持续往目标Surface(也就是你的mOverlay SurfaceView)输出帧数据,每一张预览帧都会把你之前画的内容覆盖掉。而你只在setUpCameraOutputs()里调用了一次drawonSurfaceView(),所以画的矩形刚出来就被新的预览帧冲没了😅
下面给你两个靠谱的解决思路,你可以根据自己的需求选:
方案一:分离预览层和绘图层(最推荐)
最稳妥的方式是把相机预览和自定义绘图拆成两个独立的View层,彻底避免互相干扰:
- 先调整布局,用
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>
- 写一个自定义的
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(); } }
- 之后不管相机预览怎么刷新,你的绘图都在独立的上层View里,不会被覆盖。要是需要更新绘图,只要调用
overlayDrawView.refreshDraw()就行。
方案二:在预览帧回调中重复绘图(适合必须用同一个SurfaceView的场景)
如果你一定要在相机预览的SurfaceView上绘图,那得让绘图逻辑跟着预览帧走,每来一帧就重新画一次:
- 首先,初始化
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
- 调整你的
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
相关产品推荐
相关产品推荐

