ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

开始使用Android-CameraX

2022-07-19 20:35:08  阅读:357  来源: 互联网

标签:CameraX int void 使用 相机 viewFinder new Android


安卓中使用相机从来就不是一件容易的事。

Camera1要自己管理Camera相机实例,要处理SufraceView相关的一堆东西,还有预览尺寸跟画面尺寸的选择,页面生命周期切换等等问题。。。

后来推出了Camera2,从官方Demo
就上千行代码来看,Camera2并不解决用起来复杂的问题,它提供了更多的调用接口,可定制性更好,结果就是对普通开发者来说更难用了。。。

终于Google也意识到这个问题,推出了最终版CameraX. CameraX实际上还是用的Camera2,但它对调用API进行了很好的封装,使用起来非常方便。官方教程也很详细,如下:
https://codelabs.developers.google.com/codelabs/camerax-getting-started/#0

官方用的Kotlin代码,我转成了Java,其实用起来差不多。

注意:CameraX跟Camera2一样最低支持API21,也就是5.0及以上。
开发环境用Android Studio3.3及以上,依赖库都用androidx的

1 导入依赖

在app的build.gradle中加入


    implementation 'androidx.appcompat:appcompat:1.1.0-rc01'

    // Use the most recent version of CameraX, currently that is alpha04
    def camerax_version = "1.0.0-alpha04"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"

2 布局文件和权限

放一个TextureView就行了

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextureView
            android:id="@+id/view_finder"
            android:layout_width="640px"
            android:layout_height="640px"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

权在AndroidManifest.xml中加入相机权限

<uses-permission android:name="android.permission.CAMERA" />

并加入动态申请权限代码,这里省略掉(你要在App安装后手动打开相机权限)。

3 启动相机

给TextureView设置布局变化的监听,用updateTransform()更新相机预览,然后startCamera()启动相机


        TextureView viewFinder = findViewById(R.id.view_finder);
        viewFinder.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
                updateTransform();
            }
        });

        viewFinder.post(new Runnable() {
            @Override
            public void run() {
                startCamera();
            }
        });

更新相机预览:主要是给TextureView设置一个旋转的矩阵变化,防止预览方向不对

    private void updateTransform() {
        Matrix matrix = new Matrix();
        // Compute the center of the view finder
        float centerX = viewFinder.getWidth() / 2f;
        float centerY = viewFinder.getHeight() / 2f;

        float[] rotations = {0,90,180,270};
        // Correct preview output to account for display rotation
        float rotationDegrees = rotations[viewFinder.getDisplay().getRotation()];

        matrix.postRotate(-rotationDegrees, centerX, centerY);

        // Finally, apply transformations to our TextureView
        viewFinder.setTransform(matrix);
    }

启动相机:创建PreviewConfig和Preview这两个对象,可以设置预览图像的尺寸和比例,在OnPreviewOutputUpdateListener回调中用setSurfaceTexture方法,将相机图像输出到TextureView。最后用CameraX.bindToLifecycle方法将相机与当前页面的生命周期绑定。

    private void startCamera() {
        // 1. preview
        PreviewConfig previewConfig = new PreviewConfig.Builder()
                .setTargetAspectRatio(new Rational(1, 1))
                .setTargetResolution(new Size(640,640))
                .build();

        Preview preview = new Preview(previewConfig);
        preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
            @Override
            public void onUpdated(Preview.PreviewOutput output) {
                ViewGroup parent = (ViewGroup) viewFinder.getParent();
                parent.removeView(viewFinder);
                parent.addView(viewFinder, 0);

                viewFinder.setSurfaceTexture(output.getSurfaceTexture());
                updateTransform();
            }
        });

        CameraX.bindToLifecycle(this, preview);

这样就实现了基本的相机预览功能。这几个方法都很简单明了,对外只依赖一个TextureView。生命周期自动绑定,这意味着代码可以写在一块,在一处调用。不像以前这里插一段代码,那里插一段代码。

还有最大的好处,就是可扩展性。相机预览使用了PreviewConfig和Preview两个对象,加入新的相机功能同样是加两个对象XXXConfig和XXX,其他地方都不同改!

加入拍照功能就加入ImageCaptureConfig和ImageCapture,加入图像分析功能就加入ImageAnalysisConfig和ImageAnalysis,非常方便统一。

4 拍照

创建ImageCaptureConfig和ImageCapture这两个对象,用imageCapture.takePicture方法传入相片保存地址就行了。当然在生命周期绑定中也加上imageCapture。

ImageCaptureConfig可以定制相片尺寸和长宽比例,这里的尺寸和比例跟相机预览的尺寸比例无关,我测试传入任何比例都能得到图片。


        // 2. capture
        ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder()
                .setTargetAspectRatio(new Rational(1,1))
                .setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
                .build();
        final ImageCapture imageCapture = new ImageCapture(imageCaptureConfig);
        viewFinder.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                File photo = new File(getExternalCacheDir() + "/" + System.currentTimeMillis() + ".jpg");
                imageCapture.takePicture(photo, new ImageCapture.OnImageSavedListener() {
                    @Override
                    public void onImageSaved(@NonNull File file) {
                        showToast("saved " + file.getAbsolutePath());
                    }

                    @Override
                    public void one rror(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) {
                        showToast("error " + message);
                        cause.printStackTrace();
                    }
                });
                return true;
            }
        });

        CameraX.bindToLifecycle(this, preview, imageCapture);

5 图片分析

图片分析名字很高大上,实际上就是图像数据回调,实时获取相机的图像数据,可以自己处理这些图像。

创建ImageAnalysisConfig和ImageAnalysis这两个对象,创建一个HandlerThread用于在子线程中处理数据,创建一个ImageAnalysis.Analyzer接口实现类,在analyze(ImageProxy imageProxy, int rotationDegrees)回调方法中就能拿到图像数据了。当然ImageAnalysis对象也要绑定生命周期。

我这里分析图像数据用了之前写的一个工具YUVDetectView,来分析图像属于哪种YUV420格式。

        // 3. analyze
        HandlerThread handlerThread = new HandlerThread("Analyze-thread");
        handlerThread.start();

        ImageAnalysisConfig imageAnalysisConfig = new ImageAnalysisConfig.Builder()
                .setCallbackHandler(new Handler(handlerThread.getLooper()))
                .setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
                .setTargetAspectRatio(new Rational(2, 3))
//                .setTargetResolution(new Size(600, 600))
                .build();

        ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
        imageAnalysis.setAnalyzer(new MyAnalyzer());

        CameraX.bindToLifecycle(this, preview, imageCapture, imageAnalysis);



    private class MyAnalyzer implements ImageAnalysis.Analyzer {

        @Override
        public void analyze(ImageProxy imageProxy, int rotationDegrees) {
            final Image image = imageProxy.getImage();
            if(image != null) {
                Log.d("chao", image.getWidth() + "," + image.getHeight());
                imageView.input(image);
            }
        }
    }

Github地址

https://github.com/rome753/android-YuvTools
(CameraX代码在camerax包下面)

标签:CameraX,int,void,使用,相机,viewFinder,new,Android
来源: https://www.cnblogs.com/rome753/p/16495606.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有