Haga un SurfaceView más grande que la pantalla (Ajuste de una vista previa de cámara a un SurfaceView más grande que la pantalla)

Tengo una aplicación de cámara personalizada y quiero que cualquier tamaño de vista previa para mostrarlo en modo de pantalla completa sin estirar la imagen de previsualización de la cámara. Para esto, necesito hacer que la superficie sea más grande que la pantalla para mantener la relación de aspecto, así que en realidad el usuario verá menos que la cámara realmente capta.

Por alguna razón, no puedo hacer que SurfaceView más grande que el tamaño de la pantalla.

Lo que he probado hasta ahora:

  • surfaceChanged tamaño de la vista previa de la cámara en el método surfaceChanged

  • onMeasure el tamaño de la vista previa de la cámara en el método onMeasure

  • onLayout tamaño en in onLayout método
  • Agregando FLAG_LAYOUT_NO_LIMITS a activity – info
  • Añadir android: clipChildren para la vista de la superficie – info
  • Ancho de configuración en xml: android:layout_width="852px"
  • getWindow().setLayout(852, 1280); En actividad

Pero sin ningún éxito – el comportamiento es el mismo cada vez: aparece bien durante 1 segundo y después de que se estira.

Aquí está el código:

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final int CAMERA_ROTATE_ANGLE = 90; private SurfaceHolder cameraHolder; private Camera androidHardCamera; private Context context; public CameraPreview(Context context) { super(context); this.context = context; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. cameraHolder = getHolder(); cameraHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 cameraHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera camera) { this.androidHardCamera = camera; if (androidHardCamera != null) { requestLayout(); } } @Override public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { if (androidHardCamera != null) { androidHardCamera.stopPreview();//this is needed for devices with API level < 14 (from doc.: Starting // from API level 14, this method, aka setDisplayOrientation, can be called when preview is active.) androidHardCamera.setDisplayOrientation(CAMERA_ROTATE_ANGLE);//force the preview Display Orientation // to Portrait (rotate camera orientation/display to portrait) //holder.setFixedSize(852, 1280); androidHardCamera.setPreviewDisplay(holder); androidHardCamera.startPreview(); } } catch (IOException e) { } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 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 (cameraHolder.getSurface() == null) { // preview surface does not exist return; } // stop preview before making changes try { androidHardCamera.stopPreview(); } catch (Exception e) { // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) this.getLayoutParams(); layoutParams.height = 1280; layoutParams.width = 852; this.setLayoutParams(layoutParams); //cameraHolder.setFixedSize(852, 1280); requestLayout(); // start preview with new settings try { androidHardCamera.setPreviewDisplay(cameraHolder); androidHardCamera.startPreview(); } catch (Exception e) { } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } // @Override // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // // super.onMeasure(widthMeasureSpec, heightMeasureSpec); //To change body of overridden methods use File | Settings | File Templates. // //super.onMeasure(852, 1280); // setMeasuredDimension(852, 1280); // } } public class MyActivity extends Activity{ private Camera camera; private CameraPreview previewCamera; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); setContentView(R.layout.camera_screen); previewCamera = new CameraPreview(this); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(previewCamera); //getWindow().setLayout(852, 1280); } @Override protected void onResume() { // Create an instance of Camera camera = getCameraInstance(1); if (camera == null) { Toast.makeText(this, "Camera in use!", Toast.LENGTH_LONG).show(); } else { previewCamera.setCamera(camera); camera.stopPreview(); Camera.Parameters p = camera.getParameters(); p.setPreviewSize(176, 144); // p.setPreviewSize(480, 800); camera.setParameters(p); startPreviewCamera(); } super.onResume(); } @Override protected void onPause() { releaseCameraAndPreview(); super.onPause(); } public Camera getCameraInstance(int cameraInstance) { Camera c = null; try { c = Camera.open(cameraInstance); } catch (Exception e) { // Camera is not available (in use or does not exist) System.out.println("exception: " + e); } return c; } public void startPreviewCamera() { //Force the preview Display Orientation to Portrait (rotate camera orientation/display to portrait) camera.setDisplayOrientation(90); camera.startPreview(); } public void releaseCameraAndPreview() { if (camera != null) { camera.stopPreview(); // updating the preview surface camera.setPreviewCallback(null); // camera.lock(); //if we don't lock the camera, release() will fail on some devices camera.release(); camera = null; } } } <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- This is the container for the camera preview screen --> <FrameLayout android:id="@+id/camera_preview" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:layout_weight="1"/> </LinearLayout> 

Aquí está el proyecto completo: https://www.dropbox.com/sh/96jih9kw5zmmnzy/z7VX16T30M

Estoy probando en un dispositivo S3. En un dispositivo S2 parece wok bien … Simplemente no sé qué hacer más para resolver este problema …

ACTUALIZACIÓN 1

Por ejemplo, Sony Xperia tiene una pantalla de 480 / 854. Uno de los tamaños de vista previa que puedo utilizar es 176/144.

Para mostrar el tamaño de pantalla completa necesito tener el tamaño de la cámara de previsualización de 698/854 – pero no sé cómo establecer este valor y dónde.

El código siguiente no funciona … la vista previa de la cámara se estira / alarga.

 import android.app.Activity; import android.graphics.Point; import android.hardware.Camera; import android.os.Bundle; import android.view.Display; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; public class CameraPreview extends Activity implements Preview.PreviewListener { private Preview mPreview; private Camera mCamera; FrameLayout preview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.main); // Create our Preview view and set it as the content of our activity. mPreview = new Preview(this); preview = (FrameLayout) findViewById(R.id.surface_camera); preview.addView(mPreview); Display display = getWindowManager().getDefaultDisplay(); getDisplaySize(display); } private static Point getDisplaySize(final Display display) { final Point point = new Point(); try { display.getSize(point); } catch (java.lang.NoSuchMethodError ignore) { point.x = display.getWidth(); point.y = display.getHeight(); } System.out.println("============: Screen " + point.x + "/" + point.y); return point; } @Override protected void onResume() { super.onResume(); mCamera = Camera.open(1); mPreview.setCamera(mCamera, this); } @Override protected void onPause() { super.onPause(); if (mCamera != null) { mPreview.setCamera(null, null); mCamera.release(); mCamera = null; } } @Override public void onSurfaceChanged() { LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) preview.getLayoutParams(); params.setMargins(0, -218, 0, 0); preview.setLayoutParams(new FrameLayout.LayoutParams(480, 854)); preview.setLayoutParams(params); preview.setVisibility(View.VISIBLE); } } 

 import android.content.Context; import android.hardware.Camera; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.io.IOException; import java.util.List; class Preview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; private PreviewListener listener; public static interface PreviewListener { void onSurfaceChanged(); } Preview(Context context) { super(context); mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera camera, PreviewListener listener) { this.listener = listener; mCamera = camera; if (mCamera != null) { List<Camera.Size> mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); for (Camera.Size s : mSupportedPreviewSizes) { System.out.println("============: " + s.width + "/" + s.height); } requestLayout(); } } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, acquire the camera and tell it where // to draw. try { if (mCamera != null) { mCamera.setPreviewDisplay(holder); } } catch (IOException exception) { Log.e("Error: ", "IOException caused by setPreviewDisplay()", exception); } } public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. if (mCamera != null) { mCamera.stopPreview(); } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { mCamera.stopPreview(); // pe Xpedia daca nu pui asta crapa la setDisplayOrientation // Now that the size is known, set up the camera parameters and beginthe preview. Camera.Parameters parameters = mCamera.getParameters(); mCamera.setDisplayOrientation(90); parameters.setPreviewSize(176, 144); requestLayout(); mCamera.setParameters(parameters); mCamera.startPreview(); } // @Override // protected void onSizeChanged(\int w, int h, int oldw, int oldh) { // super.onSizeChanged(w, h, oldw, oldh); // //setLayoutParams(new LayoutParams((int)RATIO * w, w)); // // FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams(); // setLayoutParams(new FrameLayout.LayoutParams(960, 1280)); // params.setMargins(0, -120, 0,0); // setLayoutParams(params); // // //preview.setVisibility(View.VISIBLE); // } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //To change body of overridden methods use File | Settings | File Templates. // FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams(); // setLayoutParams(new FrameLayout.LayoutParams(698, 854)); // params.setMargins(0, -218, 0,0); // setLayoutParams(params); } //https://stackoverflow.com/questions/11853297/change-size-of-android-custom-surfaceview @Override public void onLayout(boolean changed, int left, int top, int right, int bottom) { if (changed) { //setLayoutParams(); listener.onSurfaceChanged(); //(this).layout(0, 0, viewWidth , viewHeight); } } } 

Se trata de una prueba de clase que calcula el tamaño de la vista de superficie correcta en función del tamaño de la pantalla y del tamaño de vista previa de la cámara:

 public class Test { /** * Determine proper width to be used for surface view in order to not stretch the camera live preview. */ public static void main(String[] args) { // camera preview size: int surfaceViewWidth = 176; int surfaceViewHeight = 144; int holder; if (surfaceViewWidth > surfaceViewHeight) { holder = surfaceViewWidth; surfaceViewWidth = surfaceViewHeight; surfaceViewHeight = holder; } //device screen display sizes: int width = 480; int height = 854; double sc1 = (double) width / surfaceViewWidth; double sc2 = (double) height / surfaceViewHeight; double rez; if (sc1 > sc2) { rez = sc1; } else { rez = sc2; } System.out.println("Width/height: " + (int) (surfaceViewWidth * rez) + "/" + (int) (surfaceViewHeight * rez)); // size of the preview size we need to set System.out.println(((int) (surfaceViewWidth * rez))-width); // difference between preview size and device screen size = whit how much is bigger the preview size than screen size } } 

En primer lugar, eliminar la fuente de los accidentes: startPreviewCamera llamado onResume. La previsualización de la cámara se iniciará en los métodos SurfaceHolder.Callback.

A continuación, debe saber que puede establecer el tamaño de vista previa sólo a los tamaños informados por Camera.Parameters.getSupportedPreviewSizes. Y estos tamaños probablemente serán más pequeños o iguales al tamaño de pantalla del dispositivo.

Entonces simplemente llame

 Camera.Parameters p = camera.getParameters(); p.setPreviewSize(w, h); // one of supported sizes camera.setParameters(p); 

Entonces la superficie de la previsualización tendrá ese tamaño (posiblemente girado y w / h intercambiado). Y esta superficie será reescalada por Android al tamaño de su vista CameraPreview cuando se dibuja, por lo que también es importante cómo establecer el tamaño de su CameraPreview.

Puede configurar el tamaño fijo de su CameraPreview simplemente llamando al

 previewCamera.setLayoutParams(new FrameLayout.LayoutParams(w, h)); 

Así que en pocas palabras, establecer el tamaño de vista previa solicitada en Camera.setParameters, y el tamaño de su vista previa como desee, posiblemente, al mismo tamaño como vista previa, como es su requisito. Su vista previa puede ser igual al tamaño de la pantalla o ser más pequeña (suponiendo que la cámara no proporciona una vista previa mayor que la pantalla). Si la cámara proporciona una vista previa más grande que la pantalla, todavía puede llamar a preview.setX, preview.setY para moverla.

  • Cambiar tamaño de Android personalizado SurfaceView
  • Adición de vista de texto a la vista de superficie
  • ¿Puedo usar lockCanvas () en onPreviewFrame callback?
  • Android: graba y reproduce video en el mismo SurfaceView
  • Android Surfaceview Crash sin salida de error
  • Android VideoView.setAlpha no funciona (Xoom con 3.0)
  • Ocultar una vista de superficie que se superpone a otra
  • Fragmentos - El niño especificado ya tiene un padre. Debe llamar a removeView () en el padre del niño primero
  • El nuevo procesamiento de gms.maps.MapView se retrasa un poco cuando está en un ListView?
  • Android Multiple SurfaceViews
  • Cómo crear y guardar una captura de pantalla desde una vista de superficie?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.