AutoFitTextureView захватывает кадр, который не виден в предварительном просмотре камеры - PullRequest
1 голос
/ 30 апреля 2019

Я использую Android Camera2 в моем приложении. В моей компоновке textureview находится в центре, и есть некоторые виды сверху, а также снизу текстуры. Когда я снимаю изображение, фотография содержит даже ту рамку, которая не была видна при предварительном просмотре. Следующий код, который я использую для захвата изображения.

Файл макета:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainCameraLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    android:background="#000000"
    android:fitsSystemWindows="false"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <RelativeLayout
                android:id="@+id/cameraRootView"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <RelativeLayout
                    android:id="@+id/controlLayout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:padding="12dp">

                    <FrameLayout
                        android:id="@+id/sellerProfileLayout"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_centerHorizontal="true">

                        <de.hdodenhof.circleimageview.CircleImageView
                            android:id="@+id/sellerProfilePic"
                            android:layout_width="75dp"
                            android:layout_height="75dp"
                            android:layout_centerHorizontal="true"
                            android:scaleType="centerCrop"
                            android:src="@drawable/user_placeholder"
                            app:civ_border_color="@color/white"
                            app:civ_border_width="2dp" />


                        <ImageView
                            android:layout_width="20dp"
                            android:layout_height="20dp"
                            android:layout_gravity="center|bottom"
                            android:layout_marginTop="60dp"

                  android:background="@drawable/layout_edit_image_background"
                            android:padding="4dp"
                            android:src="@drawable/ic_edit_black_24dp"
                            android:tint="@color/white" />

                    </FrameLayout>

                    <ImageView
                        android:id="@+id/cameraBack"
                        android:layout_width="35dp"
                        android:layout_height="35dp"
                        android:layout_gravity="end"
                        android:layout_marginEnd="8dp"
                        android:alpha="0.8"
                        android:src="@drawable/ic_keyboard_arrow_left_black_24dp"
                        android:tint="@android:color/white" />


                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_toEndOf="@id/sellerProfileLayout"
                        android:gravity="end"
                        android:orientation="horizontal">

                        <ImageView
                            android:id="@+id/cameraSwitchIV"
                            android:layout_width="30dp"
                            android:layout_height="30dp"
                            android:layout_gravity="bottom"
                            android:layout_marginEnd="8dp"
                            android:alpha="0.8"
                            android:padding="4dp"
                            android:src="@drawable/back_cam" />


                        <ImageView
                            android:id="@+id/cameraFlashIV"
                            android:layout_width="30dp"
                            android:layout_height="30dp"
                            android:layout_gravity="end"
                            android:layout_marginEnd="8dp"
                            android:alpha="0.8"
                            android:padding="4dp"
                            android:src="@drawable/ic_flash_auto_black"
                            android:tint="@android:color/white" />
                    </LinearLayout>
                </RelativeLayout>
            </RelativeLayout>


        </LinearLayout>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

            <com.teetra.work.camera2.AutoFitTextureView
                android:id="@+id/texture"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center" />

      </FrameLayout>


        <LinearLayout
            android:id="@+id/bottomLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:layout_marginBottom="16dp"
            android:animateLayoutChanges="true"
            android:orientation="vertical">


            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginTop="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginBottom="8dp">

                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:orientation="horizontal">

                    <ImageView
                        android:id="@+id/cameraHelpIV"
                        android:layout_width="32dp"
                        android:layout_height="32dp"
                        android:src="@drawable/ic_help_black"
                        android:tint="@android:color/white" />

                    <ImageView
                        android:id="@+id/cameraFormIV"
                        android:layout_width="32dp"
                        android:layout_height="32dp"
                        android:layout_marginStart="16dp"
                        android:src="@drawable/ic_more_horiz_black_"
                        android:tint="@android:color/white" />
                </LinearLayout>

                <ImageView
                    android:id="@+id/picture"
                    android:layout_width="60dp"
                    android:layout_height="60dp"
                    android:layout_centerHorizontal="true"
                    android:layout_centerVertical="true"
                    android:layout_gravity="bottom|center_horizontal"
                    android:src="@drawable/ic_click"
                    android:text="@string/camera_picture_click" />

                <android.support.v7.widget.AppCompatButton
                    android:id="@+id/cameraNextBT"
                    style="@style/ButtonTheme.Camera"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentEnd="true"
                    android:layout_centerVertical="true"
                    android:layout_marginStart="16dp"
                    android:layout_toEndOf="@+id/picture"
                    android:lines="1"
                    android:text="@string/next" />
            </RelativeLayout>
        </LinearLayout>

    </LinearLayout>

Файл AutoFitTextureView:

class AutoFitTextureView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
 ) : TextureView(context, attrs, defStyle) {

private var ratioWidth = 0
private var ratioHeight = 0
private var mSquarePreview = true





fun setAspectRatio(width: Int, height: Int) {
    if (width < 0 || height < 0) {
        throw IllegalArgumentException("Size cannot be negative.")
    }
    ratioWidth = width
    ratioHeight = height
    mSquarePreview = false
    requestLayout()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    val width = View.MeasureSpec.getSize(widthMeasureSpec)
    val height = View.MeasureSpec.getSize(heightMeasureSpec)
    if (ratioWidth == 0 || ratioHeight == 0) {
        setMeasuredDimension(width, height)
    } else {
        if (width > height * ratioWidth / ratioHeight) {
            setMeasuredDimension(width, width * ratioHeight / ratioWidth)
        } else {
            setMeasuredDimension(height * ratioWidth / ratioHeight, height)
        }
    }
}

Захват событий:

 override fun onClick(view: View) {
    when (view.id) {
        R.id.picture -> lockFocus()

    }
}

 private fun lockFocus() {
    try {

        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                CameraMetadata.CONTROL_AF_TRIGGER_START)

        state = STATE_WAITING_LOCK
        captureSession?.capture(previewRequestBuilder.build(), captureCallback,
                backgroundHandler)
    } catch (e: CameraAccessException) {
        Log.e(TAG, e.toString())
    }

}

  private val captureCallback = object : CameraCaptureSession.CaptureCallback() {

    private fun process(result: CaptureResult) {
        when (state) {
            STATE_PREVIEW -> Unit // Do nothing when the camera preview is working normally.
            STATE_WAITING_LOCK -> capturePicture(result)
            STATE_WAITING_PRECAPTURE -> {
                // CONTROL_AE_STATE can be null on some devices
                val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
                if (aeState == null ||
                        aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
                        aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
                    state = STATE_WAITING_NON_PRECAPTURE
                }
            }
            STATE_WAITING_NON_PRECAPTURE -> {
                // CONTROL_AE_STATE can be null on some devices
                val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
                if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                    state = STATE_PICTURE_TAKEN
                    captureStillPicture()
                }
            }
        }
    }

    private fun capturePicture(result: CaptureResult) {
        val afState = result.get(CaptureResult.CONTROL_AF_STATE)
        if (afState == null) {
            captureStillPicture()
        } else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
                || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
            // CONTROL_AE_STATE can be null on some devices
            val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
            if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                state = STATE_PICTURE_TAKEN
                captureStillPicture()
            } else {
                runPrecaptureSequence()
            }
        }
    }

    private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {

    override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
        openCamera(width, height)
    }

    override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) {
        configureTransform(width, height)
    }

    override fun onSurfaceTextureDestroyed(texture: SurfaceTexture) = true

    override fun onSurfaceTextureUpdated(texture: SurfaceTexture) = Unit

} 

 private val stateCallback = object : CameraDevice.StateCallback() {

    override fun onOpened(cameraDevice: CameraDevice) {
        cameraOpenCloseLock.release()
        this@Camera2BasicFragment.cameraDevice = cameraDevice
        createCameraPreviewSession()
    }

    override fun onDisconnected(cameraDevice: CameraDevice) {
        cameraOpenCloseLock.release()
        cameraDevice.close()
        this@Camera2BasicFragment.cameraDevice = null
    }

    override fun onError(cameraDevice: CameraDevice, error: Int) {
        onDisconnected(cameraDevice)
        this@Camera2BasicFragment.activity?.finish()
    }

}


 private fun setUpCameraOutputs(width: Int, height: Int) {
    val manager = activity?.getSystemService(Context.CAMERA_SERVICE) as CameraManager
    try {
        for (cameraId in manager.cameraIdList) {
            val characteristics = manager.getCameraCharacteristics(cameraId)

            // We don't use a front facing camera in this sample.
            val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)
            if (cameraDirection != null &&
                    cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) {
                continue
            }

            val map = characteristics.get(
                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue

            // For still image captures, we use the largest available size.
            val largest = Collections.max(
                    Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
                    CompareSizesByArea())
            imageReader = ImageReader.newInstance(largest.width, largest.height,
                    ImageFormat.JPEG, /*maxImages*/ 2).apply {
                setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
            }

            // Find out if we need to swap dimension to get the preview size relative to sensor
            // coordinate.
            val displayRotation = activity?.windowManager?.defaultDisplay?.rotation

            sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)
            val swappedDimensions = areDimensionsSwapped(displayRotation!!)

            val displaySize = Point()
            activity?.windowManager?.defaultDisplay?.getSize(displaySize)
            val rotatedPreviewWidth = if (swappedDimensions) height else width
            val rotatedPreviewHeight = if (swappedDimensions) width else height
            var maxPreviewWidth = if (swappedDimensions) displaySize.y else displaySize.x
            var maxPreviewHeight = if (swappedDimensions) displaySize.x else displaySize.y

            if (maxPreviewWidth > MAX_PREVIEW_WIDTH) maxPreviewWidth = MAX_PREVIEW_WIDTH
            if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) maxPreviewHeight = MAX_PREVIEW_HEIGHT

            // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera
            // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
            // garbage capture data.
            previewSize = Size(maxPreviewWidth , maxPreviewHeight)


                    /*chooseOptimalSize(map.getOutputSizes(SurfaceTexture::class.java),
                    rotatedPreviewWidth, rotatedPreviewHeight,
                    maxPreviewWidth, maxPreviewHeight,
                    largest)*/

            // We fit the aspect ratio of TextureView to the size of preview we picked.
            if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                textureView.setAspectRatio(previewSize.width, previewSize.height)
            } else {
                textureView.setAspectRatio(previewSize.height, previewSize.width)
            }

            // Check if the flash is supported.
            flashSupported =
                    characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true

            this.cameraId = cameraId

            // We've found a viable camera and finished setting up member variables,
            // so we don't need to iterate through other available cameras.
            return
        }
    } catch (e: CameraAccessException) {
        Log.e(TAG, e.toString())
    } catch (e: NullPointerException) {
        // Currently an NPE is thrown when the Camera2API is used but not supported on the
        // device this code runs.

    }

}

  private fun openCamera(width: Int, height: Int) {
    val permission = ContextCompat.checkSelfPermission(activity!!, Manifest.permission.CAMERA)
    if (permission != PackageManager.PERMISSION_GRANTED) {
        requestCameraPermission()
        return
    }
    setUpCameraOutputs(width, height)
    configureTransform(width, height)
    val manager = activity?.getSystemService(Context.CAMERA_SERVICE) as CameraManager
    try {
        // Wait for camera to open - 2.5 seconds is sufficient
        if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw RuntimeException("Time out waiting to lock camera opening.")
        }
        manager.openCamera(cameraId, stateCallback, backgroundHandler)
    } catch (e: CameraAccessException) {
        Log.e(TAG, e.toString())
    } catch (e: InterruptedException) {
        throw RuntimeException("Interrupted while trying to lock camera opening.", e)
    }

}

Я выполнил все шаги камеры 2 api. Но он по-прежнему фиксирует кадр, который не виден при предварительном просмотре камеры.

...