伴随着智能手机的高速发展,人们获取信息的方式也变的越来越多样化,一些新技术新概念都逐步走入人们日常生活中。
低功耗蓝牙4.0,近场通信NFC,指纹识别,柔性屏幕等最新技术也将会很快的普及。
由于现在的工作的项目中刚刚添加二维码扫描的功能,所以这里就介绍一下 Windows Phone 开发中 二维码功能的添加。
二维码的原理就不赘述了,直奔主题:
1.windows phone二维码的解析类库。
由于wp中没有直接解析二维码信息的api接口,所以我们要用到第三方类库ZXing.Net
ZXing.Net在开源网站CodePlxe.com可以下载到(http://zxingnet.codeplex.com/)
下载完成后选择对应的ZXing.dll添加到项目引用就可以了。
2.从windows phone摄像头获取二维码信息。
以下以wp8平台为例,界面模仿微信扫描二维码页面效果。
UI部分:(文章下面有图)
a.让Rectangle的填充属性(fill)为VideoBrush, 把VideoBrush的数据源设为一个PhotoCamera实例
b.在ContentPanel容器里面放置4个黑色半透明Rectangle形成周围图像比中间的暗的效果
c.在中间明亮区域边缘放置8个几像素的Rectangle形成图中红色的四角边框
d.用一个高度很窄的Rectangle模仿中间区域绿色的扫描线效果,并设置一个扫动动画
代码实现部分:
a.新建一个Reader实例用来解码得到的图片,reader可以看做是一个解码器,可以设定解码的类型(条形码,二维码等)
b.新建一个继承自LuminanceSource抽象类型的PhotoCameraLuminanceSource的类型
c.原理是通过设置一个计时器间隔几秒获取camera的数据复制到PhotoCameraLuminanceSource,最后让reader来解析
3.从图片中识别二维码信息
a.通过PhotoChooserTask图片选择器选取手机图片库中的图片
b.将图片stream数据复制到一个WriteableBitmap对象里面
c.新建一个继承自LuminanceSource抽象类型的BitmapLuminanceSource的类型类获取图形中的数据
d.使用reader来解析BitmapLuminanceSource数据
这里出现的一个问题是我们熟知的BitmapImage类型并不能直接得到位图的像素数据,
后来我发现WriteableBitmap类型恰好有Pixels属性,那么可否通过新建WriteableBitmap类型然后设置Source来获取位图数据呢,经过实践就是用它了.
其实ZXing库很强大,能解析很多种类型的条码二维码,可以使用MultiFormatReader来读取多种类型的电子码
文章很简单,希望在大家做简单的wp8二维码识别的时候会起到参考作用。
-------------------截图---------------------------
-------------------------------代码-------------------------------------------
ScanPage.xaml代码如下
<phone:PhoneApplicationPage x:Class="Demo.Pages.ScanPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" mc:Ignorable="d" shell:SystemTray.IsVisible="False"> <phone:PhoneApplicationPage.Resources> <Storyboard x:Name="storyScanning" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="recScanning"> <EasingDoubleKeyFrame KeyTime="0" Value="-147"/> <EasingDoubleKeyFrame KeyTime="0:0:3" Value="147"/> <EasingDoubleKeyFrame KeyTime="0:0:6" Value="-147"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </phone:PhoneApplicationPage.Resources> <Grid x:Name="LayoutRoot" Background="#FF212021"> <Grid.RowDefinitions> <RowDefinition Height="66"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid Height="66" VerticalAlignment="Top"> <Grid.Background> <ImageBrush Stretch="Fill" ImageSource="/PayeasePay;component/Resources/nav-top@2x.png"/> </Grid.Background> <TextBlock x:Name="tbkTitle" FontSize="32" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center" Text="扫描二维码"/> <!--<Image x:Name="imgReturn" Visibility="Collapsed" Height="40" Margin="0,0,12,0" Tap="imgReturn_Tap" HorizontalAlignment="Right" Source="/PayeasePay;component/Resources/return.png"/>--> </Grid> <Grid x:Name="ContentPanel" Grid.Row="1" Height="640"> <Rectangle x:Name="rec" Height="640"> <Rectangle.Fill> <VideoBrush x:Name="vBrush"> <VideoBrush.RelativeTransform> <CompositeTransform x:Name="transform" CenterX="0.5" CenterY="0.5"/> </VideoBrush.RelativeTransform> </VideoBrush> </Rectangle.Fill> </Rectangle> <Rectangle HorizontalAlignment="Left" Width="90" Fill="#99000000"/> <Rectangle HorizontalAlignment="Right" Width="90" Fill="#99000000"/> <Rectangle VerticalAlignment="Top" Width="300" Height="170" Fill="#99000000"/> <Rectangle VerticalAlignment="Bottom" Width="300" Height="170" Fill="#99000000"/> <TextBlock x:Name="tbkTip" VerticalAlignment="Bottom" Foreground="White" HorizontalAlignment="Center" Margin="36" Text="提示:请将二维码图案放置在取景框内"/> <Grid Width="306" Height="306"> <Rectangle Width="3" Height="50" Fill="Red" HorizontalAlignment="Left" VerticalAlignment="Top"/> <Rectangle Width="3" Height="50" Fill="Red" HorizontalAlignment="Right" VerticalAlignment="Top"/> <Rectangle Width="3" Height="50" Fill="Red" HorizontalAlignment="Left" VerticalAlignment="Bottom"/> <Rectangle Width="3" Height="50" Fill="Red" HorizontalAlignment="Right" VerticalAlignment="Bottom"/> <Rectangle Width="50" Height="3" Fill="Red" HorizontalAlignment="Left" VerticalAlignment="Top"/> <Rectangle Width="50" Height="3" Fill="Red" HorizontalAlignment="Right" VerticalAlignment="Top"/> <Rectangle Width="50" Height="3" Fill="Red" HorizontalAlignment="Left" VerticalAlignment="Bottom"/> <Rectangle Width="50" Height="3" Fill="Red" HorizontalAlignment="Right" VerticalAlignment="Bottom"/> <Rectangle x:Name="recScanning" Margin="12,3" Height="2" RenderTransformOrigin="0.5,0.5"> <Rectangle.RenderTransform> <CompositeTransform/> </Rectangle.RenderTransform> <Rectangle.Projection> <PlaneProjection/> </Rectangle.Projection> <Rectangle.Fill> <LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5"> <GradientStop Color="#331CF106" Offset="0.15"/> <GradientStop Color="#331CF106" Offset="0.85"/> <GradientStop Color="#FF1CF106" Offset="0.5"/> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> </Grid> </Grid> <TextBlock x:Name="tbkResult" Grid.Row="1" Foreground="White" VerticalAlignment="Top" TextWrapping="Wrap" Margin="12,18" FontSize="25" Text="扫描结果:"/> </Grid> <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBar.Buttons> <shell:ApplicationBarIconButton x:Name="appbarSelectImage" IconUri="/Assets/qr_code.png" Text="从相册选取" IsEnabled="True" Click="appbarSelectImage_Click"/> </shell:ApplicationBar.Buttons> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar> </phone:PhoneApplicationPage>
ScanPage.xaml.cs代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using ZXing; using Microsoft.Devices; using System.Windows.Threading; using ZXing.QrCode; using ZXing.OneD; using ZXing.Common; using Demo.Utils; using Microsoft.Phone.Tasks; using System.Windows.Media.Imaging; using Windows.UI; namespace Demo.Pages { public partial class ScanPage : PhoneApplicationPage { private const string StrScanning = "正在扫描......"; private const string StrScanCompleted = "扫描结果:"; private const string StrNoQRDecode = "图片中可能不包含二维码信息,请重新选取"; private static PhotoCamera photoCamera; private PhotoCameraLuminanceSource _luminance; private readonly DispatcherTimer _timer; private Reader _reader = null;//解码器 public ScanPage() { InitializeComponent(); this._timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) }; this._timer.Tick += _timer_Tick; MainPage.tabControl.Hide(); photoCamera = new PhotoCamera(); photoCamera.Initialized += photoCamera_Initialized; this.vBrush.SetSource(photoCamera); } protected override void OnNavigatedFrom(NavigationEventArgs e) { this._timer.Stop(); try { photoCamera.CancelFocus(); } catch { } if (photoCamera != null) { photoCamera.Dispose(); } base.OnNavigatedFrom(e); } protected override void OnNavigatedTo(NavigationEventArgs e) { string type = ""; if (NavigationContext.QueryString.TryGetValue("type", out type) && type == "qrcode") { this._reader = new QRCodeReader(); } else { this._reader = new EAN13Reader(); } base.OnNavigatedTo(e); } private void photoCamera_Initialized(object sender, CameraOperationCompletedEventArgs e) { if (e.Succeeded) { int width = Convert.ToInt32(photoCamera.PreviewResolution.Width); int height = Convert.ToInt32(photoCamera.PreviewResolution.Height); this._luminance = new PhotoCameraLuminanceSource(width, height); this.Dispatcher.BeginInvoke(() => { this.transform.Rotation = photoCamera.Orientation; this._timer.Start(); this.storyScanning.Begin(); this.tbkResult.Text = StrScanning; }); photoCamera.FlashMode = FlashMode.Off; photoCamera.Focus(); this._isCameraInitialized = true; } } private void _timer_Tick(object sender, EventArgs e) { this.ScanPreviewBuffer(); } private void ScanPreviewBuffer() { try { photoCamera.GetPreviewBufferY(_luminance.PreviewBufferY); var binarizer = new HybridBinarizer(_luminance); var binBitmap = new BinaryBitmap(binarizer); Result result = _reader.decode(binBitmap); if (result != null) { this._timer.Stop(); this.storyScanning.Stop(); Dispatcher.BeginInvoke(() => { this.tbkResult.Text = StrScanCompleted + result.Text; }); } else { photoCamera.Focus(); } } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message); } } private void appbarSelectImage_Click(object sender, EventArgs e) { PhotoChooserTask select = new PhotoChooserTask() { ShowCamera = false, PixelWidth = 480, PixelHeight = 480 }; select.Completed += select_Completed; select.Show(); } private void select_Completed(object sender, PhotoResult e) { if (e.TaskResult == TaskResult.OK) { WriteableBitmap r = new WriteableBitmap(480, 480); r.SetSource(e.ChosenPhoto); //多格式解码 //MultiFormatReader multiFormatReader = new MultiFormatReader(); //// 解码的参数 //Dictionary<DecodeHintType, Object> hint = new Dictionary<DecodeHintType, Object>(2); //// 可以解析的编码类型 //List<BarcodeFormat> decodeFormats = new List<BarcodeFormat>(); //if (decodeFormats == null || decodeFormats.Count == 0) //{ // decodeFormats = new List<BarcodeFormat>(); // 设置可解码格式 // decodeFormats.Add(BarcodeFormat.EAN_13); // decodeFormats.Add(BarcodeFormat.QR_CODE); // decodeFormats.Add(BarcodeFormat.DATA_MATRIX); //} //hint.Add(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); //// 设置继续的字符编码格式为UTF8 //hint.Add(DecodeHintType.CHARACTER_SET, "UTF8"); //// 设置解析配置参数 //multiFormatReader.Hints = hint; BitmapLuminanceSource luminance = new BitmapLuminanceSource(r); HybridBinarizer binarizer = new HybridBinarizer(luminance); BinaryBitmap binBitmap = new BinaryBitmap(binarizer); Result result = _reader.decode(binBitmap); if (result != null) { this.Dispatcher.BeginInvoke(delegate { this.tbkResult.Text = StrScanCompleted + result.Text; }); } else { this.tbkResult.Text = StrNoQRDecode; MessageBox.Show(StrNoQRDecode, "提示", MessageBoxButton.OK); } } } } internal class PhotoCameraLuminanceSource : LuminanceSource { public byte[] PreviewBufferY { get; private set; } public PhotoCameraLuminanceSource(int width, int height) : base(width, height) { PreviewBufferY = new byte[width * height]; } public override byte[] Matrix { get { return (byte[])(Array)PreviewBufferY; } } public override byte[] getRow(int y, byte[] row) { if (row == null || row.Length < Width) { row = new byte[Width]; } for (int i = 0; i < Height; i++) row[i] = (byte)PreviewBufferY[i * Width + y]; return row; } } internal class BitmapLuminanceSource : LuminanceSource { private byte[] _bitmapPixels; public BitmapLuminanceSource(WriteableBitmap bitmap) : base(bitmap.PixelWidth, bitmap.PixelHeight) { int[] data = new int[bitmap.PixelWidth * bitmap.PixelHeight]; this._bitmapPixels = new byte[bitmap.PixelWidth * bitmap.PixelHeight]; data = bitmap.Pixels; for (int i = 0; i < data.Length; i++) { this._bitmapPixels[i] = (byte)data[i]; } } public override byte[] getRow(int y, byte[] row) { Array.Copy(this._bitmapPixels, y * this.Width, row, 0, this.Width); return row; } public override byte[] Matrix { get { return this._bitmapPixels; } } } }