Androidで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ですか。
しかも新しい名前は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); }