现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing进行二维码开发。
1.如何将zxing的Android源码导入工程。
在导入zxing的android源码之前,先去官方下载zxing的源码:http://code.google.com/p/zxing/downloads/list。
我这里下载的是1.6版本的,我试验了几个版本,发现2.0以后的版本实际用起来没有1.6和1.7版本的好用,最后选择了1.6版本。
zxing 1.6源码结构如下:
其中android文件夹就是android平台下的官方例子。
在导入之前先要对core文件下的源码进行编译,得到核心包core.jar。
编译方法请参照:http://blog.163.com/yimigao@126/blog/static/671560502011611111116747/
然后就可以导入android平台下的例子了,导入方法如下:
1)打开Eclipse,新建android项目:(注意不要直接把android文件夹拷到workspace下导入,那样会无法导入)
2)导入核心包core.jar。
3)修改strings.xml文件。在导入core.jar之后工程还是会有错误:
出现这种错误可能是由于字符错误导致的,只需要把所有的%s 和%f改成 %1s和s和f 即可。
修改完之后重新清理项目,此时已经没有错误了:
4)运行效果:
2.代码简化
上面代码中,很多功能我们在自己的项目中都用不到,因此需要对其进行简化,至于如何简化这里就不赘述了,网上有很多教程。下面只给出简化后的结果:
如果只进行二维码识别和二维码生成的话,只需要上面包中的文件。其中CaptureActivity.java是拍照取景的类,camera包下面的类主要与照相机相关的类,decoding和encoding是解码和编码相关的类,view是取景框相关的类。
3.将简化的zxing代码嵌入自己的工程。
在自己的工程中嵌入简化的zxing代码即可实现二维码生成和识别功能。
嵌入方法:
1)将上述简化的代码拖到自己工程目录下;
2)将values文件夹和raw文件夹复制自己工程目录下;
3)建立CaptureActivity.java的布局文件capture.xml。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <SurfaceView android:id="@+id/preview_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <com.zxing.view.ViewfinderView android:id="@+id/viewfinder_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerInParent="true" android:gravity="center" android:paddingBottom="10dp" android:paddingTop="10dp" android:text="Scan Barcode" android:textColor="@android:color/white" android:textSize="18sp" android:textStyle="bold" /> <Button android:id="@+id/btn_cancel_scan" android:layout_width="230dp" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:layout_marginBottom="75dp" android:text="Cancel" android:textSize="15sp" android:textStyle="bold" /> </RelativeLayout> </FrameLayout>
3)导入core.jar包
4)修改AndrodMainfest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.qrcode" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="7" /> <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" /> <!-- 自动聚焦权限 --> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </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> </application> </manifest>
5)运行效果:
在这里识别二维码之后是把结果返回到上一个activity显示,所以在MainActivity中调用方法是:
Intent openCameraIntent = new Intent(MainActivity.this,CaptureActivity.class); startActivityForResult(openCameraIntent, 0);
然后获取结果在onActivityResult中进行:
@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); } }
如果要直接在CaptureActivity中弹出识别结果,可以更改CaptureActivity.java的handleDecode方法:
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(); }
上述代码是得到结果之后传给之前调用CaptureActivity的activity,所以如果要改的话只需要把else语句块下面的语句和最后一句finish注释掉,补上自己的代码就可以了。比如可以改成:
public void handleDecode(Result result, Bitmap barcode) { inactivityTimer.onActivity(); playBeepSoundAndVibrate(); final 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);*/ AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create(); resutlDialog.setTitle("扫描结果"); resutlDialog.setMessage(resultString); resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); resutlDialog.show(); } //CaptureActivity.this.finish(); }
我的工程源码下载:http://files.cnblogs.com/dolphin0520/QRCode.rar
3.关于zxing源码的若干问题。
在实际使用过程中发现zxing源码存在一些问题,下面逐一来说一下:
1)竖屏问题。
zxing给出的官方例子是横屏的,但是对于手机实际上竖屏操作更加方便一点,如果要改成竖屏,需要修改以下几个文件:
首先将AndrodMainfest.xml下CaptureActivity的配置改为竖屏:
android:screenOrientation="portrait"
然后需要修改CameraConfigurationManager.java下的setDesiredCameraParameters方法,该方法直接使用
camera.setDisplayOrientation(90);
来让屏幕旋转90度,这个办法在android 2.2版本以后是可行的,但是2.2之前的版本并没有提供这个接口,因此需要对sdk版本进行判断:
if (Integer.parseInt(Build.VERSION.SDK) >=8) setDisplayOrientation(camera, 90); else { if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { parameters.set("orientation", "portrait"); parameters.set("rotation", 90); } if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { parameters.set("orientation", "landscape"); parameters.set("rotation", 90); } }
在修改为竖屏之后发现取景框会发生拉伸,需要进行以下几点修改:
在CameraManager.java中将:
rect.left = rect.left * cameraResolution.x / screenResolution.x; rect.right = rect.right * cameraResolution.x / screenResolution.x; rect.top = rect.top * cameraResolution.y / screenResolution.y; rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
修改为:
rect.left = rect.left * cameraResolution.y / screenResolution.x; rect.right = rect.right * cameraResolution.y / screenResolution.x; rect.top = rect.top * cameraResolution.x / screenResolution.y; rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
然后将DecodeHandler类中的方法decode改为:
private void decode(byte[] data, int width, int height) { long start = System.currentTimeMillis(); Result rawResult = null; //modify here byte[] rotatedData = new byte[data.length]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) rotatedData[x * height + height - y - 1] = data[x + y * width]; } int tmp = width; // Here we are swapping, that's the difference to #11 width = height; height = tmp; PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap); } catch (ReaderException re) { // continue } finally { multiFormatReader.reset(); } if (rawResult != null) { long end = System.currentTimeMillis(); Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString()); Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult); Bundle bundle = new Bundle(); bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap()); message.setData(bundle); //Log.d(TAG, "Sending decode succeeded message..."); message.sendToTarget(); } else { Message message = Message.obtain(activity.getHandler(), R.id.decode_failed); message.sendToTarget(); } }
再将CameraConfigurationManager类中的方法initFromCameraParameters修改为:
void initFromCameraParameters(Camera camera) { Camera.Parameters parameters = camera.getParameters(); previewFormat = parameters.getPreviewFormat(); previewFormatString = parameters.get("preview-format"); Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString); WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); screenResolution = new Point(display.getWidth(), display.getHeight()); Log.d(TAG, "Screen resolution: " + screenResolution); Point screenResolutionForCamera = new Point(); screenResolutionForCamera.x = screenResolution.x; screenResolutionForCamera.y = screenResolution.y; // preview size is always something like 480*320, other 320*480 if (screenResolution.x < screenResolution.y) { screenResolutionForCamera.x = screenResolution.y; screenResolutionForCamera.y = screenResolution.x; } cameraResolution = getCameraResolution(parameters, screenResolutionForCamera); //cameraResolution = getCameraResolution(parameters, screenResolution); Log.d(TAG, "Camera resolution: " + screenResolution); }
最后将PlanarYUVLuminanceSource.java中的renderCroppedGreyscaleBitmap方法改为:
public Bitmap renderCroppedGreyscaleBitmap() { int width = getWidth(); int height = getHeight(); int[] pixels = new int[width * height]; byte[] yuv = yuvData; int inputOffset = top * dataHeight + left; for (int y = 0; y < height; y++) { int outputOffset = y * width; for (int x = 0; x < width; x++) { int grey = yuv[inputOffset + x] & 0xff; pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101); } inputOffset += dataHeight; } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; }
3)取景框的绘制
取景框的绘制可以参照这篇博文:http://www.cnblogs.com/forrestsun/archive/2012/11/06/2757005.html
4)闪光灯的开启和关闭
如果需要开启和关闭闪光灯,在CameraManager.java中添加2个方法:
public void openLight() //打开闪光灯 { if(camera!=null) { Parameters parameter=camera.getParameters(); parameter.setFlashMode(Parameters.FLASH_MODE_TORCH); camera.setParameters(parameter); } } public void closeLight() //关闭闪光灯 { if(camera!=null) { Parameters parameter=camera.getParameters(); parameter.setFlashMode(Parameters.FLASH_MODE_OFF); camera.setParameters(parameter); } }
然后在CaptureActivity中的initCamera方法中打开闪光灯:
CameraManager.get().openDriver(surfaceHolder); CameraManager.get().openLight(); //开闪光灯
在需要的地方关闭闪光灯即可。
5)连续扫描问题
如果在识别二维码成功之后,需要连续多次扫描二维码,只需在扫描完成之后添加代码(在CaptureActivity的dandleDecode方法中添加):
if(handler!=null) //实现连续扫描 handler.restartPreviewAndDecode();
比如:
public void handleDecode(Result result, Bitmap barcode) { inactivityTimer.onActivity(); playBeepSoundAndVibrate(); final 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);*/ AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create(); resutlDialog.setTitle("扫描结果"); resutlDialog.setMessage(resultString); resutlDialog.setButton(AlertDialog.BUTTON_POSITIVE, "打开链接", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); if(!isLegalUrl(resultString)) //如果url不合法 { Toast.makeText(getApplicationContext(), "该链接不是合法的URL", Toast.LENGTH_SHORT).show(); if(handler!=null) //实现连续扫描 handler.restartPreviewAndDecode(); return; } Intent intent = new Intent(); //打开链接 intent.setAction("android.intent.action.VIEW"); Uri content_url = Uri.parse(resultString); intent.setData(content_url); startActivity(intent); } }); resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); if(handler!=null) //实现连续扫描 handler.restartPreviewAndDecode(); } }); resutlDialog.show(); } //CaptureActivity.this.finish(); }
6)识别完成之后的震动。
如果需要取消二维码识别之后的震动,只需要在CaptureActivity类的onResume方法中将vibrate 设置为false即可。
7)修改取景框距屏幕顶部位置
如果需要修改取景框距屏幕顶部位置,只需要修改CameraManager.java的getFramingRect方法,在getFramingRect方法中,
int topOffset = (screenResolution.y - height) / 2
这句是控制取景框到屏幕顶部的距离,若需要减小距屏幕顶部的距离,只需要将分母变大即可。
8)取景框下方提示文字的绘制。
在ViewfinderView.java的 Collection<ResultPoint> currentPossible = possibleResultPoints前面加入以下代码:
TextPaint textPaint = new TextPaint();
textPaint.setARGB(0xFF, 0xFF, 0xFF,0xFF); //字体颜色 textPaint.setTextSize(TEXT_SIZE * density); textPaint.setAntiAlias(true); //设置抗锯齿,否则字迹会很模糊
StaticLayout layout = new StaticLayout(getResources().getString(R.string.scan_text),textPaint, frame.right-frame.left,Alignment.ALIGN_NORMAL,1.0F,0.0F,true); canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density)); //绘制起始位置 layout.draw(canvas);
这里解释一下,textPaint.setAntiAlias(true); 是设置为抗锯齿,否则字体会很模糊。StaticLayout的第一个参数就是要绘制的字符串,第二个是画笔,第三个参数是设置每一行的宽度,即超过该宽度就换行,第四个是对齐方式。
canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density))的参数是绘制字符串的起始位置。
9)乱码问题
在扫描一部分二维码时会出现乱码的情况,是由于编码的问题,可以参照这篇文章:
http://www.2cto.com/kf/201209/152900.html
如果不想编译core包,我的工程源码中的core包是已经改好了的,可以识别GBK二维码,需要的朋友可以直接拿去用。
关于zxing 二维码识别的问题暂时就讨论这么多,有兴趣的朋友可以读源码深入研究一下。
相关推荐
本文将详细介绍如何在Android项目中利用ZXing进行二维码开发,包括源码导入、代码简化以及将其嵌入到自己的工程中。 1. 导入ZXing源码: 首先,你需要从ZXing项目的官方网站...
本文将详细介绍如何在Android环境下利用ZXing进行二维码开发。 首先,ZXing库是Java编写的,支持多种平台,包括Android。在Android上,ZXing通常以一个叫做"Barcode Scanner"的应用形式存在,用户可以下载使用,也...
在Android平台上实现二维码扫描功能,通常我们会利用开源库ZXing(Zebra Crossing)来完成。ZXing是一个强大的、跨平台的条形码和二维码处理库,由Google开发并维护。本篇文章将详细介绍如何在Android应用中集成...
在Android应用中,我们可以利用ZXing库轻松实现二维码的生成和识别。 首先,集成ZXing库到项目中。通常,我们可以通过Gradle依赖来引入ZXing,添加以下代码到`build.gradle`文件的dependencies部分: ```groovy ...
在这里,我们将深入探讨如何利用ZXing库在Android应用中生成二维码名片。 首先,我们需要了解二维码的基本原理。二维码(Quick Response Code)是一种二维条形码,可以存储大量的文本、网址、联系人信息等数据。...
在Android平台上,我们可以利用Zxing的Android端库,即`com.google.zxing:core`和`com.google.zxing:android-integration`,来实现扫描功能。 1. **集成Zxing**: 首先,我们需要在项目的`build.gradle`文件中添加...
本教程将详细介绍如何利用ZXing库在Android平台上实现一个简单的二维码扫描Demo。 首先,我们需要了解ZXing的基本概念。ZXing是Java编写的一个跨平台的库,支持多种一维码(如EAN, UPC等)和二维码(如QR Code, ...
在Android平台上,实现二维码扫描功能通常会借助第三方库,Zxing(又称为“ZXing”,意为“zebra crossing”,斑马线)就是一个非常流行的开源库。本篇将详细介绍如何在Android项目中集成并使用Zxing来实现二维码...
在Android应用中,我们可以利用Zxing的Android端库,集成到自己的项目中,实现实时扫描和解码二维码。 集成Zxing到Android项目,通常有以下步骤: 1. 添加依赖:在项目的build.gradle文件中,添加Zxing的依赖库。...
本教程将聚焦于如何在Android Studio环境中利用Zxing库实现二维码的生成与扫描功能。 首先,我们从集成Zxing库开始。要将Zxing库导入到Android Studio项目中,你可以选择以下两种方式之一: 1. **添加Gradle依赖**...
通过以上步骤,你可以利用Zxing库在Android应用中实现一个仿微信的二维码和条形码扫描功能。在实际开发中,你可能需要根据具体需求进行调整和定制,例如增加错误处理、支持多线程扫描等。这个实例源代码为初学者提供...
本实例将详细讲解如何利用Zxing库在Android应用中实现二维码的识别与生成。 首先,我们来了解Zxing库的核心功能。Zxing是一个跨平台的条码处理库,支持多种格式的条形码和二维码,如QR Code、EAN-13、UPC-A等。在...
在Android平台上,ZXing常用于实现二维码扫描功能。 2. **在Delphi XE10中集成ZXing**: Delphi是一款流行的RAD(快速应用开发)工具,用于创建跨平台的应用程序。要在Delphi中使用ZXing,我们需要将ZXing的Android...
ZXing,全称为“Zebra Crossing”,是一款开源的、跨平台的...通过以上步骤和策略,开发者可以充分利用ZXing库在Android应用中实现高效、稳定的二维码扫描功能。这个"zxing扫描二维码jar"就是实现这一切的关键组件。
在"Android zxing二维码扫描 从相册选择二维码识别"这个主题中,我们将深入探讨如何利用ZXing库实现在Android应用中进行二维码的扫描和识别,以及如何从相册选取图片进行识别。 首先,为了在Android应用中使用ZXing...
通过以上步骤,你已经在VS2017中的Xamarin.Android项目中实现了ZXing二维码扫描功能。这只是一个基本的实现,你可以根据具体需求进行扩展,比如添加错误处理、优化用户体验等。同时,持续关注ZXing.Net.Mobile的更新...
以下是关于ZXing二维码开发以及解决中文乱码问题的详细知识点: 1. **ZXing框架概述**: ZXing是一个多平台的条码读取库,最初由Google开发并维护。它支持多种条码格式,如QR码、Code 128、EAN等。在Android中,...
在Xamarin Android开发中,有时候我们需要集成二维码扫描功能,这时候就可以利用ZXing.Net.Mobile这个库。ZXing(Zebra Crossing)是一个开源项目,提供多种平台上的条形码和二维码读取与生成。在Xamarin环境中,...
本教程将详细介绍如何利用ZXing插件在Android应用中实现二维码扫描,并且支持手动调焦功能。 首先,我们需要在项目中集成ZXing库。可以通过添加Gradle依赖来实现,将以下代码添加到你的`build.gradle`(Module: app...