Android (X06HT Desire) カメラプレビューが横向きに表示されてしまう!の対応

カメラのプレビューが横向きに表示されてしまう!

X06HT Desre Android2.1 の時に、アプリでカメラを使用した Activity を作成していたのだが、普通に使うと何故か横向きにプレビューされてしまっていたので、何の気なしにか、なにかで調べてか忘れたが、以下の様なコードを書いて対応していた。

camera01

↑ こんな風にプレビューされるので、↓ こんな風に対応しておいたら、うまいこと動いていた。

Camera.Parameters p = camera.getParameters(); p.setRotation(90); camera.setParameters(p);

が、10月8日に、待ちわびた、Froyo化を行ったところ、元の横向きに戻ってしまい、Camera.Paramteres の値を変えても、書き方を変えても、うんともすんとも言わなくなってしまった。

不具合か~

と思ったが、どうも元々Androidのカメラとはそういうものらしい。

もはやケータイに必須のカメラをAndroidで制御しよう」によると、

そもそも、Android のカメラには、向きの概念が無くて、常にランドスケープモードで作動するようだ。

なんと。

なので、Activity のオリエンテーションモードを、ランドスケープにした上で、上記のようにRotation してあげれば、意図した方向に画面プレビューを表示出来ることが分かった。

・・・が、いくつか問題が。

メニューが横向きに出てきてしまう

camera02

こんな感じ。これは当然か~ でもこれじゃーメニューつかえねー

トーストが横向きに出てしまう。

これも使えない。ちなみに通知バーも横向きに出るので、フルスクリーンモードにして、通知バーも消す必要がある。

AndroidManifest.xml のアクティビティ要素にて、向きとフルスクリーン&タイトルバー無しを指定。
<activity android:name="info.typea.shujiroid.core.CameraActivity"
          android:screenOrientation="landscape"
          android:theme="@android:style/Theme.NoTitleBar.Fullscreen"/>
あと、以下のパーミッションを指定する。
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-feature android:name="android.hardware.camera"></uses-feature>
<uses-feature android:name="android.hardware.camera.autofocus"></uses-feature>
<uses-feature android:name="android.hardware.camera.flash"></uses-feature>

カメラのコントロールのための UI を自分で作らなきゃいけない!?

まぁ、そもそも インテントで、カメラを呼び出せばいいだけかもしれないが(呼び出せるよね?)乗りかかった船なので、何とか使える形に持って行きたい。

先ほどのサイトのサンプルをダウンロードすると、AR(!?)のサンプルというか、プレビュー上にDroid君が表示される例があったので、参考にさせてもらい、Activity に カメラプレビュー用の SurfaceView を乗っけて、その上に、カメラコントロール用の View を乗っけることで対応してみた。

package info.typea.shujiroid.core;

import info.typea.shujiroid.free.R;
import info.typea.shujiroid.free.ShujiActivity;

import java.io.IOException;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.hardware.Camera;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Toast;

/**
 * カメラ用画面
 * 
 * @author piroto
 */
public class CameraActivity extends Activity {
    public static final String KEY_CAMERA_DATA = "camera_data";
    public static final String KEY_PREF_FLASH_MODE = "flash_mode";
    
    public static final int MENU_SHUTTER = Menu.FIRST;
    private SurfaceView preview;
    private SurfaceHolder holder;
    CameraControlView cameraCtrl;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera_main);

        // カメラコントロール用ビュー
        cameraCtrl = new CameraControlView(this);
        addContentView(cameraCtrl, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        
        preview = (SurfaceView) findViewById(R.id.surview_preview);
        
        holder = preview.getHolder();
        holder.addCallback(cameraCtrl);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
    
    /**
     * カメラのコントロール用のビュー
     * @author piroto
     */
    class CameraControlView extends View implements SurfaceHolder.Callback, 
                                                    Camera.ShutterCallback, 
                                                    Camera.AutoFocusCallback,
                                                    Camera.PictureCallback {
        SharedPreferences pref = null;
        private Camera camera;
        private boolean isInProcess;
        
        private float shutterX = 10f;
        private float shutterY = 10f;
        private float shutterR = 50f;

        private String currentFlashMode = null;
        private float flashX = 20f;
        private float flashY = 10f;
        private float flashIconSize = 100f;
        
        /**
         * @param context
         */
        public CameraControlView(Context context) {
            super(context);
            setFocusable(true);
            
            pref = PreferenceManager.getDefaultSharedPreferences(getContext());
            currentFlashMode = pref.getString(KEY_PREF_FLASH_MODE, Camera.Parameters.FLASH_MODE_OFF);
        }
        
        /**
         * フラッシュモードに応じたアイコンを取得する
         * @param flashMode
         * @return
         */
        private Bitmap getFlashModeIcon(String flashMode) {
            int iconId = R.drawable.flash_off_icon;
            if (Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) {
                iconId = R.drawable.flash_auto_icon;
            } else if (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {
                iconId = R.drawable.flash_on_icon;
            } 
            return BitmapFactory.decodeResource(getResources(), iconId);
        }       
        
        /**
         * フラッシュモードを変更する
         * AUTO -> ON -> OFF
         */
        private void changeFlashMode() {
            if (Camera.Parameters.FLASH_MODE_AUTO.equals(currentFlashMode)) {
                currentFlashMode = Camera.Parameters.FLASH_MODE_ON;
            } else if (Camera.Parameters.FLASH_MODE_ON.equals(currentFlashMode)) {
                currentFlashMode = Camera.Parameters.FLASH_MODE_OFF;
            } else {
                currentFlashMode = Camera.Parameters.FLASH_MODE_AUTO;
            }
            
            // プリファレンスに設定を保存
            pref.edit().putString(KEY_PREF_FLASH_MODE, currentFlashMode).commit();

            Camera.Parameters p = camera.getParameters();
            p.setFlashMode(currentFlashMode);
            camera.setParameters(p);

            invalidate();
            return;
        }
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            
            int w = canvas.getWidth();
            int h = canvas.getHeight();
            
            // シャッターボタンを描画
            shutterX = w - (shutterR/2f+40f);
            shutterY = h / 2.0f;
            
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStrokeWidth(4);
            paint.setColor(Color.BLUE);
            
            paint.setStyle(Style.FILL);
            paint.setAlpha(92);
            canvas.drawCircle(shutterX, shutterY, shutterR, paint);
            
            paint.setStyle(Style.STROKE);
            canvas.drawCircle(shutterX, shutterY, shutterR, paint);
            
            // フラッシュモードアイコンを描画
            canvas.drawBitmap(getFlashModeIcon(currentFlashMode), flashX, flashY, paint);
        }
        
        /**
         * 写真を撮る
         */
        public void takePicture() {
            isInProcess = true;
            camera.takePicture(this, null, this);
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch(event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!isInProcess) {
                    float x = event.getX();
                    float y = event.getY();
        
                    // シャッターが押されたかを判定
                    float minX = shutterX - shutterR;
                    float maxX = shutterX + shutterR;
                    float minY = shutterY - shutterR;
                    float maxY = shutterY + shutterR;
                    
                    if ( (minX <= x && x <= maxX) &&
                         (minY <= y && y <= maxY) ) {
                        takePicture();
                    }
                    
                    // フラッシュアイコンが押されたかを判定
                    minX = flashX;
                    maxX = flashX + flashIconSize;
                    minY = flashY;
                    maxY = flashY + flashIconSize;
                    if ( (minX <= x && x <= maxX) &&
                         (minY <= y && y <= maxY) ) {
                            changeFlashMode();
                    }
                }
                break;
            default:
                if (!isInProcess) {
                    camera.autoFocus(this);
                    isInProcess = true;
                }
                break;
            }
            return true;
        }
        
        @Override
        public void onShutter() {
        }

        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            isInProcess = false;
        }

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            // 撮影した写真データを呼び出し元の Activity へ返す
            Intent intent = new Intent();
            intent.putExtra(KEY_CAMERA_DATA, data);
            setResult(ShujiActivity.REQUEST_CODE_CAMERA, intent);
            
            isInProcess = false;
            finish();
        }
        
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                camera = Camera.open();
                camera.setPreviewDisplay(holder);
            } catch (IOException e) {
                String msg = getString(R.string.msg_error_occured) + "\n"
                                 + e.getMessage();
                (Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG)).show();
            }
        }
        
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            camera.stopPreview();
            // 擬似的にポートレートモードに
            Camera.Parameters p = camera.getParameters();
            p.setRotation(90);
            p.setFlashMode(currentFlashMode);
            camera.setParameters(p);
            camera.startPreview();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            camera.stopPreview();
            camera.release();
        }
    }
}

ちなみに、このアクティビティの呼び出しと、結果の受け取りは以下な感じ。

インテントを回答待ちで呼び出して、

// インテントを起動
startActivityForResult(new Intent(this, CameraActivity.class),
    REQUEST_CODE_CAMERA);

結果を byte 配列のまま受け取って、Bitmapに変換。

// 結果を受け取ってBitmapに変換
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    switch (requestCode) {
    case REQUEST_CODE_CAMERA:
        Bundle bundle = data.getExtras();
        byte[] picdata = bundle.getByteArray(CameraActivity.KEY_CAMERA_DATA);
        Bitmap pic = BitmapFactory.decodeByteArray(picdata, 0, picdata.length);
                :

拙いながらも、まぁこれでそれなりに動くようにはなった。

しかしながら、Home キー長押しで出てくるタスクリストは横向き。

だけど、標準のカメラも同じなので、割り切るしかないな。

 

あと、他機種で全く検証出来ないのが難点だが、知人がIS03を購入するようだし、これから Android もちが増えていくことを期待。

こんな感じになります。・・・わかりにくいかな。

 

また一歩野望に近づいた!