Android平台下利用zxing实现二维码开发
现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有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和sf 即可。
修改完之后重新清理项目,此时已经没有错误了:
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实现二维码开发的更多相关文章
-
Android平台下利用zxing实现二维码开发
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平 ...
-
【转】Android平台下利用zxing实现二维码开发
http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...
-
android 使用开源库zxing生成二维码,扫描二维码【转】
转自:http://blog.csdn.net/qq_16064871/article/details/52422723 zxing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库 ...
-
Java利用Zxing生成二维码
Zxing是Google提供的关于条码(一维码.二维码)的解析工具,提供了二维码的生成与解析的方法,现在我简单介绍一下使用Java利用Zxing生成与解析二维码 1.二维码的生成 1.1 将Zxing ...
-
利用zxing生成二维码
使用zxing类库可以很容易生成二维码QRCode,主要代码如下: private Bitmap createQRCode(String str,int width,int height) { Bit ...
-
Android笔记之使用ZXing扫描二维码
ZXing发布版下载地址:https://github.com/zxing/zxing/releases 为了能让官方Demo跑起来,先把ZXing核心部分core复制到自己的工程里 还要把andro ...
-
(转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例
Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21我要评论 这篇文章主要介绍了Android项目 ...
-
[置顶]
 xamarin android使用zxing扫描二维码
好久没写了,这片文章篇幅不长,概述一下在xamarin android中用 ZXing.Net.Mobile库扫描二维码读取url的示例.扫码支付,扫码登录,App上各种各样的扫码,好像没个扫码的就有 ...
-
Android高级控件(三)——&#160;使用Google ZXing实现二维码的扫描和生成相关功能体系
Android高级控件(三)-- 使用Google ZXing实现二维码的扫描和生成相关功能体系 摘要 现在的二维码可谓是烂大街了,到处都是二维码,什么都是二维码,扫一扫似乎已经流行到习以为常了,今天 ...
随机推荐
-
DL论文
题目:Accurate Image Super-Resolution Using Very Deep Convolutional Networks(2016CVPR) 摘要:文中提出了一种高精度处理单 ...
-
mac 下php运行bug
如下所说bug在window下没有,在mac下存在. mac下的php报如下错误: fopen("data.json") Error: failed to open stream: ...
-
【三支火把】---常用C语言控制台函数总结(持续更新)
写了这么久的C程序,每次看到输出的结果都是从上往下排列的黑白框,有没有感觉很无聊啊?今天再次总结一个常用的控制台函数,能够帮助你做好一个好看的界面. 1.设置光标位置代码如下: int main(vo ...
-
单实例支撑每天上亿个请求的SSDB
SSDB 是一个 C++ 开发的 NoSQL 存储服务器, 支持 zset, map 数据结构, 可替代 Redis, 特别适合存储集合数据. SSDB 被开发和开源出来后, 已经在生产环境经受了3个 ...
-
ios项目开发汇总
UI界面 iOS和Android 界面设计尺寸规范 http://www.alibuybuy.com/posts/85486.html iPhone app界面设计尺寸规范 http://www. ...
-
vue各种插件汇总
https://blog.csdn.net/wh8_2011/article/details/80497620(copy) Vue是什么? Vue.js(读音 /vjuː/, 类似于 view) 是一 ...
-
js属性对象的propertyIsEnumerable方法
定义 每个对象都有一个propertyIsEnumerable()方法.此方法返回一个布尔值,表明指定的属性是否是可枚举. This method can determine whether the ...
-
TXMLDocument use case (Delphi)
Description This example illustrates the basic operations on an XML document. Code procedure CreateD ...
-
关于Unity中OnGUI()的简单使用
有时候想要输出一些数据到屏幕上方便查看,新建一个UI对象又挺麻烦,用OnGUI()在屏幕上直接绘制UI比较方便. GUI.Label(, , , ), “aaa", style); 这条语句 ...
-
如何解决ORA-12547错误!
最近在布置一个应用程序连接oracle后台数据库,数据库为oracle11g(11.2.0.2).应用程序属于root用户,所以需要以root用户通过系统认证的方式连接数据库.设置了root用户的环境 ...