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

如何在展示H264解码内容的Android SurfaceView上绘制矩形

Android SurfaceView叠加绘制矩形解决方案

你可以根据自身业务需求选择以下两种实现方案,均不会干扰原有H264解码渲染逻辑:

方案1:叠加透明覆盖层(开发成本最低)

该方案通过在SurfaceView上层叠加一个同尺寸的透明自定义View专门绘制矩形,无需修改原有视频渲染逻辑,适配性最强:

  • 布局调整:将原有SurfaceView和自定义透明View放在同一个FrameLayout容器内,保证二者尺寸完全对齐
  • 自定义透明View需要做如下配置:
    • 初始化时调用setZOrderOnTop(true)保证View层级在SurfaceView之上,同时设置背景为透明色
    • 内部新增成员变量存储矩形坐标、宽高参数,以及绘制矩形用的Paint实例
    • 对外暴露更新矩形参数的方法,参数更新后调用invalidate()触发重绘
    • 重写onDraw()方法,参数合法时调用canvas.drawRect()绘制矩形
  • Socket接收矩形参数的逻辑中,收到新参数后需切换到主线程调用自定义View的参数更新方法,避免子线程刷新UI报错
  • 如果Socket传输的矩形参数是基于原始视频分辨率的坐标,需要先根据SurfaceView实际显示尺寸做比例换算再传入

方案2:直接在渲染画布绘制(性能最优)

该方案直接在视频渲染的同一画布上绘制矩形,无额外View的过度绘制开销,适合对性能要求高的场景:

  • 找到原有H264解码后渲染帧的逻辑位置,在视频帧渲染完成后、提交画布之前插入矩形绘制逻辑
  • 对矩形参数的读写加线程同步锁,避免Socket线程更新参数到一半时被渲染线程读取,出现坐标错乱
  • 直接使用渲染视频帧的同一个Canvas实例绘制矩形,绘制完成后再正常调用unlockCanvasAndPost()提交画布即可

坐标换算逻辑(两种方案通用)

如果Socket传入的矩形坐标基于原始视频分辨率,需要先做比例适配再绘制:

// 原始视频分辨率
int videoWidth = 1920;
int videoHeight = 1080;
// SurfaceView实际显示尺寸
int surfaceWidth = videoSurface.getWidth();
int surfaceHeight = videoSurface.getHeight();
// 比例换算
float scaleX = (float) surfaceWidth / videoWidth;
float scaleY = (float) surfaceHeight / videoHeight;
float realX = socketX * scaleX;
float realY = socketY * scaleY;
float realWidth = socketWidth * scaleX;
float realHeight = socketHeight * scaleY;

核心示例代码

自定义矩形覆盖层View

public class RectOverlayView extends View {
    private final Paint rectPaint;
    private RectF currentRect = null;

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

    public RectOverlayView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        rectPaint = new Paint();
        rectPaint.setColor(Color.RED);
        rectPaint.setStyle(Paint.Style.STROKE);
        rectPaint.setStrokeWidth(4f);
        rectPaint.setAntiAlias(true);
        // 配置透明背景和上层显示权限
        setBackgroundColor(Color.TRANSPARENT);
        setZOrderOnTop(true);
    }

    // 外部更新矩形参数,需在主线程调用
    public void updateRect(float x, float y, float width, float height) {
        currentRect = new RectF(x, y, x + width, y + height);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (currentRect != null) {
            canvas.drawRect(currentRect, rectPaint);
        }
    }
}

布局示例

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <SurfaceView
        android:id="@+id/video_surface"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <com.yourpackage.RectOverlayView
        android:id="@+id/rect_overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

Socket参数更新示例

// Socket子线程收到矩形参数后切换到主线程更新
runOnUiThread(() -> {
    // 可先做坐标换算再传入
    rectOverlay.updateRect(realX, realY, realWidth, realHeight);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 16:42:00