Pokédex – Kleines Xamarin.Forms Projekt – Teil 7

Ich habe bereits sechs Teile dieser kleinen Xamarin.Forms Serie in meinem Blog veröffentlicht. Im ersten Beitrag haben wir begonnen unsere Pokédex-App zu erstellen und ein generelles Grundsetup durchgeführt. Im zweiten Beitrag ging es mit der Entwicklung weiter. So haben wir die ersten Services und auch die ersten Calls in Richtung der öffentliche API gemacht und diese Informationen in der App anzeigen. Im dritten Beitrag haben wir begonnen die Pokémon-Details abzurufen und per inkrementellem Laden auf der Übersichtsseite anzuzeigen. Im vierten Beitrag haben wir ein kleines Popup entwickelt, welches uns weitere Details anzeigt, sofern der Nutzer einen Eintrag in der Übersicht angeklickt hat. Im fünften Teil dieser Serie haben wir noch ein paar Anpassungen vornehmen und unseren Code ein wenig aufräumen bzw. optimieren. Im sechsten Beitrag folgten dann die App-Icons für die verschiedenen Plattformen hinzufügen. In nun vorliegenden siebten Beitrag wollen wir noch einen kleinen SplashScreen erstellen und hinzufügen und damit wäre unsere App zum jetzigen Zeitpunkt vollständig.

Den bisherigen Stand könnt ihr über folgenden Download-Button herunterladen.

Beginnen möchte ich dieses Mal mit UWP, denn hier ist das Hinzufügen eines SplashScreens sehr einfach. Wir öffnen die Datei Package.appxmanifest und wechseln auf den Tab Visual Assets. Im letzten Beitrag haben wir ja bereits das App-Icon hinzugefügt und gleichzeitig wurde das Bild auch für den SplashScreen verwendet. Wir müssen nur noch in das Feld Splash screen background unsere Farbe #1E3D59 eintragen und wenn wir die App nun starten, so sehen wir zu Beginn den Pokéball auf blauem Hintergrund.

Nun wollen wir uns Android näher anschauen. Im Ordner drawable, welcher sich im Ordner Resources in unserem Android-Projekt befindet, müssen wir zunächst ein Bild mit dem Namen splash_logo.png ablegen. Hierfür nutzen wir einfach eines unserer App-Icons. Anschließend erstellen wir eine neue XML-Datei in dem Ordner drawable mit den Namen splash_screen.xml. Der folgende Code-Ausschnitt zeigt euch den Inhalt dieser Datei.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
	<item>
		<color android:color="@color/splash_background"/>
	</item>
	<item android:width="150dp"
		  android:height="150dp"
		  android:gravity="center">
		<bitmap android:src="@drawable/splash_logo"
				android:tileMode="disabled" />
	</item>
</layer-list>

Ihr seht, dass wir auf eine Farbe splash_background verweisen, welche wir bisher noch nicht angelegt haben. Dafür öffnen wir im Ordner values die Datei colors.xml und fügen einen Eintrag mit unserem Blau hinzu.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="launcher_background">#FFFFFF</color>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
	<color name="splash_background">#1E3D59</color>
</resources>

Nun erstellen wir in der Datei styles.xml noch einen Style für unseren SplashScreen. Hierbei verweisen wir auf das gerade angelegte Drawable und setzen noch ein paar Properties, so dass der SplashScreen auch im Fullscreen angezeigt wird.

<?xml version="1.0" encoding="utf-8" ?>
<resources>

	<style name="Pokedex.Splash" parent ="Theme.AppCompat.Light.NoActionBar">
		<item name="android:windowBackground">@drawable/splash_screen</item>
		<item name="android:windowNoTitle">true</item>
		<item name="android:windowFullscreen">true</item>
		<item name="android:windowContentOverlay">@null</item>
		<item name="android:windowActionBar">true</item>
	</style>

	<style name="MainTheme" parent="MainTheme.Base">
	</style>
</resources>

Nun müssen wir auf gleicher Ebene wie die MainActivity eine weitere Activity mit dem Namen SplashActivity erstellen. Dieser weisen wir unseren Style hinzu und außerdem setzen wir die Eigenschaft MainLauncher auf true und NoHistory ebenfalls auf true.

[Activity(Theme = "@style/Pokedex.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : AppCompatActivity
{
    protected override void OnResume()
    {
        base.OnResume();

        StartActivity(new Intent(Application.Context, typeof(MainActivity)));
    }
}

Da nur eine Activity die Eigenschaft MainLauncher besitzen darf, müssen wir den Eintrag in der MainActivity jetzt entfernen. Außerdem ergänzen wir noch das Setzen der Farbe der StatusBar in der MainActivity.

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

    Rg.Plugins.Popup.Popup.Init(this);
    FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true);

    Platform.Init(this, savedInstanceState);
    Forms.Init(this, savedInstanceState);
    FormsMaterial.Init(this, savedInstanceState);

    LoadApplication(new App());

    if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            var activity = Platform.CurrentActivity;
            var window = activity.Window;

            // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
            window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);

            // clear FLAG_TRANSLUCENT_STATUS flag:
            window.ClearFlags(WindowManagerFlags.TranslucentStatus);

            // set status bar
            window.SetStatusBarColor(Android.Graphics.Color.Argb(255, 30, 61, 89));
        });
    }
}

Wenn wir unsere App nun auf unserem Android-Device starten, so sehen wir zum einen den neuen SplashScreen und zum anderen auch die passend eingefärbte StatusBar.

Abschließend kümmern wir uns noch um iOS. Hierfür müssen wir im iOS-Projekt im Ordner Resources ebenfalls ein Icon hinzufügen, welches wir splashscreen.png nennen wollen und unseren Pokéball beinhaltet. Außerdem erstellen wir uns noch ein einfarbiges Hintergrundbild, welches wir background.png nennen und ebenfalls im Ordner Resources ablegen.

Nun öffnen wir die Datei LaunchScreen.storyboard und tauschen den Inhalt aus. Hierbei erstellen wir eine Kombination aus dem Hintergrundbild und dem SplashScreen-Bild.

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB"
		  version="3.0"
		  toolsVersion="17701"
		  targetRuntime="iOS.CocoaTouch"
		  propertyAccessControl="none"
		  useAutolayout="YES"
		  useTraitCollections="YES"
		  colorMatched="YES"
		  initialViewController="X5k-f2-b5h">
	<device id="retina5_5"
			orientation="portrait"
			appearance="light"/>
	<dependencies>
		<deployment identifier="iOS"/>
		<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin"
				version="17703"/>
		<capability name="documents saved in the Xcode 8 format"
					minToolsVersion="8.0"/>
	</dependencies>
	<scenes>
		<!--View Controller-->
		<scene sceneID="gAE-YM-kbH">
			<objects>
				<viewController id="X5k-f2-b5h"
								sceneMemberID="viewController">
					<layoutGuides>
						<viewControllerLayoutGuide type="top"
												   id="Y8P-hJ-Z43"/>
						<viewControllerLayoutGuide type="bottom"
												   id="9ZL-r4-8FZ"/>
					</layoutGuides>
					<view key="view"
						  contentMode="scaleToFill"
						  id="yd7-JS-zBw">
						<rect key="frame"
							  x="0.0"
							  y="0.0"
							  width="414"
							  height="736"/>
						<autoresizingMask key="autoresizingMask"
										  widthSizable="YES"
										  heightSizable="YES"/>
						<subviews>
							<imageView clipsSubviews="YES"
									   userInteractionEnabled="NO"
									   contentMode="scaleAspectFill"
									   horizontalHuggingPriority="251"
									   verticalHuggingPriority="251"
									   image="background.png"
									   translatesAutoresizingMaskIntoConstraints="NO" 
									   id="NNH-I9-bfx">
								<rect key="frame"
									  x="0.0"
									  y="0.0"
									  width="414"
									  height="736"/>
								<color key="tintColor"
									   white="0.0"
									   alpha="0.0"
									   colorSpace="custom"
									   customColorSpace="genericGamma22GrayColorSpace"/>
							</imageView>
							<imageView clipsSubviews="YES"
									   userInteractionEnabled="NO"
									   contentMode="scaleAspectFit"
									   horizontalHuggingPriority="251"
									   verticalHuggingPriority="251"
									   image="splashscreen.png"
									   translatesAutoresizingMaskIntoConstraints="NO"
									   id="Hpd-Cl-JOs">
								<rect key="frame"
									  x="107"
									  y="268"
									  width="200"
									  height="200"/>
								<constraints>
									<constraint firstAttribute="width"
												constant="200"
												id="Ame-tf-ABy"/>
									<constraint firstAttribute="height"
												constant="200"
												id="h1H-V7-tCL"/>
								</constraints>
							</imageView>
						</subviews>
						<color key="backgroundColor"
							   white="0.0"
							   alpha="0.0"
							   colorSpace="custom"
							   customColorSpace="genericGamma22GrayColorSpace"/>
						<color key="tintColor"
							   white="0.0"
							   alpha="0.0"
							   colorSpace="custom"
							   customColorSpace="genericGamma22GrayColorSpace"/>
						<constraints>
							<constraint firstItem="NNH-I9-bfx"
										firstAttribute="leading"
										secondItem="yd7-JS-zBw"
										secondAttribute="leading"
										id="1p6-kC-3gq"/>
							<constraint firstItem="9ZL-r4-8FZ"
										firstAttribute="top"
										secondItem="NNH-I9-bfx"
										secondAttribute="bottom"
										id="6oC-iY-XWk"/>
							<constraint firstItem="Hpd-Cl-JOs"
										firstAttribute="centerY"
										secondItem="yd7-JS-zBw"
										secondAttribute="centerY"
										id="7DU-SQ-9MR"/>
							<constraint firstAttribute="trailing"
										secondItem="NNH-I9-bfx"
										secondAttribute="trailing"
										id="HKv-81-TlY"/>
							<constraint firstItem="NNH-I9-bfx"
										firstAttribute="top"
										secondItem="Y8P-hJ-Z43"
										secondAttribute="bottom"
										id="ivN-Gn-Uvr"/>
							<constraint firstItem="Hpd-Cl-JOs"
										firstAttribute="centerX"
										secondItem="yd7-JS-zBw"
										secondAttribute="centerX"
										id="z6q-BY-lxe"/>
						</constraints>
					</view>
				</viewController>
				<placeholder placeholderIdentifier="IBFirstResponder"
							 id="XAI-xm-WK6"
							 userLabel="First Responder"
							 sceneMemberID="firstResponder"/>
			</objects>
			<point key="canvasLocation"
				   x="349"
				   y="339"/>
		</scene>
	</scenes>
	<resources>
		<image name="background.png"
			   width="831"
			   height="1800"/>
		<image name="splashscreen.png"
			   width="642"
			   height="642"/>
	</resources>
</document>

Wenn die App nun auf dem iOS-Simulator gestartet wird, so sehen wir auch hier den neuen SplashScreen.

Damit sind wir jetzt tatsächlich am Ende dieser kleinen Serie angekommen. Wir haben eine kleine, aber feine App geschrieben, welche sowohl unter Android, iOS als auch UWP zur Verfügung steht. Dabei ruft die App Daten aus dem Internet ab und speichert diese lokal in einer Datenbank. Ich hoffe, dass ihr genauso viel Spaß beim Nachprogrammieren hattet, wie ich beim Schreiben dieser Serie.

Abschließend stelle ich euch den fertigen Code natürlich über den folgenden Download-Button zur Verfügung.

Pokédex – Kleines Xamarin.Forms Projekt – Teil 6 Foto-Galerie App mit Xamarin.Forms AppCenter.Analytics: Einblick in die Nutzung eurer App