Erste Schritte mit AWS

Erstellen einer Android-Anwendung

Erstellen einer einfachen Android-Anwendung mit AWS Amplify

Modul 1: Erstellen und Bereitstellen einer Android-App

In diesem Modul erstellen Sie eine Android-Anwendung und stellen sie mit dem Webhosting-Service von AWS Amplify in der Cloud bereit.

Einführung

AWS Amplify bietet einen Git-basierten Workflow zur Erstellung, Verwaltung, Integration und Bereitstellung von serverlosen Backends für Web- und mobile Anwendungen. Die Amplify CLI bietet eine einfache textbasierte Benutzeroberfläche zur Bereitstellung und Verwaltung von Backend Services, wie z. B. Benutzerauthentifizierung oder eine REST- oder GraphQL-API für Ihre Anwendungen. Die Amplify-Bibliotheken ermöglichen die einfache Integration dieser Amplify Backend Services mit nur wenigen Zeilen Code in Ihre Anwendungen.

In diesem Modul beginnen wir mit der Erstellung einer neuen Android-Anwendung zur Aufnahme von Reisenotizen. Es werden ein Titel, eine Beschreibung und ein Bild notiert. Wir werden diese Anwendung in den folgenden Modulen erweitern.

Lerninhalte

  • Erstellen einer Android-Anwendung
  • Aktualisieren der Hauptansicht, um eine Liste von Elementen anzuzeigen
  • Erstellen und Testen Ihrer Anwendung

Wichtige Konzepte

Kotlin: Kotlin wurde als Programmiersprache für dieses Tutorial gewählt, weil es Ihnen erlaubt, einen Großteil des Boilerplate-Codes zu reduzieren, und weil es mit Blick auf die Typsicherheit entwickelt wurde.

Jetpack: Dieses Tutorial verwendet das Jetpack von Android, eine Sammlung von Android-Bibliotheken, die bewährte Methoden enthalten und Abwärtskompatibilität in Ihren Android-Apps bieten.

 Veranschlagte Zeit

10 Minuten

 Verwendete Services

Implementierung

  • Starten Sie Android Studio, und wählen Sie im Startbildschirm die Option Start eines neuen Android Studio-Projekts:

    Wählen Sie unter Telefon und Tablet die Option Grundlegende Aktivität und klicken Sie auf Weiter:

    Geben Sie einen Namen für Ihr Projekt ein, zum Beispiel Android Erste Schritte. Stellen Sie sicher, dass Sprache Kotlin und Minimum SDK API 26: Android 8.0 (oreo) ist, und klicken Sie dann auf Fertig stellen:

    Nun, da das Gerüst des Projekts existiert, benötigen wir 4 Schritte, um unsere grundlegende Anwendung zum Laufen zu bringen:

    1. Löschen unerwünschter Klassen und Dateien und Hinzufügen von Plugins
    2. Erstellen von Klassen zur Aufnahme unserer Datenstrukturen
    3. Eine Ansicht erstellen, um einzelne Notizen in der Liste zu halten
    4. Ändern der MainActivity, um eine Liste der Notizen anzuzeigen
  • Unter res/layout und java/com.example.androidgettingstarted löschen Sie die vier Fragment-Dateien (markieren Sie die 4 Dateien, klicken Sie mit der rechten Maustaste und wählen Sie Löschen aus dem Kontextmenü):

    Öffnen Sie unter Gradle Scripts build.gradle (Modul:app) und fügen Sie das Kotlin Extension Plugin hinzu.

    plugins {
        id 'com.android.application'
        id 'kotlin-android'
        id 'kotlin-android-extensions' //  <== add this line
    }
  • Die Klasse UserData enthält den Benutzerstatus: ein isSignedIn-Flag und eine Liste von Notizwerten.

    Um eine neue Klasse zu erstellen, klicken Sie mit der rechten Maustaste auf java/com.example/androidgettingstarted und wählen Sie Neu -> Kotlin-Datei/Klasse. Geben Sie UserData als Namen ein.

    Fügen Sie den unten stehenden Code in die soeben erstellte neue Datei (UserData.kt)

    package com.example.androidgettingstarted
    
    import android.graphics.Bitmap
    import android.util.Log
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    
    // a singleton to hold user data (this is a ViewModel pattern, without inheriting from ViewModel)
    object UserData {
    
        private const val TAG = "UserData"
    
        //
        // observable properties
        //
    
        // signed in status
        private val _isSignedIn = MutableLiveData<Boolean>(false)
        var isSignedIn: LiveData<Boolean> = _isSignedIn
    
        fun setSignedIn(newValue : Boolean) {
            // use postvalue() to make the assignation on the main (UI) thread
            _isSignedIn.postValue(newValue)
        }
    
        // the notes
        private val _notes = MutableLiveData<MutableList<Note>>(mutableListOf())
    
        // please check https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata
        private fun <T> MutableLiveData<T>.notifyObserver() {
            this.postValue(this.value)
        }
        fun notifyObserver() {
            this._notes.notifyObserver()
        }
    
        fun notes() : LiveData<MutableList<Note>>  = _notes
        fun addNote(n : Note) {
            val notes = _notes.value
            if (notes != null) {
                notes.add(n)
                _notes.notifyObserver()
            } else {
                Log.e(TAG, "addNote : note collection is null !!")
            }
        }
        fun deleteNote(at: Int) : Note?  {
            val note = _notes.value?.removeAt(at)
            _notes.notifyObserver()
            return note
        }
    
        fun resetNotes() {
            this._notes.value?.clear()  //used when signing out
            _notes.notifyObserver()
        }
    
    
        // a note data class
        data class Note(val id: String, val name: String, val description: String, var imageName: String? = null) {
            override fun toString(): String = name
    
            // bitmap image
            var image : Bitmap? = null
    
        }
    }

    Was haben wir gerade hinzugefügt?

    • Die Klasse UserData ist dafür verantwortlich, Benutzerdaten zu halten, nämlich ein isSignedIn-Flag zur Verfolgung des aktuellen Authentifizierungsstatus und eine Liste von Notizobjekten.
    • Diese beiden Eigenschaften sind entsprechend dem LiveData Publish/Abonnement Framework implementiert. Sie ermöglicht es der Graphischen Benutzeroberfläche (GUI), Änderungen zu abonnieren und entsprechend zu reagieren. Um mehr über LiveData zu erfahren, können Sie dieses Dokument lesen oder diesem kurzen Video-Tutorial folgen. Um der bewährten Methode zu befolgen, halten Sie die MutableLiveData-Eigenschaft privat und legen Sie nur die schreibgeschützte LiveData-Eigenschaft offen. Wenn es sich bei den zu veröffentlichenden Daten um eine Liste handelt, ist ein zusätzlicher Boilerplate-Code erforderlich, um sicherzustellen, dass die Beobachter benachrichtigt werden, wenn einzelne Komponenten in der Liste geändert werden.
    • Wir haben auch eine Notizdatenklasse hinzugefügt, um die Daten einzelner Notizen zu speichern. Für ImageName und Image werden zwei unterschiedliche Eigenschaften verwendet. Das Bild wird in einem späteren Modul behandelt.
    • Das Singleton-Entwurfsmuster für das UserData-Objekt wurde implementiert, da es den Verweis darauf von überall in der Anwendung nur mit UserData erlaubt.
  • Einzelne Zellen in einer Bildlaufliste werden als RecyclerView bezeichnet, da die Ansicht recycelt werden kann, wenn der Benutzer nach oben und unten blättert, wenn die Ansicht auf dem Bildschirm nicht mehr sichtbar ist.

    Genau wie bei einer normalen Ansicht erstellen wir Layout-XML-Dateien und eine Kotlin-Klasse. Eine einzelne Zelle sieht wie folgt aus:

    Um die Layout-Datei zu erzeugen, klicken Sie mit der rechten Maustaste auf res/layout und wählen Sie Neu -> Layout-Ressourcen-Datei. Geben Sie content_note als Namen ein und lassen Sie alle anderen Werte stehen, da wir die XML-Datei direkt bearbeiten werden.

    Öffnen Sie die Code-Ansicht für die neu erstellte Datei.

    Ersetzen Sie in der Code-Ansicht der soeben erstellten neuen Datei (content_note.xml) den generierten Code durch Einfügen des unten stehenden Codes:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingVertical="16dp">
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:padding="8dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_launcher_background" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="5dp"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="20sp"
                android:textStyle="bold" />
    
            <TextView
                android:id="@+id/description"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="15sp" />
    
        </LinearLayout>
    </LinearLayout>

    Erstellen Sie schließlich die Kotlin-Klasse: Klicken Sie mit der rechten Maustaste auf java/com.example/androidgettingstarted und wählen Sie Neu -> Kotlin-Datei/Klasse. Geben Sie NoteRecyclerViewAdapter als Namen ein.

    Fügen Sie den folgenden Code in die soeben erstellte neue Datei ein (NoteRecyclerViewAdapter.kt)

    package com.example.androidgettingstarted
    
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ImageView
    import android.widget.TextView
    import androidx.recyclerview.widget.RecyclerView
    
    // this is a single cell (row) in the list of Notes
    class NoteRecyclerViewAdapter(
        private val values: MutableList<UserData.Note>?) :
        RecyclerView.Adapter<NoteRecyclerViewAdapter.ViewHolder>() {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.content_note, parent, false)
            return ViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    
            val item = values?.get(position)
            holder.nameView.text = item?.name
            holder.descriptionView.text = item?.description
    
            if (item?.image != null) {
                holder.imageView.setImageBitmap(item.image)
            }
        }
    
        override fun getItemCount() = values?.size ?: 0
    
        inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val imageView: ImageView = view.findViewById(R.id.image)
            val nameView: TextView = view.findViewById(R.id.name)
            val descriptionView: TextView = view.findViewById(R.id.description)
        }
    }

    Was haben wir gerade hinzugefügt?
    Der obige Code besteht aus:

    • einer Layout-xml-Datei, die das Layout der Komponente auf einer Zelle beschreibt, die eine Notiz darstellt. Sie zeigt das Bild, den Titel der Notiz und die Beschreibung der Notiz an.
    • einer unterstützenden Kotlin-Klasse. Sie erhält bei der Erstellung ein Notizen-Datenobjekt und ordnet den entsprechenden Ansichten (Bild, Titel und Beschreibung) individuelle Werte zu.
  • Nachdem wir nun die Datenklassen (UserData und Note) und die Ansicht der einzelnen Notizen (NoteRecyclerViewAdapter) haben, erstellen wir die Notizliste zur Hauptaktivität.

    Öffnen Sie in der Dateiliste auf der linken Seite von Android Studio die Datei res/layout/content_main.xml und ersetzen Sie den Code durch diesen Inhalt:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/item_list"
            android:name="com.example.myapplication.ItemListFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="60dp"
    
            android:paddingHorizontal="8dp"
            android:paddingVertical="8dp"
            app:layoutManager="LinearLayoutManager"
            tools:context=".MainActivity"
            tools:listitem="@layout/content_note" />
    
    </FrameLayout>

    Öffnen Sie in der Dateiliste auf der linken Seite von Android Studio die Datei java/com.example/androidgettingstarted/MainActivity.kt und ersetzen Sie den Code durch diesen Inhalt:

    package com.example.androidgettingstarted
    
    import android.os.Bundle
    import android.util.Log
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.Observer
    import androidx.recyclerview.widget.ItemTouchHelper
    import androidx.recyclerview.widget.RecyclerView
    import kotlinx.android.synthetic.main.activity_main.*
    import kotlinx.android.synthetic.main.content_main.*
    
    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            setSupportActionBar(toolbar)
    
            // prepare our List view and RecyclerView (cells)
            setupRecyclerView(item_list)
        }
    
        // recycler view is the list of cells
        private fun setupRecyclerView(recyclerView: RecyclerView) {
    
            // update individual cell when the Note data are modified
            UserData.notes().observe(this, Observer<MutableList<UserData.Note>> { notes ->
                Log.d(TAG, "Note observer received ${notes.size} notes")
    
                // let's create a RecyclerViewAdapter that manages the individual cells
                recyclerView.adapter = NoteRecyclerViewAdapter(notes)
            })
        }
    
        companion object {
            private const val TAG = "MainActivity"
        }
    }

    Was haben wir gerade hinzugefügt?

    • Das Hauptlayout ist eine RecyclerView, die die Liste der einzelnen Zellen verwaltet, die wir zuvor erstellt haben.
    • Die Hauptaktivitätsklasse beobachtet Änderungen an der Liste der Notizen und erstellt einen NoteRecyclerViewAdapter, um einzelne Zellen zu erstellen.
  • Öffnen Sie unter Gradle Scripts die Datei build.gradle (Modul:app) und überprüfen Sie, ob die generierten Abhängigkeiten korrekt sind. Die `Bibliotheksversionen` müssen überprüft werden.

    ```gradle
    dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation 'androidx.core:core-ktx:1.3.2'
        implementation 'androidx.appcompat:appcompat:1.2.0'
        implementation 'com.google.android.material:material:1.2.1'
        implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
        implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
        implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
        testImplementation 'junit:junit:4.+'
        androidTestImplementation 'androidx.test.ext:junit:1.1.2'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    }
  • Erstellen und starten Sie nun die Anwendung im Simulator. Klicken Sie auf das Symbol Ausführen ▶️ in der Symbolleiste oder geben Sie ^ R ein.

    Nach einer Weile startet die Anwendung im Android-Simulator mit einem leeren Anfangsbildschirm.

    In diesem Stadium gibt es keine Daten, die zur Laufzeit gerendert werden müssen. Wir werden das Mail-Symbol in einem späteren Schritt abschaffen.

Fazit

Sie haben erfolgreich eine Android-App erstellt. Sie sind bereit, mit Amplify zu arbeiten!

War das Modul hilfreich?

Amplify starten