我正在尝试将
CameraX
集成到我的 flutter 项目中。我是通过平台渠道这样做的。我不想集成任何第三方库。
这是Android中相机仅占据三分之一高度的截图
下面是我的代码
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
,高度仍然保持不变。我完全停止了应用程序并重新运行了多次但没有效果
编辑 我创建了一个要点,其中删除了线性布局。检查这里
我观察到了一些可能会导致相机预览行为的事情:
Preview-
和ImageAnalysis-
)用例指定宽高比。默认值为 4:3。请注意,大多数相机的原始长宽比确实是 4:3。如果您使用 16:9,图像的可见部分会更小,但更适合普通手机。例如:Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9).build()
PreviewView
指定音阶类型。这也可能导致图像裁剪。例如:preview.scaleType = PreviewView.ScaleType.FILL_CENTER
相机方向不适合您的屏幕。您应该更新用例的 targetRotation。您可以在 CameraX 文档
中阅读有关该主题的更多信息您还应该将
LayoutParams
的 LinearLayout
设置为 MATCH_PARENT
。我还认为你可以只使用 FrameLayout
。还要删除 viewTreeObserver
。
您必须听取权限请求的结果。您必须在 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)
请在startCamera()中添加两行代码 功能
preview.scaleType = PreviewView.ScaleType.FILL_CENTER cameraPreview.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
尝试设置implementationMode
cameraPreview.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
1 不要占据整个屏幕。 很简单,您可能会忘记在 Android 代码上动态设置宽度,因为发生的情况是 Android 代码仅获得 400 宽度和 400 高度(在 林斯 LinearLayout.layoutParams.width = 400 线性布局.layoutParams.height = 400 )并且在 flutter 调用之后它只接收到这个大小, 所以你必须以自适应的方式进行,请参阅此答案。
2 未获得许可: 只需使用此包permission_handler即可在所有设备上为您提供帮助和自动化。
如果我的回答对你有帮助别忘了点赞哦
检查方向并将其设置为纵向
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
runApp(MyApp());
...
}
尝试用
来擦拭你的显示区域SizedBox.expand(
child: YOUR DISPLAY AREA,
)
也试试
Center(
child: ,
)