将本机代码与 CameraX 集成到自定义 React Native 组件失败
Integrating Native code with CameraX to Custom React Native Component Fails
您好,我有一个应用程序使用 android 中现已弃用的相机模块来显示相机视图并使用 mlkit 人脸检测在其上绘制滤镜。最近我们决定将它升级到 CameraX,我完成了所有必要的步骤以使其作为一个单独的 android 应用程序运行并且它运行正常。当我想将它合并到我们现有的 Custom React Native 模块时,无论我做什么,它都不会在屏幕上显示任何内容。我检查了视图大小,它们似乎没问题。下面是我用来启动相机模块的代码。
package com.myProject.speech.components
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.hardware.camera2.CameraManager
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.size
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import com.facebook.react.uimanager.ThemedReactContext
import com.google.mlkit.common.MlKitException
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.myProject.grpc.speech.StickerDatabase
import com.myProject.speech.R
import com.myProject.speech.utils.PreferenceUtils
import java.util.*
import androidx.databinding.DataBindingUtil
import com.myProject.camera_test.mlkit.facedetection.FaceContourDetectionProcessor
import com.myProject.speech.mlkit.GraphicOverlay
import java.util.concurrent.ExecutorService
class myProjectCameraView : RelativeLayout {
private var previewView: PreviewView? = null
private var graphicOverlay: GraphicOverlay? = null
private var context: ThemedReactContext? = null
private lateinit var cameraExecutor: ExecutorService
var faceRecognitionEnabled = false
var cameraSelector: CameraSelector? = null
private var cameraProvider: ProcessCameraProvider? = null
var cameraLifecycleObserver: CameraLifecycleObserver? = null
private var lensFacing = CameraSelector.LENS_FACING_FRONT
private lateinit var cameraManager: CameraManager
private var imageCapture: ImageCapture? = null
val TAG = "myProjectCameraView"
constructor(context: ThemedReactContext,
cameraLifecycleObserver: CameraLifecycleObserver?) : super(context) {
this.context = context
this.cameraLifecycleObserver = cameraLifecycleObserver;
Log.d(TAG, "constructor Called")
val inflater = context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
inflater.inflate(R.layout.live_preview, this, true)
previewView = findViewById(R.id.firePreview)
graphicOverlay = findViewById(R.id.fireFaceOverlay)
if (allPermissionsGranted()) {
startCamera()
if (cameraLifecycleObserver != null) {
cameraLifecycleObserver.registerActionHandler(this);
}
} else {
Log.d(TAG, "Not all Permissions Granted")
}
this.cameraLifecycleObserver!!.registerActionHandler(this)
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this.context as Context)
cameraProviderFuture.addListener(Runnable {
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(previewView!!.createSurfaceProvider())
}
imageCapture = ImageCapture.Builder()
.build()
val imageAnalyzer = ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor, FaceContourDetectionProcessor(graphicOverlay!!))
}
// Select back camera as a default
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
.build()
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this.context!!.currentActivity!! as AppCompatActivity, cameraSelector, preview, imageCapture, imageAnalyzer)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this.context as Context))
}
fun enableFaceRecognition(enabled: Boolean) {
Log.d(TAG, "enableFaceRecognition Called")
if (enabled) {
Log.d(TAG, "With FaceRecognition")
Log.d(TAG, "Parent size: ${this!!.measuredWidth}x${this!!.measuredHeight}")
Log.d(TAG, "previewView sizes: ${previewView!!.measuredWidth}x${previewView!!.measuredHeight}")
Log.d(TAG, "graphicOverlay sizes: ${graphicOverlay!!.measuredWidth}x${graphicOverlay!!.measuredHeight}")
} else {
Log.d(TAG, "Without FaceRecognition")
}
}
fun onActivityStopped() {
Log.d(TAG, "onActivityStopped")
}
fun onActivityResumed() {
Log.d(TAG, "onActivityResumed")
}
fun onActivityPaused() {
Log.d(TAG, "onActivityPaused")
}
fun onActivityDestroyed() {
Log.d(TAG, "onActivityDestroyed")
}
private fun allPermissionsGranted(): Boolean {
for (permission in requiredPermissions) {
if (!isPermissionGranted(getContext(), permission)) {
return false
}
}
return true
}
private val requiredPermissions: Array<String?>
private get() = try {
val info = getContext().packageManager.getPackageInfo(
getContext().packageName, PackageManager.GET_PERMISSIONS)
val ps = info.requestedPermissions
if (ps != null && ps.size > 0) {
ps
} else {
arrayOfNulls(0)
}
} catch (e: Exception) {
arrayOfNulls(0)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context?, attrs: AttributeSet?,
defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
}
val viewBitmap: Bitmap
get() {
val v = rootView
val b = Bitmap.createBitmap(v.layoutParams.width,
v.layoutParams.height,
Bitmap.Config.ARGB_8888)
val c = Canvas(b)
v.layout(v.left, v.top, v.right, v.bottom)
v.draw(c)
return b
}
private fun isPermissionGranted(context: Context,
permission: String?): Boolean {
if (ContextCompat.checkSelfPermission(context, permission!!) ==
PackageManager.PERMISSION_GRANTED) {
Log.i(this.TAG, "Permission granted: $permission")
return true
}
Log.i(TAG, "Permission NOT granted: $permission")
return false
}
companion object {
private const val FACE_DETECTION = "Face Detection"
private const val CLASSIFICATION = "Classification"
private const val PERMISSION_REQUESTS = 1
private const val lastDBPath = ""
private var context: ThemedReactContext? = null
val lastDB: StickerDatabase? = null
}
}
如有任何见解,我们将不胜感激。
谢谢
出现这个问题是因为相对布局在添加到场景后没有调整大小。解决方法不是我的,但我现在找不到它的位置,所以我将它留在此处以防其他人遇到类似问题。我通过在 RelativeLayout
的构造函数中调用 layoutHack 解决了这个问题
fun setupLayoutHack() {
Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
manuallyLayoutChildren()
viewTreeObserver.dispatchOnGlobalLayout()
Choreographer.getInstance().postFrameCallback(this)
}
})
}
fun manuallyLayoutChildren() {
for (i in 0 until childCount) {
val child = getChildAt(i)
child.measure(MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY))
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
}
您好,我有一个应用程序使用 android 中现已弃用的相机模块来显示相机视图并使用 mlkit 人脸检测在其上绘制滤镜。最近我们决定将它升级到 CameraX,我完成了所有必要的步骤以使其作为一个单独的 android 应用程序运行并且它运行正常。当我想将它合并到我们现有的 Custom React Native 模块时,无论我做什么,它都不会在屏幕上显示任何内容。我检查了视图大小,它们似乎没问题。下面是我用来启动相机模块的代码。
package com.myProject.speech.components
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.hardware.camera2.CameraManager
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.size
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import com.facebook.react.uimanager.ThemedReactContext
import com.google.mlkit.common.MlKitException
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.myProject.grpc.speech.StickerDatabase
import com.myProject.speech.R
import com.myProject.speech.utils.PreferenceUtils
import java.util.*
import androidx.databinding.DataBindingUtil
import com.myProject.camera_test.mlkit.facedetection.FaceContourDetectionProcessor
import com.myProject.speech.mlkit.GraphicOverlay
import java.util.concurrent.ExecutorService
class myProjectCameraView : RelativeLayout {
private var previewView: PreviewView? = null
private var graphicOverlay: GraphicOverlay? = null
private var context: ThemedReactContext? = null
private lateinit var cameraExecutor: ExecutorService
var faceRecognitionEnabled = false
var cameraSelector: CameraSelector? = null
private var cameraProvider: ProcessCameraProvider? = null
var cameraLifecycleObserver: CameraLifecycleObserver? = null
private var lensFacing = CameraSelector.LENS_FACING_FRONT
private lateinit var cameraManager: CameraManager
private var imageCapture: ImageCapture? = null
val TAG = "myProjectCameraView"
constructor(context: ThemedReactContext,
cameraLifecycleObserver: CameraLifecycleObserver?) : super(context) {
this.context = context
this.cameraLifecycleObserver = cameraLifecycleObserver;
Log.d(TAG, "constructor Called")
val inflater = context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
inflater.inflate(R.layout.live_preview, this, true)
previewView = findViewById(R.id.firePreview)
graphicOverlay = findViewById(R.id.fireFaceOverlay)
if (allPermissionsGranted()) {
startCamera()
if (cameraLifecycleObserver != null) {
cameraLifecycleObserver.registerActionHandler(this);
}
} else {
Log.d(TAG, "Not all Permissions Granted")
}
this.cameraLifecycleObserver!!.registerActionHandler(this)
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this.context as Context)
cameraProviderFuture.addListener(Runnable {
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(previewView!!.createSurfaceProvider())
}
imageCapture = ImageCapture.Builder()
.build()
val imageAnalyzer = ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor, FaceContourDetectionProcessor(graphicOverlay!!))
}
// Select back camera as a default
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
.build()
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this.context!!.currentActivity!! as AppCompatActivity, cameraSelector, preview, imageCapture, imageAnalyzer)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this.context as Context))
}
fun enableFaceRecognition(enabled: Boolean) {
Log.d(TAG, "enableFaceRecognition Called")
if (enabled) {
Log.d(TAG, "With FaceRecognition")
Log.d(TAG, "Parent size: ${this!!.measuredWidth}x${this!!.measuredHeight}")
Log.d(TAG, "previewView sizes: ${previewView!!.measuredWidth}x${previewView!!.measuredHeight}")
Log.d(TAG, "graphicOverlay sizes: ${graphicOverlay!!.measuredWidth}x${graphicOverlay!!.measuredHeight}")
} else {
Log.d(TAG, "Without FaceRecognition")
}
}
fun onActivityStopped() {
Log.d(TAG, "onActivityStopped")
}
fun onActivityResumed() {
Log.d(TAG, "onActivityResumed")
}
fun onActivityPaused() {
Log.d(TAG, "onActivityPaused")
}
fun onActivityDestroyed() {
Log.d(TAG, "onActivityDestroyed")
}
private fun allPermissionsGranted(): Boolean {
for (permission in requiredPermissions) {
if (!isPermissionGranted(getContext(), permission)) {
return false
}
}
return true
}
private val requiredPermissions: Array<String?>
private get() = try {
val info = getContext().packageManager.getPackageInfo(
getContext().packageName, PackageManager.GET_PERMISSIONS)
val ps = info.requestedPermissions
if (ps != null && ps.size > 0) {
ps
} else {
arrayOfNulls(0)
}
} catch (e: Exception) {
arrayOfNulls(0)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context?, attrs: AttributeSet?,
defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
}
val viewBitmap: Bitmap
get() {
val v = rootView
val b = Bitmap.createBitmap(v.layoutParams.width,
v.layoutParams.height,
Bitmap.Config.ARGB_8888)
val c = Canvas(b)
v.layout(v.left, v.top, v.right, v.bottom)
v.draw(c)
return b
}
private fun isPermissionGranted(context: Context,
permission: String?): Boolean {
if (ContextCompat.checkSelfPermission(context, permission!!) ==
PackageManager.PERMISSION_GRANTED) {
Log.i(this.TAG, "Permission granted: $permission")
return true
}
Log.i(TAG, "Permission NOT granted: $permission")
return false
}
companion object {
private const val FACE_DETECTION = "Face Detection"
private const val CLASSIFICATION = "Classification"
private const val PERMISSION_REQUESTS = 1
private const val lastDBPath = ""
private var context: ThemedReactContext? = null
val lastDB: StickerDatabase? = null
}
}
如有任何见解,我们将不胜感激。 谢谢
出现这个问题是因为相对布局在添加到场景后没有调整大小。解决方法不是我的,但我现在找不到它的位置,所以我将它留在此处以防其他人遇到类似问题。我通过在 RelativeLayout
的构造函数中调用 layoutHack 解决了这个问题 fun setupLayoutHack() {
Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
manuallyLayoutChildren()
viewTreeObserver.dispatchOnGlobalLayout()
Choreographer.getInstance().postFrameCallback(this)
}
})
}
fun manuallyLayoutChildren() {
for (i in 0 until childCount) {
val child = getChildAt(i)
child.measure(MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY))
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
}