Mobiele apps programmeren met Xamarin – Lokalisatie integreren

print
Deze handleiding maakte deel uit van het programmeertraject:

De inhoud van deze handleiding is echter grotendeels vervangen door Xamarin.Essentials.


Inhoud


Wat vooraf ging

Ik herneem hier even een vorige post over het installeren van NuGet Packages (plug-ins).

NuGet is een package manager voor Microsoft development platforms zoals .NET. U kunt deze pakketten installeren om zo de mogelijkheden van uw project uit te breiden.

  • Om een NuGet package te installeren klikt u rechts op het project waar u het NuGet package wilt voor installeren of u klikt rechts op de Solution om het NuGet package meteen te installeren voor alle projecten.
  • Vervolgens klikt u op Manage NuGet packages for Solution….

  • Selecteer het NuGet package dat u wil installeren.
  • Vink alle projecten aan waarvoor u het wilt installeren.
  • Klik op Install.

Onderstaande video toont een volledige installatie (zij het in Visual Studio 2015 en voor een ASP.NET project maar in weze verschilt dit niet).


Inleiding

Er komen twee topics aan bod die allen te maken hebben met lokalisatie.

  • Mappen integreren
  • Geolokalisatie opvragen

Mappen – integreren

Xamarin.Forms.Maps maakt gebruik van de native map-API’s op elk platform. Dit biedt een snelle, vertrouwde kaartenervaring voor gebruikers, maar betekent dat er enkele configuratiestappen nodig zijn om te voldoen aan elke platformspecifieke API-vereisten.

Plug-in installeren

  • Klik rechts op de Solution om het NuGet package meteen te installeren voor alle projecten.
  • Vervolgens klikt u op Manage NuGet packages for Solution….
  • Klik op Browse.
  • Zoek naar Xamarin.Forms.Maps, selecteer alle projecten en de laatste stabiele versie en klik op Install.

Sleutels aanvragen

Om optimaal gebruik te kunnen maken van de mappen op Android en UWP (niet op iOS) moet een sleutel opgevraagd worden.

Remember to generate another key using the keystore file that is used to sign the Release version of any application that is uploaded to the Google Play store. The key you generate for development and debugging will not work and the app downloaded from Google Play will have broken map display. Also remember to regenerate the key if the app’s Package Name changes.

Permissies instellen

Uw app moet toegang krijgen/vragen tot de lokalisatie van het toestel.

UWP

In UWP is dit vrij eenvoudig, u moet de applicatie enkel toegang geven tot de Location.

  • Open Properties onder het UWP-project.
  • Ga naar Application en klik op Package Manifest….
  • Selecteer het tabblad Capabilities en vink Location aan.

Android

Voor Android moeten onderstaande permissies ingesteld worden:

  • AccessCoarseLocation
  • AccessFineLocation
  • AccessLocationExtraCommands
  • AccessMockLocation
  • AccessNetworkState
  • AccessWifiState
  • Internet

U stelt deze in via:

  • Open Properties onder het Android-project.
  • Ga naar Android Manifest.
  • Vink bovenstaande permissies aan.

U dient ook nog de opgevraagde sleutel toe te voegen.

  • Voeg onderstaande code toe aan AndroidManifest.xml, binnen de Application-tag dat u vindt onder het Android-project.
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AbCdEfGhIjKlMnOpQrStUvWValueGoesHere" />

De volledige code voor AndroidManifest.xml is:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.xamarin.mobilecrm">
	<uses-sdk android:minSdkVersion="15" />
	<application android:label="WorkingWithMaps.Android">
		<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AbCdEfGhIjKlMnOpQrStUvWValueGoesHere" />
		<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
	</application>
	<uses-permission android:name="android.permission.INTERNET" />
	<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
	<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
</manifest>

Merk op dat u hier de permissies terugvindt die u via PropertiesAndroid Manifest hebt ingesteld. U had ze ook hier kunnen toevoegen.

iOS

Voor iOS dient u een aantal keys, en bijhorende meldingen die verschijnen wanneer u er gebruik van wilt maken, toevoegen aan het bestandje Info.plist.

  • Klik rechts Info.plist dat u vindt onder het iOS-project en kies voor Open with… en selecteer XML (Text) Editor.
  • Voeg onderstaande code toe.
<key>NSLocationWhenInUseUsageDescription</key>
<string>We are using your location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access location when open and in the background.</string>

De volledige code, van de voorbeeldapplicatie die we hieronder gaan gebruiken, is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleIdentifier</key>
	<string>com.xamarin.WorkingWithMaps.iOS</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>MinimumOSVersion</key>
	<string>9.0</string>
	<key>UIDeviceFamily</key>
	<array>
		<integer>1</integer>
		<integer>2</integer>
	</array>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>CFBundleDisplayName</key>
	<string>Maps</string>
	<key>NSLocationWhenInUseUsageDescription</key>
	<string>We are using your location</string>
	<key>NSLocationAlwaysUsageDescription</key>
	<string>Can we use your location</string>
	<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
	<string>This app needs access location when open and in the background.</string>
	<key>CFBundleName</key>
	<string>Maps</string>
</dict>
</plist>

Initialisatie

U moet het gebruik van mappen ook nog initialiseren.

UWP

  • Voeg onderstaande code toe aan MainPage.xaml.cs, binnen de constructor, dat u vindt onder het UWP-project.
Xamarin.FormsMaps.Init("INSERT_AUTHENTICATION_TOKEN_HERE");

De volledige code:

namespace WorkingWithMaps.UWP
{
    public sealed partial class MainPage
    {
        public MainPage()
        {
            this.InitializeComponent();
            Xamarin.FormsMaps.Init("INSERT_AUTHENTICATION_TOKEN_HERE");
            this.LoadApplication(new WorkingWithMaps.App());
        }
    }
}

Android

  • Voeg onderstaande code toe aan MainActivity.cs, binnen de OnCreate-methode dat u vindt onder het Android-project.
Xamarin.FormsMaps.Init(this, bundle);

De volledige code:

using Android.App;
using Android.OS;
using Android.Content.PM;


namespace WorkingWithMaps.Android
{

    [Activity (Label = "WorkingWithMaps.Android.Android", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
	public class MainActivity : 
	global::Xamarin.Forms.Platform.Android.FormsApplicationActivity // superclass new in 1.3
	{
		protected override void OnCreate (Bundle bundle)
		{
			base.OnCreate (bundle);

			global::Xamarin.Forms.Forms.Init (this, bundle);
			global::Xamarin.FormsMaps.Init (this, bundle);

			LoadApplication (new App ()); // method is new in 1.3
		}
	}

}

iOS

  • Voeg onderstaande code toe aan AppDelegate.cs, binnen de FinishedLaunching-methodedat u vindt onder het iOS-project.
Xamarin.FormsMaps.Init();

De volledige code:

using Foundation;
using UIKit;

namespace WorkingWithMaps.iOS
{
    [Register ("AppDelegate")]
	public partial class AppDelegate : 
	global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate // superclass new in 1.3
	{
		public override bool FinishedLaunching (UIApplication app, NSDictionary options)
		{
			global::Xamarin.Forms.Forms.Init ();
			global::Xamarin.FormsMaps.Init ();

			LoadApplication (new App ());  // method is new in 1.3

			return base.FinishedLaunching (app, options);
		}
	}
}

Mappen – gebruiken

Onderstaande code-snippets tonen hoe u mappen kan gebruiken. Er zal hoofdzakelijk C# worden gebruikt. Enkel de map plaatsen kan eventueel in XAML.

Begin met de gewenste namespace toe te voegen.

using Xamarin.Forms.Maps;

Map aanmaken

C#

public class MapPage : ContentPage {
    public MapPage() {
        var map = new Map(
            MapSpan.FromCenterAndRadius(
                    new Position(37,-122), Distance.FromMiles(0.3))) {
                IsShowingUser = true,
                HeightRequest = 100,
                WidthRequest = 960,
                VerticalOptions = LayoutOptions.FillAndExpand
            };
        var stack = new StackLayout { Spacing = 0 };
        stack.Children.Add(map);
        Content = stack;
    }
}

Merk op dat IsShowingUser = true, die de plaats van de gebruiker weergeeft, op het ogenblik van dit schrijven een fout veroorzaakt en misschien zal moeten weggelaten worden (of in commentaar plaatsen).

De MapSpan.FromCenterAndRadius(new Position(37,-122), Distance.FromMiles(0.3))) bepaalt de weergegeven positie.

U zou ook naar een bepaalde regio kunnen gaan via map.MoveToRegion (new MapSpan (new Position (0,0), 360, 360) );

XAML

<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps" x:Class="MapDemo.MapPage">
    <StackLayout VerticalOptions="StartAndExpand" Padding="30">
        <maps:Map WidthRequest="320" HeightRequest="200" x:Name="MyMap" IsShowingUser="true" MapType="Hybrid" />
    </StackLayout>
</ContentPage>

Merk het toevoegen van de namespace op xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps".

Het verplaatsen naar een specifiek punt op de map dient dan weer in C# te gebeuren.

MyMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37,-122), Distance.FromMiles(1)));

In- en uitzoomen

Gebruik een Slider om het zoom-niveau in te stellen.

var slider = new Slider (1, 18, 1);
slider.ValueChanged += (sender, e) => {
    var zoomLevel = e.NewValue; // between 1 and 18
    var latlongdegrees = 360 / (Math.Pow(2, zoomLevel));
    map.MoveToRegion(new MapSpan (map.VisibleRegion.Center, latlongdegrees, latlongdegrees));
};

of via XAML en code-behind:

<Slider x:Name="SliderZoom" Maximum="18.0" Minimum="1.0" Value="1.0" ValueChanged="OnSliderChanged" />
private void OnSliderChanged(object sender, ValueChangedEventArgs e)
{
    var zoomLevel = e.NewValue; // between 1 and 18
    var latlongdegrees = 360 / (Math.Pow(2, zoomLevel));
    MyMap.MoveToRegion(new MapSpan(MyMap.VisibleRegion.Center, latlongdegrees, latlongdegrees));
}

Het verplaatsen naar de ingezoomde regio gebeurt via map.MoveToRegion(new MapSpan (map.VisibleRegion.Center, latlongdegrees, latlongdegrees));.

Maptype instellen

Het gewenste maptype kan u als volgt instellen:

map.MapType = MapType.Street;
map.MapType = MapType.Hybrid;
map.MapType = MapType.Satellite;

Geocode

U kent de plaats en wilt de coördinaten weten via await geoCoder.GetPositionsForAddressAsync ():

Geocoder geoCoder = new Geocoder ();
Label l = new Label ();

var NinoveAddress = "9400 Ninove, Belgium";
var approximateLocation = await geoCoder.GetPositionsForAddressAsync (NinoveAddress);
foreach (var p in approximateLocation) {
	l.Text += p.Latitude + ", " + p.Longitude + "\n";
}

U kent de coördinaten en wilt de plaats weten via await geoCoder.GetAddressesForPositionAsync ():

Geocoder geoCoder = new Geocoder ();
Label l = new Label ();

var NinovePosition = new Position (50.8210792541504, 3.96598601341248);
var possibleAddresses = await geoCoder.GetAddressesForPositionAsync (NinovePosition);
foreach (var a in possibleAddresses){
	l.Text += a + "\n";
}

Onderstaande code test of een ingevoerde locatie (in de Entry EntryLocation.Text) wel een bestaande plaats bevat. Vervolgens wordt er ingezoomd op deze plaats.

Geocoder geoCoder = new Geocoder ();
var item = (await geoCoder.GetPositionsForAddressAsync(EntryLocation.Text)).FirstOrDefault();
if (item == null)
{
    await DisplayAlert("Error", "Unable to decode position", "OK");
    return;
}

var zoomLevel = SliderZoom.Value; // between 1 and 18
var latlongdegrees = 360 / (Math.Pow(2, zoomLevel));
MyMap.MoveToRegion(new MapSpan(item, latlongdegrees, latlongdegrees));

Pinnen toevoegen

var position = new Position(37,-122); // Latitude, Longitude
var pin = new Pin {
            Type = PinType.Place,
            Position = position,
            Label = "custom pin",
            Address = "custom detail info"
        };
map.Pins.Add(pin);

Opmaak

Het zou me te verleiden om de opmaak van de map en de pinnen in detail te bespreken. Wie meer wil weten kan hier verder lezen.


Mappen – Praktisch voorbeeld

U kunt hier een praktisch voorbeeld downloaden.

  • Vergeet niet een key voor Android en/of UWP aan te vragen.
  • Op het ogenblik van dit schrijven veroorzaakte IsShowingUser = true, een fout, ik heb deze code dan ook moeten verwijderen (of in commentaar plaatsen)

GPS lokalisatie – integreren

Plug-in installeren

  • Klik rechts op de Solution om het NuGet package meteen te installeren voor alle projecten.
  • Vervolgens klikt u op Manage NuGet packages for Solution….
  • Klik op Browse.
  • Zoek naar Xam.Plugin.GeoLocator, selecteer alle projecten en de laatste stabiele versie en klik op Install.

Uw app moet toegang krijgen/vragen tot de lokalisatie van het toestel. De gedetailleerde uitleg leest u hier.

UWP

In UWP is dit vrij eenvoudig, u moet de applicatie enkel toegang geven tot de Location.

  • Open Properties onder het UWP-project.
  • Ga naar Application en klik op Package Manifest….
  • Selecteer het tabblad Capabilities en vink Location aan.

Android

Voor Android moeten onderstaande permissies worden toegevoegd aan de reeds ingestelde permissies (vertrekkende vanuit een nieuw project). Deze worden echter automatisch toegevoegd wanneer u het project compileert.

  • AccessCoarseLocation
  • AccessFineLocation

Deze plug-in bouwt verder op de Permission Plugin, die eveneens, automatisch, toegevoegd is. Deze Permission Plugin vraagt onderstaande aanpassing.

  • Voeg onderstaande code toe aan MainActivity.cs dat u eveneens vindt onder het Android-project.
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
    Plugin.Permissions.PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

De volledige code voor MainActivity.cs is:

using Android.App;
using Android.Content.PM;
using Android.OS;

namespace TestGeoLocationPlugin.Droid
{
    [Activity(Label = "TestGeoLocationPlugin", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
        {
            Plugin.Permissions.PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

iOS

Voor iOS dient u een aantal keys, en bijhorende meldingen die verschijnen wanneer u er gebruik van wilt maken, toevoegen aan het bestandje Info.plist. Dit zijn dezelfde als voor het gebruik van de map (zie hierboven).

  • Klik rechts Info.plist dat u vindt onder het iOS-project en kies voor Open with… en selecteer XML (Text) Editor.
  • Voeg onderstaande code toe.
<key>NSLocationWhenInUseUsageDescription</key>
<string>We are using your location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access location when open and in the background.</string>

GPS lokalisatie – gebruiken

Onderstaande code-snippets tonen hoe u mappen kan gebruiken.

Begin met de gewenste namespace toe te voegen.

using Plugin.Geolocator;

Uw locatie detecteren

var locator = CrossGeolocator.Current;
var position = await locator.GetPositionAsync(TimeSpan.FromSeconds(10));
 
Debug.WriteLine("Position Status: {0}", position.Timestamp);
Debug.WriteLine("Position Latitude: {0}", position.Latitude);
Debug.WriteLine("Position Longitude: {0}", position.Longitude);

of uitgebreid als een functie:

public async Task<Position> GetCurrentLocation()
{
  Position position = null;
  try
  {
    var locator = CrossGeolocator.Current;
    locator.DesiredAccuracy = 100;

    position = await locator.GetLastKnownLocationAsync();

    if (position != null)
    {
      //got a cahched position, so let's use it.
      return;
    }
	
    var available = await locator.GetIsGeolocationAvailableAsync();
    var enabled = await locator.GetIsGeolocationEnabled();

    if(!available || !enabled)
    {
      //not available or enabled
      return;
    }

    position = await locator.GetPositionAsync(TimeSpan.FromSeconds(20), null, true);

  }
  catch (Exception ex)
  {
    //Display error as we have timed out or can't get location.
  }

  if(position == null)
    return;

  var output = string.Format("Time: {0} \nLat: {1} \nLong: {2} \nAltitude: {3} \nAltitude Accuracy: {4} \nAccuracy: {5} \nHeading: {6} \nSpeed: {7}",
      position.Timestamp, position.Latitude, position.Longitude,
      position.Altitude, position.AltitudeAccuracy, position.Accuracy, position.Heading, position.Speed);

  Debug.WriteLine(output);
}

Gewijzigde positie

Maak hiertoe gebruik van de ingebouwde PositionChanged -event.

locator.PositionChanged += (sender, e) => {
    var position = e.Position;
    
    latitudeLabel.Text = position.Latitude;
    longitudeLabel.Text = position.Longitude;
};

of uitgebreid.

async Task StartListening()
{
  if(CrossGeolocator.Current.IsListening)
    return;
	
  await CrossGeolocator.Current.StartListeningAsync(TimeSpan.FromSeconds(5), 10, true);

  CrossGeolocator.Current.PositionChanged += PositionChanged;
  CrossGeolocator.Current.PositionError += PositionError;
}

private void PositionChanged(object sender, PositionEventArgs e)
{
  
  //If updating the UI, ensure you invoke on main thread
  var position = e.Position;
  var output = "Full: Lat: " + position.Latitude + " Long: " + position.Longitude;
  output += "\n" + $"Time: {position.Timestamp}";
  output += "\n" + $"Heading: {position.Heading}";
  output += "\n" + $"Speed: {position.Speed}";
  output += "\n" + $"Accuracy: {position.Accuracy}";
  output += "\n" + $"Altitude: {position.Altitude}";
  output += "\n" + $"Altitude Accuracy: {position.AltitudeAccuracy}";
  Debug.WriteLine(output);
} 

private void PositionError(object sender, PositionErrorEventArgs e)
{
  Debug.WriteLine(e.Error);
  //Handle event here for errors
} 

async Task StopListening()
{
  if(!CrossGeolocator.Current.IsListening)
    return;
	
  await CrossGeolocator.Current.StopListening();

  CrossGeolocator.Current.PositionChanged -= PositionChanged;
  CrossGeolocator.Current.PositionError -= PositionError;
}

Hier vindt u de volledige documentatie.


GPS lokalisatie + mappen – videovoorbeeld


Behandelde Basiscompetenties uit de module ICT Programmeren – Integratie externe functionaliteiten

  • IC BC024 – * kan zijn eigen deskundigheid inzake ICT opbouwen
  • IC BC232 – kan digitale tools gebruiken om modellen, simulaties en visualisaties van de realiteit te maken
  • IC BC254 – kan externe content integreren en structureren
  • IC BC258 – houdt rekening met regelgeving m.b.t. licenties voor het gebruik en de publicatie van broncode
  • IC BC288 – kan ICT-problemen oplossen

Geef een reactie

Deze website gebruikt Akismet om spam te verminderen. Bekijk hoe je reactie-gegevens worden verwerkt.

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.