読者です 読者をやめる 読者になる 読者になる

マサルの備忘録

ハイオク仕様のトルクフルな備忘録

AndroidでQRコードを読むアプリをつくろうと思ったんだけど、そもそもカメラのプレビューを表示したことなかったからやってみたんだけど、結構エグかった

スポンサーリンク

無性にQRコードを読みたくなること、ありますよね?

表示できたけど、結構めんどい

f:id:domomasarudesu:20150227175236p:plain
こんな感じで表示はできたけど、ここに至るまで結構困った。
プレビューのオリエンテーションおかしくね?っていうツッコミはなしで。
これから直します。メソッドありました

やりたかったことはQRを読むこと

QR読むアプリってたくさんありますが、やっぱりカメラのプレビュー画面が出ますよね。っていうことは、まずカメラのプレビュー画面を出すところから始まるってことなんですよねきっと。

ここにもロリポップの魔の手が

Camera | Android Developers
いろんなサイトを拝見すると、android.hardware.Cameraクラスを使えばいいと書いてあるのが散見されます。が、

This class was deprecated in API level 21.
We recommend using the new android.hardware.camera2 API for new applications.

おやおや、deprecatedですか。

f:id:domomasarudesu:20150227194107p:plain

しかも新しい名前はCamera2ですか、そうですか。
クラス⇒パッケージへと進化していてできることが増えてそうなのはいいですが、命名がいい加減ではないでしょうか。そして2があると3が出るんじゃないか、そう思わざるを得ません。

じゃあどちらを使うのか、Cameraでしょ

まともな動作をするLollipop端末をもっていないので、今回はCameraクラスを使いました。



詳しいことはほかのサイトをご覧ください

Cameraクラスでプレビューを表示するのに必要なのがSurfaceView。

	mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
	mSurfaceView.getHolder().addCallback(mCallback);

SurfaceHolder.CallbackをOverrideして、そのなかでCameraのプレビューを表示するように実装します。

	private SurfaceHolder.Callback mCallback = new Callback() {

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			mCamera.release();
			mCamera = null;
		}

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			mCamera = Camera.open();

			Camera.Parameters parameters = mCamera.getParameters();

			List<Camera.Size> sizes = parameters.getSupportedPictureSizes();

			int dispWidth = getWindowManager().getDefaultDisplay().getWidth();
			int dispHeight = getWindowManager().getDefaultDisplay().getHeight();

			Camera.Size size = sizes.get(0);

			for (Camera.Size tmpSize : sizes) {
				if (tmpSize.width < dispWidth && tmpSize.height < dispHeight) {
					size = tmpSize;
					break;
				}
			}

			mTextView.setText(size.width + " x " + size.height);

			parameters.setPreviewSize(size.width, size.height);
			RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
					size.width, size.height);
			params.addRule(RelativeLayout.CENTER_IN_PARENT);

			mSurfaceView.setLayoutParams(params);

			mCamera.setParameters(parameters);

			try {
				mCamera.setPreviewDisplay(holder);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			mCamera.startPreview();
		}
	};

SurfaceViewのCreate時、Destroy時にOpen()、release()するのはいいんだけどさぁ。

表示できるPreviewSizeが決まっていて、複数ある

Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();

これでサポートされてるPreviewのサイズが取れる。
Huawei Ascend G6は、

3264 / 2448
3200 / 2400
2592 / 1944
2048 / 1536
1920 / 1080
1600 / 1200
1280 / 768
1280 / 720
1024 / 768
800 / 600
800 / 480
720 / 480
640 / 480
352 / 288
320 / 240
176 / 144

が対応しているようです。
ここからテキトーに選んで、

parameters.setPreviewSize(size.width, size.height);

こんな感じでSet。

Setしても、SurfaceViewの大きさに引き伸ばされる

これが厄介。
良いか悪いかはべつとして、SurfaceViewのCreate時に、SetするPreviewSizeをつかって、
SurfaceViewのサイズを変更。ついでに、親Viewの中心に表示。

RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size.width, size.height);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
mSurfaceView.setLayoutParams(params);

今は何も考えずに、LayoutParamsに入れてる。
うまいこと縮尺を考えてSurfaceViewの大きさを変えれば、もっといい感じに表示できそう。

さて・・・

画面がPortrait時もCameraクラスがSurfaceViewに描画するのは横長の画像。
ちょっと90度回転させる方法を考えますかね・・・。

(追記)
Camera | Android Developers
http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)

できそうです。
なんだ、簡単じゃん・・・。

 public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, info);
     int rotation = activity.getWindowManager().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;
     }
     camera.setDisplayOrientation(result);
 }