orientación de la superficie de la cámara android

Ok, tengo una clase que amplía SurfaceView y anula

surfaceChanged – solo llamadas startPreview
surfaceCreated: abre la cámara, edita los parámetros *, establece la superficie.
surfaceDestroyed – Llama a stopPreview, libera la cámara

todo esto funciona bien porque cuando la orientación es Retrato:

de superficieCreated *

m_camera = Camera.open(); Camera.Parameters p = m_camera.getParameters(); if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { p.set("orientation", "portrait"); // CameraApi is a wrapper to check for backwards compatibility if (CameraApi.isSetRotationSupported()) { CameraApi.setRotation(p, 90); } } 

Sin embargo, cada vez que cambia la orientación, llama a Camera.open () … que, como ya sabrá, es una operación bastante costosa, lo que hace que las transiciones no sean tan suaves.

Cuando forzo la orientación al paisaje, la vista previa es genial. Crear solo se llama una vez que funciona porque la vista previa está en el paisaje; la cámara es siempre lo que el usuario ve. Sin embargo, necesito una forma de establecer la orientación de la imagen real tomada cuando estoy en retrato. Sin embargo, cuando fuerzo el paisaje, la superficie nunca se vuelve a crear y los parámetros nunca se configuran cuando la cámara se sostiene en vertical.

Entonces, ¿cómo puedo hacer uno de los siguientes (exclusivamente)?

  1. Sosténgase en m_camera entre onDestroy y onCreate cuando la orientación cambie para que la transición sea suave

  2. Forzar el paisaje y detectar la orientación cambia de otra manera … girando la última imagen recortada si se mantiene en vertical.

Además, si estoy fuera de la base, ¿alguien me puede indicar una mejor dirección? Gracias.

La forma en que lo implementé:

 private Camera mCamera; private OrientationEventListener mOrientationEventListener; private int mOrientation = -1; private static final int ORIENTATION_PORTRAIT_NORMAL = 1; private static final int ORIENTATION_PORTRAIT_INVERTED = 2; private static final int ORIENTATION_LANDSCAPE_NORMAL = 3; private static final int ORIENTATION_LANDSCAPE_INVERTED = 4; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // force Landscape layout setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); /* Your other initialization code here */ } @Override protected void onResume() { super.onResume(); if (mOrientationEventListener == null) { mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) { @Override public void onOrientationChanged(int orientation) { // determine our orientation based on sensor response int lastOrientation = mOrientation; if (orientation >= 315 || orientation < 45) { if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) { mOrientation = ORIENTATION_PORTRAIT_NORMAL; } } else if (orientation < 315 && orientation >= 225) { if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) { mOrientation = ORIENTATION_LANDSCAPE_NORMAL; } } else if (orientation < 225 && orientation >= 135) { if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) { mOrientation = ORIENTATION_PORTRAIT_INVERTED; } } else { // orientation <135 && orientation > 45 if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) { mOrientation = ORIENTATION_LANDSCAPE_INVERTED; } } if (lastOrientation != mOrientation) { changeRotation(mOrientation, lastOrientation); } } }; } if (mOrientationEventListener.canDetectOrientation()) { mOrientationEventListener.enable(); } } @Override protected void onPause() { super.onPause(); mOrientationEventListener.disable(); } /** * Performs required action to accommodate new orientation * @param orientation * @param lastOrientation */ private void changeRotation(int orientation, int lastOrientation) { switch (orientation) { case ORIENTATION_PORTRAIT_NORMAL: mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 270)); mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 270)); Log.v("CameraActivity", "Orientation = 90"); break; case ORIENTATION_LANDSCAPE_NORMAL: mSnapButton.setImageResource(android.R.drawable.ic_menu_camera); mBackButton.setImageResource(android.R.drawable.ic_menu_revert); Log.v("CameraActivity", "Orientation = 0"); break; case ORIENTATION_PORTRAIT_INVERTED: mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 90)); mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 90)); Log.v("CameraActivity", "Orientation = 270"); break; case ORIENTATION_LANDSCAPE_INVERTED: mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 180)); mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 180)); Log.v("CameraActivity", "Orientation = 180"); break; } } /** * Rotates given Drawable * @param drawableId Drawable Id to rotate * @param degrees Rotate drawable by Degrees * @return Rotated Drawable */ private Drawable getRotatedImage(int drawableId, int degrees) { Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId); Matrix matrix = new Matrix(); matrix.postRotate(degrees); Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true); return new BitmapDrawable(rotated); } 

Y luego en su PictureCallback establece los metadatos para indicar el nivel de rotación:

  private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { try { // Populate image metadata ContentValues image = new ContentValues(); // additional picture metadata image.put(Media.DISPLAY_NAME, [picture name]); image.put(Media.MIME_TYPE, "image/jpg"); image.put(Media.TITLE, [picture title]); image.put(Media.DESCRIPTION, [picture description]); image.put(Media.DATE_ADDED, [some time]); image.put(Media.DATE_TAKEN, [some time]); image.put(Media.DATE_MODIFIED, [some time]); // do not rotate image, just put rotation info in switch (mOrientation) { case ORIENTATION_PORTRAIT_NORMAL: image.put(Media.ORIENTATION, 90); break; case ORIENTATION_LANDSCAPE_NORMAL: image.put(Media.ORIENTATION, 0); break; case ORIENTATION_PORTRAIT_INVERTED: image.put(Media.ORIENTATION, 270); break; case ORIENTATION_LANDSCAPE_INVERTED: image.put(Media.ORIENTATION, 180); break; } // store the picture Uri uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI, image); try { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); OutputStream out = getContentResolver().openOutputStream( uri); boolean success = bitmap.compress( Bitmap.CompressFormat.JPEG, 75, out); out.close(); if (!success) { finish(); // image output failed without any error, // silently finish } } catch (Exception e) { e.printStackTrace(); // handle exceptions } mResultIntent = new Intent(); mResultIntent.setData(uri); } catch (Exception e) { e.printStackTrace(); } finish(); } }; 

Espero que ayude.

ACTUALIZACIÓN Ahora cuando aparecen dispositivos basados ​​en paisaje, se requiere una comprobación adicional en OrientationEventListener.

 Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices } else { // portrait oriented device } 

Código completo (un poco derrochador por LC, pero demuestra fácilmente el enfoque)

 @Override public void onOrientationChanged(int orientation) { // determine our orientation based on sensor response int lastOrientation = mOrientation; Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices if (orientation >= 315 || orientation < 45) { if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) { mOrientation = ORIENTATION_LANDSCAPE_NORMAL; } } else if (orientation < 315 && orientation >= 225) { if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) { mOrientation = ORIENTATION_PORTRAIT_INVERTED; } } else if (orientation < 225 && orientation >= 135) { if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) { mOrientation = ORIENTATION_LANDSCAPE_INVERTED; } } else if (orientation <135 && orientation > 45) { if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) { mOrientation = ORIENTATION_PORTRAIT_NORMAL; } } } else { // portrait oriented devices if (orientation >= 315 || orientation < 45) { if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) { mOrientation = ORIENTATION_PORTRAIT_NORMAL; } } else if (orientation < 315 && orientation >= 225) { if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) { mOrientation = ORIENTATION_LANDSCAPE_NORMAL; } } else if (orientation < 225 && orientation >= 135) { if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) { mOrientation = ORIENTATION_PORTRAIT_INVERTED; } } else if (orientation <135 && orientation > 45) { if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) { mOrientation = ORIENTATION_LANDSCAPE_INVERTED; } } } if (lastOrientation != mOrientation) { changeRotation(mOrientation, lastOrientation); } } 

¿Ha considerado usar el método estándar que se proporciona en el documento API, al que puede llamar en SurfaceChanged? Puede guardar los grados en una variable global para utilizarlos más adelante al guardar la imagen. También podría hacer un simple verificador nulo en la variable de su cámara, para que no lo vuelva a crear en surfaceCreated.

 public void setCameraDisplayOrientation() { if (mCamera == null) { Log.d(TAG,"setCameraDisplayOrientation - camera null"); return; } Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(CAM_ID, info); WindowManager winManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); int rotation = winManager.getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } mCamera.setDisplayOrientation(result); } 

Como has visto por las otras respuestas, este código se vuelve muy complicado. Es posible que desee investigar el uso de una biblioteca para ayudarlo a proporcionar esta función, por ejemplo, CWAC-Camera es compatible con OS 2.3 y versiones superiores (con suerte, ahora puede colocar soporte para OS 2.1 y OS 2.2):
https://github.com/commonsguy/cwac-camera

CWAC-Camera admite el locking de la vista previa de la cámara en horizontal, y rotará automáticamente las imágenes en la orientación de corrección por usted. Explore los problemas del proyecto si desea conocer todos los problemas específicos del dispositivo que deben resolverse, qué OMI son más razones para tratar de usar una biblioteca en lugar de mantener todo este código y probarse usted mismo.