Android CameraX 打开摄像头预览
目标很简单,用CameraX打开摄像头预览,实时显示在界面上。看看CameraX有没有Google说的那么好用。先按最简单的来,把预览显示出来。
引入依赖
模块gradle的一些配置,使用的Android SDK版本为31,启用了databinding
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 31
buildToolsVersion "31.0.0"
defaultConfig {
minSdkVersion 21
targetSdkVersion 31
}
dataBinding {
enabled = true
}
}
引入CameraX依赖(CameraX 核心库是用camera2实现的),目前主要用1.1.0-alpha11版本
dependencies {
implementation "androidx.camera:camera-core:1.1.0-alpha11"
implementation "androidx.camera:camera-camera2:1.1.0-alpha11"
implementation "androidx.camera:camera-lifecycle:1.1.0-alpha11"
implementation "androidx.camera:camera-view:1.0.0-alpha31"
implementation "androidx.camera:camera-extensions:1.0.0-alpha31"
}
使用1.0.2版本的CameraX核心库会报错,找不到getOrCreateInstance方法。
??? bug "NoSuchMethodError getOrCreateInstance"
```log
CrashHandler: In thread: Thread[main,5,main]
UncaughtException detected: java.lang.NoSuchMethodError: No static method getOrCreateInstance(Landroid/content/Context;)Lcom/google/common/util/concurrent/ListenableFuture; in class Landroidx/camera/core/CameraX; or its super classes (declaration of 'androidx.camera.core.CameraX' appears in /data/app/com.rustfisher.tutorial2020-1/base.apk)
at androidx.camera.lifecycle.ProcessCameraProvider.getInstance(ProcessCameraProvider.java:149)
at com.rustfisher.tutorial2020.camera.SimplePreviewXAct.onCreate(SimplePreviewXAct.java:36)
at android.app.Activity.performCreate(Activity.java:6161)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1112)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2507)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2640)
at android.app.ActivityThread.access$800(ActivityThread.java:182)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1493)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5682)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:963)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:758)
```
权限
需要动态申请android.permission.CAMERA权限
<uses-permission android:name="android.permission.CAMERA" />
本文略过动态申请权限的地方
界面
CameraX为开发者贴心地准备了androidx.camera.view.PreviewView
把它放在一个FrameLayout里,如下的act_simple_preivew_x.layout
<?xml version="1.0" encoding="utf-8"?>
<layout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</layout>
开启预览
在activity中开启相机预览
// SimplePreviewXAct.java
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.LifecycleOwner;
import com.google.common.util.concurrent.ListenableFuture;
// import com.rustfisher.tutorial2020.R;
// import com.rustfisher.tutorial2020.databinding.ActSimplePreivewXBinding;
import java.util.concurrent.ExecutionException;
public class SimplePreviewXAct extends AppCompatActivity {
private ActSimplePreivewXBinding mBinding;
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.act_simple_preivew_x);
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// 这里不用处理
}
}, ContextCompat.getMainExecutor(this));
}
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
preview.setSurfaceProvider(mBinding.previewView.getSurfaceProvider());
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview);
}
}
注意我们这里使用的是androidx.appcompat.app.AppCompatActivity
为了获得ProcessCameraProvider,用ProcessCameraProvider.getInstance方法拿到一个cameraProviderFuture。
在cameraProviderFuture完成后取出ProcessCameraProvider(cameraProvider)。
要开启预览,通过Preview.Builder构建一个Preview。用CameraSelector来选择后置摄像头。
Preview的SurfaceProvider由layout中的androidx.camera.view.PreviewView提供。
cameraProvider.bindToLifecycle绑定上后,启动摄像头预览
运行测试
运行到手机上,打开这个Activity就可以看到摄像头预览。图像宽高比正常,没有拉伸现象。
- 荣耀 EMUI 3.1 Lite,Android 5.1 运行正常
- Redmi 9A,MIUI 12.5.1稳定版,Android 10 运行正常
- 一加5,H2OS 10.0.3,Android 10 运行正常
增加开关
在layout里加2个按钮,控制相机开关
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:orientation="horizontal"
android:padding="4dp">
<Button
android:id="@+id/start"
style="@style/NormalBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="打开" />
<Button
android:id="@+id/end"
style="@style/NormalBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="关闭" />
</LinearLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
</layout>
根layout换成LinearLayout
修改bindPreview方法,先检查传入的ProcessCameraProvider是否为空
private void bindPreview(ProcessCameraProvider cameraProvider) {
if (cameraProvider == null) {
Toast.makeText(getApplicationContext(), "没获取到相机", Toast.LENGTH_SHORT).show();
return;
}
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
preview.setSurfaceProvider(mBinding.previewView.getSurfaceProvider());
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview);
}
修改后的activity部分代码
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import com.google.common.util.concurrent.ListenableFuture;
// import com.rustfisher.tutorial2020.R;
// import com.rustfisher.tutorial2020.databinding.ActSimplePreivewXBinding;
import java.util.concurrent.ExecutionException;
public class SimplePreviewXAct extends AppCompatActivity {
private ActSimplePreivewXBinding mBinding;
private ListenableFuture<ProcessCameraProvider> mCameraProviderFuture;
private ProcessCameraProvider mCameraProvider;
private boolean mRunning = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.act_simple_preivew_x);
mCameraProviderFuture = ProcessCameraProvider.getInstance(this);
mCameraProviderFuture.addListener(() -> {
try {
mCameraProvider = mCameraProviderFuture.get();
} catch (ExecutionException | InterruptedException e) {
// 这里不用处理
}
}, ContextCompat.getMainExecutor(this));
mBinding.start.setOnClickListener(v -> {
if (mCameraProvider != null && !mRunning) {
bindPreview(mCameraProvider);
}
});
mBinding.end.setOnClickListener(v -> {
mCameraProvider.unbindAll();
mRunning = false;
});
}
private void bindPreview(ProcessCameraProvider cameraProvider) {
if (cameraProvider == null) {
Toast.makeText(getApplicationContext(), "没获取到相机", Toast.LENGTH_SHORT).show();
return;
}
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
preview.setSurfaceProvider(mBinding.previewView.getSurfaceProvider());
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview);
mRunning = true;
}
}
拿到mCameraProvider后不要立刻绑定生命周期。
如果要开启预览,则调用bindPreview(mCameraProvider)。记录一下现在相机已经开启预览mRunning = true。
如果要停止预览,则解绑生命周期mCameraProvider.unbindAll()。这个方法需要在主线程调用。
运行起来后,可以用按钮来控制相机预览的开关。相比之前,PreviewView的高度变小了一点(让了点位置给按钮)。
但视频宽高比例正常,没有被拉伸。默认的配置下,还有自动对焦的功能。
小结
从简单的打开相机预览来看,CameraX简化了开发者的工作。提供了PreviewView,开发者不需要自定义SurfaceView或者TextureView。实时预览中,相机能够自动对焦。本文用的是1.1.0-alpha11,而CameraX还在发展之中。
参考
- camerax实现预览 - developer.android.com
- https://developer.android.com/jetpack/androidx/releases/camera
- NoSuchMethodError - stackoverflow
原文链接CameraX 打开摄像头预览
Android CameraX 打开摄像头预览的更多相关文章
- Android 摄像头预览悬浮窗
用CameraX打开摄像头预览,显示在界面上.结合悬浮窗的功能.实现一个可拖动悬浮窗,实时预览摄像头的例子. 这个例子放进了单独的模块里.使用时注意gradle里的细微差别. 操作摄像头,打开预览.这 ...
- Android 摄像头预览悬浮窗,可拖动,可显示在其他app上方
市面上常见的摄像头悬浮窗,如微信.手机QQ的视频通话功能,有如下特点: 整屏页面能切换到一个小的悬浮窗 悬浮窗能运行在其他app上方 悬浮窗能跳回整屏页面,并且悬浮窗消失 我们探讨过用CameraX打 ...
- 基于开源的GOCW和Directshow.net,实现摄像头预览、采集、录像等操作
本文基于开源的GOCW和Directshow.net,实现图像采集等操作.最为关键的部分在于可以实现摄像头的控制,同时关于视频采集进行了实现. 具体的内容请关注首发于51CTO的课程<基于Csh ...
- Android平台之不预览获取照相机预览数据帧及精确时间截
在android平台上要获取预览数据帧是一件极其容易的事儿,但要获取每帧数据对应的时间截并不那么容易,网络上关于这方面的资料也比较少.之所以要获取时间截,是因为某些情况下需要加入精确时间轴才能解决问题 ...
- Android开发中遇到的问题(三)——eclipse创建android项目无法正常预览布局文件
一.问题描述 今天使用SDK Manager将Android SDK的版本更新到了Android 5.1的版本,eclipse创建android项目时,预览activity_main.xml文件时提示 ...
- 基于DevExpress的SpreadsheetControl实现对Excel的打开、预览、保存、另存为、打印(附源码下载)
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- DevExpress的PdfViewer添加工具栏实现PDF打开、预览、保存、打印
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- 使用DevExpress的PdfViewer实现PDF打开、预览、另存为、打印(附源码下载)
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- 关于降低android手机摄像头预览分辨率
假设现在有这样一个需求需要一直开着手机摄像头 但是不做任何拍照动作 但是每个手机的相机分辨率都不同 而默认预览的时候参数是最大分辨率 这样有时候就回导致电量损耗的加快 所以我们可以采取降低相机分辨率的 ...
随机推荐
- 第四周PTA笔记 好吃的巧克力+特殊的翻译+下次一定(续)+走迷宫
好吃的巧克力 超市正在特价售卖巧克力,正好被贪吃的Lucky_dog看见了. 巧克力从左到右排成一排,一共有N个,M种. 超市有一个很奇怪的规定,就是你在购买巧克力时必须提供两个数字a和b,代表你要购 ...
- 部署一个支持Dapr 的Kubernetes APISIX Ingress
在这篇文章中,我将展示如何创建一个 APISIX控制器,该控制器在 Kubernetes 集群中公开启用 Dapr 的应用程序. 本质上,APISIX控制器将配置相同的标准 Dapr annotati ...
- 「期末」一文带你系统回顾C 语言
超详细 c 语言回顾 前言 c 语言是一种底层语言,是一种系统底层级的语言,例如Windows.Linux.Unix等操作系统就是使用c语言编写的.所以由此看来,不论是火爆了25年的Java,还是近年 ...
- filter筛选数组
和map()类似,array的filter也接收一个函数 和map()不同的是,filter把传入的函数依次作用于每个函数,然后根据返回TRUE还是FALSE来做决定保留还是舍弃该元素 例如,删除一个 ...
- Arduino uno r3 使用 ESP8266 UART-WiFi 透传模块
一.所需硬件材料 1.ESP8266:01s某宝上3.5块钱 2.杜邦线:某宝几块钱一组40P,这里只需要三根,用于连接 树莓派与继电器 3.烧录器 二.ESP8266 AT固件烧录 ESP8266主 ...
- [loj3526]修改DNA
如果$a[x..y]$和$b[x..y]$的某种字符数量不同,显然无解 考虑一个$[x,y]$的排列$p[x..y]$,使得$\forall x\le i\le y,a_{i}=b_{p_{i}}$, ...
- [bzoj1115]石子游戏
考虑令$bi=ai-a_{i-1}$,那么每一次操作相当于让$bi-=x$且$b_{i+1}+=x$,相当于从i向i+1移动x个石子,那么容易发现偶数堆没有用处,因为另一方可以用同样的操作,因此问题相 ...
- AOP声明式事务
1.spring-dao.xml修改 参考上面工程配置 <?xml version="1.0" encoding="UTF-8"?> <bea ...
- 2021年春秋杯网络安全联赛秋季赛 勇者山峰部分wp
1.签到题-Crypto Vigenere 根据题目Vigenere可看出是维吉尼亚密码 使用在线网站破解 https://guballa.de/vigenere-solver flag:53d613 ...
- 从一个简单的Delete删数据场景谈TiDB数据库开发规范的重要性
故事背景 前段时间上线了一个从Oracle迁移到TiDB的项目,某一天应用端反馈有一个诡异的现象,就是有张小表做全表delete的时候执行比较慢,而且有越来越慢的迹象.这个表每次删除的数据不超过20行 ...