Ich arbeite derzeit an der Portierung einer Xamarin Forms App zu .NET MAUI. Die App verwendet auch Karten von Apple oder Google Maps, um Standorte anzuzeigen. Obwohl es bis zur Veröffentlichung von .NET 7 keine offizielle Unterstützung in MAUI gab, möchte ich Ihnen eine Möglichkeit zeigen, Karten über einen benutzerdefinierten Handler anzuzeigen.
Dieser Artikel ist eine gemeinsame Arbeit von Giulien und mir. Während er sich hauptsächlich um die App und den grundlegenden iOS-Handler kümmerte, habe ich die Dokumentation, den Android-Handler und die erweiterte iOS Map-Handler-Implementierung erstellt. In diesem Artikel werden wir die folgenden Themen behandeln:
Wenn Sie einfach nur eine Karte anzeigen möchten, reicht es aus, die integrierte Lösung mit .NET7-Unterstützung zu verwenden. Dazu müssen Sie nur die folgenden drei Schritte ausführen:
UseMauiMaps()
zum Builder hinzu.Wenn Ihnen dieser Ansatz nicht ausreicht, lesen Sie weiter und erstellen Sie Ihren eigenen Google Maps und Apple Maps Handler.
Wenn Sie ein Projekt in Xamarin oder .NET MAUI haben, können wir Ihnen Zeit sparen.
Bevor Sie alles selbst in die Hand nehmen, werfen Sie einen Blick auf unsere Dienstleistungen. Unser Team aus erfahrenen Entwicklern freut sich darauf, Ihr Projekt zu besprechen und Ihnen dabei zu helfen.
Werfen Sie einen Blick auf unsere Entwicklungsleistungen
Damit MAUI als plattformübergreifendes UI-Framework funktionieren kann, muss es wissen, welche nativen Steuerelemente je nach Plattform verwendet werden sollen, wenn jemand z. B. eine Schaltfläche oder ein Etikett in der Benutzeroberfläche definiert. Wenn zum Beispiel jemand einen Button in .NET MAUI definiert, entscheidet der plattformspezifische Handler, einen UIButton auf iOS zu verwenden.
Dem Diagramm aus der offiziellen Dokumentation folgend, haben wir unsere Zielarchitektur unten dargestellt. Um einen schnelleren Start zu ermöglichen, haben wir uns entschieden, den Code für die Kartenansicht aus der Xamarin Forms-Implementierung zu kopieren und eine entsprechende IMap-Schnittstelle zu erstellen. Sie finden die Implementierung für die Map-Klasse im Xamarin Forms GitHub Repository. Sie ist auch eine Inspiration für fortgeschrittenere Themen wie die Anzeige von Pins oder das Setzen einer bestimmten Position auf der Karte. Mit der Veröffentlichung von .NET MAUI für .NET 7 müssen Sie diese Dateien nicht mehr kopieren, da sie jetzt zusammen mit den Handlern Teil des .NET MAUI-Frameworks sind.
Ein Handler in .NET MAUI wird auch verwendet, um Funktionen zu bestehenden Steuerelementen im Framework hinzuzufügen oder um sie mit vollständig benutzerdefinierten Implementierungen zu erweitern. Wenn Sie bereits mit dem Prinzip der benutzerdefinierten Renderer von Xamarin Forms vertraut sind, wird der Wechsel zu Handlern in MAUI nicht schwierig sein.
Um .NET MAUI mit einem neuen Map Control zu erweitern, benötigen Sie einen BaseHandler für den plattformunabhängigen Teil und die Implementierungen für jede zu unterstützende Plattform. In unserem Fall Android und iOS. Der BaseHandler besteht im Wesentlichen nur aus den Methoden und Eigenschaften, die unser Map-Handler zur Verwendung bereitstellen will.
public partial class MapHandler
{
public static IPropertyMapper<IMap, MapHandler> MapMapper = new PropertyMapper<IMap, MapHandler>(ViewMapper)
{ };
public MapHandler() : base(MapMapper)
{ }
}
Es ist wichtig zu erwähnen, dass wir nicht gegen das konkrete Map Control arbeiten, sondern gegen die IMap-Schnittstelle. Unsere plattformspezifischen Handler-Implementierungen werden von der generischen ViewHandler-Implementierung abgeleitet. Damit müssen drei wichtige Methoden implementiert werden:
Aufgrund der Tatsache, wie die Apple Maps API aufgebaut ist, ist es einfacher, mit dem Schreiben eines MAUI Maps Handlers zu beginnen. Wir müssen eine iOS-MapHandler-Klasse erstellen, die auch eine partielle Klasse mit demselben Namen ist, aber im plattformspezifischen Ordner abgelegt ist. Wie bereits erwähnt, haben wir von der generischen ViewHandler-Implementierung abgeleitet, in der wir unser Karten-Steuerelement mit dem iOS-spezifischen MKMapView-Steuerelement verbinden. Ein vereinfachter Apple Maps MAUI-Handler kann wie die folgende Implementierung aussehen.
public partial class MapHandler : ViewHandler<Map, MKMapView>
{
public MapHandler(IPropertyMapper mapper, CommandMapper commandMapper = null)
: base(mapper, commandMapper)
{ }
protected override MKMapView CreatePlatformView()
{
return new MKMapView(CoreGraphics.CGRect.Empty);
}
protected override void ConnectHandler(MKMapView PlatformView)
{ }
protected override void DisconnectHandler(MKMapView PlatformView)
{
// Clean-up the native view to reduce memory leaks and memory usage
if (PlatformView.Delegate != null)
{
PlatformView.Delegate.Dispose();
PlatformView.Delegate = null;
}
PlatformView.RemoveFromSuperview();
}
}
Mac und iOS teilen sich die zugrunde liegenden Frameworks und APIs für die Handhabung der Benutzeroberfläche. Auf diese Weise erhalten wir durch Kopieren des Handlers in den MacCatalyst-Ordner des Projekts auch diese Plattform unterstützt.
Damit Google Maps in unserer .NET MAUI-Anwendung funktioniert, müssen wir einige Vorarbeiten leisten.
Zunächst müssen wir das NuGet-Paket Xamarin.GooglePlayServices.Maps referenzieren. Dann müssen verschiedene Einträge in AndroidManifest.xml vorgenommen werden, damit Google Maps einen API-Schlüssel verwenden kann. Der beste Weg, dies zu tun, ist, die Xamarin Google Maps-Dokumentation zu lesen.
Werfen wir einen Blick auf den .NET MAUI Android Handler. Damit wir mit dem Google Maps-Steuerelement interagieren können, müssen wir auf den OnMapReady-Callback warten. Leider stoßen wir auf eine InvalidCastException, wenn wir versuchen, die IOnMapReady-Schnittstelle direkt in unserem ViewHandler zu implementieren und zu verwenden. Die Lösung für dieses Problem ist unsere MapHelper-Klasse. Sie wird von Java.Lang.Object abgeleitet und macht es uns daher leicht, die IOnMapReady-Schnittstelle zu implementieren.
public partial class MapHandler : ViewHandler<MapView, Android.Gms.Maps.MapView>
{
private MapHelper _mapHelper;
internal static Bundle Bundle { get; set; }
public MapHandler(IPropertyMapper mapper, CommandMapper commandMapper = null)
: base(mapper, commandMapper)
{ }
protected override Android.Gms.Maps.MapView CreatePlatformView()
{
return new Android.Gms.Maps.MapView(Context);
}
protected override void ConnectHandler(Android.Gms.Maps.MapView platformView)
{
base.ConnectHandler(platformView);
_mapHelper = new MapHelper(Bundle, platformView);
_mapHelper.MapIsReady += _mapHelper_MapIsReady;
_mapHelper.CallCreateMap();
}
private void _mapHelper_MapIsReady(object sender, EventArgs e)
{
_mapHelper.Map.UiSettings.ZoomControlsEnabled = true;
_mapHelper.Map.UiSettings.CompassEnabled = true;
}
}
class MapHelper : Java.Lang.Object, IOnMapReadyCallback
{
private Bundle _bundle;
private Android.Gms.Maps.MapView _mapView;
public event EventHandler MapIsReady;
public GoogleMap Map { get; set; }
public MapHelper(Bundle bundle, Android.Gms.Maps.MapView mapView)
{
_bundle = bundle;
_mapView = mapView;
}
public void CallCreateMap()
{
_mapView.OnCreate(_bundle);
_mapView.OnResume();
_mapView.GetMapAsync(this);
}
public void OnMapReady(GoogleMap googleMap)
{
Map = googleMap;
MapIsReady?.Invoke(this, EventArgs.Empty);
}
}
Zu guter Letzt müssen wir das Android OnCreate Lifecycle Event an unsere MapView übergeben. Dazu benötigen wir ein Android Bundle.
Alles ist bereits im vorherigen Code enthalten. Es gibt eine statische Bundle-Eigenschaft, die wir nun setzen müssen. Dazu wechseln wir in die MainActivity des Android-Projekts und passen den Code entsprechend an.
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
MapHandler.Bundle = savedInstanceState;
}
}
Wenn Sie eine Meldung sehen wie: Warnung: Warnung XA4212: Typ
MapControlDemo.Handlers.MapHandler
implementiertAndroid.Runtime.IJavaObject
, erbt aber nichtJava.Lang.Object
oderJava.Lang.Throwable
. Dies wird nicht unterstützt. (MapControlDemo)Das Hinzufügen des folgenden PropertyGroup-Eintrags in Ihr csproj sollte dies beheben:
<PropertyGroup Condition="$(TargetFramework.Contains('-android'))"> <AndroidErrorOnCustomJavaObject>false</AndroidErrorOnCustomJavaObject> </PropertyGroup>
Da Sie es bis hierher geschafft haben, gehen wir davon aus, dass Sie ein gewisses Interesse an unserer Arbeit gewonnen haben. Das ist völlig in Ordnung. Seien Sie unser Gast und geben Sie uns ein Feedback oder sprechen wir über Ihr Projekt. Wir freuen uns, von Ihnen zu hören.
Reden wir
Damit unsere .NET MAUI-Anwendung den Map-Handler verwenden kann, muss er noch registriert werden. Gehen Sie dazu in die Datei MauiProgram.cs und fügen Sie die folgenden Zeilen in den Builder ein:
.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(MapHandlerDemo.Maps.Map),typeof(MapHandler));
})
Diese Zeile teilt MAUI mit, dass jedes Mal, wenn ein Aufruf von MapHandlerDemo.Maps.Map erfolgt, MAUI den MapHandler zum Rendern und Anzeigen dieses Steuerelements verwenden soll. Wenn der Handler nicht korrekt registriert wurde, wird jedes Mal, wenn Sie versuchen, eine Instanz des Handlers zu verwenden, die folgende Fehlermeldung ausgelöst: „System.Exception are thrown: Handler not found for view...“.
Nun ist es an der Zeit, sich die Karte anzusehen. Der Einfachheit halber habe ich eine neue ContentPage für unsere Karte direkt im Haupteinstiegspunkt erstellt und sie als Hauptseite festgelegt.
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MapsPage();
}
}
public class MapsPage : ContentPage
{
public MapsPage()
{
Content = new Maps.Map();
}
}
Je nach gewählter Plattform entspricht das Ergebnis der unten stehenden Grafik.
Wir haben eine Karte in unsere .NET MAUI-App integriert. Sie können nun die Funktionalität und das Verhalten nach Ihren Wünschen anpassen und erweitern. Auch der komplette Austausch des Kartenanbieters, z.B. statt Google Maps einfach Open Street Maps verwenden, ist nun möglich.
Mit der neuen Projektstruktur und dem Handler-Konzept von .NET MAUI können plattformspezifische Steuerelemente recht schnell und einfach integriert werden. Ob Sie partielle Klassen mögen oder nicht, bleibt Ihnen überlassen. Was mir aber auf jeden Fall gefällt, ist der saubere und strukturierte Aufbau der Handler. Das Demoprojekt und alle Dateien sind auf dem öffentlichen Cayas Bitbucket zu finden.
Als Mobile-Enthusiast und Geschäftsführer der Cayas Software GmbH ist es mir ein großes Anliegen, mein Team und unsere Kunden zu unterstützen, neue potenziale zu entdecken und gemeinsam zu wachsen. Hier schreibe ich vor allem zur Entwicklung von Android und iOS-Apps mit Xamarin und .NET MAUI.
.NET MAUI ermöglicht es uns, plattform- und geräteunabhängige Anwendungen zu schreiben, was eine dynamische Anpassung an die Bildschirmgröße und -form des Benutzers erforderlich macht. In diesem Blog-Beitrag erfahren Sie, wie Sie Ihre XAML-Layouts an unterschiedliche Geräteausrichtungen anpassen können. Dabei verwenden Sie eine ähnliche Syntax wie OnIdiom und OnPlatform, die Ihnen vielleicht schon bekannt ist.
This post is a continuation of the Hackathon topic post, where the technical implementation of voice commands in .NET MAUI is revealed, as well as the challenges the development team faced and how they successfully solved them.
As mobile app developer, we constantly have the need to exchange information between the app and the backend. In most cases, a RESTful-API is the solution. But what if a constant flow of data exchange in both directions is required? In this post we will take a look at MQTT and how to create your own simple chat app in .NET MAUI.