2011-03-01 114 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更愿意尝试使用库而不是维护所有代码并进行测试。