web-dev-qa-db-ja.com

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()...が呼び出されます。ご存知のように、これは非常に費用のかかる操作であり、移行がそれほどスムーズではありません。

向きを強制的に横向きにすると、プレビューは素晴らしいです。 Createは一度だけ呼び出されます。プレビューは横向きで、カメラは常にユーザーに表示されるためです。ただし、ポートレートで撮影した実際の写真の向きを設定する方法が必要です。ただし、風景を強制すると、サーフェスが再作成されることはなく、カメラが縦向きに保持されているときにパラメーターが設定されることもありません。

それでは、次のいずれかを(排他的に)行うにはどうすればよいですか?

  1. m_cameraを押したまま方向が変わるときにonDestroyとonCreateの間で遷移がスムーズになるようにする

  2. 強制横向き向きの変更を別の方法で検出します...ポートレートで保持されている場合、最後のスナップされた画像を回転します。

また、私が基地を離れている場合、誰かが私をより良い方向に向けることができますか?ありがとうございました。

29
Tom Fobear

私がそれを実装した方法:

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

Surfacedocで呼び出すことができるAPIドキュメントで提供されている標準メソッドの使用を検討しましたか?後で画像を保存するときに使用するために、グローバル変数に度を保存できます。また、カメラ変数に対して単純な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);
}
18
OriginalCliche

他の回答からもわかるように、このコードは非常に複雑になります。この機能の提供に役立つライブラリを使用して調査することをお勧めします。たとえば、CWAC-CameraはOS 2.3以降をサポートします(OS 2.1およびOS 2.2のサポートを今すぐ削除できます)。
https://github.com/commonsguy/cwac-camera

CWAC-Cameraは、カメラプレビューを横向きにロックすることをサポートしており、画像を修正方向に自動回転します。 プロジェクトの問題 を参照して、解決する必要があるデバイス固有のすべての問題の味を知りたい場合は、このIMOが、このすべてのコードを維持して自分でテストする代わりにライブラリを使用しようとするより多くの理由です。

3
Dan J