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.comhttps://developer.android.com/jetpack/androidx/releases/cameraNoSuchMethodError - stackoverflow

原文链接CameraX 打开摄像头预览

到此这篇关于Android CameraX 打开摄像头预览的文章就介绍到这了,更多相关Android CameraX 摄像头预览内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android CameraX结合LibYUV和GPUImage自定义相机滤镜

    目录 实现效果 实现步骤 1.引入依赖库 2.引入libyuv 3.编写CameraX预览代码 4.增加相机数据回调 5.对回调数据进行处理 6.拍摄照片 作者:itfitness 链接:https://www.jianshu.com/p/f084082cc0c6 本文目录: 实现效果 实现步骤 1.引入依赖库 这里我引入的依赖库有CameraX.GPUImage(滤镜库).Utilcodex(一款好用的工具类) // CameraX core library using camera2 imp

  • 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 plug

  • Android CameraX 打开摄像头预览功能

    目录 引入依赖 权限 界面 开启预览 运行测试 增加开关 小结 目标很简单,用CameraX打开摄像头预览,实时显示在界面上.看看CameraX有没有Google说的那么好用.先按最简单的来,把预览显示出来. 引入依赖 模块gradle的一些配置,使用的Android SDK版本为31,启用了databinding apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotli

  • Android CameraX打开摄像头预览教程

    目录 前言 引入依赖 权限 界面 开启预览 运行测试 增加开关 小结 前言 目标很简单,用CameraX打开摄像头预览,实时显示在界面上.看看CameraX有没有Google说的那么好用.先按最简单的来,把预览显示出来. 引入依赖 模块gradle的一些配置,使用的Android SDK版本为31,启用了databinding apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin:

  • vue+vant使用图片预览功能ImagePreview的问题解决

    如果您搜到这篇文章的话,那员外估计您遇到跟我一样的问题了,即在打开图片预览功能后,如果不关闭预览的图片,同时改变路由的话,会发现即使路由改变了,预览的图片还在文档的最顶层显示,如图: 着实让员外百思不其解,在调用vant 的 ImagePreview图片预览组件中,没有html,有的只是引入ImagePreview 和js的调用.在这种情况下,员外是想自己添加任何方法都没法实现的,同时想在离开路由时的beforeRouteLeave 钩子中关闭这个 ImagePreview  也实现不了,因为根

  • Android Camera2 实现预览功能

    1. 概述 最近在做一些关于人脸识别的项目,需要用到 Android 相机的预览功能.网上查阅相关资料后,发现 Android 5.0 及以后的版本中,原有的 Camera API 已经被 Camera2 API 所取代. 全新的 Camera2 在 Camera 的基础上进行了改造,大幅提升了 Android 系统的拍照功能.它通过以下几个类与方法来实现相机预览时的工作过程: •CameraManager :摄像头管理器,主要用于检测系统摄像头.打开系统摄像头等: •CameraDevice

  • Android编程滑动效果之Gallery+GridView实现图片预览功能(附demo源码下载)

    本文实例讲述了Android编程滑动效果之Gallery+GridView实现图片预览功能.分享给大家供大家参考,具体如下: Android系统自带一个GridView和Gallery两个控件,GridView网格显示,Gallery单个浏览,两者结合起来可以真正实现Gallery浏览图片效果. 本示例通过GridView和Gallery两个控件,模仿实现一个完整的仿Gallery图像集的图片浏览效果.效果图如下: 1.GridView 首先,自定义一个GridImageAdapter图片适配器

  • Android 通过腾讯TBS实现文件预览功能

    1.集成腾讯TBS 使用腾讯TBS来预览pdf,word,excel,ppt等多种类型的文件,去 腾讯浏览服务官网下载SDK,按照官方文档文档集成SDK. 2.使用TbsReaderView来加载文件 动态创建TbsReaderView,然后添加到布局中. // 回调 TbsReaderView.ReaderCallback readerCallback = new TbsReaderView.ReaderCallback() { @Override public void onCallBack

  • Android实现pdf在线预览或本地预览的方法

    最近项目中需要使用在线预览pdf,并要能实现自动播放,我想这样的需求无论如何来说都是很操蛋的 由于本人水平有限,最后讨论将项目需求改成将pdf下载到本地再实现自动播放. 接下来总结下目前能够实现pdf阅读的方案,开发当中需要根据实际需求去选择相应的方案. 1.使用Google doc支持来展示word,excel,pdf,txt(WebView方式在线预览): <span style="font-size:18px;">WebView urlWebView = (WebVi

  • ASP.NET实现word文档在线预览功能代码

    于是考虑在每个文件上传时为其生存一份HTMl文件,这样就能实现在线预览功能.主要代码如下 复制代码 代码如下: using System; using System.Collections; using System.Configuration; using System.Data; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using S

  • php实现头像上传预览功能

    本文实例为大家分享了php实现头像上传预览功能的具体代码,供大家参考,具体内容如下 主页面1.php <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>上传头像</title> <style type="text/css"> *{ font-fa

随机推荐