我在应用程序中使用的是android camera2。在我的布局中,textureview位于中间,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)
}
}
我已经遵循了Camera 2 API的所有步骤。但它仍会捕获在相机预览中不可见的帧。