最近のプロジェクトでは、QR コードの生成と認識を使用しました。これまで経験したことがなかったので、オンラインで検索したところ、この分野、特に Google Zxing による QR コードのカプセル化に関するリソースがたくさんあることがわかりました。ソースコードをダウンロードした後、長押し認識の機能をデモに追加しただけです。インターネット上には長押し認識のデモもあります。 、それらの多くはダウンロード後に実行できません。 そこで、それを要約して以下のデモに追加しました。
このデモのメインクラスを紹介しましょう
public class BarCodeTestActivity extends Activity { private TextView resultTextView; private EditText qrStrEditText; private ImageView qrImgImageView; private String time; private File file = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); resultTextView = (TextView) this.findViewById(R.id.tv_scan_result); qrStrEditText = (EditText) this.findViewById(R.id.et_qr_string); qrImgImageView = (ImageView) this.findViewById(R.id.iv_qr_image); Button scanBarCodeButton = (Button) this.findViewById(R.id.btn_scan_barcode); scanBarCodeButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //打开扫描界面扫描条形码或二维码 Intent openCameraIntent = new Intent(BarCodeTestActivity.this,CaptureActivity.class); startActivityForResult(openCameraIntent, 0); } }); qrImgImageView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { // 长按识别二维码 saveCurrentImage(); return true; } }); Button generateQRCodeButton = (Button) this.findViewById(R.id.btn_add_qrcode); generateQRCodeButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { String contentString = qrStrEditText.getText().toString(); if (!contentString.equals("")) { //根据字符串生成二维码图片并显示在界面上,第二个参数为图片的大小(350*350) Bitmap qrCodeBitmap = EncodingHandler.createQRCode(contentString, 350); qrImgImageView.setImageBitmap(qrCodeBitmap); }else { //提示文本不能是空的 Toast.makeText(BarCodeTestActivity.this, "Text can not be empty", Toast.LENGTH_SHORT).show(); } } catch (WriterException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } //这种方法状态栏是空白,显示不了状态栏的信息 private void saveCurrentImage() { //获取当前屏幕的大小 int width = getWindow().getDecorView().getRootView().getWidth(); int height = getWindow().getDecorView().getRootView().getHeight(); //生成相同大小的图片 Bitmap temBitmap = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 ); //找到当前页面的根布局 View view = getWindow().getDecorView().getRootView(); //设置缓存 view.setDrawingCacheEnabled(true); view.buildDrawingCache(); //从缓存中获取当前屏幕的图片,创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收 temBitmap = view.getDrawingCache(); SimpleDateFormat df = new SimpleDateFormat("yyyymmddhhmmss"); time = df.format(new Date()); if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){ file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen",time + ".png"); if(!file.exists()){ file.getParentFile().mkdirs(); try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } FileOutputStream fos = null; try { fos = new FileOutputStream(file); temBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen/" + time + ".png"; final Result result = parseQRcodeBitmap(path); runOnUiThread(new Runnable() { public void run() { if(null!=result){ resultTextView.setText(result.toString()); }else{ Toast.makeText(BarCodeTestActivity.this, "无法识别", Toast.LENGTH_LONG).show(); } } }); } }).start(); //禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图 view.setDrawingCacheEnabled(false); } } //解析二维码图片,返回结果封装在Result对象中 private com.google.zxing.Result parseQRcodeBitmap(String bitmapPath){ //解析转换类型UTF-8 Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>(); hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); //获取到待解析的图片 BitmapFactory.Options options = new BitmapFactory.Options(); //如果我们把inJustDecodeBounds设为true,那么BitmapFactory.decodeFile(String path, Options opt) //并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你 options.inJustDecodeBounds = true; //此时的bitmap是null,这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了 Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath,options); //我们现在想取出来的图片的边长(二维码图片是正方形的)设置为400像素 /** options.outHeight = 400; options.outWidth = 400; options.inJustDecodeBounds = false; bitmap = BitmapFactory.decodeFile(bitmapPath, options); */ //以上这种做法,虽然把bitmap限定到了我们要的大小,但是并没有节约内存,如果要节约内存,我们还需要使用inSimpleSize这个属性 options.inSampleSize = options.outHeight / 400; if(options.inSampleSize <= 0){ options.inSampleSize = 1; //防止其值小于或等于0 } /** * 辅助节约内存设置 * * options.inPreferredConfig = Bitmap.Config.ARGB_4444; // 默认是Bitmap.Config.ARGB_8888 * options.inPurgeable = true; * options.inInputShareable = true; */ options.inJustDecodeBounds = false; bitmap = BitmapFactory.decodeFile(bitmapPath, options); //新建一个RGBLuminanceSource对象,将bitmap图片传给此对象 RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap); //将图片转换成二进制图片 BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource)); //初始化解析对象 QRCodeReader reader = new QRCodeReader(); //开始解析 Result result = null; try { result = reader.decode(binaryBitmap, hints); } catch (Exception e) { // TODO: handle exception } return result; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //处理扫描结果(在界面上显示) if (resultCode == RESULT_OK) { Bundle bundle = data.getExtras(); String scanResult = bundle.getString("result"); resultTextView.setText(scanResult); } } }
次に長押ししてQRコードを認識し、RGBLuminanceSourceクラスを呼び出します
public class RGBLuminanceSource extends LuminanceSource { private byte bitmapPixels[]; protected RGBLuminanceSource(Bitmap bitmap) { super(bitmap.getWidth(), bitmap.getHeight()); // 首先,要取得该图片的像素数组内容 int[] data = new int[bitmap.getWidth() * bitmap.getHeight()]; this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()]; bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight()); // 将int数组转换为byte数组,也就是取像素值中蓝色值部分作为辨析内容 for (int i = 0; i < data.length; i++) { this.bitmapPixels[i] = (byte) data[i]; } } @Override public byte[] getMatrix() { // 返回我们生成好的像素数据 return bitmapPixels; } @Override public byte[] getRow(int y, byte[] row) { // 这里要得到指定行的像素数据 System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth()); return row; } }
カメラはQRコードを認識し、CaptureActivityクラスを呼び出します
public class CaptureActivity extends Activity implements Callback { private CaptureActivityHandler handler; private ViewfinderView viewfinderView; private boolean hasSurface; private Vector<BarcodeFormat> decodeFormats; private String characterSet; private InactivityTimer inactivityTimer; private MediaPlayer mediaPlayer; private boolean playBeep; private static final float BEEP_VOLUME = 0.10f; private boolean vibrate; private Button cancelScanButton; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera); CameraManager.init(getApplication()); viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view); cancelScanButton = (Button) this.findViewById(R.id.btn_cancel_scan); hasSurface = false; inactivityTimer = new InactivityTimer(this); } @Override protected void onResume() { super.onResume(); SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view); SurfaceHolder surfaceHolder = surfaceView.getHolder(); if (hasSurface) { initCamera(surfaceHolder); } else { surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } decodeFormats = null; characterSet = null; playBeep = true; AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE); if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { playBeep = false; } initBeepSound(); vibrate = true; //quit the scan view cancelScanButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { CaptureActivity.this.finish(); } }); } @Override protected void onPause() { super.onPause(); if (handler != null) { handler.quitSynchronously(); handler = null; } CameraManager.get().closeDriver(); } @Override protected void onDestroy() { inactivityTimer.shutdown(); super.onDestroy(); } /** * Handler scan result * @param result * @param barcode */ public void handleDecode(Result result, Bitmap barcode) { inactivityTimer.onActivity(); playBeepSoundAndVibrate(); String resultString = result.getText(); //FIXME if (resultString.equals("")) { //扫描失败 Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show(); }else { // System.out.println("Result:"+resultString); Intent resultIntent = new Intent(); Bundle bundle = new Bundle(); bundle.putString("result", resultString); resultIntent.putExtras(bundle); this.setResult(RESULT_OK, resultIntent); } CaptureActivity.this.finish(); } private void initCamera(SurfaceHolder surfaceHolder) { try { CameraManager.get().openDriver(surfaceHolder); } catch (IOException ioe) { return; } catch (RuntimeException e) { return; } if (handler == null) { handler = new CaptureActivityHandler(this, decodeFormats, characterSet); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { if (!hasSurface) { hasSurface = true; initCamera(holder); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { hasSurface = false; } public ViewfinderView getViewfinderView() { return viewfinderView; } public Handler getHandler() { return handler; } public void drawViewfinder() { viewfinderView.drawViewfinder(); } private void initBeepSound() { if (playBeep && mediaPlayer == null) { // The volume on STREAM_SYSTEM is not adjustable, and users found it // too loud, // so we now play on the music stream. setVolumeControlStream(AudioManager.STREAM_MUSIC); mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnCompletionListener(beepListener); AssetFileDescriptor file = getResources().openRawResourceFd( R.raw.beep); try { mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); file.close(); mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); mediaPlayer.prepare(); } catch (IOException e) { mediaPlayer = null; } } } private static final long VIBRATE_DURATION = 200L; private void playBeepSoundAndVibrate() { if (playBeep && mediaPlayer != null) { mediaPlayer.start(); } if (vibrate) { Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); vibrator.vibrate(VIBRATE_DURATION); } } /** * When the beep has finished playing, rewind to queue up another one. */ private final OnCompletionListener beepListener = new OnCompletionListener() { public void onCompletion(MediaPlayer mediaPlayer) { mediaPlayer.seekTo(0); } }; }
以下はメインレイアウトのmianファイルです
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:color/white" android:orientation="vertical" > <Button android:id="@+id/btn_scan_barcode" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="Open camera" /> <LinearLayout android:orientation="horizontal" android:layout_marginTop="10dp" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textSize="18sp" android:text="Scan result:" /> <TextView android:id="@+id/tv_scan_result" android:layout_width="fill_parent" android:textSize="18sp" android:textColor="@android:color/black" android:layout_height="wrap_content" /> </LinearLayout> <EditText android:id="@+id/et_qr_string" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:hint="Input the text"/> <Button android:id="@+id/btn_add_qrcode" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Generate QRcode" /> <ImageView android:id="@+id/iv_qr_image" android:layout_width="250dp" android:layout_height="250dp" android:scaleType="fitXY" android:layout_marginTop="10dp" android:layout_gravity="center"/> </LinearLayout>
詳細については、デモをダウンロードしてご自身の目で確認してください。このデモでは、垂直デコード中に QR コードが引き伸ばされる問題が解決されています。
しかし、QRコードのスキャン枠が拡大された後、スキャン感度が低下するという問題が発生しました。知っている友人が私にアドバイスを与えることを願っています
上記がこの記事の全内容です。皆様の学習に役立てていただければ幸いです。また、PHP 中国語 Web サイトをサポートしていただければ幸いです。
Google Zxing に基づく Android の QR コード生成の実装に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。