Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
Vorwort
Teil I Grundlagen
1 Android – eine offene, mobile Plattform
2 Hallo Android!
3 Von der Idee zur Veröffentlichung
Teil II Elementare Anwendungsbausteine
4 Wichtige Grundbausteine von Apps
5 Benutzeroberflächen
6 Multitasking
Teil III Gerätefunktionen nutzen
7 Telefonieren und surfen
8 Sensoren, GPS und Bluetooth
Teil IV Dateien und Datenbanken
9 Dateien lesen, schreiben und drucken
10 Datenbanken
Teil V Multimedia und Produktivität
11 Multimedia
12 Kontakte und Organizer
A Einführung in Kotlin
B Jetpack Compose
C Häufig benötigte Codebausteine
D Literaturverzeichnis
E Die Begleitmaterialien
Stichwortverzeichnis

Ihre Meinung?
Spacer
<< zurück
Android 11 von Thomas Künneth
Das Praxisbuch für App-Entwickler
Buch: Android 11

Android 11
Pfeil 4 Wichtige Grundbausteine von Apps
Pfeil 4.1 Was sind Activities?
Pfeil 4.1.1 Struktur von Apps
Pfeil 4.1.2 Lebenszyklus von Activities
Pfeil 4.2 Kommunikation zwischen Anwendungsbausteinen
Pfeil 4.2.1 Intents
Pfeil 4.2.2 Kommunikation zwischen Activities
Pfeil 4.2.3 Broadcast Receiver
Pfeil 4.3 Fragmente
Pfeil 4.3.1 Grundlagen
Pfeil 4.3.2 Ein Fragment in eine Activity einbetten
Pfeil 4.3.3 Mehrspaltenlayouts
Pfeil 4.4 Berechtigungen
Pfeil 4.4.1 Normale und gefährliche Berechtigungen
Pfeil 4.4.2 Tipps und Tricks zu Berechtigungen
Pfeil 4.5 Navigation
Pfeil 4.5.1 Jetpack Navigation
Pfeil 4.5.2 Die Klasse »BottomNavigationView«
Pfeil 4.6 Zusammenfassung
 
Zum Seitenanfang

4.5    Navigation Zur vorigen ÜberschriftZur nächsten Überschrift

Sie haben Intents als Boten zwischen den Grundbausteinen von Apps kennengelernt. Mit startActivity() und startActivityForResult() werden beispielsweise nach einem Buttonklick Ihre und ggf. fremde Activities angezeigt. Man nennt den Wechsel von Activity zu Activity Navigation. Aber auch innerhalb einer Activity kann Navigation stattfinden, beispielsweise beim Anzeigen und Entfernen von Fragmenten. Bislang habe ich jede Navigation ausprogrammiert. Das funktioniert in einfachen Beispielen wunderbar. Sobald eine App aber etwas komplexer wird, muss man mehr Aufwand treiben.

 
Zum Seitenanfang

4.5.1    Jetpack Navigation Zur vorigen ÜberschriftZur nächsten Überschrift

Jetpack Navigation besteht aus Bibliotheken, einem Gradle-Plugin und Werkzeugen innerhalb von Android Studio. Zentrales Element ist der Navigation Graph. Dieser neue Ressourcetyp (eine XML-Datei) bündelt alle für die Navigation erforderlichen Informationen. Er wird ab Android Studio 3.3 im Navigation Editor bearbeitet. Sie können Projekten einen Navigation Graph hinzufügen, indem Sie im Werkzeugfenster Project den Knoten res mit der rechten Maustaste anklicken und New • Android Resource File auswählen. Der Dialog New Resource File ist in Abbildung 4.16 zu sehen. Sofern sie nicht bereits vorhanden sind, werden beim Anlegen die erforderlichen Bibliotheken in der modulspezifischen build.gradle-Datei eingetragen. Dies sind:

implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'

Wenn Sie bei der Navigation sichere Argumente verwenden möchten (wir kommen gleich darauf zurück), tragen Sie zusätzlich die Zeile

apply plugin: "androidx.navigation.safeargs.kotlin"

ein, am besten nach

apply plugin: 'kotlin-android-extensions'

Und noch eine Sache sollten Sie prüfen und ggf. ergänzen. In der build.gradle-Datei des Projekts muss im Block dependencies { die Zeile

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"

stehen.

Einen Navigation Graph anlegen

Abbildung 4.16    Einen Navigation Graph anlegen

Der Navigation Graph enthält Ziele und Aktionen. Ziele sind die Orte, zu denen man navigieren kann. Das können Activities oder Fragmente sein. Aktionen repräsentieren die Pfade, auf denen sich der Benutzer in der App bewegt. Ziele werden also über Aktionen miteinander verbunden. Damit das funktioniert, sind einige weitere Puzzleteile erforderlich. Der Navigation Controller (NavController) steuert die Navigation innerhalb eines Navigation Host. Das Interface androidx.navigation.NavHost wird von der Klasse androidx.navigation.fragment.NavHostFragment implementiert. Es definiert die Methode getNavController(). Apps instanziieren den Controller also üblicherweise nicht selbst, sondern erhalten ihn von einem Host oder über Hilfsmethoden der Klasse Navigation.

[»]  Hinweis

Normalerweise werden Navigation Graphen aus XML-Dateien entfaltet. Bei Bedarf können sie aber auch programmatisch erzeugt werden. Das ist dann praktisch, wenn sich die möglichen Navigationspfade aus Aufrufen von Webservices oder anderen entfernten Quellen ergeben.

Auch wenn Activities Navigationsziele sein können, wurde Jetpack Navigation doch für die Verwendung mit Fragmenten konzipiert. Mein Beispiel NavigationDemo1 (Abbildung 4.17) zeigt, wie Sie von einem Hauptfragment aus ein untergeordnetes Fragment als Ziel ansteuern.

Die App »NavigationDemo1«

Abbildung 4.17    Die App »NavigationDemo1«

Listing 4.28 zeigt die von der Haupt-Activity NavigationDemo1Activity geladene Layoutdatei activity_main.xml. Sie ist sehr einfach gehalten. Das einzige Kind des Wurzelelements ConstraintLayout ist eine FragmentContainerView. Diese Klasse leitet von FrameLayout ab. Sie wurde speziell für das Einbetten von Fragmenten entwickelt. Mit dem Attribut android:name geben Sie das Fragment an, hier ein NavHostFragment. Wenn Sie möchten, können Sie mit android:tag ein Tag definieren, um das Fragment mit findFragmentByTag() finden zu können.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NavigationDemo1Activity">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

Listing 4.28    Die Datei »activity_main.xml«

Mit dem Attribut app:navGraph geben Sie den Navigation Graph, den das NavHostFragment verwenden soll, an. In meinem Beispiel ist das die Datei nav_graph.xml. Navigation Graphen werden unter res/navigation abgelegt. app:defaultNavHost steuert, ob der Navigation Host auf das Antippen der Zurück-Schaltfläche reagieren soll.

Der Navigation Graph

Navigation Graphen werden in Android Studio mit dem Navigation Editor bearbeitet. Er ist in Abbildung 4.18 zu sehen. Mein Beispiel enthält zwei Ziele, die Fragmente ChildFragment und MainFragment. Das Haus-Symbol zeigt an, dass MainFragment der Einstiegspunkt ist, also als Erstes angezeigt wird. Wenn Sie den Editor auf Textdarstellung umschalten, sehen Sie im Tag <navigation> das Attribut app:startDestination.

Für jedes Ziel kann eine Reihe von Eigenschaften festgelegt werden. Die id wird verwendet, um zu diesem Ziel zu navigieren. Entsprechender Code könnte folgendermaßen aussehen:

findNavController().navigate(R.id.childFragment)
Der Navigation Editor

Abbildung 4.18    Der Navigation Editor

In meinem Beispiel finden Sie aber eine etwas andere Form, weil ich das Gradle-Plugin androidx.navigation.safeargs.kotlin verwende. Dazu kommen wir gleich. label legt den Titel fest, der in der Action Bar angezeigt wird. name gibt die Klasse des Fragments an. Die Pfeile, die in den Modi Split und Design des Navigation Editors zu sehen sind, heißen Aktionen. Eine Aktion definiert einen Navigationspfad, den Weg von einem Ziel zu einem anderen. NavigationDemo1 hat nur eine Aktion, nämlich von mainFragment zu childFragment. Um den Weg zurück (Antippen der Zurück-Schaltfläche) kümmert sich der Navigation Host. Auch Aktionen haben einige Eigenschaften, wichtig sind insbesondere id und destination. Darüber hinaus können Sie verschiedene Animationen festlegen und Standardwerte für Argumente angeben.

Sichere Argumente

Jedes Ziel kann ein oder mehrere Argumente erwarten. Ein Argument hat einen Namen und einen Typ. Außerdem legen Sie fest, ob das Argument vorhanden sein muss. Listing 4.29 zeigt die Definition des Arguments color in XML. Es nimmt ganze Zahlen auf. Meine Klasse ChildFragment interpretiert diese als Farbe und setzt den Hintergrund entsprechend.

<fragment
...
<argument
android:name="color"
app:argType="integer"
app:nullable="false" />
</fragment>

Listing 4.29    Auszug aus der Datei »nav_graph.xml«

Das Gradle-Plugin androidx.navigation.safeargs.kotlin generiert aus den Zielen, Aktionen und Argumenten des Navigation Graphs Klassen, die Sie bei der Navigation unterstützen. In Listing 4.30 sehen Sie zweimal den Aufruf MainFragmentDirections.mainToChildFragment(). Je nachdem, welcher Button angeklickt wurde, wird der Funktion ein anderer Farbwert übergeben. Das Ergebnis ist ein Objekt, das das Interface androidx.navigation.NavDirections implementiert. Es wird an findNavController().navigate() übergeben.

package com.thomaskuenneth.androidbuch.navigationdemo1

import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController

class MainFragment : Fragment(R.layout.fragment_main) {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<Button>(R.id.b1)?.setOnClickListener {
val action =
MainFragmentDirections.mainToChildFragment(Color.GREEN)
findNavController().navigate(action)
}
view.findViewById<Button>(R.id.b2)?.setOnClickListener {
val action =
MainFragmentDirections.mainToChildFragment(Color.RED)
findNavController().navigate(action)
}
}
}

Listing 4.30    Die Klasse »MainFragment«

Die Verwendung von sicheren Argumenten in Zielen zeigt Listing 4.31. ChildFragmentArgs ist ebenfalls eine generierte Klasse. Sie gestattet den komfortablen typsicheren Zugriff auf die übergebenen Argumente. args ist aufgrund des by eine delegierte Eigenschaft und wird erst beim Zugriff initialisiert.

package com.thomaskuenneth.androidbuch.navigationdemo1

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs

class ChildFragment : Fragment(R.layout.fragment_child) {

val args: ChildFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.setBackgroundColor(args.color)
}
}

Listing 4.31    Die Klasse »ChildFragment«

Sie haben es fast geschafft. Allerdings möchte ich Ihnen noch kurz die Klasse NavigationDemo1Activity (Listing 4.32) zeigen. Damit die Action Bar den bekannten Pfeil am linken Rand anzeigt wenn nicht das Hauptfragment zu sehen ist, ist nämlich ein bisschen Konfigurationsarbeit nötig. Sie müssen der Methode setupActionBarWithNavController() drei Objekte übergeben:

  1. eine AppCompatActivity (this)

  2. einen NavController (navHostFragment.navController)

  3. eine AppBarConfiguration

AppBarConfiguration.Builder erhält die IDs aller Ziele, die Einstiege in die Navigation bilden. Anders formuliert: Bei diesen Zielen ist kein Pfeil zu sehen, bei allen anderen schon. In meinem Beispiel trifft dies nur auf R.id.mainFragment zu.

package com.thomaskuenneth.androidbuch.navigationdemo1

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI


class NavigationDemo1Activity : AppCompatActivity() {

private lateinit var navController: NavController
private lateinit var appBarConfiguration: AppBarConfiguration

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
as NavHostFragment
navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration.Builder(
setOf(R.id.mainFragment)
).build()
NavigationUI.setupActionBarWithNavController(this, navController,
appBarConfiguration)
}

override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, appBarConfiguration)
}
}

Listing 4.32    Die Klasse »NavigationDemo1Activity«

Außerdem müssen Sie die Methode onSupportNavigateUp() wie gezeigt implementieren. NavigationUI.navigateUp() delegiert die Behandlung des Navigationspfeils inline image an den Navigation Controller.

Wie Sie gesehen haben, lassen sich mit Jetpack Navigation sehr bequem Sprünge innerhalb Ihrer App realisieren. Im folgenden Abschnitt stelle ich Ihnen die Klasse BottomNavigationView vor. Sie setzt ein sehr wichtiges Interaktionsmuster um, das Umschalten zwischen gleichberechtigten Seiten durch eine Leiste am unteren Bildschirmrand.

 
Zum Seitenanfang

4.5.2    Die Klasse »BottomNavigationView« Zur vorigen ÜberschriftZur nächsten Überschrift

com.google.android.material.bottomnavigation.BottomNavigationView gehört zu Googles Material Components. Diese Bibliothek erweitert die Standard-Widgets von Android um einige zusätzliche UI-Elemente, die ebenfalls die Designsprache Material Design umsetzen. Sie muss mit der Zeile

implementation 'com.google.android.material:material:1.1.0'

in der modulspezifischen build.gradle-Datei referenziert werden. Damit alle Komponenten richtig funktionieren, ist es wichtig, eigene Activitites von AppCompatActivity abzuleiten und ein Theme der Material Components zu verwenden. Hierfür reicht es üblicherweise, in der Datei styles.xml im Verzeichnis res/values die Zeile

<style name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">

folgendermaßen zu ändern:

<style name="AppTheme"
parent="Theme.MaterialComponents.DayNight.DarkActionBar">

Durch die Verwendung eines DayNight-Themes machen Sie Ihre App auch gleich für den Dark Mode fit.

Die App »NavigationDemo2«

Abbildung 4.19    Die App »NavigationDemo2«

In meinem Beispiel NavigationDemo2 (Abbildung 4.19) zeige ich Ihnen, wie Sie mit der Klasse BottomNavigationView zwischen mehreren primären Zielen umschalten. Lassen Sie uns als Erstes einen Blick auf die Layoutdatei in Listing 4.33 werfen. Ein ConstraintLayout enthält zwei Kinder, eine TextView sowie die BottomNavigationView. Aus Gründen der Einfachheit repräsentiert die TextView die Seiten der App. Ich erkläre Ihnen gleich, was damit gemeint ist.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center"
android:textColor="@color/colorPrimary"
android:textSize="32pt"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_navigation_menu" />

</androidx.constraintlayout.widget.ConstraintLayout>

Listing 4.33    Die Layoutdatei der App »NavigationDemo2«

Das Attribut app:menu der BottomNavigationView referenziert eine XML-Datei, die unter res/menu abgelegt wird. In Abschnitt 5.2.4, »Menüs und Action Bar«, stelle ich Ihnen das Konzept von Menüs ausführlicher vor. Fürs Erste ist wichtig, dass in dieser Datei (Listing 4.34) die umschaltbaren Seiten der App definiert werden, und zwar in <item />-Tags. android:id ordnet jeder Seite eine ID zu. android:icon legt das anzuzeigende Symbol fest, und android:title enthält den Namen der Seite. android:enabled legt fest, ob eine Seite angeklickt werden kann.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/page_home"
android:enabled="true"
android:icon="@drawable/ic_baseline_home_24"
android:title="@string/home"/>
<item
android:id="@+id/page_info"
android:enabled="true"
android:icon="@drawable/ic_baseline_info_24"
android:title="@string/info"/>
</menu>

Listing 4.34    Die Datei »bottom_navigation_menu.xml«

Die Klasse NavigationDemo2Activity (Listing 4.35) lädt die Layoutdatei activity_main.xml mit setContentView(R.layout.activity_main). findViewById<BottomNavigationView>(R.id.bottom_navigation)

liefert die Referenz auf ein BottomNavigationView-Objekt. Um informiert zu werden, wenn der Benutzer eine andere Seite anwählt, registrieren Sie mit setOnNavigationItemSelectedListener() einen OnNavigationItemSelectedListener. Dessen Methode onNavigationItemSelected() erhält eine MenuItem-Instanz. Sie muss true liefern, wenn das übergebene Menüelement als ausgewähltes Element angezeigt werden soll, sonst false.

package com.thomaskuenneth.androidbuch.navigationdemo2

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomnavigation.BottomNavigationView

class NavigationDemo2Activity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textview = findViewById<TextView>(R.id.textview)
val bottomNavigationView =
findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.page_home -> {
textview.text = getString(R.string.home)
true
}
R.id.page_info -> {
textview.text = getString(R.string.info)
true
}
else -> false
}
}
bottomNavigationView.selectedItemId = R.id.page_home
}
}

Listing 4.35    Die Klasse »NavigationDemo2Activity«

Mein Beispiel simuliert einen Seitenwechsel, indem mit textview.text = getString ( ... ) ein anderer Text angezeigt wird. Was Sie in Ihrer App tun müssen, hängt davon ab, wie Sie die darzustellenden Seiten implementieren. Sind es Fragmente, können Sie wie in den Beispielen dieses Kapitels gezeigt den FragmentManager nutzen, um sie ein- und auszublenden. Eine andere Möglichkeit beschreibe ich in Abschnitt 5.1.1, »Views«. Am praktischsten ist aber wahrscheinlich, Jetpack Navigation zu verwenden. Dann müssen Sie nur einen Navigation Graph erstellen, der die Seiten als Ziele enthält (aber keine Aktionen), und Ihrem Layout ein NavHostFragment hinzufügen.

BottomNavigationView mit eingefärbtem Hintergrund

Abbildung 4.20    BottomNavigationView mit eingefärbtem Hintergrund

Zum Schluss noch zwei Tipps. Mit labelVisibilityMode können Sie steuern, wann Navigationselemente Texte anzeigen. Fügen Sie in der Layoutdatei dem Tag <BottomNavigationView> beispielsweise den Ausdruck app:labelVisibilityMode="unlabeled" hinzu, sind nur noch die Symbole zu sehen. Ist Ihnen ein farbiger Hintergrund lieber, können Sie ihn mit

style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"

aktivieren (Abbildung 4.20).

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
Zur Rheinwerk-Konferenz für Kotlin
 Buchempfehlungen
Zum Rheinwerk-Shop: Kotlin

Kotlin


Zum Rheinwerk-Shop: Praxisbuch Usability und UX

Praxisbuch Usability und UX


Zum Rheinwerk-Shop: Flutter und Dart

Flutter und Dart


Zum Rheinwerk-Shop: App-Design

App-Design


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2023
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern