網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
目標(biāo)很簡(jiǎn)單,用CameraX打開(kāi)攝像頭預(yù)覽,實(shí)時(shí)顯示在界面上??纯碈ameraX有沒(méi)有Google說(shuō)的那么好用。先按最簡(jiǎn)單的來(lái),把預(yù)覽顯示出來(lái)。
引入依賴
模塊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 核心庫(kù)是用camera2實(shí)現(xiàn)的),目前主要用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核心庫(kù)會(huì)報(bào)錯(cuò),找不到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) ```
權(quán)限
需要?jiǎng)討B(tài)申請(qǐng)android.permission.CAMERA
權(quán)限
<uses-permission android:name="android.permission.CAMERA" />
本文略過(guò)動(dòng)態(tài)申請(qǐng)權(quán)限的地方
界面
CameraX為開(kāi)發(fā)者貼心地準(zhǔn)備了androidx.camera.view.PreviewView
把它放在一個(gè)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>
開(kāi)啟預(yù)覽
在activity中開(kāi)啟相機(jī)預(yù)覽
// 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
方法拿到一個(gè)cameraProviderFuture
。
在cameraProviderFuture
完成后取出ProcessCameraProvider(cameraProvider
)。
要開(kāi)啟預(yù)覽,通過(guò)Preview.Builder
構(gòu)建一個(gè)Preview。用CameraSelector來(lái)選擇后置攝像頭。
Preview的SurfaceProvider由layout中的androidx.camera.view.PreviewView提供。
cameraProvider.bindToLifecycle
綁定上后,啟動(dòng)攝像頭預(yù)覽
運(yùn)行測(cè)試
運(yùn)行到手機(jī)上,打開(kāi)這個(gè)Activity就可以看到攝像頭預(yù)覽。圖像寬高比正常,沒(méi)有拉伸現(xiàn)象。
- 榮耀 EMUI 3.1 Lite,Android 5.1 運(yùn)行正常
- Redmi 9A,MIUI 12.5.1穩(wěn)定版,Android 10 運(yùn)行正常
- 一加5,H2OS 10.0.3,Android 10 運(yùn)行正常
增加開(kāi)關(guān)
在layout里加2個(gè)按鈕,控制相機(jī)開(kāi)關(guān)
<?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="打開(kāi)" /> <Button android:id="@+id/end" style="@style/NormalBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="12dp" android:text="關(guān)閉" /> </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(), "沒(méi)獲取到相機(jī)", 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(), "沒(méi)獲取到相機(jī)", 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
后不要立刻綁定生命周期。
如果要開(kāi)啟預(yù)覽,則調(diào)用bindPreview(mCameraProvider)
。記錄一下現(xiàn)在相機(jī)已經(jīng)開(kāi)啟預(yù)覽mRunning = true
。
如果要停止預(yù)覽,則解綁生命周期mCameraProvider.unbindAll()
。這個(gè)方法需要在主線程調(diào)用。
運(yùn)行起來(lái)后,可以用按鈕來(lái)控制相機(jī)預(yù)覽的開(kāi)關(guān)。相比之前,PreviewView的高度變小了一點(diǎn)(讓了點(diǎn)位置給按鈕)。
但視頻寬高比例正常,沒(méi)有被拉伸。默認(rèn)的配置下,還有自動(dòng)對(duì)焦的功能。
小結(jié)
從簡(jiǎn)單的打開(kāi)相機(jī)預(yù)覽來(lái)看,CameraX簡(jiǎn)化了開(kāi)發(fā)者的工作。提供了PreviewView,開(kāi)發(fā)者不需要自定義SurfaceView或者TextureView。實(shí)時(shí)預(yù)覽中,相機(jī)能夠自動(dòng)對(duì)焦。本文用的是1.1.0-alpha11,而CameraX還在發(fā)展之中。
參考
camerax實(shí)現(xiàn)預(yù)覽 - developer.android.com
https://developer.android.com/jetpack/androidx/releases/camera
原文鏈接:https://www.cnblogs.com/rustfisher/p/15670052.html
相關(guān)推薦
- 2023-03-17 git?push時(shí)卡住的解決方法(長(zhǎng)時(shí)間不報(bào)錯(cuò)也不自動(dòng)退出)_相關(guān)技巧
- 2022-09-21 Android開(kāi)發(fā)之AAR文件的生成與使用步驟_Android
- 2022-08-27 C++?Thread實(shí)現(xiàn)簡(jiǎn)單的socket多線程通信_(tái)C 語(yǔ)言
- 2022-11-09 docker容器直接退出如何進(jìn)入容器調(diào)試模式_docker
- 2023-05-08 Android事件分發(fā)之View事件處理關(guān)鍵及示例分析_Android
- 2022-12-24 C++中的函數(shù)返回值與拷貝用法_C 語(yǔ)言
- 2022-08-06 Golang詳細(xì)講解常用Http庫(kù)及Gin框架的應(yīng)用_Golang
- 2023-01-27 Python異常與錯(cuò)誤處理詳細(xì)講解_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支