一百行代码写一个WP8路线跟踪应用 已翻译 100%

hellowalker 投递于 2013/01/16 11:01 (共 14 段, 翻译完成于 01-21)
阅读 6030
收藏 62
6
加载中

Contents

  • Introduction
  • The Application User Interface
  • Timing The Run
  • Location Tracking
  • Setting The Map Pitch and Heading
  • Background Location Tracking
  • A Live Tile
  • Conclusion 

Introduction 

I have worked with Windows Phone 7 since it was in beta, so as you can imagine, I downloaded the Windows Phone 8 SDK as soon as it went live. For a bit of fun I decided to create a simple run-tracking application that showcases a number of these features ... and for an extra challenge do it all within 100 lines of code! (without resorting to writing compact and cryptic code).

已有 1 人翻译此段
我来翻译
This article guides you through the application that I developed, delving into the following Windows Phone 8 features:
  • The new map control, with pedestrian and landmark features.
  • How to track the users location, including tracking their location in the background while other apps are running
  • Adding line annotations to the map
  • Some 3D mapping features, setting the pitch and heading
  • Live-tiles making use of one of the new tile formats

Whilst it was perfectly possible to develop a run-tracking app with Windows Phone 7 (and there are a number of good examples in the Marketplace), the new features and capabilities of Windows Phone 8 can be used to make a much more feature-rich application.

NOTE: I originally published this article on the Nokia Developer Wiki, but thought I would share it on CodeProject also, where most of my other articles have been published.

已有 1 人翻译此段
我来翻译

The Application User Interface

This application has quite a basic UI, which is composed of full-screen map, which has the run statistics overlayed on top of it as shown in the screenshot below:

The application UI is defined in XAML as follows:

<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>
已有 1 人翻译此段
我来翻译
GridUtilsis a utility class, which I wrote a number of years ago, that provides convenient shorthand for defining grid columns and rows (for WPF, Silverlight and WindowsPhone). If you are following along, by building this running app from scratch, then in order to add the map, you will have to include the following namespace definition:
xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps" 

Before building and running the application, you have to include the mapping ‘capability’. To do this open up '''WPAppManifest.xml''', navigate to the Capabilities tab and check theID_CAP_MAPcheckbox. While you’re there, you may as well includeID_CAP_LOCATIONas well:

Capabilites are used to determine the phone features that your application uses so that users can more easily determine what an application does.

With these capabilities included, build and run the application and you should see the same UI that was illustrated above.

已有 1 人翻译此段
我来翻译

One of the improvements in the maps control is that it is fully vector-based (The Windows Phone 7 map is image-tile-based), this creates a much more smooth transition when the map is zoomed, and also allows for 3D transformations (as we will see a little later on). The map control also has a few other useful features for our running app, pedestrian-features and landmarks. These can be enabled as follows:

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

With these features enabled the map illustrates useful features such as stairs, crossings and 3D landmarks:

(By the way, I’m not counting the ~50 lines of XAML in my total lines-of-code count!)

The Windows Phone 8 maps have many more new features that I have not used in this application. You could for example use the newColorMode, which allows you to render a 'dark' map which is easier on the eyes in low light conditions. You could even make the run-tracking app choose theColorModebased on the time of day!

已有 1 人翻译此段
我来翻译

Timing The Run

When the '''Start''' button is tapped the application tracks the user’s location using the phone’s built in GPS receiver, in order to mark their path on the map. It also times their run duration and generates various statistics of interest. We’ll start with the simpler of the two, timing the run. When the start button is clicked aDispatcherTimeris started and the time of the button tap recorded. On each timer ‘tick’ the label which indicates the elapsed run time is updated:

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";
    }
  }
}

With the above code in place, tapping the '''start''' button starts the timer.

已有 1 人翻译此段
我来翻译

Location Tracking

The next step is to track the location whilst the timer is running. The Windows Phone API has aGeoCoordinateWatcherclass which fires aPositionChangedevent which can be used to track the user’s location. It is very easy to render the user’s movements on a map via aMapPolyLine, which is a line path which is defined in terms of geocoordinates. Each time the event is fired, a new point is added to the line as follows:

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);
  }
}

With these few lines of extra code, the path of the user’s run is added to the map:

已有 1 人翻译此段
我来翻译
The PositionChangedevent handler can be developed further to compute the total run distance, calories burnt and pace. This makes use of theGeoCoordinate.GetDistanceTomethod which can be used to compute the distance between two locations:
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;
}

Runner’s do not measure pace in miles or kilometers per hour. Instead, pace is measured in terms of the time taken to travel a set distance. This method of measurement makes it much easier to determine your overall race time, e.g. if you are running at 4:00 minute-kilometers pace, you will complete a 5k race in 20 minutes.

已有 1 人翻译此段
我来翻译

NOTE: The code above uses a pretty basic calorie calculation, assuming a burn rate of 65 calories per kilometer. A more accurate calculation would incorporate the runner's weight and pace, and other environmental factors. I'll leave this as an exercise for the reader!

For developing applications that involve tracking a user’s location the emulator has some very useful features. You can record points along a route, then replay them at set intervals. You can also save the route as an XML file so that it can be replayed in future sessions:

It takes a while to create a realistic dataset that emulates a real run, but at least you only have to do this once!

已有 1 人翻译此段
我来翻译

Setting The Map Pitch and Heading

Because of the vector nature of the Windows Phone 8 map it is possible to transform the view using the Pitch and Heading properties. The Pitch property sets the viewing angle of the map, providing a perspective rendering, rather than a top-down rendering, while the Heading property allows you to rotate the map. Most sat-nav systems use a combination of these effects to render the map so that it looks the same as the view directly in front of you. Many people find this type of map view much easier to understand (they do not have to perform rotate transforms in their head!).

Adding this feature to the running app is really easy, firstly setting the map Pitch is simply done in the XAML:

<!-- the map -->
<maps:Map x:Name="Map"
      PedestrianFeaturesEnabled="True"
      LandmarksEnabled="True"
      Pitch="55"
      ZoomLevel="18"/> 
已有 1 人翻译此段
我来翻译
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(12)

lonno1
lonno1
开源才是王道!
SamH
SamH

引用来自“leixu_txtek”的评论

引用来自“SamH”的评论

学习了……

黄山 认识我吗?

好吧,我认出了txtek……
leixu2
leixu2

引用来自“SamH”的评论

学习了……

黄山 认识我吗?
unnamed
unnamed

引用来自“Ghost_”的评论

引用来自“棟梁”的评论

是否 Windows Phone 8 比較 Android 强大?

不是
Serysew
Serysew
目测一下WP8能否逆袭? WP7很令人失望
棟梁
多謝 Ghost 的回复
iiiiiiiii
iiiiiiiii

引用来自“棟梁”的评论

是否 Windows Phone 8 比較 Android 强大?

棟梁
是否 Windows Phone 8 比較 Android 强大?
dadait
dadait
马克。
m
missyang
很轻大
返回顶部
顶部