如何在不弄乱预览尺寸的情况下更改显示方向-Android

时间:2019-01-22 12:51:26

标签: android kotlin android-camera surfaceview

我正在设置一个Android应用,我想拍摄会员卡的照片。我想将相机视图的方向设置为纵向模式并调整预览大小。现在,我可以正确调整预览的大小,但是当我更改方向(mCamera.setDisplayOrientation(90))时,我将所有内容弄乱了,并且预览被水平拉伸。

我的XML:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.CardCaptureActivity"
android:background="@color/blue">

<android.support.v7.widget.CardView
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginStart="20dp"
    android:layout_marginTop="8dp"
    android:layout_marginEnd="20dp"
    android:layout_marginBottom="8dp"
    android:elevation="5dp"
    app:cardCornerRadius="15dp"
    app:layout_constraintBottom_toTopOf="@+id/button_capture"
    app:layout_constraintDimensionRatio="H,1.618:1"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_chainStyle="spread">

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</android.support.v7.widget.CardView>

<Button
    android:id="@+id/button_capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"
    android:text="Capture"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>

CardCaptureActivity:

class CardCaptureActivity : AppCompatActivity() {

private var mCamera: Camera? = null
private var mPreview: CardCameraPreview? = null
private var captureButton: Button? = null

companion object {
    var data: ByteArray? = null
}

private val mPicture = Camera.PictureCallback { bytes, camera ->
    CardCaptureActivity.data = bytes
    setResult(CommonStatusCodes.SUCCESS)
    finish()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_card_capture)

    captureButton = findViewById(R.id.button_capture)
    captureButton?.setOnClickListener {
        // get an image from the camera
        mCamera?.takePicture(null, null, mPicture)
    }

    mCamera = getCameraInstance()

    mPreview = mCamera?.let {
        CardCameraPreview(this, it, findViewById(R.id.camera_preview))
    }
    mPreview?.also {
        val preview: FrameLayout = findViewById(R.id.camera_preview)
        preview.addView(it)
    }
}

override fun finish() {
    mCamera?.stopPreview()
    mCamera?.release()
    mCamera = null
    super.finish()
}

override fun onDestroy() {
    mCamera?.stopPreview()
    mCamera?.release()
    mCamera = null
    super.onDestroy()
}

override fun onStop() {
    mCamera?.stopPreview()
    super.onStop()
}

/** Check if this device has a camera */
private fun checkCameraHardware(context: Context): Boolean {
    return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
}

/** A safe way to get an instance of the Camera object. */
fun getCameraInstance(): Camera? {
    return try {
        Camera.open() // attempt to get a Camera instance
    } catch (e: Exception) {
        // Camera is not available (in use or does not exist)
        null // returns null if camera is unavailable
    }
}
}

CardCameraPreview:

class CardCameraPreview(context: Context, private val mCamera: Camera, private val frameLayout: FrameLayout) : SurfaceView(context), SurfaceHolder.Callback{

private val TAG = "CardCameraPreview"

private val mHolder: SurfaceHolder = holder.apply {
    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    addCallback(this@CardCameraPreview)
    // deprecated setting, but required on Android versions prior to 3.0
    setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
}

override fun surfaceCreated(p0: SurfaceHolder?) {
    // The Surface has been created, now tell the camera where to draw the preview.
    mCamera.apply {
        try {

            parameters?.also {params ->
                params.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
                parameters = params
            }

            setPreviewDisplay(holder)
            startPreview()
        } catch (e: IOException) {
            Log.d(TAG, "Error setting camera preview: ${e.message}")
        }
    }
}

override fun surfaceDestroyed(holder: SurfaceHolder) {
    // empty. Take care of releasing the Camera preview in your activity.
}

override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
    // If your preview can change or rotate, take care of those events here.
    // Make sure to stop the preview before resizing or reformatting it.
    if (mHolder.surface == null) {
        // preview surface does not exist
        return
    }

    // stop preview before making changes
    try {
        mCamera.stopPreview()
    } catch (e: Exception) {
        // ignore: tried to stop a non-existent preview
    }

    // set preview size and make any resize, rotate or
    // reformatting changes here
    //mCamera.setDisplayOrientation(90)

    val params = mCamera.parameters
    val newSize = getOptimalPreviewSize(params.supportedPreviewSizes, frameLayout.width, frameLayout.height)
    if(newSize != null) {
        params.setPreviewSize(newSize.width, newSize.height)
    }

    params.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
    mCamera.setDisplayOrientation(90)
    mCamera.parameters = params

    // start preview with new settings
    mCamera.apply {
        try {
            setPreviewDisplay(mHolder)
            startPreview()
        } catch (e: Exception) {
            Log.d(TAG, "Error starting camera preview: ${e.message}")
        }
    }
}

private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, width: Int, height: Int): Camera.Size?{
    if(sizes == null)
        return null

    val ratio = height.toDouble()/width
    var optimalSize: Camera.Size? = null
    var actualDiff = Double.MAX_VALUE

    sizes.forEach { size ->
        val actualRatio = size.height.toDouble() / size.width

        if(ratio - actualRatio < actualDiff && ratio - actualRatio >= 0){
            actualDiff = ratio - actualRatio
            optimalSize = size
        }
        else if(actualRatio - ratio < actualDiff && actualRatio - ratio >= 0){
            actualDiff = actualRatio - ratio
            optimalSize = size
        }
    }
    return optimalSize
}
}

我的代码的实际结果是预览的方向是正确的(纵向),但是预览是水平拉伸的(图2)。如果我删除“ mCamera.setDisplayOrientation(90)”行,则预览具有完美的尺寸(没有拉伸或类似的东西),但是预览的方向不正确(图像1)。 预期的结果是纵向模式下的预览,并且不会拉伸预览。

如果您正在查看我的xml,您会看到预览是一个很小的矩形,带有圆角,而不是整个屏幕。那就是我想要的。

result without setting display orientation result with setting display orientation

0 个答案:

没有答案