android相机surfaceview方向
Posted
技术标签:
【中文标题】android相机surfaceview方向【英文标题】:android camera surfaceview orientation 【发布时间】:2011-07-06 16:35:26 【问题描述】:好的,所以我有一个扩展 SurfaceView 并覆盖的类
surfaceChanged - 只需调用 startPreview surfaceCreated - 打开相机,编辑参数 *,设置 surfaceHolder surfaceDestroyed - 调用 stopPreview,释放相机
这一切都很好,因为当方向是纵向时:
从表面创建 *
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);
但是,每次方向改变时,它都会调用 Camera.open()... 您可能知道这是一项相当昂贵的操作,导致转换不那么平滑。
当我将方向强制为横向时,预览效果很好。创建只被调用一次,因为预览是横向的,相机总是用户看到的。但是,我需要一种方法来设置纵向拍摄的实际照片的方向。但是,当我强制横向时,永远不会重新创建表面,并且当相机保持纵向时永远不会设置参数。
那么,我该如何(仅)执行以下操作之一?
当方向改变时,在 onDestroy 和 onCreate 之间按住 m_camera,以便平滑过渡
强制横向并以另一种方式检测方向变化...如果保持纵向,则旋转最终拍摄的照片。
另外,如果我不在基地,有人可以为我指出更好的方向吗?谢谢。
【问题讨论】:
+1 我也对此感兴趣。默认的谷歌相机应用程序执行得很好:它不会重新创建活动,但按钮和最后的图像预览很好地旋转以匹配横向/纵向方向。顺便说一句,在我看来,p.set("orientation", "portrait") 是一种隐藏的 API 用法,并且不受官方支持,不是吗? 我认为它实际上并没有做任何事情,哈哈。我首选的方法是强制景观。问题是我需要以另一种方式检测方向,因为这样就不会重新创建 cameraActivity。 啊,我明白你的想法了。因此,您将强制相机活动为横向,然后根据 真实 方向,只需旋转图片,对吗? This 可以帮助你。这不是一个坏主意,我可能会自己去实现它(-. 【参考方案1】:我的实现方式:
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);
然后在您的 PictureCallback 中设置元数据以指示旋转级别:
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();
;
希望对你有帮助。
更新现在,当基于横向的设备出现时,需要在 OrientationEventListener 中对其进行额外检查。
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0)
// landscape oriented devices
else
// portrait oriented device
完整代码(LC有点浪费,但很容易演示该方法)
@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);
【讨论】:
我最终使用了默认的相机活动,但是这看起来不错,我可能会在其他时间回来。谢谢。 欢迎您。老实说,我实现了自己的摄像头活动,因为与不同安卓手机上默认摄像头活动的所有不一致,我觉得做各种黑客来绕过它们是不对的。 @Audrius 我用按钮创建了简单的自定义摄像头,但我需要在设备的 4 侧旋转摄像头...怎么做? 永远不要使用mOrientationEventListene
,它的更新频率比你的应用程序方向变化的频率高……最好使用DisplayManager.DisplayListener
【参考方案2】:
您是否考虑过使用 API 文档中提供的标准方法,您可以在 surfaceChanged 上调用该方法?您可以将度数存储在全局变量中,以便以后在保存图片时使用。也可以对你的相机变量做一个简单的空检查,所以你不会在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);
【讨论】:
如果你继续实现它,请注意,CAM_ID 是一个全局变量,我在别处设置。 在surfaceCreaeted/surfaceChanged或onResume中我们必须在哪里调用setCameraDisplayOrientation() 我不知道如何使用“mCamera”和“CAM_ID”。我该如何实现它们? 此代码在设置方向修复纵向或横向时不起作用【参考方案3】:正如您从其他答案中看到的,这段代码变得非常复杂。您可能希望使用库来帮助您提供此功能,例如,CWAC-Camera 支持 OS 2.3 及更高版本(希望您现在可以放弃 OS 2.1 和 OS 2.2 支持):https://github.com/commonsguy/cwac-camera
CWAC-Camera 支持将相机预览锁定为横向,并会自动将图像旋转到适合您的校正方向。如果您想了解所有需要解决的设备特定问题,请浏览project issues,这是 IMO 尝试使用库而不是维护所有这些代码并自行测试的更多原因。
【讨论】:
以上是关于android相机surfaceview方向的主要内容,如果未能解决你的问题,请参考以下文章
Android:在方向更改时保留相机预览,而其他视图可以旋转
如何在Android中修复相机预览(surfaceview)的正确纵横比?