سلام! به مجموعه مقالات ما در مورد CameraX و Jetpack Compose خوش آمدید. در پستهای قبلی، اصول اولیه تنظیم پیشنمایش دوربین و اضافه کردن قابلیت فوکوس با لمس را پوشش دادیم.
🧱 بخش ۱ : ساخت یک پیشنمایش اولیه دوربین با استفاده از آرتیفکت جدید camera-compose. ما مدیریت مجوزها و یکپارچهسازیهای اولیه را پوشش دادیم.
👆 بخش ۲ : استفاده از سیستم ژست نوشتن، گرافیک و کوروتینها برای پیادهسازی قابلیت لمس برای فوکوس بصری.
🔦 بخش ۳ (این پست): بررسی نحوهی همپوشانی عناصر رابط کاربری Compose روی پیشنمایش دوربین برای تجربهی کاربری غنیتر.
📂 بخش ۴ : استفاده از APIهای تطبیقی و چارچوب انیمیشن Compose برای انتقال روان انیمیشن به حالت رومیزی و از آن در گوشیهای تاشو.
در این پست، به سراغ چیزی خواهیم رفت که از نظر بصری کمی جذابتر است - پیادهسازی یک افکت نورافکن در بالای پیشنمایش دوربین، با استفاده از تشخیص چهره به عنوان پایه این افکت. میگویید چرا؟ مطمئن نیستم. اما مطمئناً جالب به نظر میرسد 🙂. و مهمتر از آن، نشان میدهد که چگونه میتوانیم به راحتی مختصات حسگر را به مختصات رابط کاربری تبدیل کنیم و از آنها در Compose استفاده کنیم!

فعال کردن تشخیص چهره
ابتدا، بیایید CameraPreviewViewModel را برای فعال کردن تشخیص چهره تغییر دهیم. ما از Camera2Interop API استفاده خواهیم کرد که به ما امکان تعامل با Camera2 API زیرین از CameraX را میدهد. این به ما این امکان را میدهد که از ویژگیهای دوربین که مستقیماً توسط CameraX در معرض نمایش قرار نمیگیرند، استفاده کنیم. باید تغییرات زیر را اعمال کنیم:
- یک StateFlow ایجاد کنید که شامل مرزهای وجه به عنوان لیستی از
Rectها باشد. - گزینه درخواست ضبط
STATISTICS_FACE_DETECT_MODEرا روی FULL تنظیم کنید، که تشخیص چهره را فعال میکند. - یک
CaptureCallbackتنظیم کنید تا اطلاعات چهره را از نتیجه ضبط دریافت کند.
class CameraPreviewViewModel : ViewModel() { ... private val _sensorFaceRects = MutableStateFlow(listOf<Rect>()) val sensorFaceRects: StateFlow<List<Rect>> = _sensorFaceRects.asStateFlow() private val cameraPreviewUseCase = Preview.Builder() .apply { Camera2Interop.Extender(this) .setCaptureRequestOption( CaptureRequest.STATISTICS_FACE_DETECT_MODE, CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL ) .setSessionCaptureCallback(object : CameraCaptureSession.CaptureCallback() { override fun onCaptureCompleted( session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult ) { super.onCaptureCompleted(session, request, result) result.get(CaptureResult.STATISTICS_FACES) ?.map { face -> face.bounds.toComposeRect() } ?.toList() ?.let { faces -> _sensorFaceRects.update { faces } } } }) } .build().apply { ... }
با اعمال این تغییرات، مدل نمای ما اکنون فهرستی از اشیاء Rect را منتشر میکند که نشاندهندهی کادرهای مرزی چهرههای شناساییشده در مختصات حسگر هستند.
تبدیل مختصات حسگر به مختصات رابط کاربری
کادرهای مرزی چهرههای شناساییشده که در بخش قبل ذخیره کردیم، از مختصات موجود در سیستم مختصات حسگر استفاده میکنند. برای ترسیم کادرهای مرزی در رابط کاربری خود، باید این مختصات را طوری تغییر دهیم که در سیستم مختصات Compose صحیح باشند. ما باید:
- تبدیل مختصات حسگر به مختصات بافر پیشنمایش
- مختصات بافر پیشنمایش را به مختصات رابط کاربری Compose تبدیل کنید
این تبدیلات با استفاده از ماتریسهای تبدیل انجام میشوند. هر یک از تبدیلات، ماتریس مخصوص به خود را دارد:
-
SurfaceRequestما یک نمونهTransformationInfoرا نگه میدارد که شامل یک ماتریسsensorToBufferTranformاست. -
CameraXViewfinderما یکCoordinateTransformerمرتبط دارد. شاید به یاد داشته باشید که ما قبلاً در پست وبلاگ قبلی از این مبدل برای تبدیل مختصات tap-to-focus استفاده کردیم.
ما میتوانیم یک متد کمکی ایجاد کنیم که بتواند تبدیل را برای ما انجام دهد:
private fun List<Rect>.transformToUiCoords( transformationInfo: SurfaceRequest.TransformationInfo?, uiToBufferCoordinateTransformer: MutableCoordinateTransformer ): List<Rect> = this.map { sensorRect -> val bufferToUiTransformMatrix = Matrix().apply { setFrom(uiToBufferCoordinateTransformer.transformMatrix) invert() } val sensorToBufferTransformMatrix = Matrix().apply { transformationInfo?.let { setFrom(it.sensorToBufferTransform) } } val bufferRect = sensorToBufferTransformMatrix.map(sensorRect) val uiRect = bufferToUiTransformMatrix.map(bufferRect) uiRect }
- ما لیست چهرههای شناساییشده را مرور میکنیم و برای هر چهره، تبدیل را اجرا میکنیم.
-
CoordinateTransformer.transformMatrixکه ازCameraXViewfinderخود دریافت میکنیم، به طور پیشفرض مختصات را از UI به مختصات بافر تبدیل میکند. در مورد ما، میخواهیم ماتریس به روش معکوس عمل کند و مختصات بافر را به مختصات UI تبدیل کند. بنابراین، از متدinvert()برای معکوس کردن ماتریس استفاده میکنیم. - ابتدا با استفاده از
sensorToBufferTransformMatrixچهره را از مختصات حسگر به مختصات بافر تبدیل میکنیم و سپس آن مختصات بافر را با استفاده ازbufferToUiTransformMatrixبه مختصات رابط کاربری تبدیل میکنیم.
جلوه نورافکن را پیادهسازی کنید
حالا، بیایید کامپوننت CameraPreviewContent را برای رسم جلوه نورافکن بهروزرسانی کنیم. ما از یک کامپوننت Canvas برای رسم یک ماسک گرادیان روی پیشنمایش استفاده خواهیم کرد و چهرههای شناساییشده را قابل مشاهده میکنیم:
@Composable fun CameraPreviewContent( viewModel: CameraPreviewViewModel, modifier: Modifier = Modifier, lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current ) { val surfaceRequest by viewModel.surfaceRequest.collectAsStateWithLifecycle() val sensorFaceRects by viewModel.sensorFaceRects.collectAsStateWithLifecycle() val transformationInfo by produceState<SurfaceRequest.TransformationInfo?>(null, surfaceRequest) { try { surfaceRequest?.setTransformationInfoListener(Runnable::run) { transformationInfo -> value = transformationInfo } awaitCancellation() } finally { surfaceRequest?.clearTransformationInfoListener() } } val shouldSpotlightFaces by remember { derivedStateOf { sensorFaceRects.isNotEmpty() && transformationInfo != null} } val spotlightColor = Color(0xDDE60991) .. surfaceRequest?.let { request -> val coordinateTransformer = remember { MutableCoordinateTransformer() } CameraXViewfinder( surfaceRequest = request, coordinateTransformer = coordinateTransformer, modifier = .. ) AnimatedVisibility(shouldSpotlightFaces, enter = fadeIn(), exit = fadeOut()) { Canvas(Modifier.fillMaxSize()) { val uiFaceRects = sensorFaceRects.transformToUiCoords( transformationInfo = transformationInfo, uiToBufferCoordinateTransformer = coordinateTransformer ) // Fill the whole space with the color drawRect(spotlightColor) // Then extract each face and make it transparent uiFaceRects.forEach { faceRect -> drawRect( Brush.radialGradient( 0.4f to Color.Black, 1f to Color.Transparent, center = faceRect.center, radius = faceRect.minDimension * 2f, ), blendMode = BlendMode.DstOut ) } } } } }
نحوه کار آن به این صورت است:
- ما لیست چهرهها را از مدل نما جمعآوری میکنیم.
- برای اطمینان از اینکه هر بار که لیست چهرههای شناساییشده تغییر میکند، کل صفحه را دوباره ترکیب نمیکنیم،
derivedStateOfبرای پیگیری اینکه آیا اصلاً چهرهای شناسایی شده است یا خیر، استفاده میکنیم. سپس میتوان از این ویژگی به همراهAnimatedVisibilityبرای متحرکسازی پوشش رنگی به داخل و خارج استفاده کرد. -
surfaceRequestشامل اطلاعاتی است که برای تبدیل مختصات حسگر به مختصات بافر درSurfaceRequest.TransformationInfoبه آن نیاز داریم. ما از تابعproduceStateبرای تنظیم یک شنونده در درخواست سطحی استفاده میکنیم و این شنونده را هنگامی که composable از درخت ترکیب خارج میشود، پاک میکنیم. - ما از یک
Canvasبرای رسم یک مستطیل صورتی شفاف که کل صفحه را پوشش میدهد، استفاده میکنیم. - ما خواندن متغیر
sensorFaceRectsرا تا زمانی که داخل بلوک رسمCanvasباشیم، به تعویق میاندازیم. سپس مختصات را به مختصات UI تبدیل میکنیم. - ما روی چهرههای شناساییشده تکرار میکنیم و برای هر چهره، یک گرادیان شعاعی رسم میکنیم که داخل مستطیل چهره را شفاف میکند.
- ما از
BlendMode.DstOutاستفاده میکنیم تا مطمئن شویم که گرادیان را از مستطیل صورتی برش میدهیم و جلوه نورافکن را ایجاد میکنیم.
نکته: وقتی دوربین را به DEFAULT_FRONT_CAMERA تغییر میدهید ، متوجه خواهید شد که نورافکن منعکس میشود! این یک مشکل شناخته شده است که در ردیاب مشکلات گوگل ردیابی میشود .
نتیجه
با این کد، ما یک افکت نورافکن کاملاً کاربردی داریم که چهرههای شناساییشده را برجسته میکند. میتوانید قطعه کد کامل را اینجا پیدا کنید.
این افکت تازه شروع کار است - با استفاده از قدرت Compose، میتوانید هزاران تجربه دوربین خیرهکننده از نظر بصری ایجاد کنید. توانایی تبدیل مختصات حسگر و بافر به مختصات رابط کاربری Compose و برعکس، به این معنی است که میتوانیم از تمام ویژگیهای رابط کاربری Compose استفاده کنیم و آنها را به طور یکپارچه با سیستم دوربین زیرین ادغام کنیم. با انیمیشنها، گرافیک پیشرفته رابط کاربری، مدیریت ساده وضعیت رابط کاربری و کنترل کامل حرکات، تخیل شما حد و مرز دارد!
در آخرین پست این مجموعه، به نحوه استفاده از APIهای تطبیقی و چارچوب انیمیشن Compose برای انتقال یکپارچه بین رابطهای کاربری دوربین مختلف در دستگاههای تاشو خواهیم پرداخت. با ما همراه باشید!
قطعه کدهای موجود در این وبلاگ دارای مجوز زیر هستند:
// Copyright 2024 Google LLC. SPDX-License-Identifier: Apache-2.0
با تشکر فراوان از نیک بوچر ، الکس وانیو ، ترور مکگوایر ، دان ترنر و لورن وارد برای بررسی و ارائه بازخورد. این امر با تلاش سخت یاسیت ویدانا آراچ امکانپذیر شده است.
ادامه مطلب

چگونهها
در این مقاله یاد خواهید گرفت که چگونه از API تست waitUntil در Compose برای منتظر ماندن برای برآورده شدن شرایط خاص استفاده کنید.
Jose Alcérreca • 3 دقیقه خواندن

چگونهها
با توجه به اینکه تخلیه بیش از حد باتری برای کاربران اندروید از اهمیت بالایی برخوردار است، گوگل گامهای مهمی را برای کمک به توسعهدهندگان در ساخت برنامههای کممصرفتر برداشته است.
Alice Yuan • ۸ دقیقه مطالعه

چگونهها
ما میخواستیم نمونههایی از ویژگیهای مبتنی بر هوش مصنوعی را با استفاده از مدلهای روی دستگاه و ابری در اختیار شما قرار دهیم و شما را برای ایجاد تجربیات لذتبخش برای کاربرانتان الهام بخشیم.
Thomas Ezan , Ivy Knight • ۲ دقیقه مطالعه
در جریان باشید
جدیدترین بینشهای توسعه اندروید را به صورت هفتگی در صندوق ورودی خود دریافت کنید.






