Android - TextureView, SurfaceView和GLSurfaceView 以及 SurfaceTexture

本文解析了Android中SurfaceView、GLSurfaceView与TextureView的区别及应用场景,并介绍了SurfaceTexture的作用与工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这几个概念比较绕, 又比较相近。 初看比较糊涂, 把握关键点就好。

关键字 View

SurfaceView
GLSurfaceView
TextureView
这三个后缀都是View, 所以这三个东西都是用来显示的, 和普通的View差不多, 类似TextView等.

通过历史来说明:
SurfaceView 出现最早, 解决类似视频播放的问题(可以用单独一个线程来渲染UI)。
后来发现用起来不方便, 渲染线程需要单独编写, 一大堆都可以独立成模板。
所以后来就出现了 GLSurfaceView, 概括一句话就是 使用了模板的 SurfaceView
再后来发现GLSurfaceView不能根据屏幕的变化而变化, 这是由于GLSurfaceView同应用的Surface不是在同一层导致的问题。
人们就想到把这个GLSurfaceView弄到应用的Surface中, 所以就产生了TextureView.

关键字 Texture

SurfaceTexture
这个东西不是用来显示给用户看的, 它是一个Texture, 意识是纹理, 可以想象成一个View的中间件。
举个例子:
1 : Camera 把视频采集的内容交给 SurfaceView, 就变成了照相机。
2:Camera 把视频采集的内容交给 SurfaceTextureSurfaceTexture 在对内容做个美颜, 然后SurfaceTexture 再把内容交给 SurfaceView, 就变成了美图秀秀。

这些都是比较通俗的东西, 先有个理论再去深入了解就事半功倍。
推荐一篇:
http://blog.youkuaiyun.com/jinzhuojun/article/details/44062175

<think>首先,用户的问题是:“我想了解在Android平台上如何实现基础多SURFACE配置,包括预览拍照功能的实现方法。”用户提到了“多SURFACE配置”,意思是配置多个Surface,比如用于预览拍照的Surface。这似乎在引用[1]到[5]中提到的Android相机实现方案。从引用中:-引用[1]提到了SurfaceViewSurfaceTextureGLSurfaceViewTextureView等,这些都是用于相机预览的视图类型。-引用[2]展示了如何使用TextureViewSurfaceTexture实现相机预览拍照。-引用[3]讨论了camera2API在Android13.0上的实现问题。-引用[4]描述了使用Camera2API实现预览拍照。-引用[5]也提到了TextureViewSurfaceTexture实现预览。用户上次的问题可能与此相关,但在这个上下文中,用户是第一次问这个问题,所以系统指令说“请尽可能多的参考用户上一次的问题你的回答”,但由于这是首次交互,我只能参考给定的引用用户当前的查询。关键点是实现“基础多SURFACE配置”,包括预览拍照。在Android相机开发中,预览通常需要一个Surface来显示图像,拍照则可能需要另一个Surface或机制来捕获图像。在Camera2API中(从Android5.0+开始),支持创建多个目标Surfaces。例如:-一个Surface用于预览:比如TextureViewSurfaceView提供的Surface。-另一个Surface用于拍照:比如ImageReader对象提供的Surface,它可以用于捕获JPEG图像。用户的问题焦点是“多SURFACE配置”,所以我要覆盖如何配置多个Surface来实现预览拍照功能。系统指令要求:-所有行内数学表达式用$...$。-独立公式用$$...$$并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识。因此,回答应该:-以中文清晰解释如何实现。-结构清晰,一步步解决问题。-参考给定的引用。-末尾添加相关问题。回答结构:1.**介绍**:简述Android中多Surface配置的需求。2.**核心组件**:解释相关视图类如TextureViewSurfaceTexture、SurfaceHolder等。3.**使用Camera2API实现多Surface**:描述如何配置预览拍照的Surface。-预览使用TextureViewSurfaceView-拍照使用ImageReader创建Surface。-将两个Surface添加到相机设备。4.**代码示例**:提供简化的代码片段,展示核心逻辑。-使用引用[2][4]作为参考。5.**注意事项**:讨论权限、方向处理等(引用[3]中提到的问题)。6.**总结**:简述关键点。7.**相关问题**:生成3-5个相关问题。生成相关问题:-根据主题,如优化方法、常见问题、与旧CameraAPI的区别等。-参考系统级的示例格式。例如:§§相关问题§§1.如何在AndroidCamera2API中处理图像方向问题?2.ImageReader在拍照中的作用是什么?3.比较SurfaceViewTextureView在预览中的优缺点。4.实现多Surface配置的性能考虑。确保引用自然添加。例如,解释Surface时引用[1],使用Camera2时引用[4],方向处理引用[3]。核心内容:-在Camera2API中,可以使用`CameraDevice.createCaptureSession()`创建捕获会话,传入一个或多个Surface。这样,预览拍照可以同时配置。-预览:使用`TextureView`的`SurfaceTexture`创建Surface。-拍照:使用`ImageReader.newInstance()`创建一个ImageReader对象,然后获取其Surface。-然后,请求捕获操作。引用对应:-[^1]:介绍视图类。-[^2]:TextureView示例。-[^3]:方向镜像处理。-[^4]:Camera2实现。-[^5]:权限基本配置。回答需要用中文,保持专业性。</think> ### Android平台多SURFACE配置实现相机预览与拍照功能 在Android平台上实现相机预览与拍照功能时,常常需要配置多个Surface,以满足预览显示图像捕获的同步需求。这通常依赖于Camera2 API(Android 5.0+),它可以创建多个目标Surface(如预览拍照各一个)。以下是一个清晰、分步的实现方法,基于核心组件如`TextureView`(或`SurfaceView`)、`SurfaceTexture``ImageReader`。关键点在于使用`CameraDevice.createCaptureSession()`创建一个捕获会话,同时指定多个Surface,从而实现多Surface配置。 在开发中,多Surface配置的核心场景是: - **预览Surface**:实时显示相机图像流(通常使用`TextureView`或`SurfaceView`)。 - **拍照Surface**:捕获静态图像(如使用`ImageReader`提供的Surface)。 下面一步步解释实现方法。参考了Android开发文档相关实践[^1][^2][^3][^4]。 #### 1. **准备工作:权限布局** 首先,在`AndroidManifest.xml`中添加必要权限,包括相机权限存储权限。多Surface配置不需要额外权限,但基础权限是必需的[^4][^5]: ```xml <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera2.full" /> <uses-feature android:name="android.hardware.camera" android:required="true" /> ``` 在布局文件中,添加一个`TextureView`用于预览(预览Surface的来源)。例如: ```xml <TextureView android:id="@+id/textureView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` #### 2. **核心组件介绍** - **SurfaceTexture**:作为预览的中间载体,它提供一个Surface来接收相机数据流。`TextureView`内部使用`SurfaceTexture`来处理图像[^1]。 - **ImageReader**:用于拍照,创建一个Surface来捕获图像。它可以生成`Image`对象,转换为`Bitmap`后保存为照片[^2][^4]。 - **Camera2 API**:包括`CameraManager`、`CameraDevice``CaptureSession`。多Surface配置的核心是`createCaptureSession()`方法,它接受多个Surface目标[^4]。 在相机开发中,Surface是图像数据的承载层,多个Surface可以被绑定到同一个捕获会话中,实现并行处理[^1][^3]。 #### 3. **实现多Surface配置** 以下是简化后的核心步骤(以Java/Kotlin代码为例)。核心逻辑包括: - 初始化预览Surface(基于`TextureView`)。 - 创建拍照Surface(基于`ImageReader`)。 - 开启相机设备并创建捕获会话。 - 发起预览拍照请求。 **步骤详解:** 1. **初始化视图组件**: - 获取`TextureView`实例,并监听其可用状态。 - 创建一个`ImageReader`对象,用于拍照。设置合适的分辨率格式(如`ImageFormat.JPEG`)。 ```java private TextureView textureView; private ImageReader imageReader; private CameraDevice cameraDevice; private CameraCaptureSession captureSession; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textureView = findViewById(R.id.textureView); textureView.setSurfaceTextureListener(surfaceTextureListener); // 监听SurfaceTexture状态 // 创建ImageReader用于拍照 imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener(imageListener, null); // 设置图像可用监听 } ``` 2. **配置多Surface开启相机**: - 当`TextureView`可用时,开启相机设备。 - 创建预览Surface拍照Surface,并使用`createCaptureSession()`将它们绑定到相机设备。 - 同时请求预览潜在的拍照捕获。 ```java private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { try { // 开启相机 CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); String cameraId = cameraManager.getCameraIdList()[0]; // 使用第一个后置相机 cameraManager.openCamera(cameraId, cameraDeviceCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } }; private CameraDevice.StateCallback cameraDeviceCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraDevice = camera; try { // 准备预览Surface (从TextureView) SurfaceTexture surfaceTexture = textureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(1920, 1080); // 设置缓冲大小 Surface previewSurface = new Surface(surfaceTexture); // 准备拍照Surface (从ImageReader) Surface captureSurface = imageReader.getSurface(); // 创建捕获会话:使用多个Surface(预览拍照) List<Surface> surfaces = new ArrayList<>(); surfaces.add(previewSurface); // 预览目标 surfaces.add(captureSurface); // 拍照目标 cameraDevice.createCaptureSession(surfaces, captureSessionCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } }; ``` 3. **设置捕获会话处理请求**: - 在`CaptureSession.StateCallback`中配置预览请求(持续显示)。 - 当需要拍照时,创建CaptureRequest.Builder用于拍照。 - 注意处理图像方向问题(如旋转90度),参考引用[3]的解决方案。 ```java private CameraCaptureSession.StateCallback captureSessionCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; try { // 创建预览请求 CaptureRequest.Builder previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); previewBuilder.addTarget(surfaces.get(0)); // 指向预览Surface previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 自动对焦 session.setRepeatingRequest(previewBuilder.build(), null, null); // 持续预览 // 拍照逻辑:例如,在按钮点击时触发 // 创建拍照请求 CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(surfaces.get(1)); // 指向拍照Surface captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, 90); // 解决方向问题(如旋转90度)[^3] session.capture(captureBuilder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } }; // ImageListener处理拍照数据:保存为图片 private ImageReader.OnImageAvailableListener imageListener = reader -> { try (Image image = imageReader.acquireNextImage()) { ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); // 保存为文件(简化) saveImageToFile(data); } }; ``` 4. **保存图像处理方向问题**: - 使用`saveImageToFile`方法将字节数组转换为Bitmap并保存。引用[2][^4]提供了类似代码。 - 解决预览拍照方向不匹配问题(如设备旋转导致图像翻转),通过设置`JPEG_ORIENTATION`参数(引用[3][^3]): ```java // 在拍照请求中添加方向设置(基于设备角度) int rotation = getWindowManager().getDefaultDisplay().getRotation(); int orientation = ORIENTATIONS.get(rotation); // 自定义映射 captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, orientation); ``` #### 4. **注意事项** - **权限处理**:在运行时动态请求权限(如Android 6.0+),参考引用[2][^5]。使用`ActivityCompat.requestPermissions()`。 - **性能优化**:配置多个Surface时,确保Surface尺寸匹配相机支持的分辨率,避免不兼容错误[^1]。 - **方向处理**:Android设备相机传感器常为横向,如果预览方向不匹配(如垂直时图像翻转),需在代码中手动旋转`Bitmap`或设置`TextureView`的transform。引用[3][^3]详细介绍了在Camera2中的实现方法。 - **兼容性**:Camera2 API适用于Android 5.0及以上。对于旧设备(Android 4.x),需使用Legacy Camera API,但后者不支持多Surface配置。 - **内存管理**:`ImageReader`会占用内存,务必在不需要时释放资源(如在`onPause()`中关闭相机会话)。 #### 5. **总结** 在Android上实现多Surface配置,核心是利用Camera2 API创建多目标捕获会话:预览Surface通常基于`TextureView`或`SurfaceView`,拍照Surface基于`ImageReader`。这允许并行处理预览拍照,提高效率。关键步骤包括Surface初始化、会话创建方向处理。参考实现如TextureView+SurfaceTexture方法简单实用[^2][^4],但需注意权限高版本API适配[^5]。如果你的设备有特殊定制需求(如Android 13.0的方向问题),需深入Camera2核心类优化[^3]。 这种配置模式扩展性强,也可用于高级场景如视频录制(添加额外的Surface),但基础版本已满足预览拍照需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值