65.9K
CodeProject 正在变化。 阅读更多。
Home

一个 Windows Phone 8 跑步追踪应用程序,仅用 100 行代码实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.99/5 (60投票s)

2013年1月4日

CPOL

8分钟阅读

viewsIcon

119628

downloadIcon

1579

本文介绍了一个Windows Phone 8跑步追踪应用的开发,该应用使用了Windows Phone 8的许多新特性。

目录

  • 引言
  • 应用用户界面
  • 计时跑步
  • 位置追踪
  • 设置地图倾斜和朝向
  • 后台位置追踪
  • 动态磁贴
  • 结论 

介绍 

我从Windows Phone 7 beta版本开始就一直在使用它,所以您可以想象,Windows Phone 8 SDK一上线我就下载了它。为了好玩,我决定创建一个简单的跑步追踪应用程序,展示这些特性中的一些……并且为了增加挑战,我将所有代码都写在100行之内!(不使用紧凑且晦涩的代码)。

本文将引导您了解我开发的应用程序,深入探讨以下Windows Phone 8的特性:
  • 新的地图控件,具有行人特征和地标特征。
  • 如何追踪用户位置,包括在其他应用运行时在后台追踪用户位置。
  • 在地图上添加线路标注。
  • 一些3D地图功能,设置倾斜和朝向。
  • 动态磁贴,利用一种新的磁贴格式。

虽然使用Windows Phone 7开发跑步追踪应用是完全可能的(并且市场上有许多优秀的例子),但Windows Phone 8的新功能和能力可以用来制作功能更丰富的应用程序。

注意:我最初在Nokia开发者Wiki上发布了这篇文章,但考虑到我的其他文章大多发布在CodeProject上,我也想在这里分享一下。

应用用户界面

这个应用程序有一个非常基本的UI,它由一个全屏地图组成,地图上叠加了跑步统计数据,如下图所示。

应用程序的UI定义如下XAML:

<Grid util:GridUtils.RowDefinitions="Auto, *">

  <!-- title -->
  <StackPanel Grid.Row="0" Margin="12,17,0,28">
    <StackPanel Orientation="Horizontal">
      <Image Source="/Assets/ApplicationIconLarge.png" Height="50"/>
      <TextBlock Text="WP8Runner" VerticalAlignment="Center"
                  Margin="10 0 0 0"
                  FontSize="{StaticResource PhoneFontSizeLarge}"/>
    </StackPanel>
  </StackPanel>

  <!--ContentPanel - place additional content here-->
  <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

    <!-- the map -->
    <maps:Map x:Name="Map"
          ZoomLevel="16"/>

    <!-- run statistics -->
    <Grid Background="#99000000" Margin="20" 
          VerticalAlignment="Bottom">
      <Grid Margin="20"
            util:GridUtils.RowDefinitions="40, 40, Auto"
            util:GridUtils.ColumnDefinitions="*, *, *, *">
          
        <!-- distance -->
        <TextBlock Text="Distance:"/>
        <TextBlock Text="0 km" Grid.Column="1" x:Name="distanceLabel"
              HorizontalAlignment="Center"/>

        <!-- time -->
        <TextBlock Text="Time:" Grid.Column="2"/>
        <TextBlock Text="00:00:00" Grid.Column="3" x:Name="timeLabel"
              HorizontalAlignment="Center"/>

        <!-- calories -->
        <TextBlock Text="Calories:" Grid.Row="1"/>
        <TextBlock Text="0" Grid.Column="1" x:Name="caloriesLabel"
              HorizontalAlignment="Center" Grid.Row="1"/>

        <!-- pace -->
        <TextBlock Text="Pace:" Grid.Column="2" Grid.Row="1"/>
        <TextBlock Text="00:00" Grid.Column="3" x:Name="paceLabel"
              HorizontalAlignment="Center" Grid.Row="1"/>

        <Button Content="Start"
                Grid.Row="2" Grid.ColumnSpan="4"
                Click="StartButton_Click"
                x:Name="StartButton"/>
      </Grid>
    </Grid>
  </Grid>
</Grid> 

GridUtils是一个实用类,几年前我写过,它提供了方便的简写方式来定义网格列和行(适用于WPF、Silverlight和WindowsPhone)。如果您想跟着我一步步从头构建这个正在运行的应用程序,那么为了添加地图,您需要包含以下命名空间定义:

xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps" 

在构建和运行应用程序之前,您必须包含地图的“功能”。要做到这一点,请打开'''WPAppManifest.xml''',导航到“Capabilities”(功能)选项卡,然后勾选ID_CAP_MAP复选框。既然您在那里,也可以勾选ID_CAP_LOCATION

功能用于确定您的应用程序使用的手机功能,以便用户可以更轻松地了解应用程序的功能。

包含这些功能后,构建并运行应用程序,您应该会看到与上面所示相同的UI。

地图控件的一个改进是它是完全基于矢量的(Windows Phone 7的地图是基于图像瓦片的),这在地图缩放时创建了更平滑的过渡,并且也允许3D变换(我们稍后会看到)。地图控件还为我们的跑步应用提供了其他一些有用的功能,如行人特征和地标。这些可以如下启用:

<!-- the map -->
<maps:Map x:Name="Map"
      PedestrianFeaturesEnabled="True"
      LandmarksEnabled="True"
      ZoomLevel="16"/> 

启用了这些功能后,地图将显示楼梯、过路口和3D地标等有用特征。

(顺便说一句,我的总代码行数不包括大约50行的XAML!)

Windows Phone 8的地图还有许多我在此应用中未使用的功能。例如,您可以使用新的ColorMode,它允许您渲染一个“黑暗”地图,在弱光条件下对眼睛更友好。您甚至可以让跑步追踪应用根据一天中的时间来选择ColorMode

计时跑步

当点击'''Start'''按钮时,应用程序使用手机内置的GPS接收器追踪用户位置,以便在地图上标记他们的路线。它还会计时跑步时长,并生成各种有趣的统计数据。我们将从两者中较简单的一个开始,即计时跑步。当点击开始按钮时,会启动一个DispatcherTimer,并记录按钮点击的时间。在每次计时器“滴答”时,更新显示已用跑步时间的标签。

public partial class MainPage : PhoneApplicationPage
{
  private DispatcherTimer _timer = new DispatcherTimer();
  private long _startTime;

  public MainPage()
  {
    InitializeComponent();

    _timer.Interval = TimeSpan.FromSeconds(1);
    _timer.Tick += Timer_Tick;
  }

  private void Timer_Tick(object sender, EventArgs e)
  {
    TimeSpan runTime = TimeSpan.FromMilliseconds(System.Environment.TickCount - _startTime);
    timeLabel.Text = runTime.ToString(@"hh\:mm\:ss");
  }

  private void StartButton_Click(object sender, RoutedEventArgs e)
  {
    if (_timer.IsEnabled)
    {
      _timer.Stop();
      StartButton.Content = "Start";
    }
    else
    {
      _timer.Start();
      _startTime = System.Environment.TickCount;
      StartButton.Content = "Stop";
    }
  }
} 

有了上述代码,点击'''start'''按钮即可启动计时器。

位置追踪

下一步是在计时器运行时追踪位置。Windows Phone API有一个GeoCoordinateWatcher类,它会触发一个PositionChanged事件,可用于追踪用户位置。通过MapPolyLine(这是一个由地理坐标定义的线路路径)在地图上渲染用户移动非常容易。每次事件触发时,都会像下面这样向线路添加一个新点:

public partial class MainPage : PhoneApplicationPage
{
  private GeoCoordinateWatcher _watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
  private MapPolyline _line;
  private DispatcherTimer _timer = new DispatcherTimer();
  private long _startTime;

  public MainPage()
  {
    InitializeComponent();

    // create a line which illustrates the run
    _line = new MapPolyline();
    _line.StrokeColor = Colors.Red;
    _line.StrokeThickness = 5;
    Map.MapElements.Add(_line);

    _watcher.PositionChanged += Watcher_PositionChanged;

    //.. timer code omitted ...
  }

  //.. timer code omitted ...

  private void StartButton_Click(object sender, RoutedEventArgs e)
  {
    if (_timer.IsEnabled)
    {
      _watcher.Stop();
      _timer.Stop();
      StartButton.Content = "Start";
    }
    else
    {
      _watcher.Start();
      _timer.Start();
      _startTime = System.Environment.TickCount;
      StartButton.Content = "Stop";
    }
  }


  private void Watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
  {
    var coord = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);

    Map.Center = coord;
    _line.Path.Add(coord);
  }
} 

有了这几行额外的代码,用户跑步的路径就被添加到了地图上。

PositionChanged事件处理程序可以进一步开发,以计算总跑步距离、消耗的卡路里和配速。这利用了GeoCoordinate.GetDistanceTo方法,该方法可用于计算两个位置之间的距离。

private double _kilometres;
private long _previousPositionChangeTick;

private void Watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
  var coord = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);

  if (_line.Path.Count > 0)
  {
    // find the previos point and measure the distance travelled
    var previousPoint = _line.Path.Last();
    var distance = coord.GetDistanceTo(previousPoint);

    // compute pace
    var millisPerKilometer = (1000.0 / distance) * (System.Environment.TickCount - _previousPositionChangeTick);

    // compute total distance travelled
    _kilometres += distance / 1000.0;

    paceLabel.Text = TimeSpan.FromMilliseconds(millisPerKilometer).ToString(@"mm\:ss");
    distanceLabel.Text = string.Format("{0:f2} km", _kilometres);
    caloriesLabel.Text = string.Format("{0:f0}", _kilometres * 65);
  }
  

  Map.Center = coord;
  
  _line.Path.Add(coord);
  _previousPositionChangeTick = System.Environment.TickCount;
} 

跑步者不以每小时英里或公里来衡量配速。相反,配速是以在规定距离内花费的时间来衡量的。这种测量方式可以更容易地确定您的总体比赛时间,例如,如果您以4:00分钟/公里的配速跑步,您将在20分钟内完成一个5公里比赛。

注意:上面的代码使用了一个非常基本的卡路里计算,假设每公里消耗65卡路里。更准确的计算将纳入跑步者的体重和配速以及其他环境因素。我将留给读者作为练习!

对于开发涉及追踪用户位置的应用程序,模拟器有一些非常有用的功能。您可以沿着路线记录点,然后在设定的时间间隔内重放它们。您还可以将路线另存为XML文件,以便在将来的会话中重放。

创建模拟真实跑步的逼真数据集需要花费一些时间,但至少您只需要做一次!

设置地图倾斜和朝向

由于Windows Phone 8地图的矢量性质,可以使用Pitch(倾斜)和Heading(朝向)属性来变换视图。Pitch属性设置地图的观察角度,提供透视渲染,而不是俯视渲染,而Heading属性允许您旋转地图。大多数卫星导航系统使用这些效果的组合来渲染地图,使其看起来与您正前方视图相同。许多人发现这种地图视图更容易理解(他们不必在头脑中进行旋转变换!)。

为跑步应用添加此功能非常简单,首先在XAML中设置地图的Pitch即可。

<!-- the map -->
<maps:Map x:Name="Map"
      PedestrianFeaturesEnabled="True"
      LandmarksEnabled="True"
      Pitch="55"
      ZoomLevel="18"/> 

计算朝向稍微复杂一些。在上一节中,我们使用当前位置和前一个位置来计算配速和行驶距离。这两个位置可用于计算朝向,尽管计算起来有点复杂。幸运的是,我找到一个包含有用地理定位实用程序的.NET库,其中包括一个计算朝向的实用程序。使用.NET Extra库,查找和设置朝向非常简单。

PositionHandler handler = new PositionHandler();
var heading = handler.CalculateBearing(new Position(previousPoint), new Position(coord));
Map.SetView(coord, Map.ZoomLevel, heading, MapAnimationKind.Parabolic); 

另外,请注意,上面的代码使用了地图的SetView方法,而不是独立设置每个属性。如果直接设置属性,地图状态会立即改变,这意味着视图会从一个位置/朝向“跳”到另一个位置/朝向。而SetView会在一个位置过渡到另一个位置,产生一个更流畅的UI。

您可以在下面看到朝向和倾斜的使用,这是在纽约中央公园的一次跑步: 

 

后台位置追踪 

使用Windows Phone 7时,您可以在锁屏下运行前台应用程序,这对于运动追踪应用程序来说是一项非常重要的功能,它允许用户在手机锁定的同时仍然追踪他们的位置。Windows Phone 8更进一步,追踪地理位置的应用程序可以运行在后台,这意味着它们可以在用户使用其他应用程序时(例如收发邮件或播放音乐)继续追踪用户的位置。

要启用此功能,您必须手动编辑'''WMAppManifest.xml''',右键单击文件并选择'''View code'''(查看代码)。然后找到Tasks元素并添加以下内容。 

<Tasks>
  <DefaultTask Name="_default" NavigationPage="MainPage.xaml">
    <BackgroundExecution>
      <ExecutionType Name="LocationTracking" />
    </BackgroundExecution>
  </DefaultTask>
</Tasks> 

就是这样! 

当应用程序在后台运行时,会触发RunningInBackground应用程序事件。您可以使用此事件显示一个Toast通知,例如,但在下一节中,我们将介绍一种更有趣的方式来让用户了解持续的位置追踪。

动态磁贴 

Windows Phone 8增加了更多磁贴模板,这里我们将使用新的“Iconic Template”(图标模板)。要选择模板,请打开'''WMAppManifest.xml'''(这次使用可视化编辑器!),然后选择TemplateIconic模板。 

更新磁贴状态就像发送通知一样简单。每次位置更改时,都会执行以下代码。

ShellTile.ActiveTiles.First().Update(new IconicTileData()
{
  Title = "WP8Runner",
  WideContent1 = string.Format("{0:f2} km", _kilometres),
  WideContent2 = string.Format("{0:f0} calories", _kilometres * 65),
}); 

 现在,如果您将应用程序固定到开始屏幕并使用宽磁贴格式,当位置在后台被追踪时,磁贴会更新。

有了最后一个更改的添加,跑步应用程序就完成了! 

结论 

Windows Phone 8有一些非常酷的新功能,可以让您扩展应用程序的功能。在本文中,我展示了一个简单的跑步追踪应用程序如何受益于这些新功能。此外,API和框架的表达能力允许您用很少的代码开发复杂的应用程序。

显然,这里演示的应用程序还不够完整!为什么不自己尝试进一步开发它呢?为什么不尝试使用孤立存储来记录您的跑步历史?或者将摘要统计数据添加为图表?您可以尝试使用其他一些新的Windows Phone 8 API,例如语音命令来控制每次跑步的开始/停止?玩得开心!

那么,这个应用真的只有100行代码吗?您可以下载源代码, WP8Runner.zip,亲眼看看'''MainPage.xaml.cs'''正好是100行。 

© . All rights reserved.