我正在开发Android应用程序,使用Android Camera API打开预览,并从中获取照片。应用程序必须仅在纵向模式下工作,并且必须可以同时使用设备的前后摄像头(如果设备同时具有两个摄像头)。
我已在我的应用中打开预览,我已正确设置了显示方向(使用方法:mCamera.setDisplayOrientation(90)将其旋转90度),以便能够以纵向模式查看应用程序预览,而我添加了一个按钮,可以在前后摄像头之间切换。所有这些都在应用程序中正常工作。
当我拍照时问题是:为了以正确的方式(以肖像方式)旋转拍摄的照片,我得到了设备的方向,并将图片旋转为获得的方向。但是,当照片保存到图库时,照片尺寸很奇怪:如果使用前置摄像头拍摄照片,则全屏显示,而如果照片是使用后置摄像头拍摄的话。我的目标是始终全屏拍照。
这两个截图显示了问题:
--- BACK-CAMERA照片:
--- FRONT-CAMERA照片:
这里有我的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;
}
}
有人可以帮我解决这个问题吗?
答案 0 :(得分:0)
我已经解决了这个问题。
我在背面和前置摄像头支持的图片尺寸列表中找到了解决方案。
在我的情况下,我使用的是三星S4进行开发,支持的背景照片尺寸列表如下:
0) (4128*3096) aspect_ratio=1.333 ---> Choosen automatically
1) (4128*2322) aspect_ratio=1.777
2) (3264*2448) aspect_ratio=1.333
3) (3264*1836) aspect_ratio=1.777
4) (2048*1536) aspect_ratio=1.333
5) (2048*1152) aspect_ratio=1.777
6) (1280*720) aspect_ratio=1.777
7) (640*480) aspect_ratio=1.777
对于前置摄像头:
0) (1920*1080) aspect_ratio=1.777 ---> Choosen automatically
1) (1440*1080) aspect_ratio=1.333
2) (1280*720) aspect_ratio=1.777
3) (960*720) aspect_ratio=1.333
4) (720*480) aspect_ratio=1.5
5) (640*480) aspect_ratio=1.333
6) (320*240) aspect_ratio=1.333
假设尺寸为(宽度;高度),纵横比计算为:宽度/高度。
Android会自动为前后摄像头选择最佳图片尺寸;它不会检查前后摄像头选择的尺寸是否具有相同的宽高比。但纵横比必须相同,才能在相同尺寸的图库照片中使用。