【问题标题】:How to use CameraX to build my custom Camera?如何使用 CameraX 构建我的自定义相机?
【发布时间】:2023-03-13 21:55:01
【问题描述】:

我想创建一个不使用隐式意图的相机屏幕。所以我使用CameraX 来实现这一点。

我从 Github 获得了一个代码。我正在努力解决这个问题

错误:不兼容的类型:文件无法转换为Executor imgCap.takePicture(file,new ImageCapture.OnImageCapturedListener()

请帮我简化这段代码。

MainActivity.java

package com.example.camerax;

import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.os.Environment;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageAnalysisConfig;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureConfig;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;

import java.io.File;
import java.util.concurrent.Executor;


//saus: https://codelabs.developers.google.com/codelabs/camerax-getting-started/
public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener{

private int REQUEST_CODE_PERMISSIONS = 10; //arbitrary number, can be changed accordingly
private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA","android.permission.WRITE_EXTERNAL_STORAGE"};


TextureView txView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    txView = findViewById(R.id.view_finder);

    if(allPermissionsGranted()){
        startCamera(); //start camera if permission has been granted by user
    } else{
        ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
    }
}

private void startCamera() {
    //make sure there isn't another camera instance running before starting
    CameraX.unbindAll();

    /* start preview */
    int aspRatioW = txView.getWidth(); //get width of screen
    int aspRatioH = txView.getHeight(); //get height
    Rational rational = new Rational(aspRatioW,aspRatioH); //aspect ratio
    Size screen = new Size(aspRatioW, aspRatioH); //size of the screen

    //config obj for preview/viewfinder thingy.
    @SuppressLint("RestrictedApi") PreviewConfig pConfig = new PreviewConfig.Builder().setTargetAspectRatioCustom(rational).setTargetResolution(screen).build();
    Preview preview = new Preview(pConfig); //lets build it

    preview.setOnPreviewOutputUpdateListener(
            new Preview.OnPreviewOutputUpdateListener() {
                //to update the surface texture we have to destroy it first, then re-add it
                @Override
                public void onUpdated(Preview.PreviewOutput output){
                    ViewGroup parent = (ViewGroup) txView.getParent();
                    parent.removeView(txView);
                    parent.addView(txView, 0);

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

    /* image capture */

    //config obj, selected capture mode
    ImageCaptureConfig imgCapConfig = new ImageCaptureConfig.Builder().setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
            .setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build();
    final ImageCapture imgCap = new ImageCapture(imgCapConfig);

    findViewById(R.id.capture_button).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            File file = new File(Environment.getExternalStorageDirectory() + "/" + System.currentTimeMillis() + ".jpg");

            imgCap.takePicture(file,new ImageCapture.OnImageCapturedListener() {

                @Override
                public void onCaptureSuccess(ImageProxy image, int rotationDegrees) {
                    super.onCaptureSuccess(image, rotationDegrees);
                    String msg = "Photo capture succeeded: " + file.getAbsolutePath();
                    Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show();
                }

                @Override
                public void onError(@NonNull ImageCapture.ImageCaptureError imageCaptureError, @NonNull String message, @Nullable Throwable cause) {
                    super.onError(imageCaptureError, message, cause);
                    String msg = "Photo capture failed: " + message;
                    Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show();
                    if(cause != null){
                        cause.printStackTrace();
                    }
                }
            });

        }
    });

    /* image analyser */

    ImageAnalysisConfig imgAConfig = new ImageAnalysisConfig.Builder().setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE).build();
    ImageAnalysis analysis = new ImageAnalysis(imgAConfig);


    //bind to lifecycle:
    CameraX.bindToLifecycle((LifecycleOwner)this, analysis, imgCap, preview);
}

private void updateTransform(){
    /*
    * compensates the changes in orientation for the viewfinder, bc the rest of the layout stays in portrait mode.
    * methinks :thonk:
    * imgCap does this already, this class can be commented out or be used to optimise the preview
    */
    Matrix mx = new Matrix();
    float w = txView.getMeasuredWidth();
    float h = txView.getMeasuredHeight();

    float centreX = w / 2f; //calc centre of the viewfinder
    float centreY = h / 2f;

    int rotationDgr;
    int rotation = (int)txView.getRotation(); //cast to int bc switches don't like floats

    switch(rotation){ //correct output to account for display rotation
        case Surface.ROTATION_0:
            rotationDgr = 0;
            break;
        case Surface.ROTATION_90:
            rotationDgr = 90;
            break;
        case Surface.ROTATION_180:
            rotationDgr = 180;
            break;
        case Surface.ROTATION_270:
            rotationDgr = 270;
            break;
        default:
            return;
    }

    mx.postRotate((float)rotationDgr, centreX, centreY);
    txView.setTransform(mx); //apply transformations to textureview
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    //start camera when permissions have been granted otherwise exit app
    if(requestCode == REQUEST_CODE_PERMISSIONS){
        if(allPermissionsGranted()){
            startCamera();
        } else{
            Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

private boolean allPermissionsGranted(){
    //check if req permissions have been granted
    for(String permission : REQUIRED_PERMISSIONS){
        if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
            return false;
        }
    }
    return true;
}

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {

}

@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

}

@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return false;
}

@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {

}
//array w/ permissions from manifest

}

*build.gradle**

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.camerax"
        minSdkVersion 22
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0-beta01'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    def camerax_version = '1.0.0-alpha06'
    //noinspection GradleDependency
    implementation "androidx.camera:camera-core:${camerax_version}"
    //noinspection GradleDependency
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

【问题讨论】:

    标签: java android android-camera android-camerax


    【解决方案1】:

    试试这个 在 Activity 中定义一个 Executor

    private val executor = Executors.newSingleThreadExecutor()
    

    takePicture方法中传递执行器。

    findViewById(R.id.capture_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                File file = new File(Environment.getExternalStorageDirectory() + "/" + System.currentTimeMillis() + ".jpg");
    
                imgCap.takePicture(file, executor, new ImageCapture.OnImageCapturedListener() {
    
                    @Override
                    public void onCaptureSuccess(ImageProxy image, int rotationDegrees) {
                        super.onCaptureSuccess(image, rotationDegrees);
                        String msg = "Photo capture succeeded: " + file.getAbsolutePath();
                        Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show();
                    }
    
                    @Override
                    public void onError(@NonNull ImageCapture.ImageCaptureError imageCaptureError, @NonNull String message, @Nullable Throwable cause) {
                        super.onError(imageCaptureError, message, cause);
                        String msg = "Photo capture failed: " + message;
                        Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show();
                        if(cause != null){
                            cause.printStackTrace();
                        }
                    }
                });
    
            }
        });
    

    【讨论】:

    • 你能告诉我如何在 onImageSaved 后关闭相机吗?
    • 我认为这取决于您绑定到的生命周期。
    • 是的,我明白了。谢谢
    • 拍照时要注意使用哪个Executor,如果回调(onCaptureSuccessonError)执行UI相关操作,最好使用主线程执行器.
    • 随着 AndroidX Jetpack 的发布,您可以使用AsyncTask.THREAD_POOL_EXECUTOR。正如 Jetpack Loader developer.android.com/jetpack/androidx/releases/… 的此发行说明中所述,引用:> AsyncTaskLoader 的默认执行程序现在是 AsyncTask.THREAD_POOL_EXECUTOR,而不是自定义执行程序。。我从@Gurumod 的回答中得到了提示:stackoverflow.com/a/58414168/7001213
    猜你喜欢
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 2019-04-08
    • 2023-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多