第一个 WP 程序 : 手机条码扫描枪

前言碎语:

前段时间,我第一时间尝试了 Windows 8.1 update1 , 结果把我硬盘搞挂了!

升级之后,硬盘一直是100%,平均响应时间能高达400多毫秒。

我自认为我的配置还不错,AMD的4核推土机,8G金仕顿骇客神条, 1T的希捷单碟,两年多点,以前跑 WIN7 / WIN8 / WIN 8.1 都不带眨眼的,怎么遇到TMD Win 8.1 update 1 就变成渣了呢?基本每次启动都要自动修复一下,开机后在磁盘管理里还提示有危险。用检查工具检查了一下,有二十多个坏道,但是没办法修复!弄的头大!耽误了4天时间,窝了一肚子火!最终还是买了块SSD,完事!

 

之前写了一个用在 Android 上的小应用:

纯屌丝版无线扫描枪

今天在来一个:手机条码扫描枪 Windows phone 8.1 版,不过还没有研究怎么发布到应用商店中。

 

直到现在,我还没有弄明白 Windows Phone项目 和 Windows Phone Silverlight 项目到底有什么区别。

一开始,我新建了一个 Windows Phone 项目,但是死活不能用 VideoBrush 这个东西,折腾了两天,才在 SakeOverflow 上找找到答案:VideoBrush 在 Windows Phone Silverlight 项目中才有。

另外,我安装了 VS 2013 UPDATE 2 RC, 可以新建 Windows Phone 8.1 的应用,在 Windows Phone 项目下可以使用那些 Flip 之类的新控件,但是在 Silverlight 里仍然没有搞懂如何才能使用这些新的东西。

 

和我的第一个WPF桌面程序一样,Windows Phone 程序依然是用 Caliburn.Micro (CM) 框架,不过有些问题, MVVM的MODEL层不过问VIEW上有什么东西,但是我这里需要获取VIEW上的某一块的位置和大小,用数据绑定的方式处理的这一块,非常不理想。

 

用CM框架,必须要按照约定来命名 VIEW 和 MODEL,当然可以更改默认的规则。如下图:

第一个 WP 程序 : 手机条码扫描枪

XXXPageViewModel.cs 对应 XXXPage.xaml 。

一开始我以为MODEL的命名中不需要 Page ,结果无论如何都不能定位到 MODEL上(CM在WINDOWS Phone 上是VIEW优先)

 搞成这个样子(Views / ViewModels),还要改两个地方:Package.appxmanifest (入口点) 和 Properties 下面的 WMAppManifest.xaml (导航页),我这里指向 Views/HomePage.xaml

 

要用 CM,当然还需要用 CM的 Bootstrapper

 1 using BarCodeScanner.ViewModels;

 2 using Caliburn.Micro;

 3 using System;

 4 using System.Collections.Generic;

 5 using System.Diagnostics;

 6 using System.Linq;

 7 using System.Text;

 8 using System.Threading.Tasks;

 9 using System.Windows;

10 

11 namespace BarCodeScanner {

12     public class Bootstrapper : PhoneBootstrapper {

13 

14         private PhoneContainer Container;

15         protected override void StartRuntime() {

16             base.StartRuntime();

17         }

18         protected override void Configure() {

19             base.Configure();

20 

21             this.Container = new PhoneContainer();

22             this.Container.RegisterPhoneServices(this.RootFrame);

23 

24             this.Container.PerRequest<HomePageViewModel>();

25             this.Container.PerRequest<ConnectPageViewModel>();

26             this.Container.PerRequest<ScannerPageViewModel>();

27             this.Container.PerRequest<HelpPageViewModel>();

28         }

29 

30         protected override object GetInstance(Type service, string key) {

31             return this.Container.GetInstance(service, key);

32         }

33 

34         protected override IEnumerable<object> GetAllInstances(Type service) {

35             return this.Container.GetAllInstances(service);

36         }

37 

38         protected override void BuildUp(object instance) {

39             this.Container.BuildUp(instance);

40         }

41 

42         protected override void OnUnhandledException(object sender, System.Windows.ApplicationUnhandledExceptionEventArgs e) {

43             base.OnUnhandledException(sender, e);

44 

45             if (!Debugger.IsAttached)

46                 Debugger.Launch();

47         }

48     }

49 }
Bootstrapper.cs
 1 <Application

 2     x:Class="BarCodeScanner.App"

 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 5     xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

 6     xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

 7     xmlns:local="clr-namespace:BarCodeScanner"

 8     >

 9 

10     <!--应用程序资源-->

11     <Application.Resources>

12         <!--<local:LocalizedStrings x:Key="LocalizedStrings"/>-->

13         <local:Bootstrapper x:Key="Bootstrapper" />

14     </Application.Resources>

15 </Application>
App.xaml

 

界面我弄的也很简单,因为到目前为止,我还没来得及搞懂“中心应用程序”和“透视应用程序”有什么区别,不过,他们有一个共性:挺丑的。我的应用就4个页面,神马中心透视都没有必要。

第一个 WP 程序 : 手机条码扫描枪第一个 WP 程序 : 手机条码扫描枪第一个 WP 程序 : 手机条码扫描枪第一个 WP 程序 : 手机条码扫描枪

定义这个六边形按钮的时候,发现 ControlTemplate 没有 Triggers 这个东西,也不知道如何才能给按钮加上响应触摸的效果,和桌面应用还是有很大差别的。

 

前面说了在 VideoBrush 上耗了两天,是因为我要用它把镜头输出到屏幕上,除了用 VideoBrush 我还不知道有什么其它的办法。

这是不用MVVM的写法:

1 <Rectangle.Fill>

2 <VideoBrush x:Name="vb">

3                     <VideoBrush.RelativeTransform>

4                         <CompositeTransform CenterX=".5" CenterY=".5" Rotation="90" />

5                     </VideoBrush.RelativeTransform>

6                 </VideoBrush>

7             </Rectangle.Fill>

一目发然, CompositeTransform 那句是说,按照屏幕的中心点旋转90度,因为我的手机 (撸妹925)输出的镜头是90度的,不知道其它手机是不是也是这样

但是我用了 CM,必须用MVVM,这些东西都要放到 MODEL 里去设置,

1 this.Camera = new PhotoCamera(CameraType.Primary); this.VBrush = new VideoBrush();
2             this.VBrush.RelativeTransform = new CompositeTransform() {

3                 CenterX = 0.5,

4                 CenterY = 0.5,

5                 Rotation = 90

6             };

7             this.VBrush.SetSource(this.Camera);

然后将这个 VBrush 绑定到 Rectangle的Fill 上。

 

前面说了要在MODEL里取VIEW上的某块区域的起点和大小,是因为镜头下可能有N个条形码,如果不指定区域的话,就不知道目的条码是哪一个。

ZXing (我用的ZXing) 不像 ZBar, ZBar 有个 scanCrop ,通过它可以限定识别区域(参见:http://www.cnblogs.com/xling/archive/2013/03/21/2972640.html)

ZXing 要限定识图区域,只能自行截取了。

PhotoCaramer的 GetPreviewBufferY 、 GetPreviewBufferArgb32 和 GetPreviewBufferYCbCr 获取到的数据都是一个像素一个像素按行罗列的的一维数组(具体里面存的是什么,我也不懂)比如:

1,2,3,4

5,6,7,8

9,10,11,12

一个高3宽4像素的图片,用上面的方法都是返回诸如:1,2,3,4,5,6,7,8,9,10,11,12 这样一个一维数组。

结合到镜头返回的图像是顺时针旋转90度的,我写了这么个方法:

 1        public void Cutout(int x, int y, int w, int h) {

 2 

 3             var routedRaw = new byte[this.luminances.Length];

 4             //1,    2,    3,    4

 5             //5,    6,    7,    8

 6             //9,    10,    11,    12

 7             //13,    14,    15,    16

 8             //17    18    19    20

 9             //21    22    23    24

10 

11             //rh = 6 , rw = 4

12             //21,17,13,9,5,1  22,18,14,10,6,2  23,19,15,11,7,3  24,20,16,12,8,4

13 

14             //RW*(RH-0) - RW + 1     4*(6-0)-4+1 = 21

15             //RW*(RH-1) - RW + 1    4*(6-1)-4+1=17

16             //RW*(RH-2) - RW + 1    4*(6-2)-4+1=13

17             //RW*(RH-3) - RW + 1    4*(6-3)-4+1=9

18             //RW*(RH-4) - RW + 1    4*(6-4)-4+1=5

19             //RW*(RH-5) - RW + 1    4*(6-5)-4+1=1

20 

21             for (var i = 0; i < routedRaw.Length; i++) {

22                 //var add = i / rh + 1;

23                 //var idx = rw * (rh - i % rh) - rw + add;//下标从1开始

24                 routedRaw[i] = this.luminances[this.Width * (this.Height - i % this.Height) - this.Width + i / this.Height];

25             }

26 

27             var t = routedRaw.Skip(y * this.Width).Take(h * this.Width).ToArray();

28 

29             var datas = routedRaw

30                 .Skip(y * this.Height) //y是目标所在的起始列

31                 .Take(h * this.Height)

32                 .Where((r, i) => {

33                     var row = i % this.Height;

34                     return x <= row && (x + w) > row;

35                 }).ToArray();

36 

37             this.luminances = datas;

38             this.Height = h;

39             this.Width = w;

40         }

41     }
View Code

这个方法还有待优化,应该是先截取,再旋转的,我把这个过程弄翻了。

 

另外,在扫描的时候,采用了“循环” 的 对焦/拍照/扫描,而不是手动的去点一下拍照并扫描一下,因为识别率并不高。

 

扫描成功后,本来是想通过 TcpClient 传到电脑端的,但是 Windows Phone SDK里并没有这个东西,不过可以直接用 Socket,用了MSDN上的示例代码,做了一点点简单的修改:

http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202858(v=vs.105).aspx#BKMK_UsingtheSocketClientClassintheApplication

 

 

附上源码(没有开发者账户,而且还不想出钱购买,还没有达到那个高度):

http://files.cnblogs.com/xling/BarCodeScanner.7z

电脑端:

http://files.cnblogs.com/xling/TNS2.7z

 

 

你可能感兴趣的:(手机)