Предварительный просмотр камеры растягивается даже после установки правильных размеров

Я создаю приложение для камеры, которое реализует собственный просмотр камеры для съемки. Приложение в настоящее время переведено в портретный режим.

Моя проблема в том, что предварительный просмотр с камеры немного растянут (соотношение сторон немного выключено). Самое забавное, что я устанавливаю свой размер SurfaceView всегда в соответствии с размером предварительного просмотра. Это гарантирует, что соотношение сторон всегда должно сохраняться … но это не …

Вот макет, который я использую, чтобы показать предварительный просмотр камеры:

public class Cp extends ViewGroup implements SurfaceHolder.Callback { private final String TAG = "CameraPreview"; private boolean mPreviewRunning = false; private SurfaceView mSurfaceView; private SurfaceHolder mHolder; private Size mPreviewSize; private List<Size> mSupportedPreviewSizes; private Camera mCamera; public boolean IsPreviewRunning() { return mPreviewRunning; } public Cp(Context context) { this(context, null, 0); } public Cp(Context context, AttributeSet attrs) { this(context, attrs, 0); } public Cp(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mSurfaceView = new SurfaceView(context); addView(mSurfaceView); // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = mSurfaceView.getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera camera) { mCamera = camera; if (mCamera != null) { requestLayout(); } } public void switchCamera(Camera camera) { setCamera(camera); try { camera.setPreviewDisplay(mHolder); } catch (IOException exception) { Log.e(TAG, "IOException caused by setPreviewDisplay()", exception); } Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); requestLayout(); camera.setParameters(parameters); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // We purposely disregard child measurements because act as a wrapper to // a SurfaceView that // centers the camera preview instead of stretching it. final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); if (mSupportedPreviewSizes == null && mCamera != null) { mSupportedPreviewSizes = mCamera.getParameters() .getSupportedPreviewSizes(); } if (mSupportedPreviewSizes != null) { mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed && getChildCount() > 0) { final View child = getChildAt(0); final int width = r - l; final int height = b - t; int previewWidth = width; int previewHeight = height; if (mPreviewSize != null) { previewWidth = mPreviewSize.height; previewHeight = mPreviewSize.width; } if (previewWidth == 0) { previewWidth = 1; } if (previewHeight == 0) { previewHeight = 1; } // Center the child SurfaceView within the parent. if (width * previewHeight > height * previewWidth) { final int scaledChildWidth = previewWidth * height / previewHeight; child.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height); } else { final int scaledChildHeight = previewHeight * width / previewWidth; child.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2); } } } @Override public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, acquire the camera and tell it where to // draw. try { if (mCamera != null) { Parameters params = mCamera.getParameters(); mSupportedPreviewSizes = params.getSupportedPreviewSizes(); mCamera.setPreviewDisplay(holder); } } catch (IOException exception) { Log.e(TAG, "IOException caused by setPreviewDisplay()", exception); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. stop(); } private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size for (Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (mCamera != null) { // Now that the size is known, set up the camera parameters and // begin the preview. Camera.Parameters parameters = mCamera.getParameters(); if (mPreviewSize != null) { parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); } requestLayout(); mCamera.setParameters(parameters); mCamera.startPreview(); mPreviewRunning = true; } } public void stop() { if (mCamera != null) { mCamera.stopPreview(); mPreviewRunning = false; mCamera = null; } } } 

Обратите внимание, что в onLayout ширина и высота меняются местами, потому что приложение всегда работает в портрете.

Я включаю несколько фотографий, чтобы показать вам, как выглядит проблема: Введите описание изображения здесь

Введите описание изображения здесь

Я отлаживал код. Размер предварительного просмотра устанавливается на 1280×720, а размер макета также правильно настроен для соответствия этому размеру.

Я также пробовал разные макеты, но результат всегда был один и тот же …

Solutions Collecting From Web of "Предварительный просмотр камеры растягивается даже после установки правильных размеров"

У меня была такая же проблема, через несколько дней в этой головоломке мой Java-класс оказался в этом коде:

Итак, проблема шла, потому что дисплей камеры имел размер (ширина hight x) 576×720, а мой дисплей был 1184×720. Итак, предварительный просмотр камеры (мой класс поверхности) растянулся, чтобы заполнить родительский элемент.

Таким образом, подход, который сработал, – сделать этот вид больше, чем мой экран, и просто отобразить область моего экрана. Таким образом, мне пришлось использовать эту странную комбинацию кадров (относительный макет внутри linearlayout), так что размер родственника будет таким большим, как я хочу – поверхностный вид заполнит его, а линейка возьмет только Часть, которую я хочу отобразить.

 package com.example.frame.camera; import android.content.Context; import android.hardware.Camera; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Toast; import com.example.frame.MainActivity; public class NativeCamera extends SurfaceView implements SurfaceHolder.Callback { static private NativeCamera instance; private LinearLayout frame = null; private RelativeLayout innerFrame = null; private Camera camera; private final SurfaceHolder previewHolder; private static final String TAG = "NativeCamera.java"; private boolean inPreview = false; private boolean cameraConfigured = false; private boolean frontCamera = false; private Camera.Size size; static public NativeCamera getInstance() { if (NativeCamera.instance == null) { if (MainActivity.debug) { Log.d(TAG, "Creating Camera Singleton"); } NativeCamera.instance = new NativeCamera(MainActivity.instance); } return NativeCamera.instance; } public void onResume() { if (MainActivity.debug) { Log.d(TAG, "onResume"); } camera = Camera.open(); if (size != null) { initPreview(size.width, size.height); } startPreview(); } public void onPause() { if (MainActivity.debug) { Log.d(TAG, "onPause"); } if (inPreview) { camera.stopPreview(); } camera.release(); camera = null; inPreview = false; } public void onDestroy() { if (MainActivity.debug) { Log.d(TAG, "onDestroy"); } NativeCamera.instance = null; } public void onSwitch() { frontCamera = !frontCamera; if (inPreview) { camera.stopPreview(); } camera.release(); int cam = frontCamera ? 1 : 0; camera = Camera.open(cam); if (size != null) { initPreview(size.width, size.height); } startPreview(); } private NativeCamera(Context context) { super(context); // TODO Auto-generated constructor stub // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. previewHolder = getHolder(); previewHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 // previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); innerFrame = new RelativeLayout(MainActivity.instance); innerFrame.addView(this); frame = new LinearLayout(MainActivity.instance); frame.addView(innerFrame); } public LinearLayout getFrame() { return frame; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub if (MainActivity.debug) { Log.d(TAG, "surfaceChanged"); } initPreview(width, height); startPreview(); } public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub // no-op -- wait until surfaceChanged() } public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub // no-op } private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) { Camera.Size result = null; for (Camera.Size size : parameters.getSupportedPreviewSizes()) { if (size.width <= width && size.height <= height) { if (result == null) { result = size; } else { int resultArea = result.width * result.height; int newArea = size.width * size.height; if (newArea > resultArea) { result = size; } } } } this.size = result; return (result); } private void initPreview(int width, int height) { if (camera != null && previewHolder.getSurface() != null) { if (!cameraConfigured) { Camera.Parameters parameters = camera.getParameters(); Camera.Size size = getBestPreviewSize(width, height, parameters); if (size != null) { parameters.setPreviewSize(size.width, size.height); camera.setParameters(parameters); cameraConfigured = true; // Setting up correctly the view double ratio = size.height / (double) size.width; LayoutParams params = innerFrame.getLayoutParams(); params.height = MainActivity.size.y; params.width = (int) (MainActivity.size.y * ratio); innerFrame.setLayoutParams(params); int deslocationX = (int) (params.width / 2.0 - MainActivity.size.x / 2.0); innerFrame.animate().translationX(-deslocationX); } } try { camera.setPreviewDisplay(previewHolder); camera.setDisplayOrientation(90); } catch (Throwable t) { Log.e(TAG, "Exception in setPreviewDisplay()", t); Toast.makeText(MainActivity.instance, t.getMessage(), Toast.LENGTH_LONG).show(); } } } private void startPreview() { if (MainActivity.debug) { Log.d(TAG, "startPreview"); } if (cameraConfigured && camera != null) { camera.startPreview(); inPreview = true; } } } 

Я пытался решить ту же проблему … Ниже код работал для меня:

 private void setMyPreviewSize(int width, int height) { // Get the set dimensions float newProportion = (float) width / (float) height; // Get the width of the screen int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); float screenProportion = (float) screenWidth / (float) screenHeight; // Get the SurfaceView layout parameters android.view.ViewGroup.LayoutParams lp = surfaceView.getLayoutParams(); if (newProportion > screenProportion) { lp.width = screenWidth; lp.height = (int) ((float) screenWidth / newProportion ); } else { lp.width = (int) (newProportion * (float) screenHeight); lp.height = screenHeight; } // Commit the layout parameters surfaceView.setLayoutParams(lp); } 

Вы можете увидеть поток: изменение размера поверхности для изменения соотношения сторон в видеоизображении в андроиде

Вот решение

SurfaceChanged – это просто лучший размер предварительного просмотра

 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e("MYTAG", "surfaceChanged " ); Camera.Parameters myParameters = camera.getParameters(); Camera.Size myBestSize = getBestPreviewSize(width, height, myParameters); if(myBestSize != null){ myParameters.setPreviewSize(myBestSize.width, myBestSize.height); camera.setParameters(myParameters); camera.startPreview(); } } @Override public void onPictureTaken(byte[] data, Camera camera) { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); imageReview.setImageBitmap(bitmap); } 

Это установит захваченное изображение на imageReview, но вам нужен способ получения вращения растрового изображения

Вам нужно установить атрибут imageView scaleType для проблемы с растянутым изображением

 <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" />