Android实时取景:用SurfaceView实现
对于基于摄像头的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了,在我测试过的代码中是完全可用的。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" tools:context="com.example.chris.myapplication.MainActivity"> <SurfaceView android:id="@+id/surfaceView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"/> </LinearLayout>
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 记得添加相机权限
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.chris.myapplication" > <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > <activity android:name=".ChrisActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
iOS Storyboard全解析
来源:http://iaiai.iteye.com/blog/1493956 Storyboard)是一个能够节省你很多设计手机App界面时间的新特性,下面,为了简明的说明Storyboard的效果,我贴上本教程所完成的Storyboard的截图: 现 在,你就可以清楚的看到这个应用究竟是干些什么的,也可以清楚的看到其中的各种关系,这就是Storyboard的强大之处了。如果你要制作一个页面很多 很复杂的App,Storyboard可以帮助你解决写很多重复的跳转方法的麻烦,节省很多时间,以便你能够完全的专注于核心功能的实现上。 开始 首先启动Xcode,新建一个工程,我们在这里使用Single View App Template,这个模板会提供一个类和一个Storyboard,免去我们自己创建的麻烦。 创建完成之后,Xcode的界面大概是这样的: 这 个新的工程由两个类:AppDelegate和ViewController以及一个Storyboard组成(如果你选择了两个设备会有两个 Storyboard),注意这个项目没有xib文件,让我们首先看看Storyboard是什么样的,双击...
- 下一篇
Android学习进阶和IoC
1.成为Android高手一般分为六个阶段: 第一阶段:熟练掌握Java SE,尤其是对其内部类、线程、并发、网络编程等需要深入研究;熟练掌握基于HTTP协议的编程,清楚POST和GET等请求方式流程和细节;能够进行基本的Java Web编程,如果能够使用Java EE则更好; 第二阶段:精通Android的核心API的使用,例如四大组件所涉及的API、Context等,精通核心界面的编程,例如ListView的编程;到达这个阶段已经能够做大部分基本的应用开发了; 第三阶段:精通应用框架的原理,尤其是对IoC的理解及其在Android应用框架中的应用,精通基本的23种设计模式在Android中的应用; 第四阶段:精通JNI,熟练Android类库中C/C++组件开发;并能够使用JNI机制把现有的C/C++组件移植成为应用框架的核心组件;具备修改和编写自己的应用框架的能力; 第五阶段:做出自己的Android系统,无论是底层还是上层都能够了如指掌;能够根据实际需要设计和实现比较大Android系统,例如带领比较大的团队做出自己的Android手机产品等; 第六阶段:势。一切的...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS关闭SELinux安全模块
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8