博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android实时取景:用SurfaceView实现
阅读量:5820 次
发布时间:2019-06-18

本文共 6327 字,大约阅读时间需要 21 分钟。

对于基于摄像头的Android应用,实时取景是一个基本前提,通过前置或后置摄像头持续获取捕获到的内容,可以进一步做处理(人脸检测、美颜、滤镜等)。

所谓实时取景,简单说就是调用android的摄像头,把摄像头捕获的内容显示在apk的界面上。只要应用不关闭,相机就持续捕获,apk上看到的就是实时的取景了。

采用SurfaceView和Camera来做这件事。

是SDK自带的SurfaceView类而不是实现它的子类;在布局XML文件中使用SurfaceView而不是FrameLayout。因此,代码量很少也很容易理解。

从View到SurfaceView

android应用,和用户交互的GUI界面,是搭载在Activity上的。Activity创建的时候,往往会做setContentView(R.id.main_layout),这是根据xml布局文件设定要预先确定好的各种view对象,这些组件在xml中进行设计、设定。当然也可以在Java代码中进一步动态增加view对象。相当于layout作为各种view的容器。

android sdk自带了很多view的子类供使用。

View本身:继承自java.lang.Object类,实现了Drawable.Callback, KeyEvent.Callback, AccessibilitiyEventSource接口。

直接子类有:
AnalogClock:模拟时钟,有3个指针那种。
ImageView:显示图像。其实,任何Drawable对象都可以用ImageView来显示。
KeyboardView:内置键盘。
MediaRouteButton:媒体路由按钮(不太懂。似乎和多媒体、网络路由相关)
ProgressBar:(可视化)进度条展示。
Space:空白视图。轻量级视图。作用:在不同组件之间插入缝隙、间隔。
SurfaceView:提供了一个专门用于绘制的Surface。Surface的格式、尺寸可以控制。SurfaceView控制这个surface的绘制位置。。。
TextView:文本视图。
TextureView:纹理视图。sdk4.0之后的API中可用。常被拿来和SurfaceView比较。
ViewGroup:用来容纳其他view对象。是layout(布局)和view containers(视图容器)的基类。
ViewStub:视图存根。大小为0、不可见,用来占坑的,apk运行时把坑交给资源。

间接子类有:

AbsListView
AbsSeekBar
AbsSpinner
AbsoluteLayout
AdapterView
AdapterViewAnimator
AdapterViewFlipper
以及其他56个间接子类。

可以看到,SurfaceView和TextureView两个view子类,都能用于实时取景框的显示。但是考虑到TextureView需要开启硬件加速的支持,不考虑。以及,目前看来SurfaceView本身也能胜任实时取景的任务。

代码

layout文件:surfaceview_main.xml

看到很多教程用的都是FrameLayout,而不是SurfaceView。我很不理解:为什么不用SurfaceView呢?不好用吗?

anyway,我这里就用SurfaceView了,在我测试过的代码中是完全可用的。

java代码 ChrisActivity.java

Surface、SurfaceView、SurfaceHolder这三者相当于MVC的存在,Surface是数据,SurfaceView负责显示,SurfaceHolder控制了Surface。通过让Activity实现SurfaceHolder.Callback接口,开发者自行实现下面三个函数,开发者就完成内容的处理:

public void surfaceCreated(SurfaceHolder holder);public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);public void surfaceDestroyed(SurfaceHolder holder);

而具体到实现,一些额外的细节也要考虑到:相机的初始化和释放;应用暂停时释放相机,恢复时获取相机;屏幕方向与显示方向的一致。所以有如下代码:

package com.example.chris.myapplication;import android.app.Activity;import android.hardware.Camera;import android.os.Bundle;import android.util.Log;import android.view.Display;import android.view.Surface;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.WindowManager;import java.io.IOException;/** * Created by chris on 2017/6/25. * 网上找了一些博客、教程和代码,稍微有点头绪了,现在写自己的Activity代码 */@SuppressWarnings("deprecation")// TODO:把camera换成camera2接口??public class ChrisActivity extends Activity implements SurfaceHolder.Callback{    private static final String TAG = "ChrisAcvitity";    private Camera mCamera;    private SurfaceHolder mHolder;    private SurfaceView mView;    @Override    // 创建Activity时执行的动作    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.surfaceview_main);        mView = (SurfaceView) findViewById(R.id.surfaceView);        mHolder = mView.getHolder();        mHolder.addCallback(this);    }    @Override    // apk暂停时执行的动作:把相机关闭,避免占用导致其他应用无法使用相机    protected void onPause() {        super.onPause();        mCamera.setPreviewCallback(null);        mCamera.stopPreview();        mCamera.release();        mCamera = null;    }    @Override    // 恢复apk时执行的动作    protected void onResume() {        super.onResume();        if (null!=mCamera){            mCamera = getCameraInstance();            try {                mCamera.setPreviewDisplay(mHolder);                mCamera.startPreview();            } catch(IOException e) {                Log.d(TAG, "Error setting camera preview: " + e.getMessage());            }        }    }    // SurfaceHolder.Callback必须实现的方法    public void surfaceCreated(SurfaceHolder holder){        mCamera = getCameraInstance();        try {            mCamera.setPreviewDisplay(holder);            mCamera.startPreview();        } catch(IOException e) {            Log.d(TAG, "Error setting camera preview: " + e.getMessage());        }    }    // SurfaceHolder.Callback必须实现的方法    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){        refreshCamera(); // 这一步是否多余?在以后复杂的使用场景下,此步骤是必须的。        int rotation = getDisplayOrientation(); //获取当前窗口方向        mCamera.setDisplayOrientation(rotation); //设定相机显示方向    }    // SurfaceHolder.Callback必须实现的方法    public void surfaceDestroyed(SurfaceHolder holder){        mHolder.removeCallback(this);        mCamera.setPreviewCallback(null);        mCamera.stopPreview();        mCamera.release();        mCamera = null;    }    // === 以下是各种辅助函数 ===    // 获取camera实例    public static Camera getCameraInstance(){        Camera c = null;        try {            c = Camera.open();        } catch(Exception e){            Log.d("TAG", "camera is not available");        }        return c;    }    // 获取当前窗口管理器显示方向    private int getDisplayOrientation(){        WindowManager windowManager = getWindowManager();        Display display = windowManager.getDefaultDisplay();        int rotation = display.getRotation();        int degrees = 0;        switch (rotation){            case Surface.ROTATION_0:                degrees = 0;                break;            case Surface.ROTATION_90:                degrees = 90;                break;            case Surface.ROTATION_180:                degrees = 180;                break;            case Surface.ROTATION_270:                degrees = 270;                break;        }        android.hardware.Camera.CameraInfo camInfo =                new android.hardware.Camera.CameraInfo();        android.hardware.Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, camInfo);        // 这里其实还是不太懂:为什么要获取camInfo的方向呢?相当于相机标定??        int result = (camInfo.orientation - degrees + 360) % 360;        return result;    }    // 刷新相机    private void refreshCamera(){        if (mHolder.getSurface() == null){            // preview surface does not exist            return;        }        // stop preview before making changes        try {            mCamera.stopPreview();        } catch(Exception e){            // ignore: tried to stop a non-existent preview        }        // set preview size and make any resize, rotate or        // reformatting changes here        // start preview with new settings        try {            mCamera.setPreviewDisplay(mHolder);            mCamera.startPreview();        } catch (Exception e) {        }    }}

AndroidManifest.xml 记得添加相机权限

转载地址:http://bmzdx.baihongyu.com/

你可能感兴趣的文章
线性表4 - 数据结构和算法09
查看>>
C语言数据类型char
查看>>
Online Patching--EBS R12.2最大的改进
查看>>
Binary Search Tree Iterator leetcode
查看>>
uva-317-找规律
查看>>
Event事件的兼容性(转)
查看>>
我的2014-相对奢侈的生活
查看>>
zoj 2412 dfs 求连通分量的个数
查看>>
Java设计模式
查看>>
一文读懂 AOP | 你想要的最全面 AOP 方法探讨
查看>>
Spring Cloud 微服务分布式链路跟踪 Sleuth 与 Zipkin
查看>>
ORM数据库框架 SQLite 常用数据库框架比较 MD
查看>>
华为OJ 名字美丽度
查看>>
微信公众号与APP微信第三方登录账号打通
查看>>
onchange()事件的应用
查看>>
Windows 下最佳的 C++ 开发的 IDE 是什么?
查看>>
软件工程师成长为架构师必备的十项技能
查看>>
python 异常
查看>>
百度账号注销
查看>>
mysql-This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME 错误解决
查看>>