Flutter 中的原生相机视图不占据整个宽度和高度

问题描述 投票:0回答:5

我正在尝试将

CameraX
集成到我的 flutter 项目中。我是通过平台渠道这样做的。我不想集成任何第三方库。

这是Android中相机仅占据三分之一高度的截图

enter image description here

下面是我的代码

class ScanQr extends StatelessWidget {
  const ScanQr({super.key});

  @override
  Widget build(BuildContext context) {
    var width = MediaQuery.of(context).size.width;
    var height = MediaQuery.of(context).size.height;

    return Scaffold(
      backgroundColor: Colors.teal,
      body: SizedBox(
        height: height, 
        width: width,
        child: CealScanQrView(
          width: width,
          height: height,
        ),
      ),
    );
  }
}


class CealScanQrView extends StatelessWidget {
  const CealScanQrView({required this.width, required this.height, super.key});

  final double width;
  final double height;
 
  @override
  Widget build(BuildContext context) {
    final Map<String, dynamic> creationParams = <String, dynamic>{};

    creationParams["width"] = width;
    creationParams["height"] = height;

    return Platform.isAndroid
        ? AndroidView(
            viewType: cealScanQrView,
            layoutDirection: TextDirection.ltr,
            creationParams: creationParams,
            creationParamsCodec: const StandardMessageCodec(),
          )
        : UiKitView(
            viewType: cealScanQrView,
            layoutDirection: TextDirection.ltr,
            creationParams: creationParams,
            creationParamsCodec: const StandardMessageCodec(),
          );
  }
}

这是我的Android代码

MainActivity's configureFlutterEngine
方法中

flutterEngine
            .platformViewsController
            .registry
            .registerViewFactory("cealScanQrView", CealScanQrViewFactory(this))


class CealScanQrView(
    private val context: Context, id: Int, creationParams: Map<String?, Any?>?,
    private val activity: FlutterActivity
) : PlatformView {

    private var mCameraProvider: ProcessCameraProvider? = null
    private var linearLayout: LinearLayout = LinearLayout(context)

    private var preview: PreviewView = PreviewView(context)

    private lateinit var cameraExecutor: ExecutorService
    private lateinit var options: BarcodeScannerOptions
    private lateinit var scanner: BarcodeScanner

    private var analysisUseCase: ImageAnalysis = ImageAnalysis.Builder()
        .build()

    companion object {
        private val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = mutableListOf(Manifest.permission.CAMERA).toTypedArray()
    }


    init {
        val linearLayoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )

        linearLayout.layoutParams = linearLayoutParams
        linearLayout.orientation = LinearLayout.VERTICAL

        preview.setBackgroundColor(Color.RED)
        preview.layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )

        linearLayout.addView(preview)
        linearLayout.layoutParams.width = 400
        linearLayout.layoutParams.height = 400
        setUpCamera()

        preview.viewTreeObserver.addOnGlobalLayoutListener(object :
            ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                preview.viewTreeObserver.removeOnGlobalLayoutListener(this)
                preview.layoutParams.height =
                    creationParams?.get("height").toString().toDouble().toInt()
                preview.requestLayout()
            }
        })
    }

    private fun setUpCamera() {
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                context as FlutterActivity, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
            )
        }
        cameraExecutor = Executors.newSingleThreadExecutor()

        options = BarcodeScannerOptions.Builder()
            .setBarcodeFormats(
                Barcode.FORMAT_QR_CODE
            )
            .build()
        scanner = BarcodeScanning.getClient(options)
        analysisUseCase.setAnalyzer(
            // newSingleThreadExecutor() will let us perform analysis on a single worker thread
            Executors.newSingleThreadExecutor()
        ) { imageProxy ->
            processImageProxy(scanner, imageProxy)
        }
    }

    override fun getView(): View {
        return linearLayout
    }

    override fun dispose() {
        cameraExecutor.shutdown()
    }

    @SuppressLint("UnsafeOptInUsageError")
    private fun processImageProxy(
        barcodeScanner: BarcodeScanner,
        imageProxy: ImageProxy
    ) {
        imageProxy.image?.let { image ->
            val inputImage =
                InputImage.fromMediaImage(
                    image,
                    imageProxy.imageInfo.rotationDegrees
                )
            barcodeScanner.process(inputImage)
                .addOnSuccessListener { barcodeList ->
                    val barcode = barcodeList.getOrNull(0)
                    // `rawValue` is the decoded value of the barcode
                    barcode?.rawValue?.let { value ->
                        mCameraProvider?.unbindAll()
                        Toast.makeText(context, value, Toast.LENGTH_LONG).show()
                    }
                }
                .addOnFailureListener {
                    // This failure will happen if the barcode scanning model
                    // fails to download from Google Play Services
                }
                .addOnCompleteListener {
                    // When the image is from CameraX analysis use case, must
                    // call image.close() on received images when finished
                    // using them. Otherwise, new images may not be received
                    // or the camera may stall.
                    imageProxy.image?.close()
                    imageProxy.close()
                }
        }
    }

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
    }

    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
        cameraProviderFuture.addListener({
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
            mCameraProvider = cameraProvider
            // Preview
            val surfacePreview = Preview.Builder()
                .build()
                .also {
                    it.setSurfaceProvider(preview.surfaceProvider)
                }
            // 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(
                    activity,
                    cameraSelector,
                    surfacePreview,
                    analysisUseCase,
                )
            } catch (exc: Exception) {
                // Do nothing on exception
            }
        }, ContextCompat.getMainExecutor(context))
    }


}

AndroidManifest.xml

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

我面临两个问题

首先,当我的屏幕打开时,即使我请求相机权限,我也看不到相机权限。我必须手动进入设置屏幕才能启用相机权限 其次,相机不会占据整个屏幕

即使我设置了

linearLayout.layoutParams.height = 1000
,高度仍然保持不变。我完全停止了应用程序并重新运行了多次但没有效果

编辑 我创建了一个要点,其中删除了线性布局。检查这里

android flutter dart android-camerax
5个回答
2
投票

我观察到了一些可能会导致相机预览行为的事情:

  1. 您没有为您的(
    Preview-
    ImageAnalysis-
    )用例指定宽高比。默认值为 4:3。请注意,大多数相机的原始长宽比确实是 4:3。如果您使用 16:9,图像的可见部分会更小,但更适合普通手机。例如:
Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9).build()
  1. 您没有为
    PreviewView
    指定音阶类型。这也可能导致图像裁剪。例如:
preview.scaleType = PreviewView.ScaleType.FILL_CENTER
  1. 相机方向不适合您的屏幕。您应该更新用例的 targetRotation。您可以在 CameraX 文档

    中阅读有关该主题的更多信息
  2. 您还应该将

    LayoutParams
    LinearLayout
    设置为
    MATCH_PARENT
    。我还认为你可以只使用
    FrameLayout
    。还要删除
    viewTreeObserver

  3. 您必须听取权限请求的结果。您必须在 Activity 中重写方法

    onRequestPermissionsResult
    。它将收到结果以及您指定的请求代码:
    REQUEST_CODE_PERMISSIONS
    。然而,使用较新的 API 来请求这些权限可能会更容易:
    registerForPermissionResult
    。例如:

val launcher = (context as ComponentActivity).registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
    if (granted) {
        startCamera()
    }
}
launcher.launch(android.Manifest.permission.CAMERA)

1
投票

请在startCamera()中添加两行代码 功能

preview.scaleType = PreviewView.ScaleType.FILL_CENTER cameraPreview.implementationMode = PreviewView.ImplementationMode.COMPATIBLE


1
投票

尝试设置implementationMode

cameraPreview.implementationMode = PreviewView.ImplementationMode.COMPATIBLE

0
投票

1 不要占据整个屏幕。 很简单,您可能会忘记在 Android 代码上动态设置宽度,因为发生的情况是 Android 代码仅获得 400 宽度和 400 高度(在 林斯 LinearLayout.layoutParams.width = 400 线性布局.layoutParams.height = 400 )并且在 flutter 调用之后它只接收到这个大小, 所以你必须以自适应的方式进行,请参阅此答案

2 未获得许可: 只需使用此包permission_handler即可在所有设备上为您提供帮助和自动化。

如果我的回答对你有帮助别忘了点赞哦


-1
投票

检查方向并将其设置为纵向

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
  runApp(MyApp());
...
}

尝试用

来擦拭你的显示区域
SizedBox.expand(
  child: YOUR DISPLAY AREA,
)

也试试

Center(
  child: ,
)
© www.soinside.com 2019 - 2024. All rights reserved.