CameraX:意外的旋转值 -1(在 API 22,Lollipop / 5.1 上)

CameraX: Unexpected rotation value -1 (on API 22, Lollipop / 5.1)

在 API 22 模拟器(Android 5.1 / Lollipop)上测试时使用 CameraX API

出现以下错误:

错误:

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.cameraxtutorial, PID: 3815 java.lang.IllegalStateException: Unexpected rotation value -1 at androidx.camera.view.TransformUtils.surfaceRotationToRotationDegrees(TransformUtils.java:99) at androidx.camera.view.PreviewTransformation.getTextureViewCorrectionMatrix(PreviewTransformation.java:156) at androidx.camera.view.PreviewTransformation.transformView(PreviewTransformation.java:184) at androidx.camera.view.PreviewViewImplementation.redrawPreview(PreviewViewImplementation.java:86) at androidx.camera.view.PreviewViewImplementation.onSurfaceProvided(PreviewViewImplementation.java:93) at androidx.camera.view.TextureViewImplementation.tryToProvidePreviewSurface(TextureViewImplementation.java:241) at androidx.camera.view.TextureViewImplementation.onSurfaceTextureAvailable(TextureViewImplementation.java:139) at android.view.TextureView.getHardwareLayer(TextureView.java:370) at android.view.View.updateDisplayListIfDirty(View.java:14144) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at android.view.View.draw(View.java:15234) at android.widget.FrameLayout.draw(FrameLayout.java:598) at android.view.View.updateDisplayListIfDirty(View.java:14167) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1994) at android.view.View.updateDisplayListIfDirty(View.java:14162) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at android.view.View.updateDisplayListIfDirty(View.java:14162) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at android.view.View.updateDisplayListIfDirty(View.java:14162) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at android.view.View.updateDisplayListIfDirty(View.java:14162) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at android.view.View.updateDisplayListIfDirty(View.java:14162) at android.view.View.getDisplayList(View.java:14189) at android.view.View.draw(View.java:14959) at android.view.ViewGroup.drawChild(ViewGroup.java:3405) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3198) at android.view.View.draw(View.java:15234) at android.widget.FrameLayout.draw(FrameLayout.java:598) at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2650) at android.view.View.updateDisplayListIfDirty(View.java:14167) at android.view.View.getDisplayList(View.java:14189) at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:273) at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:279) at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:318) at android.view.ViewRootImpl.draw(ViewRootImpl.java:2530) at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2352) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1982) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) at android.vi

MainActivity.kt代码:

package com.cameraxtutorial

import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.Surface
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import com.cameraxtutorial.databinding.ActivityMainBinding
import com.google.android.material.snackbar.Snackbar
import com.google.common.util.concurrent.ListenableFuture
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
    private lateinit var cameraSelector: CameraSelector
    private var imageCapture: ImageCapture? = null
    private lateinit var imgCaptureExecutor: ExecutorService
    private val cameraPermissionResult =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { permissionGranted ->
            if (permissionGranted) {
                startCamera()
            } else {
                Snackbar.make(
                    binding.root,
                    "The camera permission is necessary",
                    Snackbar.LENGTH_INDEFINITE
                ).show()
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

        imgCaptureExecutor = Executors.newSingleThreadExecutor()

        cameraPermissionResult.launch(android.Manifest.permission.CAMERA)

        binding.imgCaptureBtn.setOnClickListener {
            takePhoto()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                animateFlash()
            }
        }

        binding.switchBtn.setOnClickListener {
            cameraSelector = if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) {
                CameraSelector.DEFAULT_FRONT_CAMERA
            } else {
                CameraSelector.DEFAULT_BACK_CAMERA
            }
            startCamera()
        }
        binding.galleryBtn.setOnClickListener {
            val intent = Intent(this, GalleryActivity::class.java)
            startActivity(intent)
        }

    }

    private fun startCamera() {
        val preview = Preview.Builder().build().also {
            it.setSurfaceProvider(binding.preview.surfaceProvider)
        }
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()

            // imageCapture = ImageCapture.Builder().build()

            imageCapture = ImageCapture.Builder().
                // setTargetResolution(Size(960, 1280)).
                // setTargetRotation(Surface.ROTATION_0).
                build()

            try {
                cameraProvider.unbindAll()
                cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
            } catch (e: Exception) {
                Log.d(TAG, "Use case binding failed")
            }
        }, ContextCompat.getMainExecutor(this))
    }

    private fun takePhoto() {
        imageCapture?.let {
            val fileName = "JPEG_${System.currentTimeMillis()}"+".jpg"
            // val file = File(externalMediaDirs[0], fileName)
            val file = File(applicationContext.filesDir, fileName)

            val file_internal = File(applicationContext.filesDir, "aaa.png")

            Log.e("file_internal", file_internal.path.toString())
            Log.e("file_path", file.path.toString())
            val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
            it.takePicture(
                outputFileOptions,
                imgCaptureExecutor,
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        Log.i(TAG, "The image has been saved in ${file.toUri()}")
                    }

                    override fun onError(exception: ImageCaptureException) {
                        Toast.makeText(
                            binding.root.context,
                            "Error taking photo",
                            Toast.LENGTH_LONG
                        ).show()
                        Log.d(TAG, "Error taking photo:$exception")
                    }

                })
        }
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun animateFlash() {
        binding.root.postDelayed({
            binding.root.foreground = ColorDrawable(Color.WHITE)
            binding.root.postDelayed({
                binding.root.foreground = null
            }, 50)
        }, 100)
    }

    companion object {
        val TAG = "MainActivity"
    }
}

Gradle 应用程序:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.cameraxtutorial"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    buildFeatures{
        viewBinding true
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    // def camerax_version = "1.0.1"
    def camerax_version = "1.2.0-alpha01"
// CameraX core library using camera2 implementation
    implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX Lifecycle Library
    implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class
    implementation "androidx.camera:camera-view:1.0.0-alpha27"
    implementation 'com.github.bumptech.glide:glide:4.12.0'
}

这个 app/Activity 在 Android 11 或 API 30 上运行良好,CameraX 文档说它支持 API 21 - 最近的 APIs,任何帮助将不胜感激。

您的 CameraX 库版本不匹配。 1.2.0-alpha01 v.s。 1.0.0-alpha27

def camerax_version = "1.2.0-alpha01"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha27"

我假设 gradle 解析 camera-corecamera-view 使用不同的版本,这导致了问题。要解决此问题,您可以使用 1.2.0-alpha01 获取最新的 alpha 版或使用 1.1.0-rc01 获取更稳定的版本。