AndroidでQRコードを読むアプリをつくった
前回まで。
AndroidでQRコードを読むアプリをつくろうと思ったんだけど、まずはカメラのプレビューを表示できた - マサルの備忘録
QRコードを読み書きするには外部ライブラリが必要
言うまでもないですが。
今回はZXing(ゼブラクロッシング)を使います。
ZXing
GitHub - zxing/zxing: Official ZXing ("Zebra Crossing") project home
元はGoogleが開発していたようです。
今ではGithub上にソースが上がってます。
そしてアパッチライセンスで使用可能。ありがたや。
License Questions · zxing/zxing Wiki · GitHub
Apache License v2.0
This project is licensed under the Apache License v2.0. It is not a copyleft license like the GPL; it is relatively generous about what you can do with the code.
Can This Be Used In Commercial Products?
The license does not forbid this. The license terms still apply, of course.
クソコードはこちら
Activity拡張クラスから、実際の処理のクラスを分けました。
package com.masaru.qrcodereader; import android.app.Activity; import android.os.Bundle; import android.view.SurfaceView; public class MainActivity extends Activity { private SurfaceView mSurfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onResume() { super.onResume(); mSurfaceView = (SurfaceView) findViewById(R.id.surface_view); mSurfaceView.getHolder().addCallback(SurfaceViewCallback.create(this)); } }
適当です。
本体はこちら。
package com.masaru.qrcodereader; import java.io.IOException; import java.util.List; import android.app.Activity; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.PreviewCallback; import android.hardware.Camera.Size; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.widget.RelativeLayout; import android.widget.TextView; import com.google.zxing.BinaryBitmap; import com.google.zxing.MultiFormatReader; import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.Reader; import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; public class SurfaceViewCallback { public static SurfaceHolder.Callback create(final Activity activity) { SurfaceHolder.Callback callback = new SurfaceHolder.Callback() { private Camera mCamera; private RelativeLayout mRelativeLayout; private SurfaceView mSurfaceView; private TextView mTextView; @Override public void surfaceDestroyed(SurfaceHolder holder) { mCamera.cancelAutoFocus(); mCamera.release(); mCamera = null; } @Override public void surfaceCreated(SurfaceHolder holder) { mTextView = (TextView) (activity .findViewById(R.id.result_textview)); mSurfaceView = (SurfaceView) (activity .findViewById(R.id.surface_view)); mRelativeLayout = (RelativeLayout) (activity .findViewById(R.id.relative_layout)); mCamera = Camera.open(); setParameters(); setDisplayOrientation(); setSurfaceViewSize(); try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } mSurfaceView.setOnClickListener(mOnClickListener); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mCamera.startPreview(); } private void setParameters() { Camera.Parameters parameters = mCamera.getParameters(); List<Camera.Size> sizes = parameters.getSupportedPreviewSizes(); Camera.Size size = sizes.get(0); parameters.setPreviewSize(size.width, size.height); mCamera.setParameters(parameters); mTextView.setText(size.width + " x " + size.height); } private void setDisplayOrientation() { int degree; switch (activity.getWindowManager().getDefaultDisplay() .getRotation()) { case Surface.ROTATION_0: degree = 0; break; case Surface.ROTATION_90: degree = 90; break; case Surface.ROTATION_180: degree = 180; break; case Surface.ROTATION_270: degree = 270; break; default: degree = 0; } CameraInfo info = new CameraInfo(); Camera.getCameraInfo(0, info); mCamera.setDisplayOrientation((info.orientation - degree + 360) % 360); } private void setSurfaceViewSize() { float relativeWidth = mRelativeLayout.getWidth(); float relativeHeight = mRelativeLayout.getHeight(); float previewWidth; float previewHeight; Size size = mCamera.getParameters().getSupportedPreviewSizes() .get(0); if (relativeHeight > relativeWidth) { previewHeight = size.width; previewWidth = size.height; } else { previewHeight = size.height; previewWidth = size.width; } int surfaceWidth; int surfaceHeight; if (previewHeight / relativeHeight > previewWidth / relativeWidth) { surfaceWidth = (int) (previewWidth * relativeHeight / previewHeight); surfaceHeight = (int) (relativeHeight); } else { surfaceWidth = (int) (relativeWidth); surfaceHeight = (int) (previewHeight * relativeWidth / previewWidth); } RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( surfaceWidth, surfaceHeight); params.addRule(RelativeLayout.CENTER_IN_PARENT); mSurfaceView.setLayoutParams(params); } View.OnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { if (success) { camera.setOneShotPreviewCallback(mPreviewCallback); } } }); } }; Camera.PreviewCallback mPreviewCallback = new PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { TextView mTextView = (TextView) activity .findViewById(R.id.result_textview); int previewWidth = camera.getParameters().getPreviewSize().width; int previewHeight = camera.getParameters().getPreviewSize().height; PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource( data, previewWidth, previewHeight, 0, 0, previewWidth, previewHeight, false); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer( source)); Reader reader = new MultiFormatReader(); Result result = null; try { result = reader.decode(bitmap); String text = result.getText(); mTextView.setText(text); } catch (Exception e) { mTextView.setText("reading..."); } } }; }; return callback; } }
消費電力的に問題がなければ、再帰的にReadするように修正しようかしら。