2011-03-01 128 views
28

好了,所以我有一個延伸SurfaceView和覆蓋Android攝像頭surfaceview方向

surfaceChanged一類 - 只是調用startPreview
surfaceCreated - 打開相機,編輯PARAMS *,設置surfaceHolder
surfaceDestroyed - 電話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()...您可能知道這是一項相當昂貴的操作,導致轉換不太流暢。

當我強迫風景的方向,預覽是偉大的。創建只獲取調用一次,這是因爲預覽在橫向上,相機總是用戶看到的。但是,我需要一種方法來設置縱向拍攝時拍攝的實際照片的方向。當我強迫風景時,表面永遠不會被重新創建,並且相機被豎直放置時從未設置參數。

那麼我怎樣才能做到以下(完全)之一?如果當持有的方向變化,使過渡是平滑

  • 部隊景觀和檢測方向變化的另一種方式......旋轉最終snaped圖片

    1. 守住m_camera的onDestroy和之間的onCreate在肖像。

    另外,如果我關閉基地有人能指點我一個更好的方向?謝謝。

  • +2

    +1我也對此感興趣。默認谷歌相機應用程序執行此美麗:它不重新創建活動,但按鈕和最後圖像預覽很好地旋轉以匹配風景/人像方向。順便說一句,在我的理解中,_p.set(「orientation」,「portrait」)_是一個隱藏的API用法,並沒有官方支持,不是嗎? – Audrius 2011-03-01 18:27:04

    +0

    我不認爲它實際上做了什麼,哈哈。我喜歡的方法是強制風景。問題是我需要以某種方式檢測方向另一種方式,因爲然後cameraActivity不會被重新創建。 – 2011-03-01 19:31:49

    +0

    啊,我明白你的想法。所以你會強制相機活動到一個風景,然後根據*真實*方向,只需旋轉一張圖片,對吧? [This](http://android-er.blogspot.com/2010/08/orientationeventlistener-detect.html)可以幫助你。這不是一個壞主意,我可能會自己去實現它( - 。 – Audrius 2011-03-02 13:33:47

    回答

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

    我希望它能幫助。

    UPDATE現在,當基於橫向的設備出現時,在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); 
        } 
    } 
    
    +1

    我最終使用了默認的相機活動,但是這看起來不錯,我可能會在另一次回來,謝謝 – 2011-03-03 19:38:10

    +2

    歡迎。誠實的,我實現了我自己的相機活動,因爲與不同的android手機上的默認相機活動的所有不一致,並且我不適合做各種各樣的黑客來繞過他們。 – Audrius 2011-03-04 09:52:10

    +0

    @Audrius我創建了帶按鈕的簡單自定義相機但我需要在設備的4側旋轉攝像頭...如何做到這一點? – 2014-02-27 05:58:18

    17

    你有沒有考慮過使用的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); 
    } 
    
    +0

    如果你進入實現它,請注意,CAM_ID是一個全局變量,我在別處設置。 – OriginalCliche 2012-06-06 23:26:20

    +2

    上面的代碼不適用於我.. **在Samsung S2 **上進行測試 – swiftBoy 2013-10-04 05:56:56

    +0

    我們在哪裏必須調用setCameraDisplayOrientation()in surfaceCreaeted/surfaceChanged或onResume – Nepster 2015-04-27 13:57:59

    2

    正如你從其他答案中看到的,這段代碼變得非常複雜。您可能需要使用一個庫,幫助您提供此功能,例如調查,CWAC,相機支持OS 2.3及以上(希望你可以刪除OS 2.1和OS 2.2支持現在):
    https://github.com/commonsguy/cwac-camera

    CWAC,相機支持將相機預覽鎖定到橫向,並將自動將圖像轉換爲您的校正方向。瀏覽project issues如果您想要了解所有需要解決的設備特定問題,哪些IMO更願意嘗試使用庫而不是維護所有代碼並進行測試。