ZXing.Delphi实战:二维码生成与条形码识别全解析
发布时间:2026/9/2 2:03:44 作者:尧图编辑部 阅读量:1,286

简介这是一份面向Delphi开发者的ZXing条形码识别库资源包适用于Delphi XE至10.3.3 Rio版本解决在桌面或移动应用中集成条形码/二维码扫描与生成功能的场景需求。库支持Code 39、EAN-13、QR Code、Data Matrix等常见码制并提供TZXingScanner等可视化组件便于通过事件驱动完成扫码业务逻辑。压缩包共263个文件约15.5MB其中以92个pas单元文件为核心代码另含fmx跨平台界面、dpr/dproj工程示例、png图标与资源文件等整体结构完整可直接导入Delphi工程参考使用。已有496人学习该资源适合需要在应用中快速实现扫码、条码生成及摄像头配置优化的中高级Delphi开发者开源属性也便于按需裁剪和二次定制。1. 为什么是ZXing.Delphi从Java生态杀到Delphi的条形码利器做Delphi开发的老哥们应该都经历过这种时刻项目做到一半客户突然说要加二维码扫码功能翻遍VCL组件库发现没有现成的去网上搜一圈结果全是C#、Java的教程心态直接崩一半。我第一次在Delphi里做二维码识别就是从这种窘境开始的。当时甚至想过自己写个解码器后来查了查ZXing的源码直接放弃了——光是图像二值化那套算法就够喝一壶的。ZXingZebra Crossing在Java生态里是事实标准的条码解析库支持二维码、Code 128、EAN、UPC、PDF417等几十种格式的生成和识别。Delphi社区的老外把它整个移植了过来就成了ZXing.Delphi这个项目。版本号能跟到3.9.0说明一直有人在维护这点比很多半死不活的Delphi第三方库强太多。这个库能解决什么往大了说Delphi项目里凡是涉及条码生成、条码识别、摄像头扫一扫的需求它都能接。往小了说你只需要拖一个组件、写三五行代码就能在Win32、Android、iOS三大平台上跑通扫码功能不需要你再单独去调底层相机API或者图像处理库。如果你正在做的项目是进销存、仓库管理、会员系统、设备巡检这类Delphi的常见主战场那它基本就是为你准备的。我建议阅读这篇分享的读者至少要对Delphi的组件安装和基本工程结构有概念完全零基础的话先补一补VCL和FireMonkey的基础知识再来不然中间涉及的路径配置和平台差异可能会让你觉得麻烦。这篇东西我尽量按实际使用的顺序来讲从下载安装到生成、识别、再到坑和优化把我踩过的坑都交代清楚。2. 环境准备与安装最容易翻车的一步建议直接照抄2.1 版本对应关系先搞清楚你的Delphi版本从项目压缩包名ZXing.Delphi-3.9.0.zip来看你下载的应该是官方GitHub Release里的Delphi版本。这个库对Delphi版本有一定要求老版本Delphi比如XE7之前大概率编译不过因为源码里用了较新的语言特性。我自己实测过的组合是Delphi 10.4.2和Delphi 11.3编译和运行都没问题。社区反馈说从Delphi 10.3开始基本都能用12.x更不用提。下载下来解压之后目录结构大致是这样的Lib核心源码包含各平台的.pas文件Resources自带的一些测试图片和说明Samples官方示例工程强烈建议先打开跑一遍packages各个平台的分包编译工程.dpk先别急着把整个目录塞进Delphi的Library路径。最稳的做法是先用你当前用的Delphi版本打开packages里对应的dpk文件编译并安装一次。安装成功后会生成设计期组件你在组件面板里就能看到ZXing相关的几个控件。如果不做这一步直接去代码里uses单元虽然也能编译但没法在设计期拖组件后面调试会麻烦不少。提示如果你用的是社区版Delphi或者不打算在设计期用组件可以跳过安装dpk这步直接把Lib路径配置到Tools - Options - Delphi Options - Library里代码里照常uses即可。两种方式我都试过都能正常工作只是前者用起来更顺手。2.2 不同平台的编译注意事项如果你只做Windows端配置好路径就能跑了。但如果目标平台是Android或iOS有几个额外的点必须注意第一Android端需要相机权限在Project Options - Uses Permissions里勾选Camera。同时建议把Internet权限也勾上因为某些ZXing版本在解析带网络数据的二维码时可能用到保险起见别漏。第二Android的Minimum SDK版本建议设置在API 21以上。ZXing.Delphi底层用了不少位图操作和相机回调接口API太低会触发旧版兼容逻辑在某些国产ROM上会出现奇怪的闪退。第三iOS端要做相机权限描述在Project Options - Version Info里给NSCameraUsageDescription填上一句用途说明比如用于扫描条形码不填的话App一启动调用相机就直接崩溃。我把这个版本的平台兼容性整理成了表做选型参考正好目标平台Delphi版本要求必要权限备注Windows10.3及以上无VCL/FMX均可路径配置好即用Android10.3及以上Camera、Internet建议API 21部分ROM需动态权限处理iOS10.3及以上NSCameraUsageDescription需在Info.plist填写相机用途macOS10.3及以上无特别要求调试摄像头设备时注意权限弹窗2.3 先跑官方示例确认环境没问题再动手环境配好以后我的建议是先打开Samples里的Demo跑一遍不要急着写自己的代码。官方示例通常包含一个完整的扫码界面和一个生成二维码的小工具双击运行能扫能生成说明你的环境没问题。这一步能省下后面排查环境问题的巨量时间。我在这一步就吃过亏。当时图快直接新建一个空工程开始写结果代码怎么改都编译不过报错全是F2613 Unit ZXing.BarcodeScanner not found。折腾了两个小时才发现是我把Lib路径配到了下一级子目录——正确路径应该是解压根目录下的Lib我配成了Lib\Core导致一堆依赖单元找不到。所以这一条真的值得认真对待。3. 生成二维码TZXingBarcodeQRCode组件的核心用法3.1 最简生成代码拖一个组件写三行ZXing.Delphi里负责生成条码的组件有好几个最常用的是TZXingBarcodeQRCode。它在组件面板上归类在ZXing分组下拖到窗体上之后不需要设置任何属性就能直接开干。生成二维码的最小代码是这样的uses ZXing.BarcodeFormat, ZXing.BarcodeGenerator; procedure TForm1.Button1Click(Sender: TObject); var Generator: TZXingBarcodeQRCode; Bitmap: TBitmap; begin Generator : TZXingBarcodeQRCode.Create(nil); try Generator.Encoding : TEncoding.UTF8; Generator.QuietZone : 4; Generator.Size : 300; Bitmap : Generator.Generate(Hello, ZXing.Delphi!); try Image1.Bitmap.Assign(Bitmap); finally Bitmap.Free; end; finally Generator.Free; end; end;这段代码生成一张300x300像素的PNG风格位图并显示在TImage上。注意Generate方法返回的是FMX框架的TBitmap所以如果你的项目是VCL而不是FMX需要做一次转换。VCL工程里我一般改用TZXingBarcodeQRCode.GenerateAsTBitmap的泛型版本或者干脆用Image1.Picture.Bitmap.Assign(Bitmap)——具体看你项目的界面框架这个在官方示例里都有对应写法。3.2 生成参数详解Size、QuietZone和Encoding这几个参数看着简单实际对扫码率影响巨大我一个个说。Size决定生成图的分辨率。注意这不是打印尺寸而是像素尺寸。如果你要打印成小标签用打印机建议生成至少512的图不然打印出来线条太细扫码枪会识别困难。如果只是屏幕上显示给手机扫300左右就够了太大了反而增加图片体积。QuietZone是二维码四周的留白宽度单位是模块。二维码规范要求至少4个模块的静区如果设为0很多扫码软件会识别失败特别是把图贴到深色背景上时。我一开始不懂这个生成出来的码经常是微信能扫、支付宝扫不出来后来发现就是QuietZone没设置。这里强烈建议保持默认的4别动。Encoding用来指定二维码内容的字符编码方式。如果你要生成的内容包含中文这里必须用TEncoding.UTF8。默认值是系统字符集在Windows中文系统下可能是GBK手机端扫出来的中文就会变成乱码。这个坑我大概在项目上线后接到客诉才发现的后面会单独展开讲。3.3 生成其他类型的条码Code 128和EAN-13ZXing.Delphi不只能生成二维码还支持一维条码。日常用得比较多的是Code 128仓库物流标签里很常见数据密度高且支持字母数字组合。用法几乎一样换组件就行uses ZXing.BarcodeGenerator; procedure TForm1.GenerateCode128; var Generator: TZXingBarcodeCode128; Bitmap: TBitmap; begin Generator : TZXingBarcodeCode128.Create(nil); try Bitmap : Generator.Generate(ABC-123456); try Image1.Bitmap.Assign(Bitmap); finally Bitmap.Free; end; finally Generator.Free; end; end;组件命名很直观TZXingBarcodeCode128、TZXingBarcodeEAN13、TZXingBarcodeCode39、TZXingBarcodeITF背一下就能记住。每个组件有各自的长度和内容校验规则比如EAN-13只允许12位数字自动补一位校验码不满足时Generate会直接抛异常。生成前先做好输入校验省得运行时崩溃。4. 识别与解码从摄像头扫一扫到图片批量解析4.1 手机扫码的核心TZXingCaptureView的封装逻辑实时扫码需要用TZXingCaptureView组件来处理相机预览流。把这个组件拖到窗体上后它会自动打开摄像头并开始识别画面中的条码你在OnFound事件里拿到结果即可。procedure TForm1.ZXingCaptureView1Found(Sender: TObject; AResult: TReadResult); begin if AResult nil then begin Label1.Text : AResult.Text; // 识别的条码格式 Memo1.Lines.Add(AResult.BarcodeFormat.ToString); // 停止连续识别防止重复触发 ZXingCaptureView1.Active : False; end; end;这里有个细节OnFound事件在识别成功后会反复触发因为相机一直在采集帧。如果你只需要跳转到下一个页面或者把结果显示出来就行但如果你想把扫码结果传给后台接口必须加一次性的防重逻辑比如用一个Boolean标志位或者直接在OnFound里立刻把Active置False。4.2 从图片文件里识别条码TZXingScannerManager的批量处理除了实时扫还有一种常见需求从相册选一张图或者服务器返回的图片里解析条码。这种场景直接用TZXingScannerManager它提供了解码静态图的能力。uses ZXing.ScanManager, ZXing.ReadResult, ZXing.BarcodeFormat; function DecodeBitmap(const ABitmap: TBitmap): string; var ScanMgr: TZXingScanManager; ReadResult: TReadResult; begin Result : ; ScanMgr : TZXingScanManager.Create( TEncoding.UTF8, [TScanBarcodeFormat.QR_CODE, TScanBarcodeFormat.CODE_128] ); try ReadResult : ScanMgr.Scan(ABitmap); if ReadResult nil then Result : ReadResult.Text; finally ScanMgr.Free; end; end;注意TZXingScanManager.Create的第一个参数是字符编码第二个参数是允许的条码格式集合。如果你把格式限定得越窄识别速度和准确率就越高。全格式识别在手机上会出现误识别的情况有时候画面里一个纸箱上的印刷图案都可能被解析成某个Code 128编码这个后面单独讲。4.3 从URL生成和识别一个自动化流程的完整示例我做过一个还算完整的场景程序每隔一段时间扫描共享目录里新生成的二维码图片识别出内容后把数据写入数据库。这个流程用到了生成和识别两端可以给有类似自动化需求的读者做参考。主要逻辑不复杂核心就是批处理时要注意图片的处理顺序和失败重试。我一般这样组织遍历目录下的图片文件过滤出.png、.jpg。对每张图调用TZXingScanManager解码。解码失败则把图片移动到一个Unrecognized文件夹方便人工复核。解码成功后把文件改名加上时间戳写入处理日志。这个流程里最影响整体速度的是图片解码时的加载和色深转换。建议先对图片做一次统一缩放长边压到1024像素以内再交给ZXing解析。图片太大会明显拖慢解码而且对识别率几乎没有帮助。实测下来一张1200万像素的照片直接解码可能要600毫秒以上压缩后只需要150毫秒准确率几乎没有区别。5. 踩坑记录Android混淆、Release模式失效和中文乱码5.1 Android ProGuard混淆Release包扫码闪退的元凶这是我遇到过最诡异的问题之一。Debug包运行得好好的一拍Release包就闪退日志里报的异常还是JNI Global Reference相关。排查了一整天最后发现是ProGuard混淆规则没把ZXing.JNI相关的类排除掉。ZXing.Delphi在Android平台要调用底层的ZXing JAR库通过JNI做图像处理。ProGuard混淆时会把Java层类的名字改掉导致Delphi通过JNI引用时找不到对应类直接崩溃。解决方法是在proguard-rules.pro里加上-keep class com.google.zxing.** { *; }如果你的工程用的是.dex配合AndroidMultiDex那套配置别忘了把com.google.zxing也加到multiDexKeepFile里不然同样会启动崩溃。5.2 Release模式下的相机预览黑屏还有一个坑是相机预览在Release模式下黑屏但扫码逻辑正常。这个问题出在FMX框架的TZXingCaptureView在处理Android生命周期时对FormResume事件的绑定符号在某些编译器优化级别下被裁剪掉了。比较实用的解决方案是在工程的Project Options - Delphi Compiler里把Release配置的Symbol license check和Optimization关闭或者保持默认Debug同款配置。如果还不行可以手动在FormShow事件里调用一次ZXingCaptureView1.Active : False然后再置True强制重置相机状态。这个方法不算根治但实测能解决90%场景下的黑屏问题而且代码侵入性很小。5.3 中文内容扫码后乱码字符编码的统一做中文二维码内容最关键的坑就是字符编码不一致。我在3.2节提过生成端用UTF-8识别端也必须是UTF-8。但事情没这么简单如果你用同一个ZXing.Delphi库生成再识别内部编码是一致的不容易出问题但如果你用别的工具生成比如某些老式收银软件内置的二维码它们通常使用GBK编码你的解码端如果强制指定UTF-8得到的字符串就是乱码。我的处理策略是在TZXingScanManager.Create时不传固定编码而是传入nil让ZXing根据内容里的编码提示自动判断。或者在识别失败时尝试换编码再解一次ReadResult : ScanMgr.Scan(ABitmap); if ReadResult nil then begin ScanMgr.Free; ScanMgr : TZXingScanManager.Create(TEncoding.Default, formats); ReadResult : ScanMgr.Scan(ABitmap); end;这种双保险做法能适配绝大多数应用场景。如果你是做系统集成面对来源不固定的二维码图片强烈建议保留这个回退逻辑。6. 性能调优与进阶把扫码体验做到接近原生6.1 相机分辨率对识别速度的影响如果用默认配置跑TZXingCaptureViewAndroid手机上相机预览分辨率可能是1080p甚至更高。每帧图像做解码时ZXing会先把图像缩放到内部处理尺寸这个缩放过程虽然开销不大但高分辨率带来的解码耗时依然能感觉到。我实测的对比结果如下相机预览分辨率单次解码耗时识别成功率近距离小码备注1280x720约120ms中近距离小码容易糊1920x1080约220ms高综合体验最好3840x2160约500ms高耗电高不推荐所以我建议在TZXingCaptureView上设置PreviewSize相关属性具体属性名查看当前版本API把预览分辨率控制到1080p上下。既保证识别距离足够的码也保持操作流畅。6.2 降低误识别率只开你要的条码格式误识别问题在仓库扫码枪场景里很突出。比如你只贴Code 128条码但周围包装上印刷的EAN码或者二维码也会被识别出来这时候程序如果做了自动处理就会把错误的货号写进数据库。解决办法非常简单就是创建TZXingScanManager时只把你要的格式放进去formats : [TScanBarcodeFormat.CODE_128];如果你做的是扫码枪的桌面端替代方案只启用Code 128能把误识别率压到零。另一个有用的参数是TryHarder在TZXingScanManager里可以设置ScanOptions的TryHarder属性。但在低质量图像上开TryHarder容易把无关图案识别成码。我的经验是在光线条件稳定的固定工位上不开TryHarder识别结果更可靠。6.3 批量解码大数据量的内存管理最后一个优化点针对大批量图片解码场景。ZXing.Delphi的解码过程会在内部创建大量临时对象如果你的程序循环处理几百张图片内存占用会缓慢上升。我之前没有主动释放结果跑完500张图内存涨了快300MB。解决方案是给扫描管理器设置一个复用实例不要每次循环都Create和Free。一个TZXingScanManager实例可以反复调用Scan方法内部会重用缓冲区和对象池。官方文档没写这点但我实际改写为复用一个实例后内存上涨幅度小了80%。同时每处理完一张图片建议调用一下TBitmap.Free并清空引用避免FMX的位图缓冲在后台攒一堆。Image1.Bitmap : nil;这个操作看似无关紧要但在我做的批处理程序里它把整个流程的内存峰值从500MB降到了200MB左右。7. 最后的实操心得三个值得记住的细节用熟了以后我总结出三个细节每次做新项目都会直接按这个来省了很多调试时间。第一个是TReadResult一定也要释放。Scan返回的TReadResult对象是非托管的不释放的话在长时间运行的扫码界面里会造成内存泄露。我在事件处理里用完就Free这点在循环处理图片时尤其重要。第二个是如果做跨平台部署建议在Android上做一次真机测试模拟器里流畅不代表真机没问题。Windows上用桌面摄像头调试会很爽但Android真机的相机API差异大最好是在项目一开始就定好目标平台不要在最后统一调。第三个是如果你要在多台机器上分发扫码程序最好在安装包里附带一份ZXing.Delphi的版本信息文件方便出了问题能快速定位是不是底层库版本不一致。我在维护老项目时就遇到过两台机器扫出来的结果不一样最后发现是Delphi版本不同导致编译时引用了不同版本的ZXing源文件。统一了版本后问题彻底消失。ZXing.Delphi这库整体上属于那种用起来简单、踩坑相对集中的第三方库。把环境配置和编码这两个基础点做好剩下的就是多测几种实际场景下的扫码效果。希望这篇分享能帮你少走点弯路。本文还有配套的精品资源点击获取