Über unsMediaKontaktImpressum
Prof. Dr. Erik Behrends 20. März 2018

React Native: Einstieg in die Entwicklung mobiler Apps

Wer eine App für Smartphones entwickeln möchte, dem stehen zahlreiche verschiedenen Ansätze zur Verfügung. So muss man sich z. B. entscheiden, ob eine mobile Webanwendung genügt oder ob eine native App für besondere Funktionen erforderlich ist. Das quelloffene Framework React Native ermöglicht es, native Apps plattformübergreifend und parallel für Android und iOS in JavaScript zu programmieren. Dieser Artikel zeigt anhand der Programmierung eines einfachen Spiels, wie der Einstieg in die App-Entwicklung mit React Native gelingt.

Das 2015 von Facebook als Open Source-Projekt vorgestellte Framework React Native besticht durch viele vorteilhafte Eigenschaften. Parallele Entwicklung von performanten, nativen Apps auf einer Codebasis für Android, iOS und weitere Zielplattformen mit vielen nützlichen Werkzeugen, die für eine hohe Produktivität sorgen, sind dabei besonders hervorzuheben [1]. Daher gehört React Native mit mehr als 60.000 Sternen zu den populärsten Projekten auf GitHub [2] und viele namhaften Unternehmen wie z. B. Airbnb und Tesla verwenden das Framework zur Entwicklung ihrer Apps [3].

Ein Spiel zum Gedächtnistraining

Sie werden ein einfaches Spiel entwickeln, das Ihr Kurzzeitgedächtnis trainiert. Wie in Abb. 1 zu sehen, besteht die App aus drei farbigen Quadraten, die untereinander angeordnet sind. Ganz unten befindet sich ein Button, mit dem Sie das Spiel starten. Nach Spielstart blinkt fünfmal hintereinander eins der drei Quadrate in zufälliger Reihenfolge auf. Danach müssen Sie genau diese Reihenfolge durch Drücken der passenden Quadrate reproduzieren, um eine Spielrunde zu gewinnen.

Vorbereitung: Apps im Browser entwickeln

Um eine mobile App mit React Native zu entwickeln, muss nichts auf dem Rechner installiert werden – es genügt ein Webbrowser. Expo Snack ist ein Dienst im Web, der eine Programmierumgebung für React Native direkt im Browser bereitstellt. Wie Abb. 01 zeigt, wird der Quelltext der App in einem Editorbereich bearbeitet und rechts daneben stellt eine Vorschau die App wahlweise in einem virtuellen Android-Gerät oder iPhone dar. Die Vorschau kann durch den Steuerungsknopf Preview in der Fußleiste aktiviert werden. Wenn Sie möchten, dann können Sie das Spiel sofort online ausprobieren. Es ist bei Expo Snack im Browser verwendbar [4].

Weil die Vorschau der App im Browser allerdings einigen Platz einnimmt und manchmal erst nach einer gewissen Wartezeit gestartet wird, empfehle ich die Verwendung der App Expo [5], die Sie kostenlos für Android oder iOS im jeweiligen AppStore erhalten [6]. Eine Suche nach Expo Client im AppStore führt Sie ebenso zu dieser App.

Expo Snack erzeugt für eine App einen eindeutigen QR-Code, der mit Hilfe des Knopfs QR Code im Browser angezeigt werden kann. In der Expo-App auf dem Smartphone gibt es eine Funktion, mit der dieser QR-Code eingescannt werden kann, sodass die App direkt auf das Handy geladen und dort ausgeführt wird. Expo Snack und die Expo-App sind Beispiele für die hervorragenden Werkzeuge im Umfeld von React Native. Insbesondere mit Expo Snack ist der Einstieg in die Programmierung mit React Native sofort möglich.

Selbstverständlich stellt React Native auch eine umfassende Entwicklungsumgebung bereit, die auf dem eigenen Rechner eingerichtet werden kann. Da dies aber einiges an Zeit beansprucht, finden Sie dazu die Hinweise erst am Ende des Artikels. Nun werden wir beginnen, die App zu programmieren!

Erste Schritte mit Expo Snack

Ich werde nun die nötigen Entwicklungsschritte für die Beispiel-App beschreiben. Wenn Sie die aufgelisteten Code-Fragmente im Browser in Expo Snack eingeben, erhalten Sie bereits erste Eindrücke, wie mit React Native programmiert wird.

Ich habe für diesen Artikel den Ausgangspunkt des Programmcodes in Expo Snack bereitgestellt [7]. Wenn Sie im Browser zu Expo Snack navigieren, dann sehen Sie zunächst einen QR-Code für die dargestellte App. Wenn Sie die App Expo auf dem Smartphone installiert haben, dann können Sie dort den QR-Code einscannen, um die App auf dem Handy auszuführen. Ansonsten können Sie den angezeigten QR-Code schließen und die Vorschau der App im Browser durch den Schalter Preview unten auf der Webseite öffnen. Außerdem empfehle ich, die Dateiansicht (Files) und die Komponentenliste (Components) mit den entsprechenden Schaltern in der Fußleiste auszublenden, sodass Sie mehr Platz für den Editor erhalten. Wenn Sie die Vorschau der App im Browser nutzen möchten, dann könnte eine gewisse Wartezeit bis zum Start der Vorschau nötig sein.

Wenn Sie Ihren Code nun ändern und zwischenspeichern möchten, dann können Sie den Knopf Save changes drücken. Beachten Sie jedoch, dass Sie dann jedes Mal eine neue URL erhalten. Wenn Sie die App auf dem Smartphone mit Expo neu laden möchten, dann können Sie das Gerät kurz hin- und herschütteln, sodass ein Menü mit nützlichen Funktionen erscheint. Beachten Sie auch, dass bei Eingabe des Codes im Editor zwischenzeitlich Fehler in der App entstehen können, da Expo Snack den Programmcode sofort bei jeder Änderung ausführt. In der App erscheint dann ein roter Bildschirm mit der Fehlermeldung. Lassen Sie sich davon nicht irritieren, denn wenn der eingegebene Quelltext schließlich korrekt ist, wird auch die App wie erwartet dargestellt.

Eine einfache Komponente

Kommen wir nun zum Code der für Sie vorbereiteten App. Im Editorbereich ist der Inhalt der Datei App.js zu sehen. Dort wird eine einfache Komponente deklariert. Für eine App, die mit React Native und Expo entwickelt wird, ist App.js der Einstiegspunkt und die dort enthaltene Komponente wird dargestellt, wenn die App geöffnet wird.

Folgendes Listing enthält den Programmcode in App.js, so wie er im Editor von Expo Snack im Browser enthalten ist:

import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.box} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  box: {
    backgroundColor: 'red',
    width: 150,
    height: 150
  }
});

Der Code besteht aus drei Abschnitten, die jeweils durch eine leere Zeile voneinander getrennt sind:

  1. import-Anweisungen für benötigte APIs und Komponenten,
  2. Definition einer eigenen Komponente als Klasse (class) und
  3. Deklaration von Styles für die Darstellung und das Layout der Komponente

Wenn Sie eine App mit React Native programmieren, dann bedeutet dies im Wesentlichen, eigene Komponenten zu implementieren und diese zusammen mit anderen Komponenten zu verwenden. Hierbei kommen häufig moderne Sprachkonzepte von JavaScript zum Einsatz, die mit ECMAScript 2015 (ES 2015) und späteren Versionen eingeführt wurden. Hier zeigt sich dies z. B. bei den import-Anweisungen und der Klassendefinition mit class. Jede eigene Komponente wird in der Regel als Subklasse von Component deklariert und muss mindestens die Methode render implementieren, welche wiederum einen Ausdruck mit return liefert, der das UI deklarativ beschreibt. Im Falle dieser Komponente ist dies folgendes Fragment:

<View style={styles.container}>
  <View style={styles.box} />
</View>

Hierbei handelt es sich um Markup, das in einer Syntax namens JSX deklariert wird. JSX erinnert zwar an HTML oder XML, wird jedoch vor Ausführung des Programmcodes in JavaScript übersetzt. Hier enthält ein äußeres View-Element eine weitere View-Komponente. In React Native entspricht View in etwa einem div in HTML – ein Blockelement ohne besonderes Erscheinungsbild. Die beiden View-Elemente haben jeweils ein style-Attribut, das auf zusammengefasste Styling-Eigenschaften verweist.

Styling in React Native

In React Native gibt es eine API namens StyleSheet, welche die Deklaration von Styles erleichtert. Diese API wird am Anfang des Codes neben den anderen benötigten Komponenten und APIs importiert. Auch Styles werden in JavaScript definiert, wobei eine gewisse Ähnlichkeit zu CSS vorhanden ist. Bei Styles handelt es sich im Prinzip um Objekte, die aus Eigenschaften bestehen, in welchen wiederum beliebige Styling-Anweisungen enthalten sind. Beispielsweise werden in der Eigenschaft container drei Anweisungen zusammengefasst:

container: {
  flex: 1,
  alignItems: 'center',
  justifyContent: 'center',
},

Mit flex: 1 wird die Komponente eine maximale Ausdehnung annehmen. Ihr Inhalt wird analog zum Flexbox-Layout aus der CSS-Spezifikation aufgrund von justifyContent: 'center' entlang der vertikalen Hauptauchse und zusätzlich senkrecht zu dieser Achse zentriert (durch alignItems: 'center'). Die View-Komponente, die durch die Styles in styles.box als rotes Quadrat angezeigt wird, erscheint daher in der Mitte des Bildschirms.

Durch die Angabe der Styles in der Datei, in der auch die Komponente deklariert wird, ist die Komponente selbstbeschreibend und ermöglicht ihre Wiederverwendung, da die komplette Information für ihre Darstellung (render und styles) an einer Stelle zusammengefasst ist. Dies ist einer der Vorteile der komponentenbasierten Programmierung, die React Native durch die zugrundeliegende Webbibliothek React erhält.

Drei Quadrate und einen Button anzeigen

Im nächsten Schritt fügen Sie der App zwei weitere Quadrate hinzu und stellen am unteren Bildschirmrand einen Button dar. Das folgende Listing beinhaltet die nötigen Änderungen:

import React, { Component } from 'react';
import { Button, View, StyleSheet } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.box} />
        <View style={styles.box} />
        <View style={styles.box} />
        <Button title="Neues Spiel" onPress={() => alert('Klick!')} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
    paddingTop: 30
  },
  box: {
    backgroundColor: 'red',
    width: 150,
    height: 150
  }
});

In den import-Anweisungen ist zusätzlich eine Button-Komponente enthalten und render stellt nun drei Quadrate (View-Elemente) untereinander gefolgt von einem Button dar. Ein Button benötigt ein title-Attribut zur Beschriftung (hier "Neues Spiel") und onPress definiert das Verhalten beim Antippen des Knopfs: eine anonyme Pfeilfunktion (eine weitere wichtige Neuerung in der Version ES 2015 von JavaScript) stellt ein Hinweisfenster mit alert dar:

onPress={() => alert('Klick!')}

Auch das Styling für das äußere View-Element (container) hat Änderungen erhalten. Damit die Quadrate mit vertikalem Abstand zueinander erscheinen, hat justifyContent nun den Wert 'space-around' und mit paddingTop: 30 bekommt das erste Quadrat etwas Abstand zum oberen Bildschirmrand.

Wie in Abb. 2 zu sehen, erscheint ein Button als natives UI-Element passend zur jeweiligen Plattform. Gemäß der UI-Richtlinien besteht dieser auf dem iPhone lediglich aus dem Text und in Android ist dies ein Button bestehend aus einer blauen, leicht schwebenden Fläche, die mit Großbuchstaben beschriftet ist.

Touchable-Komponenten einbinden

Jetzt fehlen nur noch zwei Anpassungen, damit die App wie im Spiel gewünscht aussieht: Die drei Quadrate sollen in blau, rot und grün erscheinen und es soll möglich sein, diese anzutippen. Führen Sie dazu folgende Änderungen durch:

import React, { Component } from 'react';
import { Button, TouchableOpacity, View, StyleSheet } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity>
          <View style={[styles.box, { backgroundColor: 'blue' }]} />
        </TouchableOpacity>
        <TouchableOpacity>
          <View style={[styles.box, { backgroundColor: 'red' }]} />
        </TouchableOpacity>
        <TouchableOpacity>
          <View style={[styles.box, { backgroundColor: 'green' }]} />
        </TouchableOpacity>
        <Button title="Neues Spiel" onPress={() => alert('Klick!')} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
    paddingTop: 30,
  },
  box: {
    width: 150,
    height: 150,
  },
});

Die Komponente TouchableOpacity wird importiert und jedes View-Element, das ein Quadrat repräsentiert, wird nun jeweils von TouchableOpacity umgeben. Außerdem erhält jedes Quadrat die gewünschte Farbe, z. B. das oberste Quadrat:

<TouchableOpacity>
  <View style={[styles.box, { backgroundColor: 'blue' }]} />
</TouchableOpacity>

Beachten Sie, dass die betroffenen style-Attribute nun ein Array enthalten. Dies besteht aus den Styles in styles.box für die Festlegung der Ausmaße (width und height) und enthält explizit die gewünschte Farbe (backgroundColor). TouchableOpacity sorgt dafür, dass sich die Quadrate nun antippen lassen, was durch ein kurzes Aufleuchten sichtbar ist. Die Benutzeroberfläche ist nun implementiert. Es fehlt lediglich der Code für das eigentliche Spiel.

Einfache Variante des Spiel mit Zustand (state)

Zu jedem Zeitpunkt kann sich das Spiel bzw. die App in genau einem der folgenden Zustände befinden, welche durch bestimmte Abläufe erreicht werden:

  1. Kein Spiel ist aktiv (die App wurde zuvor gestartet oder eben wurde ein Spiel beendet).
  2. Ein Spiel ist aktiv (der untere Button wurde angetippt) und es wurde eine Liste von Farben festgelegt.
  3. Der Benutzer tippt auf das falsche Quadrat in der zu reproduzierenden Reihenfolge, wodurch das Spiel endet.
  4. Der Benutzer tippt auf das richtige Quadrat in der Reihenfolge. Die aktuelle Zahl der Reihenfolge wird für das nächste Antippen eines Quadrats zwischengespeichert.

Es gibt also drei Eigenschaften, die ihren Zustand im Spiel ändern können:

  1. game - Spiel ist aktiv (true) oder nicht (false),
  2. colorList - eine Liste (Array) von Farben, die der Benutzer in genau dieser Reihenfolge auswählen muss, z. B. ['blue', 'red', 'green'] und
  3. pressed - eine Zahl mit einem Wert von 0 bis colorList.length - 1, die für das zuletzt angetippte Quadrat steht.

In React Native können Komponenten ihren Zustand in einem Objekt namens state verwalten. Ändert sich dieses Objekt, dann wird automatisch die Methode render ausgeführt, sodass sich das UI aktualisiert. Der initiale Zustand einer Komponente kann mit state am Anfang der Klasse deklariert werden. Ergänzen Sie die Klasse App mit einer entsprechenden Zuweisung:

export default class App extends Component {
  state = { game: false, colorList: [], pressed: 0 };
  // der Rest bleibt unverändert…

Die App startet so, dass kein Spiel aktiv ist, keine Farben festgelegt wurden und noch kein Quadrat angetippt wurde. Wenn der Benutzer nun den Button am unteren Bildschirmrand antippt, dann beginnt ein neues Spiel. Passen Sie daher die Button-Komponente so an, dass beim Antippen eine Methode _newGame ausgeführt wird:

<Button title="Neues Spiel" onPress={() => this._newGame()} />

Es ist eine Konvention in React Native, den Namen eigener Methoden, die nicht Teil des Frameworks sind, mit einem Unterstrich zu beginnen. Deklarieren Sie diese Methode innerhalb der Klasse z. B. vor render wie folgt:

_newGame() {
  if (this.state.game) return;
  const theColors = ['blue', 'red', 'green'];
  this.setState({ game: true, colorList: theColors, pressed: 0 });
}

Falls im Zustandsobjekt die Eigenschaft this.state.game den Wert true hat, endet die Methode vorzeitig durch die return-Anweisung, denn ein Spiel ist bereits aktiv. Ansonsten erhält für die erste leichte Variante des Spiels (noch ohne zufällige Farben) die Konstante theColors drei Farben in der Reihenfolge der drei Quadrate von oben nach unten. Es folgt ein Aufruf von this.setState, eine Methode, die React Native bereitstellt, um den Zustand der Komponente zu ändern. Hier aktivieren wir das Spiel (game: true), weisen der Farbliste die (noch nicht) zufälligen Faben zu (colorList: theColors) und initialisieren den Zähler für das zuletzt angetippte Quadrat (pressed: 0).

Fügen Sie nun die folgende Methode _checkColor der Klasse App hinzu (z. B. unterhalb von _newGame):

_checkColor(color) {
  const { game, pressed, colorList } = this.state;
  if (!game) return;
  if (colorList[pressed] !== color) {
    alert('Leider hast du verloren!');
    this.setState({ game: false });
  } else if (pressed === colorList.length - 1) {
    alert('Super, du hast gewonnen!');
    this.setState({ game: false });
  } else {
    this.setState({ pressed: pressed + 1 });
  }
}

Der Code in _checkColor liest den aktuellen Zustand durch destruktierende Zuweisungen in Konstanten ein, die wie die Zustandseigenschaften benannt sind ({ game, pressed, colorList }). Falls momentan kein Spiel aktiv ist, endet die Methode mit return. Im Falle eines aktiven Spiels gibt es drei verschiedene Situationen:

  1. Das falsche Quadrat wurde angetippt – dies führt zu einem entsprechenden Hinweis und zur Beendigung des Spiels.
  2. Das richtige Quadrat wurde angetippt und es handelt sich dabei um das letzte der Reihenfolge – ein passender Hinweis erscheint, dass der Benutzer gewonnen hat und das Spiel endet.
  3. Das richtige Quadrat wurde angetippt und der aktuelle Zähler wird um 1 erhöht (in diesem Fall ist ein Spiel aktiv und wir sind noch nicht am Ende der Reihenfolge angelangt).

Passen Sie nun die Methode render an, sodass ein Antippen (onPress) eines Quadrats die Methode _checkColor mit dem zugehörigen Farbwert aufruft:

render() {
  return (
    <View style={styles.container}>
      <TouchableOpacity onPress={() => this._checkColor('blue')}>
        <View style={[style
s.box, { backgroundColor: 'blue' }]} />
      </TouchableOpacity>
      <TouchableOpacity onPress={() => this._checkColor('red')}>
        <View style={[styles.box, { backgroundColor: 'red' }]} />
      </TouchableOpacity>
      <TouchableOpacity onPress={() => this._checkColor('green')}>
        <View style={[styles.box, { backgroundColor: 'green' }]} />
      </TouchableOpacity>
      <Button title="Neues Spiel" onPress={() => this._newGame()} />
    </View>
  );
}

Nun können Sie das Spiel bereits testen, auch wenn die Reihenfolge der Farben stets die gleiche ist: blau, rot und dann grün.

Die schrittweisen Änderungen am Programmcode enthielten u. a. zwei neue Methoden und daher stellt folgendes Listing die komplette Komponente App nach den eben erfolgten Anpassungen dar:

import React, { Component } from 'react';
import { Button, TouchableOpacity, View, StyleSheet } from 'react-native';

export default class App extends Component {
  state = { game: false, colorList: [], pressed: 0 };

  _newGame() {
    if (this.state.game) return;
    const theColors = ['blue', 'red', 'green'];
    this.setState({ game: true, colorList: theColors, pressed: 0 });
  }

  _checkColor(color) {
    const { game, pressed, colorList } = this.state;
    if (!game) return;
    if (colorList[pressed] !== color) {
      alert('Leider hast du verloren!');
      this.setState({ game: false });
    } else if (pressed === colorList.length - 1) {
      alert('Super, du hast gewonnen!');
      this.setState({ game: false });
    } else {
      this.setState({ pressed: pressed + 1 });
    }
  }

  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={() => this._checkColor('blue')}>
          <View style={[styles.box, { backgroundColor: 'blue' }]} />
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this._checkColor('red')}>
          <View style={[styles.box, { backgroundColor: 'red' }]} />
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this._checkColor('green')}>
          <View style={[styles.box, { backgroundColor: 'green' }]} />
        </TouchableOpacity>
        <Button title="Neues Spiel" onPress={() => this._newGame()} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
    paddingTop: 30,
  },
  box: {
    width: 150,
    height: 150,
  },
});

Zufällige Farben erzeugen

Nun werden wir zu Beginn eines Spiels fünf zufällige Farben festlegen. Dies geschieht in der Methode _newGame:

_newGame() {
  if (this.state.game) return;
  const theColors = ['blue', 'red', 'green'];
  const colorList = Array.from(
    { length: 5 },
    () => theColors[Math.floor(Math.random() * theColors.length)]
  );
  console.log(colorList);
  this.setState({ game: true, colorList: colorList, pressed: 0 });
}

Obiger Code generiert ein Array von fünf zufälligen Farbnamen mit den möglichen Werten aus theColors ('blue', 'red', 'green') und weist diese der Konstanten colorList zu. Mit console.log wird diese erzeugte Liste ausgegeben und anschließend im Zustandsobjekt mit setState gesetzt.

Wenn Sie das Spiel nun testen, dann erscheinen bei Spielbeginn die gewählten Farben in der Konsole. Expo Snack hat einen speziellen Bereich (LOGS), in dem Ausgaben mit console.log im Browser nachvollzogen werden können. Um diesen Bereich zu öffnen, klicken Sie unterhalb des Editors ganz links in der Fußleiste (neben Prettier).

Die Farbreihenfolge mit Animationen anzeigen

Entsprechend der fünf Farben sollen jetzt zu Beginn des Spiels die Quadrate in genau dieser Reihenfolge kurz blinken. Dazu werden wir Animationen definieren, wofür React Native die API Animated bereitstellt. Importieren Sie diese API und erweitern Sie den initialen Zustand in state mit drei zusätzlichen Eigenschaften (blue, red, green) wie folgt:

import React, { Component } from 'react';
import {
  Animated,
  Button,
  TouchableOpacity,
  View,
  StyleSheet
} from 'react-native';

export default class App extends Component {
  state = {
    game: false,
    colorList: [],
    pressed: 0,
    blue: new Animated.Value(1),
    red: new Animated.Value(1),
    green: new Animated.Value(1)
  };

  // der Rest bleibt unverändert

Um die Quadrate blinken zu lassen, werden wir für das betroffene View-Element den Wert der Styling-Eigenschaft opacity in einem Intervall von 0 bis 1 animieren, sodass das Quadrat kurz unsichtbar wird (opacity: 0) und dann wieder erscheint (opacity: 1). Dies bereiten wir mit den Eigenschaften blue, red und green im initialen state-Objekt vor. Mit Animated.Value(1) erhalten wir dazu einen Wert, der sich dynamisch animieren lässt.

Damit wir die Quadrate bzw. ihre Styling-Eigenschaft opacity mit Werten aus Animated.Value animieren können, werden wir nun anstatt der View-Elemente Komponenten vom Typ Animated.View verwenden. Ersetzen Sie also in der Methode render die View-Komponenten der drei Quadrate mit Animated.View wie in diesem Listing zu sehen:

render() {
  return (
    <View style={styles.container}>
      <TouchableOpacity onPress={() => this._checkColor('blue')}>
        <Animated.View
          style={[
            styles.box,
            { backgroundColor: 'blue', opacity: this.state.blue }
          ]}
        />
      </TouchableOpacity>
      <TouchableOpacity onPress={() => this._checkColor('red')}>
        <Animated.View
          style={[
            styles.box,
            { backgroundColor: 'red', opacity: this.state.red }
          ]}
        />
      </TouchableOpacity>
      <TouchableOpacity onPress={() => this._checkColor('green')}>
        <Animated.View
          style={[
            styles.box,
            { backgroundColor: 'green', opacity: this.state.green }
          ]}
        />
      </TouchableOpacity>
      <Button title="Neues Spiel" onPress={() => this._newGame()} />
    </View>
  );
}

Die Quadrate können nun animiert werden, denn sie enthalten im style-Attribut für opacity den aktuellen Wert der entsprechenden Eigenschaft im state-Objekt, welcher initial den Wert Animated.Value(1) hat. Die Animationen definieren wir direkt nach Beginn eines Spiels in der Methode _newGame:

_newGame() {
  if (this.state.game) return;
  const theColors = ['blue', 'red', 'green'];
  const colorList = Array.from(
    { length: 5 },
    () => theColors[Math.floor(Math.random() * theColors.length)]
  );
  this.setState({ game: true, colorList: colorList, pressed: 0 });
  let animations = [];
  colorList.forEach(color => {
    animations.push(
      Animated.timing(this.state[color], {
        toValue: 0,
        duration: 500
      })
    );
    animations.push(
      Animated.timing(this.state[color], {
        toValue: 1,
        duration: 500
      })
    );
  });
  Animated.sequence(animations).start();
}

Wir weisen einer lokalen Variablen animations zunächst ein leeres Array zu. Dieses wird für jede der zuvor zufällig gewählten Farben mit zwei Animationen befüllt. Die erste Animation setzt den entsprechenden Wert für opacity im state auf 0, was über einen Zeitraum von 500ms abläuft (Animated.timing). Dadurch wird das Quadrat mit einer Animation ausgeblendet bis es unsichtbar ist. Die zweite Animation erhöht den Wert für opacity schließlich wieder über einen Zeitraum von einer halben Sekunde auf den Wert 1, sodass das Quadrat wieder sichtbar ist. Die in animations enthaltenen 10 Animationen (zwei pro Quadrat in der Reihenfolge) werden schließlich durch den Aufruf von Animated.sequence(animations).start() nacheinander ausgeführt.

Zusammenfassung

Herzlichen Glückwunsch! Sie haben ein Spiel als mobile App online im Browser in etwas mehr als 100 Zeilen Code programmiert. Diese App funktioniert sowohl mit Android-Geräten als auch auf dem iPhone. Mit Komponenten, Styling und Animationen haben Sie bereits einige wichtige Aspekte von React Native kennengelernt.

Sie finden den kompletten Programmcode des Spiels einerseits online bei Expo Snack [4] und andererseits nochmals hier zur Vollständigkeit im folgenden Listing:

import React, { Component } from 'react';
import {
  Animated,
  Button,
  TouchableOpacity,
  View,
  StyleSheet
} from 'react-native';

export default class App extends Component {
  state = {
    game: false,
    colorList: [],
    pressed: 0,
    blue: new Animated.Value(1),
    red: new Animated.Value(1),
    green: new Animated.Value(1)
  };

  _checkColor(color) {
    const { game, pressed, colorList } = this.state;
    if (!game) return;
    if (colorList[pressed] !== color) {
      alert('Leider hast du verloren!');
      this.setState({ game: false });
    } else if (pressed === colorList.length - 1) {
      alert('Super, du hast gewonnen!');
      this.setState({ game: false });
    } else {
      this.setState({ pressed: pressed + 1 });
    }
  }

  _newGame() {
    if (this.state.game) return;
    const theColors = ['blue', 'red', 'green'];
    const colorList = Array.from(
      { length: 5 },
      () => theColors[Math.floor(Math.random() * theColors.length)]
    );
    this.setState({ game: true, colorList: colorList, pressed: 0 });
    let animations = [];
    colorList.forEach(color => {
      animations.push(
        Animated.timing(this.state[color], {
          toValue: 0,
          duration: 500
        })
      );
      animations.push(
        Animated.timing(this.state[color], {
          toValue: 1,
          duration: 500
        })
      );
    });
    Animated.sequence(animations).start();
  }

  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={() => this._checkColor('blue')}>
          <Animated.View
            style={[
              styles.box,
              { backgroundColor: 'blue', opacity: this.state.blue }
            ]}
          />
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this._checkColor('red')}>
          <Animated.View
            style={[
              styles.box,
              { backgroundColor: 'red', opacity: this.state.red }
            ]}
          />
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this._checkColor('green')}>
          <Animated.View
            style={[
              styles.box,
              { backgroundColor: 'green', opacity: this.state.green }
            ]}
          />
        </TouchableOpacity>
        <Button title="Neues Spiel" onPress={() => this._newGame()} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
    paddingTop: 30
  },
  box: {
    width: 150,
    height: 150
  }
});

Nächste Schritte

Vielleicht haben Sie Lust, das Spiel etwas schwieriger zu gestalten? Erhöhen Sie dazu im Programmcode die Anzahl der zufällig ausgewählten Farben oder verkürzen Sie die Dauer der Animationen. Vielleicht möchten Sie die App mit einem Button erweitern, durch den sich die Reihenfolge der Farben erneut abspielen lässt?

Weiterhin kann ich Ihnen ein interaktives Tutorial zu den grundlegenden Konzepten von React Native empfehlen, welches ebenso im Browser verfügbar ist [8].

Falls Sie tiefer in die Entwicklung mit React Native einsteigen möchten, dann sollten Sie Ihren Rechner mit der Entwicklungsumgebung ausstatten. Dazu benötigen Sie lediglich eine Installation von Node.js und das Tool create-react-native-app, welches zusammen mit der Expo-App eingesetzt wird. Mehr dazu und zu React Native allgemein können Sie in der offiziellen Dokumentation von React Native erfahren [9].

Autor

Prof. Dr. Erik Behrends

Erik Behrends ist Professor für Informatik an der DHBW Lörrach (Duale Hochschule Baden-Württemberg) mit mehr als 15 Jahren Erfahrung in der Softwareentwicklung.
>> Weiterlesen
Buch des Autors:

Das könnte Sie auch interessieren
botMessage_toctoc_comments_9210