Android ZXing 二维码、条形码扫描介绍
最近公司的Android项目需要用到摄像头做条码或二维码的扫描,Google一下,发现一个开源的 ZXing项目。它提供二维码和条形码的扫描。扫描条形码就是直接读取条形码的内容,扫描二维码是按照自己指定的二维码格式进行编码和解码。
1.什么是二维码和条形码?
二维条形码最早发明于日本,它是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号 信息的,在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象 输入设备或光电扫描设备自动识读以实现信息自动处理。它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能 等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化等特点。
条形码(barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符。常见 的条形码是由反射率相差很大的黑条(简称条)和白条(简称空)排成的平行线图案。条形码可以标出物品的生产国、制造厂家、商品名称、生产日期、图书分类 号、邮件起止地点、类别、日期等许多信息,因而在商品流通、图书管理、邮政管理、银行系统等许多领域都得到广泛的应用。
2.ZXing基本介绍ZXing是一个开源Java类库用于解析多种格式的条形码和二维码.官网:http://code.google.com/p/zxing/
截止目前为止最新版本提供以下编码格式的支持:
- UPC-A and UPC-E
- EAN-8 and EAN-13
- Code 39
- Code 93
- Code 128
- QR Code
- ITF
- Codabar
- RSS-14 (all variants)
- Data Matrix
- PDF 417 ('alpha' quality)
- Aztec ('alpha' quality)
同时官网提供了 Android、cpp、C#、iPhone、j2me、j2se、jruby、objc、rim、symbian等多种应用的类库,具体详情可以参考下载的源码包中。
3.Android端编码演示这里使用的ZXing是经过简化版的,去除了一些一般使用不必要的文件,项目工程和效果截图如下:
其中encoding包是在原基础上加上去的,功能是根据传入的字符串来生成二维码图片,返回一个Bitmap,其余的 包是ZXing项目自带的。另外对扫描界面的布局也进行了修改,官方的扫描界面是横向的,我改成了纵向的,并加入了顶部的Tab和取消按钮 (camera.xml),另外还需要的一些文件是colors.xml、ids.xml,这些都是原本ZXing项目中自带的,最后就是libs下面的 jar包。
接下来看如何使用,首先是把ZXing项目中的一些文件拷贝到我们自己的项目中,然后在Mainifest文件中进行配置权限:
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />
还有就是扫描界面Activity的配置:
<activity android:configChanges="orientation|keyboardHidden" android:name="com.zxing.activity.CaptureActivity" android:screenOrientation="portrait" android:theme="@android :style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden" > </activity>
接下来是我自己项目的布局文件:
<?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="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_gravity="center"/> </LinearLayout>
下面是主Activity的代码,主要功能是打开扫描框、显示扫描结果、根据输入的字符串生成二维码图片:
public class BarCodeTestActivity extends Activity { /** Called when the activity is first created. */ private TextView resultTextView; private EditText qrStrEditText; private ImageView qrImgImageView; @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); } }); 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(); } } }); } @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); } } }
其中生成二维码图片的代码在EncodingHandler.java中:
public final class EncodingHandler { private static final int BLACK = 0xff000000; public static Bitmap createQRCode(String str,int widthAndHeight) throws WriterException { Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); BitMatrix matrix = new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight); int width = matrix.getWidth(); int height = matrix.getHeight(); int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (matrix.get(x, y)) { pixels[y * width + x] = BLACK; } } } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; } }
最后是在哪里对扫描结果进行解码,进入CaptureActivity.java找到下面这个方法便可以对自己对结果进行操作:
/** * 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(); }
4.Java端编码演示
在Java端上实现条形码(EAN-13)和二维码(QRCode) 的编码和解码的示例,以供大家参考,用到了源码中core和javase下面的相关源代码,附件提供自己编译之后的lib包:
zxing.jarzxing-j2se.jar
package michael.zxing; import java.io.File; import java.util.Hashtable; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; /** * [url=home.php?mod=space&uid=618199]@blog[/url] http://sjsky.iteye.com * @author Michael */ public class ZxingEncoderHandler { /** * 编码 * @param contents * @param width * @param height * @param imgPath */ public void encode(String contents, int width, int height, String imgPath) { Hashtable<Object, Object> hints = new Hashtable<Object, Object>(); // 指定纠错等级 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 指定编码格式 hints.put(EncodeHintType.CHARACTER_SET, "GBK"); try { BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints); MatrixToImageWriter .writeToFile(bitMatrix, "png", new File(imgPath)); } catch (Exception e) { e.printStackTrace(); } } /** * @param args */ public static void main(String[] args) { String imgPath = "d:/1.png"; String contents = "Hello Word!"; int width = 300, height = 300; ZxingEncoderHandler handler = new ZxingEncoderHandler(); handler.encode(contents, width, height, imgPath); } }
解码示例:
package michael.zxing; import java.awt.image.BufferedImage; import java.io.File; import java.util.Hashtable; import javax.imageio.ImageIO; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; /** * @blog http://sjsky.iteye.com * @author Michael */ public class ZxingDecoderHandler { /** * @param imgPath * [url=home.php?mod=space&uid=7300]@return[/url] String */ public String decode(String imgPath) { BufferedImage image = null; Result result = null; try { image = ImageIO.read(new File(imgPath)); if (image == null) { System.out.println("the decode image may be not exit."); } LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Hashtable<Object, Object> hints = new Hashtable<Object, Object>(); hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); result = new MultiFormatReader().decode(bitmap, hints); return result.getText(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * @param args */ public static void main(String[] args) { String imgPath = "d:/1.png"; ZxingDecoderHandler handler = new ZxingDecoderHandler(); String decodeContent = handler.decode(imgPath); System.out.println("解码内容如下:"); System.out.println(decodeContent); } }
package michael.zxing; import java.io.File; import com.google.zxing.BarcodeFormat; import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; /** * @blog http://sjsky.iteye.com * @author Michael */ public class ZxingEAN13EncoderHandler { /** * 编码 * @param contents * @param width * @param height * @param imgPath */ public void encode(String contents, int width, int height, String imgPath) { int codeWidth = 3 + // start guard (7 * 6) + // left bars 5 + // middle guard (7 * 6) + // right bars 3; // end guard codeWidth = Math.max(codeWidth, width); try { BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.EAN_13, codeWidth, height, null); MatrixToImageWriter .writeToFile(bitMatrix, "png", new File(imgPath)); } catch (Exception e) { e.printStackTrace(); } } /** * @param args */ public static void main(String[] args) { String imgPath = "d:/2.png"; // 益达无糖口香糖的条形码 String contents = "6923450657713"; int width = 105, height = 50; ZxingEAN13EncoderHandler handler = new ZxingEAN13EncoderHandler(); handler.encode(contents, width, height, imgPath); } }
package michael.zxing; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import com.google.zxing.BinaryBitmap; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; /** * @blog http://sjsky.iteye.com * @author Michael */ public class ZxingEAN13DecoderHandler { /** * @param imgPath * @return String */ public String decode(String imgPath) { BufferedImage image = null; Result result = null; try { image = ImageIO.read(new File(imgPath)); if (image == null) { System.out.println("the decode image may be not exit."); } LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); result = new MultiFormatReader().decode(bitmap, null); return result.getText(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * @param args */ public static void main(String[] args) { String imgPath = "d:/2.png"; ZxingEAN13DecoderHandler handler = new ZxingEAN13DecoderHandler(); String decodeContent = handler.decode(imgPath); System.out.println("解码内容如下:"); System.out.println(decodeContent); } }
来自: http://www.eoeandroid.com/thread-311113-1-1.html