【发布时间】:2015-01-31 19:28:59
【问题描述】:
我正在开发一个 Android 应用程序,使用 Android Camera API 打开预览并从中拍照。应用程序必须只能在纵向模式下工作,并且必须可以同时使用设备的前置和后置摄像头(如果设备有两个摄像头)。
我已经在我的应用程序中打开了预览,我已经正确设置了显示方向(使用方法将其旋转 90 度:mCamera.setDisplayOrientation(90))以便可以在纵向模式下查看应用程序预览,并且我添加了一个按钮,可以在前后摄像头之间进行切换。所有这些都在应用程序中正常工作。
问题是当我拍照时:为了以正确的方式(纵向)旋转拍摄的照片,我动态地获取设备的方向并将图片旋转为获得的方向。但是,当照片保存到图库时,照片的大小很奇怪:如果是前置摄像头拍摄的,它是全屏的,如果是后置摄像头拍摄的,则不是。我的目标是始终全屏拍照。
这两张截图显示了问题:
---后置相机照片:
---前置摄像头照片:
这里是我的 CameraPreview 代码:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null){
return;
}
try {
mCamera.stopPreview();
} catch (Exception e){
}
try {
// Start preview in portrait mode
mCamera.setDisplayOrientation(90);
// Set the list of supported preview size in the related variable
if(mCamera != null){
if(mCamera.getParameters().getSupportedPreviewSizes() != null){
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
}
}
// Get the parameters of camera
Camera.Parameters parameters = mCamera.getParameters();
// Set output format to NV21 (which is guranteed to be supported on all devices)
parameters.setPreviewFormat(ImageFormat.NV21);
// Set the correct preview size (after applying the getOptimalPreviewSize)
if(mPreviewSize != null) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
Log.d(TAG,"Preview size is ("+mPreviewSize.width+";"+mPreviewSize.height+")");
// initializing bitmap and pixels
bitmap = Bitmap.createBitmap(mPreviewSize.width, mPreviewSize.height, Bitmap.Config.ARGB_8888);
pixels = new int[mPreviewSize.width * mPreviewSize.height];
}
// Correct the size - orientation of picture taken
if(isTablet(getContext()) == Boolean.FALSE){
onOrientationChanged(getScreenRotationOnPhone(),parameters);
}else{
onOrientationChanged(getScreenRotationOnTablet(),parameters);
}
mCamera.setPreviewDisplay(mHolder);
// Set to turn the Flash ON
// parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
// Call the setPreviewCallback and onPreviewFrame to get the incoming frame
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame ( byte[] data, Camera camera){
Log.i(TAG, "Ma entro nella onPreviewFrame?");
Camera.Parameters parameters = mCamera.getParameters();
int format = parameters.getPreviewFormat();
Log.i(TAG, "Il formato del frame e': " + format);
//YUV formats require more conversion
if (format == ImageFormat.NV21 || format == ImageFormat.YUY2 || format == ImageFormat.NV16) {
int w = parameters.getPreviewSize().width;
int h = parameters.getPreviewSize().height;
}
}
});
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
private int getScreenRotationOnPhone() {
final Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if(display.getRotation() == Surface.ROTATION_0){
System.out.println("SCREEN_ORIENTATION_PORTRAIT");
return Surface.ROTATION_0;
}else if(display.getRotation() == Surface.ROTATION_90){
System.out.println("SCREEN_ORIENTATION_LANDSCAPE");
return Surface.ROTATION_90;
}else if(display.getRotation() == Surface.ROTATION_180){
System.out.println("SCREEN_ORIENTATION_REVERSE_PORTRAIT");
return Surface.ROTATION_180;
}else if(display.getRotation() == Surface.ROTATION_270){
System.out.println("SCREEN_ORIENTATION_REVERSE_LANDSCAPE");
return Surface.ROTATION_270;
}else{
System.out.println("SCREEN_ORIENTATION_NOT_ADMISSIBLE");
return -1;
}
}
private int getScreenRotationOnTablet() {
final Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if(display.getRotation() == Surface.ROTATION_0){
System.out.println("SCREEN_ORIENTATION_LANDSCAPE");
return Surface.ROTATION_0;
}else if(display.getRotation() == Surface.ROTATION_90){
System.out.println("SCREEN_ORIENTATION_REVERSE_PORTRAIT");
return Surface.ROTATION_90;
}else if(display.getRotation() == Surface.ROTATION_180){
System.out.println("SCREEN_ORIENTATION_REVERSE_LANDSCAPE");
return Surface.ROTATION_180;
}else if(display.getRotation() == Surface.ROTATION_270){
System.out.println("SCREEN_ORIENTATION_PORTRAIT");
return Surface.ROTATION_270;
}else{
System.out.println("SCREEN_ORIENTATION_NOT_ADMISSIBLE");
return -1;
}
}
public boolean isTablet(Context context) {
boolean xlarge = ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 4);
boolean large = ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE);
return (xlarge || large);
}
public void onOrientationChanged(int orientation, Camera.Parameters mParameters) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(CameraActivity.getOpenedCamera(), info);
Log.i(TAG, "onOrientationChanged -> Camera opened actually is: "+CameraActivity.getOpenedCamera());
orientation = (orientation + 45) / 90 * 90;
int rotation = 0;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + orientation) % 360;
}
Log.i(TAG, "onOrientationChanged -> Orientation of pictures setted to: "+rotation);
mParameters.setRotation(rotation);
}
这里有CameraActivity的onPictureTaken代码,以及切换相机的方法:
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// TODO Auto-generated method stub
File pictureFile = Utility.getOutputMediaFile();
if (pictureFile == null){
Toast.makeText(this, "Couldn't create file", Toast.LENGTH_SHORT).show();
Log.d(TAG,"Couldn't create file");
return;//?
}else{
try{
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.flush();
fos.close();
}
catch (FileNotFoundException e){
Toast.makeText(this, "File not found exception", Toast.LENGTH_SHORT).show();
Log.d(TAG,"File not found: "+e.getMessage());
}
catch (IOException e){
Toast.makeText(this, "IO Exception", Toast.LENGTH_SHORT).show();
Log.d(TAG, "Error accessing file: "+e.getMessage());
}
//Per farle comparire subito nella cartella le foto:
this.mPictureFile = pictureFile;
MediaScannerConnection.scanFile(getApplicationContext(),
new String[]{this.mPictureFile.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i(TAG, "ExternalStorage Scanned " + path + ":");
Log.i(TAG, "ExternalStorage -> uri=" + uri);
}
});
camera.startPreview();
imageSaved.sendEmptyMessage(0);
}
}
public void switchCam(){
if (hasFrontCam == Boolean.TRUE && hasBackCam == Boolean.TRUE) {
// The phone has front camera and back camera
if(openedCam==Camera.CameraInfo.CAMERA_FACING_BACK){
//Chiudi la preview
if (mCamera!=null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//Apri la nuova camera
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
init();
openedCam=Camera.CameraInfo.CAMERA_FACING_FRONT;
}else{
//openedCam==Camera.CameraInfo.CAMERA_FACING_FRONT
//Chiudi la preview
if (mCamera!=null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//Apri la nuova camera
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
//riprendi la preview
init();
openedCam=Camera.CameraInfo.CAMERA_FACING_BACK;
}
}
}
使用这种实用方法:
/** Create a File for saving an image */
public static File getOutputMediaFile(){
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
if (!mediaStorageDir.exists()){
if (!mediaStorageDir.mkdirs()){
Log.d(TAG,"Failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
return mediaFile;
}else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.d(TAG,"External storage not writable but only readable");
return null;
}else{
Log.d(TAG,"External storage not writable");
return null;
}
}
有人可以帮我解决这个问题吗?
【问题讨论】:
-
我注意到图片翻转了。对吗?
-
前后摄像头切换的代码在哪里。
-
是的,图片被翻转以更正保存到图库中的方向。更改有效,实际上照片的方向是正确的。我还添加了切换相机的方法。
-
在凸轮切换后是否调用了surfaceChanged?
-
我刚刚用 logcat 验证,如果我按下 switchCam 按钮,它会在 switchCam() 之前调用,然后是 surfaceChanged()..