Android CameraX打開攝像頭預覽教程
前言
目標很簡單,用CameraX打開攝像頭預覽,實時顯示在界面上??纯碈ameraX有沒有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實現(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核心庫會報錯,找不到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)限
需要動態(tài)申請android.permission.CAMERA權(quán)限
<uses-permission android:name="android.permission.CAMERA" />
本文略過動態(tài)申請權(quán)限的地方
界面
CameraX為開發(fā)者貼心地準備了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構(gòu)建一個Preview。用CameraSelector來選擇后置攝像頭。
Preview的SurfaceProvider由layout中的androidx.camera.view.PreviewView提供。
cameraProvider.bindToLifecycle綁定上后,啟動攝像頭預覽
運行測試
運行到手機上,打開這個Activity就可以看到攝像頭預覽。圖像寬高比正常,沒有拉伸現(xiàn)象。
榮耀 EMUI 3.1 Lite,Android 5.1 運行正常
Redmi 9A,MIUI 12.5.1穩(wěn)定版,Android 10 運行正常
一加5,H2OS 10.0.3,Android 10 運行正常
增加開關(guān)
在layout里加2個按鈕,控制相機開關(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="打開" />
<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(), "沒獲取到相機", 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后不要立刻綁定生命周期。
如果要開啟預覽,則調(diào)用bindPreview(mCameraProvider)。記錄一下現(xiàn)在相機已經(jīng)開啟預覽mRunning = true。
如果要停止預覽,則解綁生命周期mCameraProvider.unbindAll()。這個方法需要在主線程調(diào)用。
運行起來后,可以用按鈕來控制相機預覽的開關(guān)。相比之前,PreviewView的高度變小了一點(讓了點位置給按鈕)。
但視頻寬高比例正常,沒有被拉伸。默認的配置下,還有自動對焦的功能。
小結(jié)
從簡單的打開相機預覽來看,CameraX簡化了開發(fā)者的工作。提供了PreviewView,開發(fā)者不需要自定義SurfaceView或者TextureView。實時預覽中,相機能夠自動對焦。本文用的是1.1.0-alpha11,而CameraX還在發(fā)展之中。
到此這篇關(guān)于Android CameraX打開攝像頭預覽教程的文章就介紹到這了,更多相關(guān)Android CameraX攝像頭預覽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Flutter?阻止系統(tǒng)鍵盤彈出的優(yōu)雅方式
這篇文章主要為大家介紹了Flutter?阻止系統(tǒng)鍵盤彈出的優(yōu)雅方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Navigation?Bundle實現(xiàn)兩個Fragment參數(shù)傳遞
這篇文章主要為大家介紹了Navigation?Bundle實現(xiàn)兩個Fragment參數(shù)傳遞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04
Android實現(xiàn)仿網(wǎng)易新聞主界面設(shè)計
這篇文章主要為大家介紹了Android實現(xiàn)仿網(wǎng)易新聞主界面設(shè)計的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-01-01
Android GridView不改變背景色實現(xiàn)網(wǎng)格線效果
這篇文章主要介紹了Android GridView不改變背景色實現(xiàn)網(wǎng)格線效果,需要的朋友可以參考下2016-03-03
Android SwipeRefreshLayout下拉刷新組件示例
SwipeRefrshLayout是Google官方更新的一個Widget,可以實現(xiàn)下拉刷新的效果。本文主要介紹了Android之SwipeRefreshLayout下拉刷新組件示例,有興趣的可以了解一下。2017-02-02
Android自定義ViewGroup實現(xiàn)側(cè)滑菜單
這篇文章主要為大家詳細介紹了Android如何通過自定義ViewGroup實現(xiàn)側(cè)滑菜單,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-01-01

