加载中

Introduction

While theAccelerometer,Compass,Gyrometer, andMotionsensors each return different types of data, they each implement the same pattern for reporting their data. Over the next several pages, you’ll learn techniques that are useful for reading data from any of the sensors. We show you these techniques as you build the foundation of the sample application. The classes and interfaces that comprise the Sensor API are found in theWindows.Devices.Sensorsnamespace.

Note In the Windows Phone 8 SDK there are two separate Sensor APIs. The APIs found inMicrosoft.Devices.Sensorswere originally part of the Windows Phone 7 SDK and have been brought forward to Windows Phone 8. The second Sensor API, found in theWindows.Devices.Sensorscomes from the Windows 8 Runtime that the Windows Phone shares with the Windows 8 operating system. If you intend to share code between Windows Phone and Windows 8 applications, you should consider using the Windows 8 Sensor API. The Windows Phone 7 Sensors API is not covered in our book.

介绍

尽管加速计,指南针,陀螺仪以及运动传感器等不同的传感器返回不同类型的数据,但它们使用相同的模式来报告(反馈)它们各自返回的数据。在接下来的几页中,你将学会如何读取不同传感器返回数据的技术我们介绍了一些方法,你可依此来构建一些基本的示例应用程序。传感器(Sensors)API的类和接口组成可以在 Windows.Devices.Sensors 命名空间中找到。

你会注意到,在Windows Phone 8 SDK中有两个独立的传感器API。Microsoft.Devices.Sensors 中的API 曾是Windows Phone 7 SDK的一部分,并引入到了Windows Phone 8。另一个传感器的API来自Windows 8 Runtime ,是Windows Phone与Windows 8操作系统共享的。如果你打算的在Windows Phone和Windows 8的应用程序之间共享代码,那么应该考虑使用 Windows 8传感器的API。 Windows Phone 7的传感器API在我们的书中并不涉及。

Even though theAccelerometer,Compass,Gyrometer,InclinometerandOrientationSensordo not share a common base class, the all have a number of identical properties, methods, and events. These common members are described in table 1.

Table 1 Common sensor class members

Member Type Description 
GetCurrentReading Method Returns a read-only object containing the currently available sensor data.
GetDefault Method A static factory method that returns a sensor instance.
MinimumReportInterval Property A read-only value specifying the smallest value that can be passed set in theReportIntervalproperty.
ReadingChanged Event An event raised when ever the current reading changes.
ReportInterval Property Specifies how often the sensor reads new data. The data returned by theGetCurrentReadingwill only change once every time interval.

An application obtains the current sensor reading by calling theGetCurrentReadingmethod. Alternatively, an application can subscribe to theReadingChangedevent to receive a sensor reading only when the sensor has new data. TheGetCurrentReadingmethod may be called even when the sensor isn’t ready, but the value returned may be null.

Note If theID_CAP_SENSORScapability isn’t present in the WMAppManifest.xml file, calls toGetDefaultfor any will result in aUnauthorizedAccessException. 

虽然加速计,罗盘,陀螺仪,重力感应传感器并不共享一个共同的基类,但它们都有一些相同的属性,方法和事件。这些共同的成员描述于表1。

表1 常见的传感器类成员

成员
类型
描述
GetCurrentReading 方法
返回一个只读的对象包含目前可用的传感器数据。
GetDefault 方法
返回一个传感器实例的静态构造方法
MinimumReportInterval 属性
只读的值,属性值指定的最小值,可以通过在ReportIntervalp属性来设置。
ReadingChanged 事件
引发的事件,由当前读取数据发生变化触发。
ReportInterval 属性
设置传感器读取新的数据的频率。每次设置时间间隔时GetCurrentReading 只改变一次并返回数据。


应用程序通过调用GetCurrentReading方法来读取当前传感器的返回。另外,应用程序可以触发ReadingChanged事件接收传感器的数据,只要传感器有新的数据GetCurrentReading方法可以在传感器没有准备好时调用,那么返回的值就为空。

注意:如果 ID_CAP_SENSORS 使用未在WMAppManifest.xml文件中配置,GetDefault for的任何调用将可导致额外的非法访问(UnauthorizedAccessException)。

Each of the sensor classes defines a static method namedGetDefault. TheGetDefaultmethod allows a developer to determine whether the sensor hardware is installed on a particular device and whether the sensor is available to the application. If the device does not have a particular sensor installed and available, the GetDefault method for the missing sensor returns null.

The Sensor API handles fast application switching on its own. Developers don’t need to unhook the sensors when the application is switched from the foreground. Unlike the camera, sensors automatically resume and don’t provide an explicit restore method. When the application is resumed, the sensors and events are reconnected and data starts to flow again. Before you learn how to work with the data flowing from the sensors, you need to understand how the sensors report data in three dimensions.

每个传感器类中的都定义一个静态方法GetDefault。 GetDefault方法允许开发人员来决定传感器的硬件是否安装在特定的设备上以及是否对该应用程序可用。如果该传感器没有安装到该设备或对应用程序不可用,GetDefault方法由于缺少传感器而返回空。

传感器的API 来处理自身应用程序的快速切换。当应用程序从前台切换过来之后,开发人员并不需要 unhook 传感器,。不像摄像头,传感器会自动恢复,并没有提供一个明确的恢复方法。当应用程序恢复,传感器和事件重新建立连接并开始记录数据流。在你学会如何处理来自传感器的数据流之前,你需要了解如何传感器如何返回三维的报告数据。

Data in three dimensions

Each of the sensors reports data relative to the x, y, z coordinate system defined by the Windows Phone device. The device’s coordinate system is fixed to the device, and moves as the phone moves. The x axis extends out the sides of the device, with positive x pointing to the right side of the device, and negative x pointing to the left side of the device. The y axis runs through the top and bottom of the device, with positive y pointing toward the top. The z axis runs from back to front, with positive z pointing out the front of the device. Figure 1 shows the x, y, and z axes from three different views of a phone.

Figure 1 The x, y, z coordinate system as defined by a Windows Phone

The coordinate system used by the sensors doesn’t necessarily match the coordinate system used by other APIs. One example is the coordinate system used by XAML. In portrait mode XAML, the y axis points in the opposite direction, with positive y pointing out the bottom of the device. Now that you understand the coordinate system used by the sensors, let’s take a closer look at reading data from the sensors.

返回数据的坐标系

每个传感器报告的数据与 x、 y、 z 坐标系统由 Windows Phone 设备定义。该设备的坐标系统固定到设备,并随手机的移动而移动。X轴是指设备的左 --> 右,+X 指设备的右侧。Y 轴指的设备的底部到顶部,+ y 指向顶部。Z 轴指设备的后面-->前面, + z 指设备的前面。图 1 所示即为手机的x,y,z轴的三视图。

图1 Windows Phone的x,y,z坐标系统

由传感器使用的坐标系统,并不一定相匹配与其他 API 所使用的坐标系。其中一个例子是由XAML使用的坐标系统。纵向模式中的XAML,+y轴指出移动设备的底部,-y轴指向相反的方向现在你应该明白传感器所使用坐标系统了,接下来将详细见绍如何从传感器获取数据。

Reading data with events

Each of the sensors supports an event-driven interaction model with theReadingChangedevent. TheReadingChangedevent sends an event args class instance to an event handler, where the type of event args class varies with each sensor. TheAccelerometersends anAccelerometerReadingChangedEventArgs, the Compass sends aCompassReadingChangedEventArgs, etc.

TheReadingChangedevent handler is called on a background thread. If the event handler updates the user interface, the update logic must be dispatched to the UI thread. The following code snippet shows an example that handles theReadingChangedevent from theGyrometersensor:

void sensor_ReadingChanged(object sender,
    GryometerReadingChangedEventArgs e)
{
    GryometerReading reading = e. Reading;
    Dispatcher.BeginInvoke(() =>
    {
        // add logic here to update the UI with data from the reading
        ...
    }
}

The Sensors sample application we build in chapter 10 of our book does not use theReadingChangedevent. Instead the sample application polls for data using theGetCurrentReadingmethod.

通过事件来读取数据

传感器通过 ReadingChanged 事件来建立一个事件驱动的交互模型。ReadingChanged 事件将一个事件参数类的实例发送到事件处理程序,事件参数类的类型随传感器不同而不同。 加速度计将传递一个传递加速度计ReadingChanged事件参数,指南针将传递一个指南针ReadingChanged的事件参数等。

ReadingChanged 事件处理程序通过后台线程来调用。如果事件处理程序更新用户界面,更新逻辑脚背被调度到UI线程。下面的代码片段显示例子,处理从来自陀螺仪传感器的 ReadingChanged事件:

void sensor_ReadingChanged(object sender,
    GryometerReadingChangedEventArgs e)
{
    GryometerReading reading = e. Reading;
    Dispatcher.BeginInvoke(() =>
    {
        // add logic here to update the UI with data from the reading
        ...
    }
}

书的第10章中我们构建的传感器示例应用程序,没有使用ReadingChanged事件的。 取而代之,示例应用程序样本使用GetCurrentReading 方法来获得数据。

Polling for data

An application doesn’t need to wait for the sensor to raise an event to ask for data. Each sensor exposes data through theGetCurrentReadingmethod. TheGetCurrentReadingmethod can be called whenever the application data determines it needs new data. For example, the reading might be initiated from a button click, a timer tick event, or a background worker:

if (compassSensor != null)
{
    CompassReading reading = compassSensor.GetCurrentReading();
    if (reading != null)
    {
        // add logic here to use the data from the reading
        ...
    }
}



You’ll read sensor data from a timer tick event in the sample application. Before we can show you the sensors in action, you need to create a new project and prepare the application to display sensor data.

获取数据

应用程序不需要等待传感器来触发一个事件来要求传送数据 。 传感器可以通过调用 GetCurrentReading 来输出数据 。 一旦应用程序发出它需要新的数据的讯息,便 通过调用 GetCurrentReading 方法来获取数据。 例如 ,数据的读取可以通过按钮 单击,计时器滴答事件或后台辅助线程来启动 :

if (compassSensor != null)
{
    CompassReading reading = compassSensor.GetCurrentReading();
    if (reading != null)
    {
        // add logic here to use the data from the reading
        ...
    }
}

 你将通过示例应用程序的计时器滴答(timer tick)事件中来读取传感器数据 。 在实际演示之前,你需要创建一个新的项目并开发一个应用能够显示传感器的数据。

Creating the sample application

Open Visual Studio and create a new Windows Phone App named Sensors. The sample application will read values from theAccelerometer, theCompass, theGyrometer, theInclinometerand theOrientationSensor. The sample application, shown in figure 2, displays a set of colored bars for data from theAccelerometer, theGyrometer, and theInclinometer. Each set of bars displays sensor readings for the x, y, and z coordinates. At the bottom of the screen, the application displays a legend and informational messages about the sensors, as well as readings from theCompassandOrientationSensor.

Figure 2 The Sensors sample application displays bars representing the x, y, and z values reported by the Accelerometer, Gyrometer, and Inclinometer

When a sensor’s value is positive, a bar will be drawn to scale above the effective zero line. A negative sensor value results in a bar drawn below the zero line. Since the range of possible values differs between each sensor, the height of the bar is transformed from the sensors value into a pixel height using a scaling factor. First, you’ll create a reusable control to display the positive and negative bars.

创建示例应用程序

打开 Visual Studio 并创建新的 Windows Phone 应用程序命名为传感器。示例应用程序将从重力、 指南针、 轴摇摆、 测斜仪和方向传感器中读取值。图2中所示的示例应用程序显示一组的彩条为重力、 轴摇摆和 测斜仪中的数据。每个组的酒吧显示传感器读数,x、 y 和 z 坐标。在屏幕的底部,该应用程序显示一个legend和关于传感器的提示信息,即来自方向传感器的读数。

图 2 传感器示例应用程序 (控件项代表 的x、 y 和 z 是由加速度计、 轴摇摆或测斜仪所返回的值)

当传感器返回正值时,将在零线之上绘制一个可缩放标签栏。返回负值是会在零线之下绘制一个标签栏。因为不同传感器拥有不同的取值范围,所以从传感器返回值到转化成以像素表示的标签栏高度的过程中应使用一个可伸缩的系数。当然,首先你得会创建一个可重用的控件栏来表示正数和负数。

Creating a reusable Bar control

To simplify the sample application, you’ll build a reusable control that allows you to set a scale factor and a sensor value. When the scale or value properties change, the control should draw the appropriate positive or negative bar, and display the value with a label. The bar control will be implemented using the Windows Phone User Control item template, accessed via the Project >> Add New Item menu. Name the new item Bar. The XAML markup for the new control is shown in the next listing.

Listing 1 Markup for the Bar control

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />                         #1
        <RowDefinition Height="1*" />                         #1
    </Grid.RowDefinitions>
    <Rectangle x:Name="positiveBar" VerticalAlignment="Bottom" />
    <Rectangle x:Name="negativeBar" Grid.Row="1" VerticalAlignment="Top" />
    <TextBlock x:Name="label" VerticalAlignment="Center"        #2
        Grid.RowSpan="2" Text="0" TextAlignment="Center" />     #2
</Grid>
#1 Divide control into two rows
#2 Center label 

The grid is divided into two halves #1 with each half containing a Rectangle. The first Rectangle displays positive values and the other represents negative values. A label is placed right in the middle #2 to show the bar’s value. Figure 10.3 demonstrates what the control will look like for a Bar with a scale value set to 2.0 and a current value set to −1.0.

Figure 3 A Bar control with a scale value of 2.0 and a current value of -1.0.

创建可重用的控件

为了简化应用程序示例,您就会生成一个可重用的控件,允许您设置比例大小和传感器的值。当属性比例大小或值更改时,该控件应绘制相应的正数或负数控件栏,并显示带有标签的值。控制栏通过使用 Windows Phone 用户控件的模板项来实现,点击项目(Project) >> 中添加新菜单项(Add New Item Menu)。为新的项目栏的命名。如下所示为新控件的 XAML 标记。
表1 标记栏控件

图 3 栏控件与 2.0 的缩放值和当前值为-1.0。

Listing 1 Markup for the Bar control

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />                         #1
        <RowDefinition Height="1*" />                         #1
    </Grid.RowDefinitions>
    <Rectangle x:Name="positiveBar" VerticalAlignment="Bottom" />
    <Rectangle x:Name="negativeBar" Grid.Row="1" VerticalAlignment="Top" />
    <TextBlock x:Name="label" VerticalAlignment="Center"        #2
        Grid.RowSpan="2" Text="0" TextAlignment="Center" />     #2
</Grid>
#1 Divide control into two rows
#2 Center label 

网格划分成两个半 #1 且每个包含一个矩形。第一个矩形显示正值,而另一个表示负值。放在标签 #2 显示栏中间的显示值为 标签栏的值。图 10.3 演示控件栏的样子,此时缩放值设置=2.0 和当前值设置=1.0。

图3  控件栏的样子,此时缩放值=2.0 和当前值=1.0。

Pages that host a Bar control need the ability to set different fill colors for the rectangles. Add a new property namedBarFillto Bar.xaml.cs code behind file:

public Brush BarFill
{
    get { return positiveBar.Fill; }
    set
    {
        positiveBar.Fill = value;
        negativeBar.Fill = value;
    }
}

The setter for theBarFillproperty assigns the specified Brush to both thepositiveBarandnegativeBarrectangles.

Note: If you were building a proper reusable XAML control, theBarFillproperty and the other properties would be dependency properties. The control would declare template parts and would provide XAML markup as the default template. See the book Silverlight 5 in Action for more details on building reusable XAML controls.

Next you create properties to set the scale and value for the bar. Since you don’t know the full range of values, you need the caller to tell the control how to scale the value to the height of the rectangles. Let’s say you need the bar to display a value between 2 and −2, and theBarcontrol is 200 pixels high. A value of 2 would require the positive bar to be a hundred pixels high, whereas a value of −1 would require the negative bar to be 50 pixels high. The next listing details how the bar height is calculated using theScaleandValueproperties.

注栏控件的页可以为矩形填充不同的颜色。如下为添加新的属性 BarFill 到在Bar.xaml.cs 文件中的:

public Brush BarFill
{
    get { return positiveBar.Fill; }
    set
    {
        positiveBar.Fill = value;
        negativeBar.Fill = value;
    }
}

通过设置BarFill的属性来为画笔分配矩形栏值为正或负

注: 如果您需要创建可重用的 XAML 控制,BarFill 属性和其他依赖项的相关属性。该控件需要声布模板部件,并将提供作为默认模板的 XAML 标记。  << Silverlight 5 in Action >> 一书中有关于创建可重用 XAML 详细资料。

下一步你将创建设置的缩放值和栏值的属性。由于你不知道值的范围,你需要调用方告诉控件如何缩放到匹配矩形的高度值。假设您需要的栏来显示值 2 和 - 2之间,而Barcontrol 是 200 像素高。值2 将需要将正的栏值设为 100 像素高,而值为 -1 的话将需要负的栏值设为 50 像素高。下一个详细列表介绍如何通过设置 ScaleandValue属性来计算信息栏的高度。

Listing 2 Calculating bar height with the Scale and Value properties


private double scale;
public double Scale
{
    get { return scale; }
    set
    {
        scale = value;
        Update();                        #1
    }
}
private double barValue;
public double Value
{
    get { return barValue; }
    set
    {
        barValue = value;
        Update(); 
    }
}
private void Update()
{
    int height = (int)(barValue * scale);                  #2
    positiveBar.Height = height > 0 ? height : 0;
    negativeBar.Height = height < 0 ? height * -1 : 0;     #3
    label.Text = barValue.ToString("0.0");   
}
#1 Recalculate when properties change
#2 Calculate height of bar
#3 Invert negative height
Both theScaleand theValueproperties are implemented with backing fields and simple getters and setters. Inside the setter of each property, you call theUpdatemethod #1 to recalculate the height of the bar rectangles and update the user interface. Inside theUpdatemethod you multiply the scale andbarValuefields #2, and the resulting value is the number of pixels high the bar should be drawn. If the calculated height value is greater than 0, thepositiveBar’s Height is updated to the new value. If the calculated height value is less than zero, you invert the calculated value #3 before assigning thenegativeBar’s height. Finally, you use theToStringmethod with a formatting string to set the label’s Text property.


Now that you have a bar control, you can create the sample application’s user interface. You need to add an XML namespace to MainPage.xaml so that you can use your new bar control:

xmlns:l="clr-namespace:Sensors"

You’re now ready to use the Bar control in the MainPage’s XAML markup. You need to design the MainPage to have three Bar controls for each sensor, for a total of nine Bar controls.

2 计算 栏的缩放 高度 值属性

private double scale;
public double Scale
{
    get { return scale; }
    set
    {
        scale = value;
        Update();                        #1
    }
}
private double barValue;
public double Value
{
    get { return barValue; }
    set
    {
        barValue = value;
        Update(); 
    }
}
private void Update()
{
    int height = (int)(barValue * scale);                  #2
    positiveBar.Height = height > 0 ? height : 0;
    negativeBar.Height = height < 0 ? height * -1 : 0;     #3
    label.Text = barValue.ToString("0.0");   
}
#1 Recalculate when properties change
#2 Calculate height of bar
#3 Invert negative height

缩放范围值和浮动属性通过后面的字段和简单的getter和setter来实现。在每个属性里面可通过调用Update方法#1来重新计算矩形栏的高度并更新用户界面。Update方法内你通过缩放比例的规模和栏的值字段 #2 得到的值就是应绘制在栏的像素。如果计算得到的的高度值大于0,正的栏高度值将更新为新值。如果计算的高度值小于零,你分配负的栏的高度值及计算之前的值取负#3。最后,你可以使用ToString方法来设置格式化的字符串来到该标签的Text属性。

现在,你有一个工具栏控件,您可以创建示例应用的用户界面。 您需要添加一个XML命名空间到MainPage.xaml以便你可以使用新的工具栏控件:

xmlns:l="clr-namespace:Sensors"

你现在可以使用在MainPage.XAML标记中使用控制栏。 你需要在MainPage为每个传感器设置三个控制栏,一共有九个控制栏。

返回顶部
顶部