android camera surfaceview orientation

蓝咒 提交于 2019-11-26 15:01:57

The way I implemented it:

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);
}

And then in your PictureCallback set metadata to indicate rotation level:

    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();
    }
};

I hope it helps.

UPDATE Now when landscape based devices are appearing an additional check for it is required in OrientationEventListener.

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

Full code (a bit wasteful by LC, but easily demonstrates the approach)

@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);
    }
}
OriginalCliche

Have you considered using the standard method thats provided in the API doc, which you can call on surfaceChanged? You could store the degrees in a global variable to later use when saving the picture. Also could do a simple null checker on your camera variable, so you don't create it again in 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);
}

As you've seen from the other answers, this code gets very complicated. You may want to investigate using a library to help you provide this feature, for example, CWAC-Camera supports OS 2.3 and up (hopefully you can drop OS 2.1 and OS 2.2 support now):
https://github.com/commonsguy/cwac-camera

CWAC-Camera supports locking the camera preview to landscape, and will auto-rotate images into the correction orientation for you. Browse the project issues if you want a taste of all the device specific problems that need to be solved, which IMO are more reasons for trying to use a library instead of maintaining all this code and testing yourself.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!