fix: Fix PreviewView
stretching on Android (now finally a real fix) (#2564)
* fix: Only resolve once SurfaceHolder actually resized * fix: Fix onMeasure not being called for `PreviewView` * fix: Auto-trigger layout computation on Surface Change * fix: Add proper LayoutParams to `PreviewView` * Format
This commit is contained in:
parent
21042048ae
commit
5df5ca9adf
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.camera2.CameraManager
|
import android.hardware.camera2.CameraManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.Gravity
|
||||||
import android.view.ScaleGestureDetector
|
import android.view.ScaleGestureDetector
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
@ -110,6 +111,11 @@ class CameraView(context: Context) :
|
|||||||
clipToOutline = true
|
clipToOutline = true
|
||||||
cameraSession = CameraSession(context, cameraManager, this)
|
cameraSession = CameraSession(context, cameraManager, this)
|
||||||
previewView = cameraSession.createPreviewView(context)
|
previewView = cameraSession.createPreviewView(context)
|
||||||
|
previewView.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
Gravity.CENTER
|
||||||
|
)
|
||||||
addView(previewView)
|
addView(previewView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import android.view.SurfaceView
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import com.facebook.react.bridge.UiThreadUtil
|
import com.facebook.react.bridge.UiThreadUtil
|
||||||
import com.mrousavy.camera.extensions.getMaximumPreviewSize
|
import com.mrousavy.camera.extensions.getMaximumPreviewSize
|
||||||
|
import com.mrousavy.camera.extensions.installHierarchyFitter
|
||||||
import com.mrousavy.camera.extensions.resize
|
import com.mrousavy.camera.extensions.resize
|
||||||
import com.mrousavy.camera.types.Orientation
|
import com.mrousavy.camera.types.Orientation
|
||||||
import com.mrousavy.camera.types.ResizeMode
|
import com.mrousavy.camera.types.ResizeMode
|
||||||
@ -19,7 +20,9 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
class PreviewView(context: Context, callback: SurfaceHolder.Callback) : SurfaceView(context) {
|
class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
|
||||||
|
FrameLayout(context),
|
||||||
|
SurfaceHolder.Callback {
|
||||||
var size: Size = getMaximumPreviewSize()
|
var size: Size = getMaximumPreviewSize()
|
||||||
private set
|
private set
|
||||||
var resizeMode: ResizeMode = ResizeMode.COVER
|
var resizeMode: ResizeMode = ResizeMode.COVER
|
||||||
@ -31,28 +34,6 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : SurfaceV
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
Log.i(TAG, "Creating PreviewView...")
|
|
||||||
layoutParams = FrameLayout.LayoutParams(
|
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
Gravity.CENTER
|
|
||||||
)
|
|
||||||
holder.setKeepScreenOn(true)
|
|
||||||
holder.addCallback(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun setSurfaceSize(width: Int, height: Int) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
size = Size(width, height)
|
|
||||||
Log.i(TAG, "Setting PreviewView Surface Size to $size...")
|
|
||||||
requestLayout()
|
|
||||||
invalidate()
|
|
||||||
holder.resize(width, height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val viewSize: Size
|
private val viewSize: Size
|
||||||
get() {
|
get() {
|
||||||
val displayMetrics = context.resources.displayMetrics
|
val displayMetrics = context.resources.displayMetrics
|
||||||
@ -60,6 +41,36 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : SurfaceV
|
|||||||
val dpY = height / displayMetrics.density
|
val dpY = height / displayMetrics.density
|
||||||
return Size(dpX.toInt(), dpY.toInt())
|
return Size(dpX.toInt(), dpY.toInt())
|
||||||
}
|
}
|
||||||
|
private val surfaceView = SurfaceView(context)
|
||||||
|
|
||||||
|
init {
|
||||||
|
Log.i(TAG, "Creating PreviewView...")
|
||||||
|
this.installHierarchyFitter()
|
||||||
|
surfaceView.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
Gravity.CENTER
|
||||||
|
)
|
||||||
|
surfaceView.holder.setKeepScreenOn(true)
|
||||||
|
surfaceView.holder.addCallback(this)
|
||||||
|
surfaceView.holder.addCallback(callback)
|
||||||
|
addView(surfaceView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun surfaceCreated(holder: SurfaceHolder) = Unit
|
||||||
|
override fun surfaceDestroyed(holder: SurfaceHolder) = Unit
|
||||||
|
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||||
|
Log.i(TAG, "PreviewView Surface size changed: $size -> ${width}x$height, re-computing layout...")
|
||||||
|
size = Size(width, height)
|
||||||
|
requestLayout()
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setSurfaceSize(width: Int, height: Int) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
surfaceView.holder.resize(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun convertLayerPointToCameraCoordinates(point: Point, cameraDeviceDetails: CameraDeviceDetails): Point {
|
fun convertLayerPointToCameraCoordinates(point: Point, cameraDeviceDetails: CameraDeviceDetails): Point {
|
||||||
val sensorOrientation = Orientation.fromRotationDegrees(cameraDeviceDetails.sensorOrientation)
|
val sensorOrientation = Orientation.fromRotationDegrees(cameraDeviceDetails.sensorOrientation)
|
||||||
|
@ -6,32 +6,36 @@ import androidx.annotation.UiThread
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
|
private const val TAG = "SurfaceHolder"
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
suspend fun SurfaceHolder.resize(width: Int, height: Int) {
|
suspend fun SurfaceHolder.resize(targetWidth: Int, targetHeight: Int) {
|
||||||
return suspendCancellableCoroutine { continuation ->
|
return suspendCancellableCoroutine { continuation ->
|
||||||
val currentSize = this.surfaceFrame
|
val currentSize = this.surfaceFrame
|
||||||
if (currentSize.width() == width && currentSize.height() == height) {
|
if (currentSize.width() == targetWidth && currentSize.height() == targetHeight) {
|
||||||
// Already in target size
|
// Already in target size
|
||||||
continuation.resume(Unit)
|
continuation.resume(Unit)
|
||||||
return@suspendCancellableCoroutine
|
return@suspendCancellableCoroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("SurfaceHolder", "Resizing SurfaceHolder to $width x $height...")
|
Log.i(TAG, "Resizing SurfaceHolder to $targetWidth x $targetHeight...")
|
||||||
|
|
||||||
val callback = object : SurfaceHolder.Callback {
|
val callback = object : SurfaceHolder.Callback {
|
||||||
override fun surfaceCreated(holder: SurfaceHolder) = Unit
|
override fun surfaceCreated(holder: SurfaceHolder) = Unit
|
||||||
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||||
|
if (width == targetWidth && height == targetHeight) {
|
||||||
holder.removeCallback(this)
|
holder.removeCallback(this)
|
||||||
Log.i("SurfaceHolder", "Resized SurfaceHolder to $width x $height!")
|
Log.i(TAG, "Resized SurfaceHolder to $width x $height!")
|
||||||
continuation.resume(Unit)
|
continuation.resume(Unit)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||||
holder.removeCallback(this)
|
holder.removeCallback(this)
|
||||||
Log.e("SurfaceHolder", "Failed to resize SurfaceHolder to $width x $height!")
|
Log.e(TAG, "Failed to resize SurfaceHolder to $targetWidth x $targetHeight!")
|
||||||
continuation.cancel(Error("Tried to resize SurfaceView, but Surface has been destroyed!"))
|
continuation.cancel(Error("Tried to resize SurfaceView, but Surface has been destroyed!"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.addCallback(callback)
|
this.addCallback(callback)
|
||||||
this.setFixedSize(width, height)
|
this.setFixedSize(targetWidth, targetHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user