返回介绍

Android 开发 摄像头 SurfaceView 预览 背景带矩形框 实现

发布于 2025-02-26 12:46:18 字数 6723 浏览 0 评论 0 收藏 0

原理:双 surfaceview,顶层画矩形框,底层预览视频

为了能在摄像头预览的时候,背景有个矩形框、或一些坐标、横线来标示关键位置,真让杂家费劲心思了。苦苦研究了两天,毫无进展。baidu 了若干资料,大都是提出这个问题,但怎么解决的没有说,都不了了之。后来转而 google,又研究了两天,终于完美解决啦!

1,网上介绍的摄像头预览,一般是用一个 surfaceview,为了能让其正常预览视频,设置属性

mySurfaceView.setZOrderOnTop(true); 
mySurfaceHolder = mySurfaceView.getHolder();
mySurfaceHolder.setFormat(PixelFormat.TRANSPARENT)

我先前也是在这种思路下,看能否加画个矩形框。这篇 http://blog.sina.com.cn/s/blog_4e6922ab01010gfz.html 文章提到一种方法,是用一个 surfaceview,然后自己定义一个继承 View 的画矩形框的类 DrawCaptureRect,经我实践,发现还是不中。用这种思路,在摄像头未预览的时候可以看到红色矩形框,但一点击预览,矩形框就被覆盖了。

2,还有一种思路是,在预览 surfaceview 的 callback 里面,取到 canvas 画布,然后画。这种思路也行不通,程序直接挂掉!

3,后来我想到了 framelayout,用帧布局,融合第一种方法的思路,结果还是未能解决预览的时候,视频遮挡矩形框的问题。

4,还有人提出,给 surfaceview 或其所在的布局文件里加一个带矩形框的图片的方法,更是行不通!说这话的人真是站着不腰疼,别说背景设成带图片的矩形框,就是背景设成有颜色的,预览的 surfaceview 就不显示了,被遮挡了。

原来,当 SurfaceHolder 对象的类型设置为 SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS 时就只能拍照不能绘制了 ,这就是为什么第二种思路程序会直接挂掉的原因。为了能够预览视频的同时绘制矩形框等信息,需要用两个同样大小的 SurfaceView 放在一个 FrameLayout 里,顶层的 SurfaceView 设成 setZOrderOnTop(true); setFormat(PixelFormat.TRANSPARENT);预览的 Surfaceview 这两个属性就不用设置了。

首先在包 yan.guoqi.testphoto 下新建一个类 SVDraw,如图:

源码如下:

package yan.guoqi.testphoto;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

/*定义一个画矩形框的类*/
public class SVDraw extends SurfaceView implements SurfaceHolder.Callback{

  protected SurfaceHolder sh;
  private int mWidth;
  private int mHeight;
  public SVDraw(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
    sh = getHolder();
    sh.addCallback(this);
    sh.setFormat(PixelFormat.TRANSPARENT);
    setZOrderOnTop(true);
  }

  public void surfaceChanged(SurfaceHolder arg0, int arg1, int w, int h) {
    // TODO Auto-generated method stub
    mWidth = w;
    mHeight = h;
  }

  public void surfaceCreated(SurfaceHolder arg0) {
    // TODO Auto-generated method stub

  }

  public void surfaceDestroyed(SurfaceHolder arg0) {
    // TODO Auto-generated method stub

  }
  void clearDraw()
  {
    Canvas canvas = sh.lockCanvas();
    canvas.drawColor(Color.BLUE);
    sh.unlockCanvasAndPost(canvas);
  }
  public void drawLine()
  {
    Canvas canvas = sh.lockCanvas();
    canvas.drawColor(Color.TRANSPARENT);
    Paint p = new Paint();
    p.setAntiAlias(true);
    p.setColor(Color.RED);
    p.setStyle(Style.STROKE);
    //canvas.drawPoint(100.0f, 100.0f, p);
    canvas.drawLine(0,110, 500, 110, p);
    canvas.drawCircle(110, 110, 10.0f, p);
    sh.unlockCanvasAndPost(canvas);

  }

}

类里面的方法 drawLine() ​就是画预览视频时背景矩形的,由于我需画一条线,上面程序就是画一条线和一个圆的。需要画什么图形,就在这里改!

注意在建这个类时构造函数里一定要有

public SVDraw(Context context, AttributeSet attrs) {
super(context, attrs);

AttributeSet attrs 这个参数,否则是不能在 xml 文件里正常解析的。先前提到的 http://blog.sina.com.cn/s/blog_4e6922ab01010gfz.html 这位就没有引用这个参数,所以后来用了

addContentView(mDraw, new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT))

将这个对象加到布局中去!

上面的类建好之后,在布局文件里引用他,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" >

  <FrameLayout
    android:id="@+id/myFramelayout"
    android:layout_width="fill_parent"
    android:layout_height="800px"
    android:orientation="vertical" >

    <SurfaceView
      android:id="@+id/mySurfaceView"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:gravity="center"
      android:visibility="visible" />

    <yan.guoqi.testphoto.SVDraw
      android:id="@+id/mDraw"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent" />
  </FrameLayout>

  <LinearLayout
    android:id="@+id/LinearLayout01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="horizontal"
    android:paddingTop="10dip" >

    <Button
      android:id="@+id/btnPreview"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="预览"
      android:textSize="28sp" />

    <Button
      android:id="@+id/btnPhoto"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="拍照"
      android:textSize="28sp" />

    <Button
      android:id="@+id/btnSave"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="保存"
      android:textSize="28sp" />
  </LinearLayout>

</LinearLayout>

注意上面两个 surfaceview 是同样大小的,mySurfaceView 是预览视频的,mDraw 是用来画矩形的。

接下来就是在主 Activity 里引用这个 mDraw。首先定义变量 private SVDraw mSVDraw = null; 然后 mSVDraw = (yan.guoqi.testphoto.SVDraw)findViewByIf(R.id.mDraw)。之后 mSVDraw 就可以用了,至于什么时候来画这个矩形,有人是在按下拍照按钮后,我选择的是在按下预览按钮后,

case R.id.btnPreview:
  Toast.makeText(TestPhotoActivity.this,
    "您按了预览按钮",
    Toast.LENGTH_SHORT).show();
  initCamera();
  mDraw.setVisibility(View.VISIBLE);
  mDraw.drawLine();
  break;

让其可见,然后调用 drawLine()方法就可以了!你可以随时控制让其可见或者隐藏!

将上面这些代码融合我 http://blog.csdn.net/yanzi1225627/article/details/7926994 文章里 http://download.csdn.net/detail/yanzi1225627/4538626 这里的源码就可以了!即可以预览,自动聚焦,缩放标准大小,背景有矩形框!

来张图,有图有真相大笑

反思:其实用第一种 http://blog.sina.com.cn/s/blog_4e6922ab01010gfz.html 这里的思路和本篇的思路合并起来,应该也是可行的,只不过一个是继承的 view 一个是 surfaceview,将 AttributeSet attrs 加上。只要处理好谁是顶层的 view 谁设成透明,预览视频的 surfaceview 设成底层,在且要在 xml 属性文件里设成 visible 就可以了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文