Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能

这篇具有很好参考价值的文章主要介绍了Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CameraX是JetPack库之一,通过CameraX可以向应用增加相机的功能。在下列内容中,将介绍一个结合CameraX实现一个简单的拍照应用。本应用必须采用Android SDK 34。并通过该简单示例,了解传统View层次组件的UI组件如何与Compose组件结合实现移动应用界面的定制。

首先,新建一个项目,选择Empty Activity。
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView

一、增加依赖使用CameraX

在新建项目的模块build.gradle.kt中增加依赖如下所示:

	//CameraX
    val camerax_version = "1.3.0-alpha04"
    implementation("androidx.camera:camera-core:$camerax_version")
    implementation("androidx.camera:camera-camera2:${camerax_version}")
    implementation("androidx.camera:camera-lifecycle:${camerax_version}")
    implementation("androidx.camera:camera-video:${camerax_version}")

    implementation("androidx.camera:camera-view:${camerax_version}" )
    implementation("androidx.camera:camera-extensions:${camerax_version}")
    
    //accompanist处理权限依赖库
    val accompanist_version = "0.31.2-alpha"
    implementation("com.google.accompanist:accompanist-permissions:$accompanist_version")

二、申请权限

1.AndroidManifest.xml配置使用照相机特性和权限


<uses-feature android:name="android.hardware.camera"   
              android:required="false" />     
<uses-permission   
              android:name="android.permission.CAMERA" />

2.申请使用拍照的权限

使用rememberPermissionState函数申请权限,该函数的参数为permission,表示需要申请的权限。rememberPermissionState函数的返回值为PermissionState类型,表示获取权限的状态。

@OptIn(ExperimentalPermissionsApi::class) 
@Preview 
@Composable 
fun CameraScreen() {    
	val cameraPermissionState = rememberPermissionState(permission = android.Manifest.permission.CAMERA)    
	LaunchedEffect(key1 = Unit){
       if(!cameraPermissionState.status.isGranted && !cameraPermissionState.status.shouldShowRationale){
           cameraPermissionState.launchPermissionRequest()
       }    
	}

   if(cameraPermissionState.status.isGranted){
       //接受拍照的授权
       }
 
  else{
       //未授权,显示未授权的界面    
       } 
 }

在上述代码中,因为通过cameraPermissionState.launchPermissionRequest()请求申请照相机权限是一个异步过程,因此,将这个请求的处理放置在LaunchedEffect代码段内。

三、定义未授权的界面

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NoPermissionScreen(cameraPermissionState:  PermissionState) {
    Column(horizontalAlignment = Alignment.CenterHorizontally){
        val message = if(cameraPermissionState.status.shouldShowRationale){
            "未获取照相机权限导致无法使用照相机功能"
        }else{
            "请授权照相机的权限"
        }
        Text(message)
        Spacer(modifier = Modifier.height(8.dp))
        Button(onClick={
            cameraPermissionState.launchPermissionRequest()
        }){
            Text("请求授权")
        }
    }
}

通过定义按钮的点击动作,请求调用cameraPermissionState.launchPermissionRequest()再次申请权限。运行界面如下图所示:
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView

四、结合PreviewView实现预览相机画面

PreviewView,这是一种可以剪裁、缩放和旋转以确保正确显示的 传统的视图组件。
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView
可以结合Preview将处于活动状态的相机中的图片预览会流式传输到 PreviewView 中的 Surface。因为是传统的View系统的组件,并不是Compose组件,因此需要在Compose界面中利用AndroidView来使用View系统的组件。

PreviewView中有些属性需要注意:

(1)缩放类型

相机预览视频分辨率常常与PreviewView 的尺寸不同,需要通过设置缩放类型scaleType来剪裁或添加遮幅式黑边使得预览画面来适应视图(保持原始宽高比)。

FIT_CENTER、FIT_START 和 FIT_END,用于添加遮幅式黑边。整个视频内容会调整(放大或缩小)为可在目标 PreviewView 中显示的最大尺寸。预览的内容会被完整显示。但是可能出现空白部分,每一帧的画面会与FIT_CENTER目标视图的中心、FIT_START起始或FIT_END结束位置对齐。CameraX 使用的默认缩放类型是 FILL_CENTER。
三种设置的实现效果如下图所示,图片来源https://developer.android.google.cn/static/images/training/camera/camerax/camera-preview/camera_preview_view_scale_type_fit.png?hl=zh-cn。
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView

(2)渲染画面的实现模式

PreviewView 的属性implementationMode用来设置画面渲染的实现模式,实现模式主要有两种:

PERFORMANCE 是默认模式。PreviewView 会使用 SurfaceView 显示视频串流,但在某些情况下会回退为使用 TextureView。SurfaceView 具有专用的绘图界面,该对象更有可能通过内部硬件合成器实现硬件叠加层,尤其是当预览视频上面没有其他界面元素(如按钮)时。通过使用硬件叠加层进行渲染,视频帧会避开 GPU 路径,从而能降低平台功耗并缩短延迟时间。

COMPATIBLE 模式。在此模式下,PreviewView 会使用 TextureView;不同于 SurfaceView,该对象没有专用的绘图表面。因此,视频要通过混合渲染,才能显示。在这个额外的步骤中,应用可以执行额外的处理工作,例如不受限制地缩放和旋转视频。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CameraContent() {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val cameraController = remember{LifecycleCameraController(context)}

    Scaffold(modifier = Modifier.fillMaxSize()) { paddingValues: PaddingValues ->
        Box(modifier = Modifier.fillMaxSize()) {
            //在Compose中使用View系统中的PreviewView
            AndroidView(modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues),
                factory = { context ->
                    PreviewView(context).apply {
                        //设置布局宽度和高度占据全屏
                        layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
                        //设置背景颜色
                        setBackgroundColor(Color.BLACK)
                        //设置渲染的实现模式
                        implementationMode = PreviewView.ImplementationMode.COMPATIBLE
                        //设置缩放方式
                        scaleType = PreviewView.ScaleType.FILL_START
                    }.also{
                        it.controller = cameraController
                        cameraController.bindToLifecycle(lifecycleOwner)
                    }
                },
                onReset = {},
                onRelease = {
                    cameraController.unbind()
                }
            )
        }
    }
}

这时,重新修改CameraScreen,代码如下:

@OptIn(ExperimentalPermissionsApi::class) 
@Preview 
@Composable 
fun CameraScreen() {    
	val cameraPermissionState = rememberPermissionState(permission = android.Manifest.permission.CAMERA)    
	LaunchedEffect(key1 = Unit){
       if(!cameraPermissionState.status.isGranted && !cameraPermissionState.status.shouldShowRationale){
           cameraPermissionState.launchPermissionRequest()
       }    
	}

   if(cameraPermissionState.status.isGranted){
       //接受拍照的授权
        CameraContent()    
       }
 
  else{
       //未授权,显示未授权的界面    
       NoPermissionScreen(cameraPermissionState)
       } 
 }

测试一下上述的代码,模拟器运行效果如下图所示:
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView
但是,从这个运行效果可以发现,它仅仅是显示相机预览的画面,功能有限。因此,需要增加其他的功能。

四、拍照的处理

CameraX提供了拍照的功能,通过调用takePicture来实现拍照。takePicture函数有两种形式:

  • takePicture(Executor, OnImageCapturedCallback):此方法为拍摄的图片提供内存缓冲区。

  • takePicture(OutputFileOptions, Executor,OnImageSavedCallback):此方法将拍摄的图片保存到提供的文件位置。 运行
    ImageCapture可自定义执行程序有两种类型:回调执行程序和 IO 执行程序。

下列代码展示了一个简单的拍照功能,通过调用takePicture(Executor, OnImageCapturedCallback)函数来实现。但是在下列代码中,拍下的图片并没有保存,仅仅是生成了一个Bitmap对象

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CameraContent() {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val cameraController = remember{LifecycleCameraController(context)}
    //请求关联context的主线程的Excutor
    val cameraExecutor = ContextCompat.getMainExecutor(context)

    Scaffold(modifier = Modifier.fillMaxSize(),
        floatingActionButton = {
            FloatingActionButton(onClick = {
                cameraController.takePicture(cameraExecutor,object : ImageCapture.OnImageCapturedCallback(){
                    override fun onCaptureSuccess(image: ImageProxy) {
                        super.onCaptureSuccess(image)
                        //拍照得到图片
                        val cameraBitmap = image.toBitmap()
                    }
                    override fun onError(exception: ImageCaptureException) {
            			//拍照失败的处理
        			}
                })
            }) {
                Icon(modifier = Modifier.clip(CircleShape), 
                     painter = painterResource(id = R.mipmap.len),
                     contentDescription = "照相机")
            }
        },
        floatingActionButtonPosition = FabPosition.Center) { paddingValues: PaddingValues ->
        Box(modifier = Modifier.fillMaxSize()) {
            //在Compose中使用View系统中的PreviewView
            AndroidView(modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues),
                factory = { context ->
                    PreviewView(context).apply {
                        layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
                        //设置背景颜色
                        setBackgroundColor(Color.BLACK)
                        //设置渲染模式
                        implementationMode = PreviewView.ImplementationMode.COMPATIBLE
                        //设置缩放方式
                        scaleType = PreviewView.ScaleType.FILL_START
                    }.also{
                        it.controller = cameraController
                        cameraController.bindToLifecycle(lifecycleOwner)
                    }
                },
                onReset = {},
                onRelease = {
                    cameraController.unbind()
                }
            )
        }
    }
}

运行效果在模拟器中如下图所示:
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView
可以注意到,上述代码虽然可以将拍下的图片生成一个Bitmap对象,但是并没有将这个图片保存到手机中。因此可以考虑通过调用 takePicture(OutputFileOptions, Executor,OnImageSavedCallback)函数来实现拍照并保存图片的功能。
为此,做如下的处理:

(1)自定义一个函数capturePicture:执行拍照功能,并将拍下的图片保存到文件中。
(2)修改CameraContent组合函数,调用capturePicture函数实现拍照并保存照片的功能。

fun capturePicture(cameraController:LifecycleCameraController,cameraExecutor:Executor){
	//创建文件名以img为开始,扩展名为jpg的临时文件
    val file = File.createTempFile("img",".jpg")
    //配置最新拍下照片文件的位置和元数据
    val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
    //定义拍照,cameraExecutor处理拍照后的回调 object:ImageCapture.OnImageSavedCallback创建匿名对象对捕获图片保存
    cameraController.takePicture(outputFileOptions,cameraExecutor,object:ImageCapture.OnImageSavedCallback{
        override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
            //拍照的图片成功保存的处理
            Log.d("CAMERA_APP","成功保存照片在:${outputFileResults.savedUri}")
        }
     
        override fun onError(exception: ImageCaptureException) {
            //拍照的图片保存失败的处理
            Log.d("CAMERA_APP","保存照片失败")
        }

    })
}

修改CameraContent函数,修改拍照的处理:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CameraContent() {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val cameraController = remember{LifecycleCameraController(context)}
    //请求关联context的主线程的Excutor
    val cameraExecutor = ContextCompat.getMainExecutor(context)

    Scaffold(modifier = Modifier.fillMaxSize(),
        floatingActionButton = {
            FloatingActionButton(onClick = {
                //处理拍照
                capturePicture(cameraController,cameraExecutor)
            }) {
                Icon(modifier = Modifier.clip(CircleShape).size(24.dp,24.dp),
                    painter = painterResource(id = R.mipmap.len),contentDescription = "照相机")
            }
        },
        floatingActionButtonPosition = FabPosition.Center) { paddingValues: PaddingValues ->
        Box(modifier = Modifier.fillMaxSize()) {
            //在Compose中使用View系统中的PreviewView
            AndroidView(modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues),
                factory = { context ->
                    PreviewView(context).apply {
                        layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
                        //设置背景颜色
                        setBackgroundColor(Color.BLACK)
                        //设置渲染模式
                        implementationMode = PreviewView.ImplementationMode.COMPATIBLE
                        //设置缩放方式
                        scaleType = PreviewView.ScaleType.FILL_START
                    }.also{
                        it.controller = cameraController
                        cameraController.bindToLifecycle(lifecycleOwner)
                    }
                },
                onReset = {},
                onRelease = {
                    cameraController.unbind()
                }
            )
        }
    }
}

运行界面如上图所示。执行多次拍照后,启动Android Studio的Device Explorer,在data/data目录中找到创建的项目模块包名:
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView

在包名的下级目录cache中可以发现多次拍照的图片,也可以根据跟踪日志发现保存图片文件的路径,如下图所示。
Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能,android,笔记,CameraX,Compose,PreviewView

参考文献

(1) ImageCapture.Builder https://developer.android.google.cn/reference/androidx/camera/core/ImageCapture.Builder#setIoExecutor(java.util.concurrent.Executor)
(2)CameraX概览
https://developer.android.google.cn/training/camerax?hl=zh-cn文章来源地址https://www.toymoban.com/news/detail-718601.html

到了这里,关于Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android笔记(九):Compose组件的状态(一)

    在使用Compose定义UI界面时,可以发现界面的变换往往与Compose组件内部的状态相关,当状态值发生变化时,Compose构成的可组合的界面也会刷新发生相应的变化。将在本笔记中将对可组合项的状态的定义、状态提升、状态丢失和状态的保存进行简单介绍。。 Compose采用了单向数据

    2024年02月06日
    浏览(35)
  • Android笔记(二十三):Paging3分页加载库结合Compose的实现分层数据源访问

    在Android笔记(二十二):Paging3分页加载库结合Compose的实现网络单一数据源访问一文中,实现了单一数据源的访问。在实际运行中,往往希望不是单纯地访问网络数据,更希望将访问的网络数据保存到移动终端的SQLite数据库中,使得移动应用在离线的状态下也可以从数据库中

    2024年01月16日
    浏览(31)
  • Android笔记(六):JetPack Compose常见的UI组件

    Text显示的文本来源可以引用res-values-strings.xml中的资源,如第一个显示文本所示。 点击按钮前: 点击按钮后: 点击第一个圆角按钮不放时,显示为按钮:true Button有两方面需要注意: (1) Buttton有一个参数interactionSource,用来监听组件状态的事件源,通过它获取组件的状态来

    2024年02月04日
    浏览(36)
  • Android笔记(七)Android JetPack Compose组件搭建Scaffold脚手架

    在去年2022年曾发布一篇关于脚手架的文章:“Android JetPack Compose组件中Scaffold的应用” 。但是Android的版本从12变更到13及以上版本,导致一些细节的实现存在不同。在本文中,将从头开始介绍整个脚手架的搭建过程。 在Android Studio(版本是Graffie)中新建模块,选择“Empty Activ

    2024年02月04日
    浏览(34)
  • Compose使用OpenGL+CameraX快速实现相机“拍视频实时滤镜“、”拍照+滤镜“

    一、前言 短视频热潮还没有褪去,写这篇文章主要是帮助大部分人,能快速上手实现类似效果,实际上是: CameraX拿相机数据,OpenGL给CameraX提供一个Surface,数据放到OpenGL渲染的线程上去做图像相关操作 OpenGL滤镜来自 aserbao_AndroidCamera 视频录制核心代码参考改造自 Google 的 g

    2023年04月17日
    浏览(34)
  • 探索Android Jetpack Compose的Surface组件

    随着声明性 UI 框架 Jetpack Compose 的出现,Android 开发变得更加简洁和直观。在这篇博客中,我们将深入探讨其中的一项基本构建块 —— Surface 组件,了解它如何影响 UI 的显示和设计。 一、Jetpack Compose和Surface组件 二、Surface组件的基本使用 三、影响Surface的属性 一、Jetpack Co

    2024年02月11日
    浏览(39)
  • Android 实现相机(CameraX)预览

    CameraX 是一个 Jetpack 库,旨在帮助您更轻松地开发相机应用。 对于新应用,我们建议从 CameraX 开始。它提供一致且易于使用的 API,适用于绝大多数 Android 设备,并向后兼容 Android 5.0(API 级别 21)。 CameraX 支持大多数常见的相机用例: 预览 :在屏幕上查看图片。 图片分析 :

    2024年02月15日
    浏览(28)
  • Android CameraX的基础使用

    本博客介绍CamaraX的基本使用,主要基于Android官方教程。CameraX是一个 Jetpack 库,能够快速的开发相机应用,并且有良好兼容性。CameraX支持最低API level 21,并且本教程要求Android Studio Arctic Fox 2020.3.1 或更高版本。 首先创建一个新项目,选择“Empty Activity”。 将项目命名为“Ca

    2024年02月09日
    浏览(30)
  • Android Jetpack组件库(第七部分)---UI工具包 Compose

    Android Jetpack 是 Google 推出的一整套帮助 Android 应用程序开发的库、工具包和架构指南,旨在为 Android 应用程序提供更快,更轻松,更稳定的开发体验。自推出以来已经发展成了一个庞大的技术生态系统,包括了许多使用方便、功能强大的库,以下是其中一些新特性、新组件:

    2024年01月16日
    浏览(38)
  • Android CameraX适配Android13的踩坑之路

    最近把AGP插件升级到8.1.0,新建项目的时候目标版本和编译版本都是33,发现之前的demo使用Camerax拍照和录像都失败了,于是查看了一下官网和各种资料,找到了Android13的适配方案. 与早期版本一样,Android 13 包含一些行为变更,这些变更可能会影响您的应用。以下行为变更仅影

    2024年02月12日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包