传感器技术的发展增强了移动设备用户的体验。传感器的种类有很多,常见的有加速计、陀螺仪、测斜仪、方向传感器等。本章将介绍如何利用传感器API获得设备旋转的角速度、线性运动的加速度、设备倾斜角度、设备方向、氛围光、地理位置以及近场通信等,开发人员可以综合使用传感器提供的数据开发出内容丰富的Windows应用商店应用。
1响应运动的传感器
程序员可根据设备的运动来开发应用程序,例如通过晃动设备对屏幕截图,使用加速计捕获设备的晃动可以实现此功能。本节将介绍能测量设备角速度的陀螺仪、可用来测量设备加速运动的加速计以及可获取设备倾斜度的测斜仪,这些传感器由Windows.Devices.Sensors命名空间提供支持。
1.1陀螺仪
陀螺仪传感器能测量设备在空间坐标中围绕X轴、Y轴和Z轴的旋转角速度。陀螺仪可以用于协助GPS定位,导航软件使用陀螺仪显示偏转角速度,若GPS信号丢失,陀螺仪能够快速测出方向和角速度的改变,从而使导航软件及时修改导航路线。下面通过示例介绍陀螺仪API的使用。
新建一个Windows应用商店的空白应用程序项目,并命名为GyrometerExample,在MainPage.xaml文件的Grid元素中添加如下代码。
<Canvas>
<!--基于事件的被动获取-->
<TextBlock Text="基于事件的被动获取" FontSize="18" Canvas.Left="394" Canvas.Top="183"/>
<Button Content="获取" Name="StartButton" Canvas.Left="394" Canvas.Top="212" Click="GetData_Click" Width="62"/>
<Button Content="停止" Name="CloseButton" Canvas.Left="470" Canvas.Top="212" Click="CloseData_Click"/>
<!--文本控件-->
<TextBlock Text="获取陀螺仪读数" FontSize="20" Canvas.Left="394" Canvas.Top="37"></TextBlock>
<TextBlock Text="X轴" FontSize="18" Canvas.Left="394" Canvas.Top="84"/>
<TextBlock Text="为空" FontSize="18" Canvas.Left="470" Canvas.Top="84" Name="ScenarioOutput_X" Width="53"/>
<TextBlock Text="Y轴" FontSize="18" Canvas.Left="394" Canvas.Top="111"/>
<TextBlock Text="为空" FontSize="18" Canvas.Left="470" Canvas.Top="112" Name="ScenarioOutput_Y" Width="53"/>
<TextBlock Text="Z轴" FontSize="18" Canvas.Left="394" Canvas.Top="139"/>
<TextBlock Text="为空" FontSize="18" Canvas.Left="470" Canvas.Top="139" Name="ScenarioOutput_Z" Width="53"/>
<TextBlock Name="InfoText" Width="200" FontSize="18" Canvas.Left="394" Canvas.Top="309"/>
<!--基于计时器的主动获取-->
<TextBlock Text="基于计时器的主动获取" FontSize="18" Canvas.Left="394" Canvas.Top="282"></TextBlock>
<!--触发事件的按钮-->
<Button Content="获取" Name="DispatcherButton" Click="Dispatcher_Click" Canvas.Left="394" Canvas.Top="316"></Button>
<Button Content="停止" Name="CloseDispatcherButton" Click="CloseDispatcher_Click" Canvas.Left="470" Canvas.Top="316"></Button>
</Canvas>
在上面的代码中,放置了多个显示信息的TextBlock控件,其中有3个用于显示陀螺仪数据。然后又添加了4个按钮,并为这4个按钮定义了事件处理方法,其中有两个用于启动陀螺仪数据获取,另外两个按钮则用于停止数据获取。
前台运行效果如图12-1所示。
图12-1 显示陀螺仪读数的界面
布局好前台界面后,接下来介绍如何从陀螺仪传感器获取读数。首先在后台定义几个全局变量并在构造方法中初始化,以便在后续代码中使用。代码如下所示:
private Gyrometer gyrometer;
//期望的时间间隔
private uint desiredReportInterval;
//定义一个计数器
private DispatcherTimer dispatcherTimer;
public MainPage()
{
this.InitializeComponent();
gyrometer = Gyrometer.GetDefault();
if (gyrometer != null)
{
//获取报告间隔
uint minReportInterval = gyrometer.MinimumReportInterval;
desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
//设置按钮为不可交互状态
CloseButton.IsEnabled = false;
CloseDispatcherButton.IsEnabled = false;
//初始化一个计时器
dispatcherTimer = new DispatcherTimer();
}
else
{
InfoText.Text = "未发现陀螺仪设备";
}
}
在上面的代码中,使用Gyrometer.GetDefault方法获得陀螺仪传感器的引用对象gyrometer,如果gyrometer对象为空,在前台界面提示“未发现陀螺仪设备”。
陀螺仪传感器以一个时间间隔来报告读数,这个间隔称为当前报告间隔,此间隔以毫秒为单位,使用gyrometer对象的MinimumReportInterval属性能获得陀螺仪的最小报告间隔,然后将它与16毫秒进行比较,如果大于16毫秒,则将desiredReportInterval变量设置成设备所支持的最短间隔,否则,将它设置为16毫秒。值得注意的是当前报告间隔设置的越长,设备的改变灵敏度就会越大,开发者可根据应用的需要设置报告间隔,选择合适的灵敏度数据,表12-1列出了报告间隔与改变灵敏度的关系对照。
表12-1陀螺仪当前报告间隔与改变灵敏度的关系
当前报告间隔(毫秒) |
改变灵敏度(度/秒) |
1 ms ~ 16 ms |
0.1(度/秒) |
17 ms ~ 32 ms |
0.5(度/秒) |
>= 33 ms |
1.0(度/秒) |
接下来对Name属性为“StartButton”按钮添加GetData_Click事件处理方法,单击这个按钮启动陀螺仪读数捕获。代码如下所示:
private void GetData_Click(object sender, RoutedEventArgs e)
{
if (gyrometer != null)
{
//设置报告间隔
gyrometer.ReportInterval = desiredReportInterval;
//注册方法
gyrometer.ReadingChanged += new TypedEventHandler<Gyrometer, GyrometerReadingChangedEventArgs>(GetChangedData);
//设置按钮的状态
StartButton.IsEnabled = false;
CloseButton.IsEnabled = true;
}
else
{
InfoText.Text = "陀螺仪设备不可用";
}
}
在上面的事件处理程序中,为gyrometer对象的ReadingChanged事件添加了事件处理方法GetChangedData,当陀螺仪传感器驱动程序捕获到设备的旋转动作发生改变时,会触发ReadingChanged事件进而执行GetChangedData方法,下面来看一下GetChangedData方法的实现代码。
在GetChangedData方法中,定义了获取陀螺仪数据并将数据显示到前台界面的过程,具体代码如下所示:
async private void GetChangedData(object sender, GyrometerReadingChangedEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//陀螺仪数据对象
GyrometerReading reading = e.Reading;
//获取设备X轴的角速度
ScenarioOutput_X.Text = String.Format("{0,5:0.00}", reading.AngularVelocityX);
//获取设备Y轴的角速度
ScenarioOutput_Y.Text = String.Format("{0,5:0.00}", reading.AngularVelocityY);
//获取设备Z轴的角速度
ScenarioOutput_Z.Text = String.Format("{0,5:0.00}", reading.AngularVelocityZ);
});
}
在上面的方法中,首先从参数e中获得GyrometerReading类型的对象reading,根据reading对象的AngularVelocityX属性获取设备围绕X轴的角速度,将它显示在ScenarioOutput_X控件中,接着使用reading对象的AngularVelocityY和AngularVelocityZ属性获取设备在Y轴、Z轴的旋转角速度,方法中调用了String.Format方法设置输出数据的格式以增加数据的可读性。为了避免UI线程的阻塞,使用调度器Dispatcher的RunAsync方法异步实现上述过程。
为“停止”按钮添加单击事件处理方法CloseData_Click(),当单击“停止”按钮时停止陀螺仪数据获取,CloseData_Click方法的代码如下所示:
private void CloseData_Click(object sender, RoutedEventArgs e)
{
if (gyrometer != null)
{
//移除事件处理程序
gyrometer.ReadingChanged -= new TypedEventHandler<Gyrometer, GyrometerReadingChangedEventArgs>(GetChangedData);
//设置“开始“按钮为可激活状态,“停止”按钮不可用
StartButton.IsEnabled = true;
CloseButton.IsEnabled = false;
}
else
{
InfoText.Text = "未发现陀螺仪设备";
}
}
在之前介绍获取传感器数据时,通过为gyrometer对象的ReadingChanged事件添加了一个GetChangedData方法来捕获陀螺仪改变时的数据,在上面的CloseData_Click事件方法中只是简单的将此事件处理方法移除掉,这样便可以停止传感器数据的捕获。
在上面的示例中,是通过在设备状态改变事件中添加事件处理方法的方式,被动的实现角速度信息的捕获。除了这种方法外,我们还可以利用计时器,通过每隔一段时间定时读取设备数据的方式,一次或者多次获得传感器数据,下面来看一下这种方法是如何实现的。
向 Name属性为“DispatcherButton”的按钮的单击事件添加事件处理方法Dispatcher_Click(),在该方法中为声明好的全局计时器对象dispatcherTimer的Tick事件添加一个事件处理方法DisplayCurrentReading,接着使用TimeSpan类定义一个时间段,用于设置计时器的时间间隔,然后调用dispatcherTimer对象的Start方法启动计时器。代码如下所示:
private void Dispatcher_Click(object sender, RoutedEventArgs e)
{
//给计时器添加处理程序
dispatcherTimer.Tick += DisplayCurrentReading;
//设置计时器的时间间隔为desiredReportInterval毫秒
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)desiredReportInterval);
//启动时器
dispatcherTimer.Start();
DispatcherButton.IsEnabled = false;
CloseDispatcherButton.IsEnabled = true;
}
接下来定义Tick事件的处理方法DisplayCurrentReading,用来在每次Tick事件触发时,获取陀螺仪数据并将数据显示在前台界面,代码如下所示:
//获取传感器读数
private void DisplayCurrentReading(object sender, object args)
{
//初始化GyrometerReading类的reading属性
GyrometerReading reading = gyrometer.GetCurrentReading();
if (reading != null)
{
//获得读数
ScenarioOutput_X.Text = String.Format("{0,5:0.00}", reading.AngularVelocityX);
ScenarioOutput_Y.Text = String.Format("{0,5:0.00}", reading.AngularVelocityY);
ScenarioOutput_Z.Text = String.Format("{0,5:0.00}", reading.AngularVelocityZ);
}
}
在上面的代码中,使用gyrometer对象的GetCurrentReading方法获得代表当前传感器状态的GyrometerReading类型的对象reading,然后将获取到的数据显示到应用界面。这种方法通过dispatcherTimer对象定期触发Tick事件主动读取传感器状态的数据。下面来看一下停止主动读取传感器数据的方法。
为Name属性为“CloseDispatcherButton”的按钮添加单击事件处理方法CloseDispatcher_Click,在这个方法中调用dispatcherTimer对象的Stop方法关闭计时器,这样便可以停止获取陀螺仪数据,代码如下所示:
//关闭计时器
private void CloseDispatcher_Click(object sender, RoutedEventArgs e)
{
dispatcherTimer.Stop();
DispatcherButton.IsEnabled = true;
CloseDispatcherButton.IsEnabled = false;
}
启动调试,便可以看到运行界面,下面以“基于事件的被动获取”为例介绍操作过程。
单击“获取”按钮后,“获取”按钮将转变为不可用状态,界面上显示当前设备陀螺仪传感器的角速度信息。“停止”按钮变为可激活状态。
单击“停止”按钮,将停止更新角速度信息,此时“停止”按钮变为不可用,“获取”按钮变为可激活。二者相互配合像“开关”一样控制数据的显示。程序运行效果如图12-2所示。
图12-2 获取陀螺仪读数的效果图
上面的示例用两种方法实现了陀螺仪传感器数据的捕获,程序员根据应用的需求选择合适的方法。另外还要注意,在测试本示例时应确保测试设备支持陀螺仪传感器。
注:本文选自自机械工业出版社3月出品的《Windows 8 应用开发权威指南》第12章 传感器