Windows Phone笔记(5)加速计和位置服务
这篇笔记主要是讲述如windows phone中两个提供外界信息的设备以及如何在程序中适用它们,在用户授权允许的情况下,加速计能够获取当前手机设备的具体方向;而位置服务则可以对手机当前所在地的位置进行定位。
1.传感器之加速计
加速计时windows phone手机中的一个硬件设备,加速计测量在某一时刻施加于设备的力。可以使用这些力来确定用户正在向哪个方向移动设备。加速度值采用 3 维矢量表示,该矢量表示在 x、y 和 z 轴中的加速度分量(采用重力单位)。当设备面朝平台时,加速度的方向相对于设备以便对 z 轴应用 -1g,当垂直于平台顶部放置设备时,对 y 轴应用 -1g。从加速计的这些特性我们可以知道,windows phone中的程序中处理方向改变的基础。加速度也会对突然的移动做出响应,比如摇晃(微信中的摇一摇功能)等等。对于加速计的使用,创意是对开发人员最大的挑战。
程序使用加速计需要引用microsoft.devices.sensors库,并且在properties文件夹中的wmappmanifest.xml文件中设置(这是默认设置):
<capability name="id_cap_sensors"/>
下面让我们通过观察一个示例程序,该程序获取加速计测量的数据,并使用line元素和textblock元素展示出来:
这是mainpage.xaml页面的代码:
1 <!--contentpanel - 在此处放置其他内容-->
2 <grid x:name="contentpanel" grid.row="1" margin="12,0,12,0">
3 <stackpanel orientation="vertical">
4 <stackpanel orientation="horizontal">
5 <textblock text="状态:" margin="10 0"/>
6 <textblock name="txtblkaccelerometerstate" text="停止采集数据"/>
7 </stackpanel>
8 <stackpanel orientation="horizontal">
9 <textblock text="更新加度计数据时间:" margin="10 0"/>
10 <textblock name="txtblkupdatedataaccelerometer" text="20ms"/>
11 </stackpanel>
12 <grid>
13 <textblock height="45" horizontalalignment="left" name="txtblkx" text="x:1.0" verticalalignment="top" foreground="red" fontweight="bold" fontsize="28" margin="10 0" />
14 <textblock height="45" horizontalalignment="center" name="txtblky" text="y:1.0" verticalalignment="top" foreground="green" fontweight="bold" fontsize="28" />
15 <textblock height="45" horizontalalignment="right" name="txtblkz" text="z:1.0" verticalalignment="top" foreground="blue" fontweight="bold" fontsize="28" margin="10 0" />
16 </grid>
17 <grid height="300">
18 <line x:name="xline" x1="240" y1="150" x2="340" y2="150" stroke="red" strokethickness="4"></line>
19 <line x:name="yline" x1="240" y1="150" x2="240" y2="60" stroke="green" strokethickness="4"></line>
20 <line x:name="zline" x1="240" y1="150" x2="190" y2="200" stroke="blue" strokethickness="4"></line>
21 </grid>
22 <button content="开始采集" margin="0 100" name="btnswitch" click="btnswitch_click"></button>
23 </stackpanel>
24 </grid>
这是mainpage.xaml.cs后台处理程序:
public partial class mainpage : phoneapplicationpage
{
accelerometer accelerometer;
dispatchertimer timer;
vector3 acceleration;
bool isdatavalid;
// 构造函数
public mainpage()
{
initializecomponent();
if (!accelerometer.issupported)
{
txtblkaccelerometerstate.text = "设备不支持加速计";
}
else
{
//初始化timer,并绑定tick事件
timer = new dispatchertimer();
timer.interval = timespan.frommilliseconds(30);
timer.tick += new eventhandler(timer_tick);
}
}
/// <summary>
/// 开始和停止采集加速计数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnswitch_click(object sender, routedeventargs e)
{
if (accelerometer != null && accelerometer.isdatavalid)
{
//停止采集加速计数据
accelerometer.stop();
timer.stop();
txtblkaccelerometerstate.text = "停止加速度度计";
btnswitch.content = "开始采集数据";
}
else
{
if (accelerometer == null)
{
//实例化一个accelerometer对象
accelerometer = new accelerometer();
//20毫秒更新一次数据
accelerometer.timebetweenupdates = timespan.frommilliseconds(20);
//更新传感器数据的时间
txtblkupdatedataaccelerometer.text = accelerometer.timebetweenupdates.totalmilliseconds + " ms";
//从传感器获得新数据时发生
accelerometer.currentvaluechanged += new eventhandler<sensorreadingeventargs<accelerometerreading>>(accelerometer_currentvaluechanged);
}
try
{
txtblkaccelerometerstate.text = "开始采集数据";
btnswitch.content = "停止采集数据";
accelerometer.start();
timer.start();
}
catch (invalidoperationexception)
{
txtblkaccelerometerstate.text = "无法启动加速计.";
}
}
}
void accelerometer_currentvaluechanged(object sender, sensorreadingeventargs<accelerometerreading> e)
{
//这个事件处理程序是被主执行线程(ui)之外的线程调用的
//不能直接访问页面上的元素,因为它们位于ui线程中
isdatavalid = accelerometer.isdatavalid;
acceleration = e.sensorreading.acceleration;
}
void timer_tick(object sender, eventargs e)
{
if (isdatavalid)
{
txtblkaccelerometerstate.text = "正在从加速计获取数据";
// 显示加速计的数值
txtblkx.text = "x: " + acceleration.x.tostring("0.00");
txtblky.text = "y: " + acceleration.y.tostring("0.00");
txtblkz.text = "z: " + acceleration.z.tostring("0.00");
// 使用line元素显示加速计返回的值
xline.x2 = xline.x1 + acceleration.x * 100;
yline.y2 = yline.y1 - acceleration.y * 100;
zline.x2 = zline.x1 - acceleration.z * 50;
zline.y2 = zline.y1 + acceleration.z * 50;
}
}
}
编译运行上面的程序:
这是运行程序后的效果(没有开始采集数据) 点击开始采集数据后,y等于-1g,表示手机(模拟器)是纵向竖立的
但这样看好像并不能直观的了解加速计的作用,我们可以通过使用该模拟器的加速计模拟器工具来测试应用程序。
点击模拟器的加速计工具的“播放按钮”或者是拖动加速计工作中手机上的桔红色圆点,模拟手机的各个移动状态。我们可看到页面的3个分别代表着x/y/z的三个line元素随着加速计的数据变化而不断变化。
2.位置服务(location service)
windows phone应用程序可以通过一种被称为a-gps(assisted-gps,辅助gps)的技术获取手机当前所在的地理位置。对手机进行定位所实用的核心类是:geocoordinatewatcher。使用时需要引用system.device.dll程序集,并引用system.device.location命名空间。wmappmanifest.xml做如下标记(默认包含):
<capability name="id_cap_loction"/>
geocoordinatewatcher的构造方法可以接收一个getpositionaccuracy枚举类型的参数,该枚举的成员有:
• default(默认精度)
• high(高精度)
geocoordinatewatcher对象需要注册一个名为:positionchanged事件,检测到位置更改时发生。postitionchanged事件会传递一个geocoordinate对象,该对象有八个属性,分别是:
• latitude(纬度),double类型,-90至90之间。
• longitude(经度),double类型,-180到180之间
• altitude(高度),double类型
• horizontalaccuracy(水平精度)和verticalaccuracy(竖直精度),double类型
• course(航向),double类型,0至360之间
• speed(速度),double类型
• isunknown,boolean类型,当latitude(纬度)和longitude(经度)为非数字时,则为ture
geocoordinate类有一个方法getdistanceto,用于计算两个geocoordinate对象的距离。
北纬为正值,南纬为负值;东经为正值,西经为负值。如果应用程序没有获取用户位置的授权,latitude(纬度)和longitude(经度)的值为double.nan。
现在我们通过一个示例来学习如何使用windows phone中的位置服务。
mainpage.xaml
1 <!--contentpanel - 在此处放置其他内容-->
2 <grid x:name="contentpanel" grid.row="1" margin="12,0,12,0">
3 <stackpanel >
4 <stackpanel orientation="horizontal">
5 <textblock height="30" name="textblock1" text="状态:" margin="10 0" />
6 <textblock height="30" name="txtblklocationstate" text="位置服务当前不可用" />
7 </stackpanel>
8 <stackpanel>
9 <grid height="500">
10 <textblock text="经度:" fontsize="28" foreground="red" horizontalalignment="left" verticalalignment="top"/>
11 <textblock text="0.000" fontsize="28" foreground="red" horizontalalignment="center" verticalalignment="top" name="txtblklatitude" margin="0 0 200 0" />
12 <textblock text="纬度:" fontsize="28" foreground="red" horizontalalignment="left" verticalalignment="center" />
13 <textblock text="0.000" fontsize="28" foreground="red" horizontalalignment="center" verticalalignment="center" name="txtblklongitude" margin="0 0 200 0"/>
14 </grid>
15 </stackpanel>
16 <grid>
17 <button content="开始定位" height="72" name="btnstartlocation" width="160" horizontalalignment="left" verticalalignment="bottom" margin="50 0" click="btnstartlocation_click" />
18 <button content="停止定位" height="72" name="btnstoplocation" width="160" horizontalalignment="right" verticalalignment="bottom" margin="50 0" click="btnstoplocation_click" />
19 </grid>
20 </stackpanel>
21 </grid>
这里是mainpage.xaml.cs后台处理程序:
1 public partial class mainpage : phoneapplicationpage
2 {
3 geocoordinatewatcher watcher;
4 // 构造函数
5 public mainpage()
6 {
7 initializecomponent();
8 }
9
10 /// <summary>
11 /// 开始定位
12 /// </summary>
13 /// <param name="sender"></param>
14 /// <param name="e"></param>
15 private void btnstartlocation_click(object sender, routedeventargs e)
16 {
17 if (watcher == null)
18 {
19 watcher = new geocoordinatewatcher(geopositionaccuracy.high); // 采用高精度
20 watcher.movementthreshold = 20; // positionchanged事件之间传送的最小距离
21
22 watcher.statuschanged += new eventhandler<geopositionstatuschangedeventargs>(watcher_statuschanged);
23 watcher.positionchanged += new eventhandler<geopositionchangedeventargs<geocoordinate>>(watcher_positionchanged);
24
25 watcher.start();//开始使用位置服务
26 }
27 else//方便测试,实际使用需要注意
28 {
29 watcher.start();//开始使用位置服务
30 }
31 }
32
33 //检测到位置更改时
34 //当定位服务已准备就绪并接收数据时,它将开始引发 positionchanged 事件
35 void watcher_positionchanged(object sender, geopositionchangedeventargs<geocoordinate> e)
36 {
37 this.txtblklatitude.text = e.position.location.latitude.tostring("0.000");
38 this.txtblklongitude.text = e.position.location.longitude.tostring("0.000");
39 }
40
41 //当位置服务状态发生变化时
42 //在 geopositionstatuschangedeventargs 对象中传递的 geopositionstatus 枚举获取该服务的当前状态。
43 //可以使用它在应用程序中启用基于位置的功能,以及将服务的当前状态通知给用户。
44 void watcher_statuschanged(object sender, geopositionstatuschangedeventargs e)
45 {
46 switch (e.status)
47 {
48 //如果服务的状态为 disabled,则可以检查 permission 属性,看用户是否禁用了应用程序的定位服务功能。
49 case geopositionstatus.disabled:
50 if (watcher.permission == geopositionpermission.denied)
51 {
52 //用户禁用了定位服务
53 this.txtblklocationstate.text = "对位置服务的访问被拒绝。";
54 }
55 else
56 {
57 this.txtblklocationstate.text = "设备的定位服务不能够正常使用。";
58 }
59 break;
60 case geopositionstatus.initializing:
61 // 位置服务正在尝试获取数据
62 this.btnstartlocation.isenabled = false;
63 break;
64 case geopositionstatus.nodata:
65 this.txtblklocationstate.text = "当前位置无法进行定位";
66 this.btnstoplocation.isenabled = true;
67 break;
68 case geopositionstatus.ready:
69 this.txtblklocationstate.text = "位置服务已启用,并准备就绪";
70 this.btnstoplocation.isenabled = true;
71 break;
72 }
73 }
74
75 /// <summary>
76 /// 停止定位
77 /// </summary>
78 /// <param name="sender"></param>
79 /// <param name="e"></param>
80 private void btnstoplocation_click(object sender, routedeventargs e)
81 {
82 watcher.stop();
83 this.btnstartlocation.isenabled = true;
84 this.txtblklocationstate.text = "位置服务已经停止";
85 }
86 }
编译运行程序后,通过模拟器可以模拟当前位置,通过使用模拟器中模拟位置工具变化当前设备所在坐标,我们可以看到程序上的数字也随之不断变化:
摘自 晴天猪の博客
下一篇: 加速器在模拟器中的尝试