Adding a QR Code Reader in Flex on Android
One of the features I commonly see requested for Android applications is QR code reader or barcode scanner integration. Some native Android applications actually use an external application for QR code/barcode scanning. That, as far as I know, is not an option at the moment in AIR on Android. However, thanks to the open source ZXing library, some direction from Amer Dababneh and a blog post by Michael B, I was able to create basic sample Flex-based mobile application that accessed the camera and read QR codes. This blog post will show you how.
For any smart asses out there (yes, I mean you Ray) - yes, I can still code. It’s just been a while since I had something “blogworthy.” :)
Create Your Project
To begin with, of course, you need to create your new Flex mobile project - I used a standard view-based application. I should note that while I only tested this on Android (via my Nexus One), it’s entirely possible that this code will work as is on iOS or Blackberry Tablet OS via the recent Flash Builder 4.5.1 update.
The important thing to note here is that you will need to enable Camera permissions in your app.xml file. This can be done during the new project wizard or by manually adding the below items to your app.xml within the Android “manifestAdditions” section:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus"/>
In my experience, if you forget to do this or don’t do it properly, the camera will simply fail silently (which can lead to needless debugging frustration - so just don’t forget).
Adding the Camera
Now that we’ve created our new Flex mobile application, we need to lay out our sample application. My sample is very simple with only a home view that contains the camera, a label to display any results and a button to start the process and capture the QR codes. For reference, I borrowed some of the camera placement code from this blog post by Guillaume.
You can see the visual elements of the page laid out below. The SpriteVisualElement is the container that will hold the view to our camera.
<s:VGroup width="100%" horizontalAlign="center" id="vg">
<s:SpriteVisualElement id="sv" width="360" height="400"/>
<s:Label id="lbl" text="" />
<s:Button id="btn" label="Start Camera" width="220" height="93" click="button1_clickHandler(event)"/>
</s:VGroup>
The first time the user clicks the button, it will start the camera. Inside my click handler for the button, I have the following code that first gets an instance of the Camera, attaches it to a VideoDisplay object which is added as a child to our SpriteVisualElement. We also initialize our QRCodeReader class which is from the ZXing library.
if (Camera.isSupported)
{
camera=Camera.getCamera();
camera.setMode(360, 360, 24);
videoDisplay.x = 360;
sv.addChild(videoDisplay);
videoDisplay.attachCamera(camera);
videoDisplay.rotation=90;
qrReader=new QRCodeReader;
btn.label = "Scan Now";
lbl.text = "";
cameraStarted = true;
}
else {
lbl.text = "no camera found";
}
If you ran your app right now (see the full code at the end of this post for all the necessary variables and imports) you would see a view into your device’s camera appear once you click the button to start - that is if our QRCodeReader wasn’t throwing compile errors. Let’s get those fixed.
Modifying the ZXing Library
Obviously, in order to create this project you must download the ZXing library from Google Code. Simply dump the library into your “src” folder where it should be under a com.google.zxing package. As I noted, you will get some complile errors as soon as you try to use the necessary files for QRCodeScanning. Luckily they are very easy to correct as they all involve unnecessary or missing imports.
Here are the changes required to get this working on our project:
- In QRCodeMultiReader add import com.google.zxing.BinaryBitmap; (or just press ctrl+space after the BinaryBitmap on the line with the compile error and have Flash Builder add the import for you);
- In DecoderResult comment out import of mx.controls.List;
- In BufferedImageLuminanceSource comment out import mx.controls.Image;.
Reading the QR Code
Most of the ZXing library usage code to read the QR code was taken verbatim from the blog post by Michael B referenced above. The decodeSnapshot method, which is triggered when the user presses the button to capture a QR code, gets the bitmap data from the Camera and passes it to the decodeBitmapData method. The decodeBitmapData method mostly handles passing this bitmap data to the ZXing library for decoding and then displaying the result in the label. The getAllHints method actually tells the ZXing library what kind of QR/barcode we are trying to decode. Theoretically, if you wanted to decode other standard barcode types, you would just add those to the hashtable. ZXing supports many types of barcode scanning and if you simply add the other types, it should just work - but I emphasize should because I didn’t test it yet.
public function decodeSnapshot():void
{
lbl.text="checking...";
bmd=new BitmapData(300, 300);
bmd.draw(videoDisplay, null, null, null, null, true);
videoDisplay.cacheAsBitmap=true;
videoDisplay.cacheAsBitmapMatrix=new Matrix;
decodeBitmapData(bmd, 300, 300);
bmd.dispose();
bmd=null;
System.gc();
}
public function decodeBitmapData(bmpd:BitmapData, width:int, height:int):void
{
var lsource:BufferedImageLuminanceSource=new BufferedImageLuminanceSource(bmpd);
var bitmap:BinaryBitmap=new BinaryBitmap(new GlobalHistogramBinarizer(lsource));
var ht:HashTable=null;
ht=this.getAllHints();
var res:Result=null;
try {
res=qrReader.decode(bitmap, ht);
}
catch (event:Error) {
}
if (res == null) {
videoDisplay.clear();
lbl.text="nothing decoded";
}
else {
var parsedResult:ParsedResult=ResultParser.parseResult(res);
lbl.text=parsedResult.getDisplayResult();
sv.removeChild(videoDisplay);
cameraStarted = false;
btn.label = "Start Camera";
}
}
public function getAllHints():HashTable
{
var ht:HashTable=new HashTable;
ht.Add(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
return ht;
}
Testing Your QR Code Reading
For testing, I used this QR Code generator for encode both URL and straight text encoded QR codes. In my tests, not only did the QR code reading work well but it ran pretty fast (and my Nexus One is no powerhouse any longer - if it ever was). One thing I did note though is that it can be pretty picky about where the QR code is placed within the image. It should be centered and should not be so close that it takes up the entire camera. I think this is partly because we are actually passing only a 300x300 bitmap out of our 360x360 camera image to be decoded, so this might be fixable. If that isn’t correctable in that manner than I thought it would be wise to add some guides as an overlay to help the user know where to align the QR code.
The other thing I noticed was that when I created a timer event and tested checked the camera periodically using that (as in the linked sample I sourced), I did get some performance issues where the app periodically became unusable (mostly during debugging mode but it seemed indicative of a problem you’d want to potentially eliminate).
That’s it. Not too complicated eh? For reference, below is the finished Flex Mobile View with all the elements you need to get this sample running. Let me know if this helps you or if you manage to expand upon the example (and feel free to share your code if you do).
P.S. I am not posting an FXP because I am unsure if there are any redistribution restrictions on the license for ZXing.
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.BufferedImageLuminanceSource;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.detector.Detector;
import spark.events.ViewNavigatorEvent;
protected var camera:Camera;
private var videoDisplay:Video=new Video(360, 360);
private var qrReader:QRCodeReader;
private var bmd:BitmapData;
private var cameraStarted:Boolean = false;
protected function button1_clickHandler(event:MouseEvent):void
{
if (!cameraStarted) {
if (Camera.isSupported)
{
camera=Camera.getCamera();
camera.setMode(360, 360, 24);
videoDisplay.x = 360;
sv.addChild(videoDisplay);
videoDisplay.attachCamera(camera);
videoDisplay.rotation=90;
qrReader=new QRCodeReader;
btn.label = "Scan Now";
lbl.text = "";
cameraStarted = true;
}
else {
lbl.text = "no camera found";
}
}
else {
decodeSnapshot();
}
}
public function decodeSnapshot():void
{
lbl.text="checking...";
bmd=new BitmapData(300, 300);
bmd.draw(videoDisplay, null, null, null, null, true);
videoDisplay.cacheAsBitmap=true;
videoDisplay.cacheAsBitmapMatrix=new Matrix;
decodeBitmapData(bmd, 300, 300);
bmd.dispose();
bmd=null;
System.gc();
}
public function decodeBitmapData(bmpd:BitmapData, width:int, height:int):void
{
var lsource:BufferedImageLuminanceSource=new BufferedImageLuminanceSource(bmpd);
var bitmap:BinaryBitmap=new BinaryBitmap(new GlobalHistogramBinarizer(lsource));
var ht:HashTable=null;
ht=this.getAllHints();
var res:Result=null;
try {
res=qrReader.decode(bitmap, ht);
}
catch (event:Error) {
}
if (res == null) {
videoDisplay.clear();
lbl.text="nothing decoded";
}
else {
var parsedResult:ParsedResult=ResultParser.parseResult(res);
lbl.text=parsedResult.getDisplayResult();
sv.removeChild(videoDisplay);
cameraStarted = false;
btn.label = "Start Camera";
}
}
public function getAllHints():HashTable
{
var ht:HashTable=new HashTable;
ht.Add(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
return ht;
}
]]>
</fx:Script>
<s:VGroup width="100%" horizontalAlign="center" id="vg">
<s:SpriteVisualElement id="sv" width="360" height="400"/>
<s:Label id="lbl" text="" />
<s:Button id="btn" label="Start Camera" width="220" height="93" click="button1_clickHandler(event)"/>
</s:VGroup>
</s:View>
http://remotesynthesis.com/post.cfm/adding-a-qr-code-reader-in-flex-on-android
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2007-01-12 [转][开源]整合梅花雨日历控件,推出.NET版本
2007-01-12 SQL Server 2005 - 实作CLR存储过程