惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
Google Online Security Blog
Google Online Security Blog
月光博客
月光博客
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
G
Google Developers Blog
罗磊的独立博客
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
A
About on SuperTechFans
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
The Register - Security
The Register - Security
U
Unit 42
D
Docker
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
博客园_首页
Google DeepMind News
Google DeepMind News

博客园 - 永不言败

JavaScript 语言基础知识点总结(思维导图) 关于Visual Studio项目系统属性中的宏 MongoDb的相关资料 javascript apply 和call的区别,javascript设计模式 castle 在mvc3如何注入 Castle Automatic Transaction Management python webpy模板介绍 使用 asp.net mvc和 jQuery UI 控件包 webpy demo核心代码 Howto create a System.Linq.Expressions.Expression for Like 实体框架继承关系。很好 淘宝店铺图片数据迁移核心代码 mvc扩展 ASP.NET MVC 2 Templates C#、VB.NET使用HttpWebRequest访问https地址(SSL)的实现 Nhibernate继承 Castle 容器注入学习例子 - 永不言败 javaScript 运算符 && 和 || 的返回值 event.x,event.clientX,event.offsetX区别 - 永不言败
Adding a QR Code Reader in Flex on Android
永不言败 · 2012-01-12 · via 博客园 - 永不言败

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