28

좋아 내가 서피스 뷰 SurfaceView를 확장하고안드로이드 카메라 서피스 뷰 SurfaceView 방향

이 surfaceChanged 우선 클래스 그래서 - 편집이 SurfaceHolder에
surfaceDestroyed을 설정 * PARAMS, 카메라를 엽니 다 - - 단지 startPreview
surfaceCreated 호출을 stopPreview, 릴리스 카메라

호출 surfaceCreated에서

*

: 방향이 세로 인 경우 때문에

이 모두 잘 작동

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. 홀드 방향 변경 전환이 원활

  2. 포스 풍경과 방향을 감지하는 것이 또 다른 방법 ... 최종 snaped 사진을 회전을 변경 있도록 열린 경우 초상화에서.

또한 내가 오프베이스 인 경우 다른 사람이 나를 더 나은 방향으로 안내 할 수 있습니까? 고맙습니다.

+2

+1 나는 이것에 또한 흥미가있다. 기본 Google 카메라 앱은이 작업을 아름답게 수행합니다. 즉, 활동을 재현하지는 않지만 버튼과 마지막 이미지 미리보기가 가로/세로 방향에 맞게 멋지게 회전됩니다. Btw, _p.set ("orientation", "portrait") _ 내 이해에서 숨겨진 API 사용법이며 공식적으로 지원되지 않습니다. 그렇지 않습니까? – Audrius

+0

나는 그것을 어리석은 일로 생각하지 않는다. 롤. 내 prefered 방법은 강제로 풍경 것입니다. 문제는 카메라 액티비티가 다시 생성되지 않기 때문에 어떻게 든 오리엔테이션을 다른 방식으로 감지해야한다는 것입니다. –

+0

아, 당신이 염두에두고있는 것을 봅니다. 그래서 당신은 풍경에 카메라의 활동을 강요하고 * 진짜 * 방향에 따라, 그냥 사진을 회전, 맞죠? [This] (http://android-er.blogspot.com/2010/08/orientationeventlistener-detect.html)가 도움이 될 수 있습니다. 나쁜 생각은 아니지만 직접 구현할 수도 있습니다. (-. – Audrius

답변

57

내가 그것을 구현하는 방법 :

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

.

업데이트 이제 Landscape 기반 장치가 나타나면 OrientationEventListener에 추가 검사가 필요합니다.

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

전체 코드는 surfaceChanged에 호출 할 수 있습니다

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

기본 카메라 사용이 끝났지 만 다른 시간에 다시 돌아올 수 있습니다. 감사합니다. –

+2

환영합니다. 솔직히, 나는 다른 안드로이드 핸드셋에서의 기본 카메라 활동과의 모순 때문에 카메라 자체의 활동을 구현 했었고 나는 모든 종류의 해킹을하고 다니는 것을 좋아하지 않았다. – Audrius

+0

@Audrius 나는 버튼으로 간단한 커스텀 카메라를 만들었다. 하지만 장치의 4면에서 카메라를 회전시켜야합니다 ... 어떻게해야합니까? –

17

당신이 API의 문서에서 제공하는 이잖아 표준 방법을 사용하여 고려 가지고, (쉽게 조금 LC에 의해 낭비 만이 접근 방식을 보여줍니다)? 이미지를 저장할 때 나중에 사용할 수 있도록 전역 변수에도를 저장할 수 있습니다. 또한 카메라 변수에 간단한 null 검사기를 사용할 수 있으므로 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); 
} 
+0

구현하려고하면 CAM_ID가 다른 곳에서 설정 한 전역 변수입니다. – OriginalCliche

+2

위의 코드는 저에게 효과가 없습니다. .. ** 삼성 S2 테스트 ** – swiftBoy

+0

surfaceCreaeted/surfaceChanged에서 setCameraDisplayOrientation()을 호출해야 할 곳과 onResume – Nepster

2

다른 답변에서 보았 듯이이 코드는 매우 복잡해졌습니다. , CWAC - 카메라 OS 2.3을 지원하고 최대 당신은 (잘하면 2.2 지원 이제 OS 2.1 OS를 삭제할 수 있습니다) 당신은 예를 들어,이 기능을 제공하기 위해 라이브러리를 사용하여 조사 할 수 있습니다 :
https://github.com/commonsguy/cwac-camera

CWAC - 카메라 카메라 미리보기를 가로 방향으로 고정시키는 기능을 지원하며 이미지를 보정 방향으로 자동 회전합니다. 해결해야 할 모든 장치 관련 문제를 원한다면 project issues을 찾아보십시오.이 코드는이 코드를 모두 유지 관리하는 대신 라이브러리를 사용하려는 더 많은 이유입니다.