如何使用Camerax API在Android上显示拍摄的图像

3
如何在Android中显示使用CameraX拍摄的图像。
我的情况:我有两个片段。在其中一个片段中,我已经实现了CameraX功能以拍摄照片并将其保存到本地。现在我想将这个图像传递给另一个片段,并在那里显示它在一个ImageView中。
最好的方法是什么(如果您可以提供一个有效的代码,那将是很有帮助的)?
我想到解决这个问题的几种方法:
第一种方法:发送从“overriding onImageSaved”中提取的Uri Id(我们在“ImageCapture.OutputFileResults”中获得Uri id)。但是,这种方法存在一些问题。使用Android Navigation将Uri作为bundle或safe args发送时将其显示为空值。这意味着没有跨导航传输Uri。
第二种方法:将uri转换为位图,然后将此位图发送到另一个片段。在那里解码位图以将其放置在ImageView上。我不确定这种方法是否可行,如果您曾经使用过,请让我知道。
还有其他处理此问题的方式吗?我在官方Android文档中找不到任何文章介绍这个问题。
Camera Fragment(第1个片段):
class CameraFragment : Fragment() {

    private var _binding: FragmentCameraBinding? = null
    private val binding: FragmentCameraBinding get() = _binding!!

    private var imageCapture: ImageCapture? = null
    private lateinit var cameraExecutor: ExecutorService

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        _binding = FragmentCameraBinding.inflate(inflater, container, false)

        //Request Camera Permissions
        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                requireActivity(), REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
            )
        }
        cameraExecutor = Executors.newSingleThreadExecutor()

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //Setting up listeners for taking Photo
        binding.imageCaptureButton.setOnClickListener {
            takePhoto()
        }
    }

    private fun takePhoto() {
        // Get a stable reference of the modifiable image capture use case
        val imageCapture = imageCapture ?: return

        // Create time stamped name and MediaStore entry.
        val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
            .format(System.currentTimeMillis())
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, name)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
                put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
            }
        }


        // Create output options object which contains file + metadata
        val outputOptions = ImageCapture.OutputFileOptions
            .Builder(
                requireContext().contentResolver,
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                contentValues
            )
            .build()

        // Set up image capture listener, which is triggered after photo has
        // been taken
        imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(requireContext()),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Timber.e("Photo capture failed: ${exc.message}", exc)
                }

                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    val msg = "Photo capture succeeded: ${output.savedUri}"
                    Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
                    Timber.d(msg)


                    val uriFilePath = output.savedUri?.path
                    val path: String = uriFilePath.toString()

                    val bundle = Bundle()
                    bundle.putString("image", path)

                    Navigation.findNavController(requireView()).navigate(R.id
                        .cameraFragmentToCameraFinalFragment, bundle)
                }
            })
    }
 private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            requireContext(), it
        ) == PackageManager.PERMISSION_GRANTED
    }


    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())

        cameraProviderFuture.addListener({
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview
            val preview = Preview.Builder()
                .build()
                .also { preview ->
                    preview.setSurfaceProvider(binding.viewFinder.surfaceProvider)
                }

            imageCapture = ImageCapture.Builder()
                .build()

            val imageAnalyzer = ImageAnalysis.Builder()
                .setTargetResolution(Size(1280, 720))
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build()
                .also {
                    it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
                        Timber.d("Average luminosity: $luma")
                    })
                }

            // Select back camera as a default
            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                // Unbind use cases before rebinding
                cameraProvider.unbindAll()

                // Bind use cases to camera
                cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture, imageAnalyzer
                )

            } catch (exc: Exception) {
                Timber.e("Use case binding failed", exc)
            }

        }, ContextCompat.getMainExecutor(requireContext()))
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults:
        IntArray
    ) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(
                    requireContext(), "Permissions not granted by the user.",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }
    }


    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
        cameraExecutor.shutdown()
    }

    companion object {
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = mutableListOf(
            Manifest.permission.CAMERA,
        ).apply {
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
                add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            }
        }.toTypedArray()
    }
}

PhotoFinalFragment(第二个片段):

class PhotoFinalFragment : Fragment() {

    private var _binding: FragmentPhotoFinalBinding? = null
    private val binding: FragmentPhotoFinalBinding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        _binding = FragmentPhotoFinalBinding.inflate(inflater, container, false)

        val bundle: Bundle? = arguments?.getBundle("image")
        val uri: Uri = bundle?.get("image") as Uri

        binding.imageView.setImageURI(uri)

        binding.btnYes.setOnClickListener {
            Navigation.findNavController(requireView()).navigate(R.id.cameraFinalFragmentToStoryFragment)
        }

        binding.btnNo.setOnClickListener {
            Navigation.findNavController(requireView()).navigate(R.id.cameraFinalFragmentTocameraFragment)
        }
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

我认为你可以使用SharedViewModel来在片段之间传递信息。 - Edgar
请查看以下链接:https://developer.android.com/codelabs/basic-android-kotlin-training-shared-viewmodel#0 - Edgar
2个回答

1
请使用ImageCapture.OnImageCapturedCallback而不是ImageCapture.OnImageSavedCallback。
当使用OnImageCapturedCallback()时,您可以重写onCaptureSuccess方法,该方法提供了可转换为图像并设置到ImageView中的ImageProxy。
请查看OnImageCapturedCallback
val buffer = imageProxy.planes[0].buffer
val bytes = ByteArray(buffer.capacity())
buffer[bytes]
val bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
imageProxy.close()

imageView.rotation = imageProxy.imageInfo.rotationDegrees.toFloat()
imageView.setImageBitmap(bitmapImage)

0
onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { Uri selectedImageUri = outputFileResults.getSavedUri(); Bitmap imageBitmap = BitmapUtils.loadFromUri(MainActivity.this, selectedImageUri); }
                    imgView.setImageBitmap(imageBitmap);

当拍摄成功时,有一个重写方法onimage saved,你可以从中设置图像


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接