android sqlite introT8_wowomnom_Fotolia_85154667

Android SQLite Datenbank Tutorial – Teil 8: SQLite Datenbank Upgrade in Android


Anschließend fügen wir unserer App die Abhaken-Funktion hinzu. Die realisieren wir, indem folgende Änderungen an den Klassen unseres Android Projekts vorgenommen werden:

  1. ShoppingMemo – Diese Klasse ist unser Datenmodell und repräsentiert einen Datensatz der SQLite Datenbank. Wir fügen in sie ein weiteres Attribut ein, mit dem wir den Status der jeweiligen ShoppingMemo als checked oder not checked speichern.
  2. ShoppingMemoDbHelper – Sie ist eine Hilfsklasse mit deren Hilfe wir die SQLite Datenbank erstellen lassen. Sie enthält weiterhin wichtige Konstanten, die wir für die Arbeit mit der Datenbank benötigen, wie den Tabellennamen, die Datenbankversion oder die Namen der Spalten. Wir fügen ihr eine weitere Spalte hinzu und implementieren ihre onUpgrade() Methode, um die SQLite Datenbank neu zu erstellen.
  3. ShoppingMemoDataSource – Diese Klasse ist unser Data Access Object und für das Verwalten der Daten verantwortlich. Sie unterhält die Datenbankverbindung und ist für das Hinzufügen, Auslesen und Löschen von Datensätzen zuständig. Wir nehmen in dieser Klasse Änderungen an den Methoden updateShoppingMemo() und cursorToShoppingMemo() vor.
  4. MainActivity – Von dieser Klasse aus, steuern wir unsere SQLite App. Wir fügen ihrem ListView einen OnItemClickListener zu, um damit auf kurze Klicks zu reagieren.

Am Ende der achten Lektion unseres Android SQLite Tutorials werden wir unsere App auf einem Android Gerät installieren und testen. Dabei werden wir auch wieder die Log-Meldungen analysieren und in einem kleinen Video die neue Funktion präsentieren.

Nun wünschen wir euch viel Spaß beim achten Teil unseres Android SQLite Datenbank Tutorials. Los geht’s!

1. SQLite Datenbank Upgrade in Android durchführen

Android Apps Programmieren Online-Kurs

Unser großes
Android Online-Kurs
Gesamtpaket



Weitere Infos

Der SQLite Datenbank Upgrade ist recht unkompliziert. Damit wir unsere ShoppingMemoDbHelper-Klasse von der SQLiteOpenHelper-Klasse ableiten konnten, mussten wir die beiden Methoden onCreate() und onUpgrade() überschreiben.

Die onCreate() Methode wird nur aufgerufen, falls die SQLite Datenbank noch nicht existiert.

Die onUpgrade() Methode wird aufgerufen, sobald die neue Versionsnummer höher als die alte Versionsnummer ist und somit ein Datenbank-Upgrade notwendig wird.

Wir müssen daher die onUpgrade() Methode implementieren und in ihr den SQLite Datenbank Upgrade ausführen.

Dafür gibt es verschiedene Ansätze. Man kann die bereits vorhandene Tabelle verändern mit dem SQL-Befehl alter und dadurch sicherstellen, dass keine Daten verloren gehen.

Da wir bisher aber nur Testdaten in unserer SQLite Datenbank gespeichert haben, werden wir einen anderen Ansatz wählen.

Wir werden die bisherige Tabelle löschen und anschließend eine neue Tabelle mit vier anstelle der bisherigen drei Spalten erzeugen.


Dazu werden wir den folgenden Quellcode verwenden:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    Log.d(LOG_TAG, "Die Tabelle mit Versionsnummer " + oldVersion + " wird entfernt.");
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHOPPING_LIST);
    onCreate(db);
}

Mit den oberen Zeilen wird das SQLite Datenbank Upgrade durchgeführt, sobald die Versionsnummer der SQLite Datenbank erhöht wurde. Mit der Anweisung in Zeile 4 lassen wir die alte Tabelle aus der SQLite Datenbank entfernen.

Anschließend rufen wir in Zeile 5 die onCreate() Methode auf und lassen dadurch eine neue Tabelle erzeugen. Damit auch wirklich eine neue Tabelle in der SQLite Datenbank erzeugt wird, müssen vorher noch einige Konstanten der ShoppingMemoDbHelper-Klasse verändert werden. Welche das sind werden wir im dritten Abschnitt ausführlich besprechen.

2. Ein neues Attribut in der ShoppingMemo-Klasse anlegen

Wir haben jetzt den theoretischen Teil dieser Lektion abgeschlossen und werden nun das Gelernte in die Praxis umsetzen.

Mit unserer Android App soll es möglich sein, Listeneinträge durch kurzes Antippen durchzustreichen bzw. abzuhaken. Momentan können wir diese Zustandsänderung der Listeneinträge nicht in der SQLite Datenbank abbilden.

Dies werden wir nun ändern und in der ShoppingMemo-Klasse, dem Datenmodell unserer Datenbank, ein zusätzliches Attribut definieren, welches den Status des zugehörigen Einkaufslisten-Eintrag festhält. Die neue Instanzvariable soll den Namen checked tragen und vom Datentyp boolean sein.

Um die neue Membervariable zu initialisieren, müssen wir auch den bisherigen Konstruktor anpassen. Außerdem müssen wir die Get– und Set-Methoden der Membervariable definieren, damit wir den Wert des hinzugefügten Attributs auslesen und festlegen können.

Die eben aufgezählten Änderungen werden wir nun an der ShoppingMemo-Klasse vornehmen. Dazu öffnen wir die Klassendatei ShoppingMemo.java im Editorfenster von Android Studio. Die Klassendatei befindet sich im Package-Ordner de.codeyourapp.shoppinglist unseres Projekts.

Anschließend fügen wir den markierten Quellcode in die ShoppingMemo-Klasse ein:

ShoppingMemo.java

package de.codeyourapp.shoppinglist;

public class ShoppingMemo {

    private String product;
    private int quantity;
    private long id;
    private boolean checked;


    public ShoppingMemo(String product, int quantity, long id, boolean checked) {
        this.product = product;
        this.quantity = quantity;
        this.id = id;
        this.checked = checked;
    }


    public String getProduct() {
        return product;
    }

    public void setProduct(String product) {
        this.product = product;
    }


    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }


    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }


    public boolean isChecked() {
        return checked;
    }

    public void setChecked (boolean checked) {
        this.checked = checked;
    }


    @Override
    public String toString() {
        String output = quantity + " x " + product;

        return output;
    }
}

Der eingefügte Quellcode ist selbsterklärend. Die neue Instanzvariable checked wird den Status der jeweiligen ShoppingMemo speichern. Der Wert true wird für abgehakt stehen und bedeutet, dass das entsprechende Produkt gekauft wurde.

In Android Studio sollte die ShoppingMemo.java Klassendatei nun wie folgt aussehen:

android_sqlite_upgrade_memo

Die Klassendatei ShoppingMemo.java mit den vorgenommenen Änderungen

In der oberen Abbildung ist die ShoppingMemo.java Klassendatei dargestellt. Es wurden die folgenden drei Änderungen vorgenommen:

  1. A – Deklarieren der neuen Membervariable checked vom Datentyp boolean.
  2. B – Anpassen des Konstruktors an die zusätzliche Membervariable.
  3. C – Definieren der Get– und Set-Methoden der neuen Membervariable.

Mit diesen Änderungen verfügt unser Datenmodell über eine neue Eigenschaft. Dadurch ist es aber nicht mehr mit den Datensätzen unserer SQLite Datenbank identisch. Dieses Problem müssen wir als Nächstes beheben und die Vorbereitungen für das SQLite Datenbank Upgrade treffen. Dazu nehmen wir Anpassungen an unserer Hilfsklasse ShoppingMemoDbHelper vor.

3. Die ShoppingMemoDbHelper-Klasse für den SQLite Datenbank Upgrade vorbereiten

An unserer Hilfsklasse ShoppingMemoDbHelper müssen wir nun einige Änderungen vornehmen, da sich das zugrunde liegende Datenmodell geändert hat.

Unsere ShoppingMemo besitzt nun vier Attribute, ein Datensatz unserer Tabelle aber nur drei Spalten.

Wir müssen daher die alte Tabelle aus der SQLite Datenbank entfernen und eine neue Tabelle, die vier Spalten besitzt, erstellen lassen.

plhq_teaser_hbox_gelb_fotolia_RA Studio_46292813

Unser großes
Android Online-Kurs
Gesamtpaket



Weitere Infos

Um dies zu realisieren, werden wir die folgenden Änderungen vornehmen:

  1. Erhöhen der Datenbank Versionsnummer – Konstante DB_VERSION von 1 auf 2 erhöhen.
  2. Definieren einer neuen Spalte – Neue Konstante COLUMN_CHECKED als vierte Spalte anlegen.
  3. Erweitern des Create-Befehls – Die Konstante SQL_CREATE erweitern, so dass durch sie nun eine Tabelle mit vier Spalten erzeugt wird.
  4. Definieren eines Drop-Befehls – Anlegen der neuen Konstante SQL_DROP, mit deren Hilfe die alte Tabelle aus der SQLite Datenbank gelöscht wird.
  5. Implementieren der onUpgrade() Methode – In der onUpgrade() Methode lassen wir die alte Tabelle entfernen und eine neue Tabelle mit vier Spalten erstellen.

Wir öffnen nun die Klassendatei ShoppingMemoDbHelper.java in dem Editor von Android Studio. Die Klassendatei befindet sich im Package-Ordner de.codeyourapp.shoppinglist unseres Projekts.

In dem unten angegebenen Quellcode sind die oben aufgezählten Änderungen an der ShoppingMemoDbHelper-Klasse bereits vorgenommen und markiert worden:

ShoppingMemoDbHelper.java

package de.codeyourapp.shoppinglist;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class ShoppingMemoDbHelper extends SQLiteOpenHelper{

    private static final String LOG_TAG = ShoppingMemoDbHelper.class.getSimpleName();

    public static final String DB_NAME = "shopping_list.db";
    public static final int DB_VERSION = 2;

    public static final String TABLE_SHOPPING_LIST = "shopping_list";

    public static final String COLUMN_ID = "_id";
    public static final String COLUMN_PRODUCT = "product";
    public static final String COLUMN_QUANTITY = "quantity";
    public static final String COLUMN_CHECKED = "checked";

    public static final String SQL_CREATE =
            "CREATE TABLE " + TABLE_SHOPPING_LIST +
                    "(" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    COLUMN_PRODUCT + " TEXT NOT NULL, " +
                    COLUMN_QUANTITY + " INTEGER NOT NULL, " +
                    COLUMN_CHECKED + " BOOLEAN NOT NULL DEFAULT 0);";

    public static final String SQL_DROP = "DROP TABLE IF EXISTS " + TABLE_SHOPPING_LIST;


    public ShoppingMemoDbHelper(Context context) {
        //super(context, "PLATZHALTER_DATENBANKNAME", null, 1);
        super(context, DB_NAME, null, DB_VERSION);
        Log.d(LOG_TAG, "DbHelper hat die Datenbank: " + getDatabaseName() + " erzeugt.");
    }

    // Die onCreate-Methode wird nur aufgerufen, falls die Datenbank noch nicht existiert
    @Override
    public void onCreate(SQLiteDatabase db) {
        try {
            Log.d(LOG_TAG, "Die Tabelle wird mit SQL-Befehl: " + SQL_CREATE + " angelegt.");
            db.execSQL(SQL_CREATE);
        }
        catch (Exception ex) {
            Log.e(LOG_TAG, "Fehler beim Anlegen der Tabelle: " + ex.getMessage());
        }
    }

    // Die onUpgrade-Methode wird aufgerufen, sobald die neue Versionsnummer höher
    // als die alte Versionsnummer ist und somit ein Upgrade notwendig wird
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.d(LOG_TAG, "Die Tabelle mit Versionsnummer " + oldVersion + " wird entfernt.");
        db.execSQL(SQL_DROP);
        onCreate(db);
    }
}

Vergleicht bitte ganz genau den oberen Quellcode mit eurem eigenen Quellcode in Android Studio. Es ist wichtig, dass ihr die markierten Änderungen an exakt den gleichen Stellen im Quelltext der ShoppingMemoDbHelper-Klasse durchgeführt habt.

In Android Studio sollte die ShoppingMemoDbHelper.java Klassendatei nun wie folgt aussehen:

android_sqlite_upgrade_helper

Die Klassendatei ShoppingMemoDbHelper.java mit den vorgenommenen Änderungen

In der oberen Abbildung ist die ShoppingMemoDbHelper.java Klassendatei dargestellt. Es wurden die folgenden fünf Änderungen vorgenommen:

  1. A – Erhöhen der Datenbank Versionsnummer von 1 auf 2.
  2. B – Definieren der Konstante COLUMN_CHECKED als vierte Spalte der Datenbank.
  3. C – Erweitern der Konstante SQL_CREATE, so dass durch sie eine Tabelle mit vier Spalten erzeugt wird.
  4. D – Anlegen der Konstante SQL_DROP zum Löschen der alte Tabelle aus der SQLite Datenbank.
  5. E – Implementieren der onUpgrade() Methode, die das Löschen der alten Tabelle und Erstellen der Tabelle mit vier Spalten veranlasst.

Mit den nun vorgenommenen Änderungen an der DbHelper-Klasse haben wir unsere SQLite App für den Datenbank Upgrade vorbereitet. Würden wir nun unsere Anwendung ausführen, dann würde die SQLiteOpenHelper-Klasse anhand der erhöhten Versionsnummer erkennen, dass ein Datenbank Upgrade notwendig ist und die onUpgrade() Methode aufrufen.

Bevor wir jedoch unsere Android App starten können, müssen wir noch Änderungen an den restlichen beiden Klassen unseres Projekts vornehmen und diese an die geänderte Tabellenstruktur anpassen. Als Nächstes überarbeiten wir die Klasse ShoppingMemoDataSource und im Anschluss daran die MainActivity-Klasse.

4. Die ShoppingMemoDataSource-Klasse anpassen

Unsere Datenquelle müssen wir nun auch an die neue Tabellenstruktur anpassen. Betroffen sind die beiden Methoden updateShoppingMemo() und cursorToShoppingMemo() sowie die Variable columns.

Wir öffnen nun die Klassendatei ShoppingMemoDataSource.java in dem Editor von Android Studio. Die Klassendatei befindet sich im Package-Ordner de.codeyourapp.shoppinglist unseres Projekts.

Anschließend fügen wir den markierten Quellcode in die ShoppingMemoDataSource-Klasse ein:

An dieser Stelle endet der freie Inhalt dieser Lektion. Wir hoffen, sie hat dir bis hierher gefallen! Du kannst sie im geschützten Bereich von ProgrammierenLernenHQ fortsetzen, in welchem sich alle Lektionen unserer Android Online-Kurse befinden.

Unsere Android Kurse bestehen aus insgesamt 43 großen Lektionen und sind unterteilt in 13 frei zugängliche und 30 Premium-Lektionen. Die Premium-Lektionen befinden sich in dem geschützten Bereich und sind nur für Käufer unseres Android Online-Kurs Gesamtpaket zugänglich.

plhq_teaser_hbox_gelb_fotolia_RA Studio_46292813 An dieser Stelle endet der freie Inhalt dieser Lektion. Wir hoffen, sie hat dir bis hierher gefallen! Du kannst sie durch Kauf unseres Android Online-Kurs Gesamtpakets freischalten.

In unserem Android Online-Kurs Gesamtpaket befinden sich 43 große Lektionen, in denen wir dir schrittweise zeigen, wie voll funktionstüchtige Android Apps programmiert werden.

Diese Lektion ist Teil unseres Android Gesamtpakets. Insgesamt sind unsere Online-Kurse unterteilt in 13 frei zugängliche und 30 Premium-Lektionen.

Die Premium-Lektionen befinden sich in dem geschützten Bereich und sind nur für Käufer des Android Online-Kurs Gesamtpakets zugänglich.



Welche Inhalte befinden sich im Android Online-Kurs Gesamtpaket?

  • Das Gesamtpaket enthält alle Android Online-Kurse von ProgrammierenLernenHQ.
  • Im Paket enthalten ist unser großer Android Apps Programmieren Online-Kurs. Er ist unser Hauptkurs und besteht aus 35 großen Lektionen. Die Grundlagen der Android App Entwicklung praxisnah und verständlich zu lehren, ist das Hauptziel des Android Apps Programmieren Kurses.
  • Im Paket enthalten ist auch unser SQLite Datenbank App Programmieren Online-Kurs. Dieser Spezialkurs besteht aus 8 großen Lektionen und ist als weiterführender Kurs konzipiert worden. Der Kurs schließt an unseren Android Apps Programmieren Hauptkurs an und widmet sich dem speziellen Thema der SQLite Datenbanken.
  • Durch den Kauf erhältst du unbegrenzten Zugang zu allen Inhalten unseres Android Online-Kurs Gesamtpakets. Wir werden in Zukunft weitere Lektionen hinzufügen. Auch auf alle zukünftigen Lektionen erhältst du vollen Zugriff.

Wir hoffen, Dich bald als neuen Kursteilnehmer unserer Android Online-Kurse begrüßen zu dürfen!

Einmal kaufen und dadurch zeitlich unbegrenzten Zugriff auf alle Inhalte unseres Android Online-Kurs Gesamtpakets erhalten.



Hinweis: Der untere Quellcode ist Teil des geschützten Bereichs von ProgrammierenLernenHQ. Durch Freischalten unserer Android Online-Kurse erhältst du Zugriff auf alle geschützten Inhalte.
Erfahre mehr über unsere Android Online-Kurse.

ShoppingMemoDataSource.java

android_sqlite_table_datasource_blurry

Vergleicht bitte ganz genau den oberen Quellcode mit eurem eigenen Quellcode in Android Studio. Es ist wichtig, dass ihr die markierten Änderungen an exakt den gleichen Stellen im Quelltext der ShoppingMemoDataSource-Klasse durchgeführt habt.

Mit der ersten Änderung im oberen Quellcode fügen wir dem String-Array columns ein weiteres Element, die neue Spalte, hinzu. Die neue Spalte fügen wir in Zeile 23 dem Array hinzu.

Anschließend überarbeiten wir die updateShoppingMemo() Methode. Wir erweitern ihre Parameterliste um einen vierten Parameter vom Datentyp boolean. Da die SQLite Datenbank diesen Datentyp nicht kennt, wandeln wir ihn mit der Anweisung in Zeile 71 in einen int-Wert um.

Diesen int-Wert fügen wir in Zeile 76 der ContentValues-Variable hinzu und legen ihn darin mit dem neuen Spaltennamen als Schlüssel ab. Somit werden ab jetzt drei Werte plus die ID in die Tabelle der SQLite Datenbank geschrieben.

Weitere Änderungen nehmen wir nicht an der updateShoppingMemo() Methode vor.

Als letzte Änderung im Quellcode passen wir die Methode cursorToShoppingMemo() an. In Zeile 98 fragen wir den Spaltenindex der neuen Spalte beim Cursor-Objekt an. Anschließend lesen wir in Zeile 103 den Wert der vierten Spalte im übergebenen Datensatz aus.

Da dieser Wert aber vom Datentyp int ist, wandeln wir ihn mit der Anweisung in Zeile 105 in einen boolean-Wert um. Jetzt können wir mit den ausgelesenen Datenbankwerten ein ShoppingMemo-Objekt erzeugen lassen. Wir müssen dazu aber noch Zeile 107 ändern und den Konstruktor der Klasse ShoppingMemo mit vier Argumenten aufrufen.

In Android Studio sollte die ShoppingMemoDataSource.java Klassendatei nun wie folgt aussehen:

android_sqlite_upgrade_data

Die Klassendatei ShoppingMemoDataSource.java mit den vorgenommenen Änderungen

In der oberen Abbildung ist die ShoppingMemoDataSource.java Klassendatei dargestellt. Es sind nur die Methoden aufgeklappt, an denen Änderungen vorgenommen wurden. Es wurden die folgenden drei Änderungen vorgenommen:

  1. A – Erweitern des String-Arrays columns um ein weiteres Element, der neuen Spalte.
  2. B – Anpassen der Methode updateShoppingMemo() an die neue Tabellenstruktur.
  3. C – Anpassen der Methode cursorToShoppingMemo() an die neue Tabellenstruktur.

Jetzt haben wir alle notwendigen Änderungen an der Klasse ShoppingMemoDataSource durchgeführt. Unsere Datenquelle beachtet nun die neue Tabellenstruktur.

Als Nächstes nehmen wir umfassende Erweiterungen an unserer MainActivity-Klasse vor. Dies werden auch die letzten Änderungen an den Dateien unseres SQLite Projekts sein.

5. Erweitern der MainActivity-Klasse

Nun kommen wir zu den größten Änderungen bzw. Erweiterungen des Quellcodes. Wir werden in der Klasse MainActivity für unseren ListView einen OnItemClickListener registrieren. Mit dem Listener werden wir auf kurze Klicks auf die Listeneinträge reagieren und diese dann durchstreichen.

plhq_teaser_hbox_gelb_fotolia_RA Studio_46292813

Unser großes
Android Online-Kurs
Gesamtpaket



Weitere Infos

Um dies zu realisieren, nehmen wir die folgenden Änderungen am Quellcode der Klasse MainActivity vor:

  1. Einfügen der Import-Anweisungen – Zuerst importieren wir die benötigten Klassen.
  2. Deklarieren des ListViews als Membervariable – Anschließend werden wir die Membervariable mShoppingMemosListView deklarieren, so dass wir in den Methoden darauf zugreifen können.
  3. Überarbeiten der onCreate() Methode – Wir fügen in diese Methode eine Anweisung ein und zwar den Aufruf der initializeShoppingMemosListView() Methode, die wir im nächsten Arbeitsschritt definieren.
  4. Definieren der initializeShoppingMemosListView() Methode – Wir definieren eine neue Methode, die den ListView initialisiert und für ihn einen OnItemClickListener registriert.
  5. Überarbeiten der showAllListEntries() Methode – Bisher haben wir in dieser Methode den ArrayAdapter immer wieder neu erstellt, um neue Daten anzuzeigen. Dies verbessern wir nun, indem wir nicht mehr den ganzen Adapter löschen, sondern nur seinen Inhalt und ihm anschließend die neuen Daten zuweisen.
  6. Kleine Anpassung der createEditShoppingMemoDialog() Methode – Als letzten Arbeitsschritt passen wir den Methodenaufruf updateShoppingMemo() an. Wir übergeben der Methode nun vier anstelle der bisherigen drei Argumente.

Hinweis: Wir werden jetzt die Arbeitsschritte der Reihe nach ausführen. Sollte etwas unklar sein, könnt ihr im Anschluss daran den gesamten Quellcode der MainActivity-Klasse in Ruhe analysieren und direkt erkennen, an welcher Stelle welcher Code-Block eingefügt werden muss.

Nun beginnen wir mit dem ersten Arbeitsschritt.

5.1 Einfügen der Import-Anweisungen

Wir öffnen nun die Klassendatei MainActivity.java in dem Editor von Android Studio. Die Klassendatei befindet sich im Package-Ordner de.codeyourapp.shoppinglist unseres Projekts.

Anschließend fügen wir den folgenden Quellcode unter den bereits vorhandenen Import-Anweisungen ein:

MainActivity.java

import android.graphics.Color;
import android.graphics.Paint;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
import java.util.ArrayList;

Die sechs Klassen machen wir mittels Import-Anweisungen innerhalb der Klasse MainActivity sichtbar, so dass wir sie ohne den Namen ihres Packages verwenden können.

Die Klassen Color und Paint benötigen wir für das Durchstreichen und Ausgrauen der Listeneinträge. Die Klassen ViewGroup, TextView und ArrayList werden für das Implementieren eines angepassten ArrayAdapters benötigt. Die Klasse AdapterView wird für das Implementieren des OnItemClickListener des ListViews benötigt.

5.2 Deklarieren des ListViews als Membervariable

Nun legen wir die Membervariable mShoppingMemosListView im Variablenbereich der MainActivity-Klasse an. In der Variable speichern wir eine Referenz die auf unser ListView-Objekt verweist.

Wir fügen nun den folgenden Quellcode in die MainActivity.java Klasse direkt nach der dataSource Membervariable ein:

MainActivity.java

private ListView mShoppingMemosListView;

Durch das Deklarieren der Membervariable mShoppingMemosListView können wir von den Methoden der MainActivity-Klasse aus direkt auf die ListView-Instanz unserer Android App zugreifen. Dadurch wird die Arbeit mit dem ListView stark erleichtert.

5.3 Überarbeiten der onCreate() Methode

Nun fügen wir den Methodenaufruf initializeShoppingMemosListView() in die onCreate() Methode der MainActivity ein. Mit Hilfe dieser Methode werden wir das ListView-Objekt unserer App initialisieren. Wir werden die Methode im nächsten Arbeitsschritt definieren.

Wir fügen nun die markierte Zeile in die Methode onCreate() der MainActivity.java Klasse ein:

Hinweis: Der untere Quellcode ist Teil des geschützten Bereichs von ProgrammierenLernenHQ. Durch Freischalten unserer Android Online-Kurse erhältst du Zugriff auf alle geschützten Inhalte.
Erfahre mehr über unsere Android Online-Kurse.

MainActivity.java

android_sqlite_table_mainactivity_blurry

Da die initializeShoppingMemosListView() Methode noch nicht existiert, wird der eingefügte Methodenaufruf in Android Studio rot unterstrichen angezeigt. Wir werden dieses Problem nun beheben und die Methode definieren.

5.4 Definieren der initializeShoppingMemosListView() Methode

Im vierten Arbeitsschritt definieren wir die Methode initializeShoppingMemosListView(), die für uns den ListView initialisieren wird. Die neue Methode erstellt einen ArrayAdapter, weist diesen dem ListView zu und registriert einen OnItemClickListener für den ListView.

Wir fügen nun den folgenden Quellcode in den Methodenbereich der MainActivity.java Klasse direkt nach der onPause() Methode ein:

MainActivity.java

private void initializeShoppingMemosListView() {
    List<ShoppingMemo> emptyListForInitialization = new ArrayList<>();

    mShoppingMemosListView = (ListView) findViewById(R.id.listview_shopping_memos);

    // Erstellen des ArrayAdapters für unseren ListView
    ArrayAdapter<ShoppingMemo> shoppingMemoArrayAdapter = new ArrayAdapter<ShoppingMemo> (
            this,
            android.R.layout.simple_list_item_multiple_choice,
            emptyListForInitialization) {

        // Wird immer dann aufgerufen, wenn der übergeordnete ListView die Zeile neu zeichnen muss
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            View view =  super.getView(position, convertView, parent);
            TextView textView = (TextView) view;

            ShoppingMemo memo = (ShoppingMemo) mShoppingMemosListView.getItemAtPosition(position);

            // Hier prüfen, ob Eintrag abgehakt ist. Falls ja, Text durchstreichen
            if (memo.isChecked()) {
                textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
                textView.setTextColor(Color.rgb(175,175,175));
            }
            else {
                textView.setPaintFlags( textView.getPaintFlags() & (~ Paint.STRIKE_THRU_TEXT_FLAG));
                textView.setTextColor(Color.DKGRAY);
            }

            return view;
        }
    };

    mShoppingMemosListView.setAdapter(shoppingMemoArrayAdapter);

    mShoppingMemosListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
            ShoppingMemo memo = (ShoppingMemo) adapterView.getItemAtPosition(position);

            // Hier den checked-Wert des Memo-Objekts umkehren, bspw. von true auf false
            // Dann ListView neu zeichnen mit showAllListEntries()
            ShoppingMemo updatedShoppingMemo = dataSource.updateShoppingMemo(memo.getId(), memo.getProduct(), memo.getQuantity(), (!memo.isChecked()));
            Log.d(LOG_TAG, "Checked-Status von Eintrag: " + updatedShoppingMemo.toString() + " ist: " + updatedShoppingMemo.isChecked());
            showAllListEntries();
        }
    });

}

Mit dem oberen Quellcode realisieren wir die folgenden drei Dinge:

  1. Erstellen eines ArrayAdapters – Mit den Zeilen 7 bis 33 legen wir den ArrayAdapter an und überschreiben seine getView() Methode. Die getView() Methode wird immer dann aufgerufen, wenn ein Listeneintrag gezeichnet werden muss. In dieser Methode verändern wir die Eigenschaften des TextView-Elements, mit welchem wir die jeweiligen SQLite Datenbankeinträge anzeigen. Wir prüfen, ob der entsprechende Datenbankeintrag abgehakt ist, also ob das Attribut checked den Wert true besitzt. Ist dies der Fall, streichen wir den Text durch und ändern die Schriftfarbe in ein helles Grau. Ist der Wert false, dann geben wir den Text normal aus.
  2. ArrayAdapter an den ListView binden – Anschließend binden wir den erzeugten ArrayAdapter mit der Anweisung in Zeile 35 an unseren ListView.
  3. Registrieren eines OnItemClickListener für den ListView – Damit wir auf kurze Klicks auf Listeneinträge reagieren können, registrieren wir mit den Zeilen 37 bis 48 einen OnItemClickListener für unseren ListView. In dem Listener kehren wir den Status des angeklickten Listeneintrags um. Wir fragen dazu das angeklickte ShoppingMemo-Objekt an und setzen anschließend den checked-Wert neu. Den alten Wert kehren wir dafür um. Anschließend lassen wir den ListView neu zeichnen.

5.5 Überarbeiten der showAllListEntries() Methode

Als nächste Änderung werden wir die Methode showAllListEntries() überarbeiten. In ihr fragen wir alle Einträge aus der SQLite Datenbank an und übergeben die ausgelesenen Daten an den ArrayAdapter. Anschließend weisen wir über den ArrayAdapter unseren ListView an, sich neu zu zeichnen.

Wir löschen dazu unsere bisherige showAllListEntries() Methode in der MainActivity.java Klasse und ersetzen sie mit folgendem Quellcode:

MainActivity.java

private void showAllListEntries () {
    List<ShoppingMemo> shoppingMemoList = dataSource.getAllShoppingMemos();

    ArrayAdapter<ShoppingMemo> adapter = (ArrayAdapter<ShoppingMemo>) mShoppingMemosListView.getAdapter();

    adapter.clear();
    adapter.addAll(shoppingMemoList);
    adapter.notifyDataSetChanged();
}

Bisher hatten wir in dieser Methode den ArrayAdapter immer wieder neu erstellt, um neue Daten anzuzeigen. Dies haben wir nun verbessert, indem wir nicht mehr den ganzen Adapter löschen, sondern nur dessen Inhalt und ihm anschließend die neuen Daten zuweisen.

5.6 Kleine Anpassung der createEditShoppingMemoDialog() Methode

Als letzte Änderung nehmen wir eine Anpassung am Aufruf der updateShoppingMemo() Methode in der Methode createEditShoppingMemoDialog() vor. Dies müssen wir tun, da die updateShoppingMemo() Methode jetzt 4 Argumente erwartet, anstelle der bisherigen drei.

Wir passen nun die markierte Zeile in der Methode createEditShoppingMemoDialog() der MainActivity.java Klasse an:

MainActivity.java

private AlertDialog createEditShoppingMemoDialog(final ShoppingMemo shoppingMemo) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    LayoutInflater inflater = getLayoutInflater();

    View dialogsView = inflater.inflate(R.layout.dialog_edit_shopping_memo, null);

    final EditText editTextNewQuantity = (EditText) dialogsView.findViewById(R.id.editText_new_quantity);
    editTextNewQuantity.setText(String.valueOf(shoppingMemo.getQuantity()));

    final EditText editTextNewProduct = (EditText) dialogsView.findViewById(R.id.editText_new_product);
    editTextNewProduct.setText(shoppingMemo.getProduct());

    builder.setView(dialogsView)
            .setTitle(R.string.dialog_title)
            .setPositiveButton(R.string.dialog_button_positive, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int id) {
                    String quantityString = editTextNewQuantity.getText().toString();
                    String product = editTextNewProduct.getText().toString();

                    if ((TextUtils.isEmpty(quantityString)) || (TextUtils.isEmpty(product))) {
                        Log.d(LOG_TAG, "Ein Eintrag enthielt keinen Text. Daher Abbruch der Änderung.");
                        return;
                    }

                    int quantity = Integer.parseInt(quantityString);

                    // An dieser Stelle schreiben wir die geänderten Daten in die SQLite Datenbank
                    ShoppingMemo updatedShoppingMemo = dataSource.updateShoppingMemo(shoppingMemo.getId(), product, quantity, shoppingMemo.isChecked());

                    Log.d(LOG_TAG, "Alter Eintrag - ID: " + shoppingMemo.getId() + " Inhalt: " + shoppingMemo.toString());
                    Log.d(LOG_TAG, "Neuer Eintrag - ID: " + updatedShoppingMemo.getId() + " Inhalt: " + updatedShoppingMemo.toString());

                    showAllListEntries();
                    dialog.dismiss();
                }
            })
            .setNegativeButton(R.string.dialog_button_negative, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
                }
            });

    return builder.create();
}

In Zeile 29 erfolgt der Methodenaufruf der updateShoppingMemo() Methode. Wir übergeben der Methode nun die vier Argumente: id, product, quantity und checked.

5.7 Der komplette Quellcode der MainActivity-Klasse

Nun haben wir alle Änderungen an der MainActivity-Klasse vorgenommen. Mit dem eingefügtem Quellcode reagieren wir auf kurze Klicks des Benutzers und ändern das Aussehen des angeklickten Listeneintrags.

In dem unten angegebenen Quellcode ist die gesamte MainActivity-Klasse zur Kontrolle für euch aufgeführt. Die neu eingefügten Zeilen sind markiert worden.

Vergleicht bitte ganz genau den oberen Quellcode mit eurem eigenen Quellcode in Android Studio. Es ist wichtig, dass ihr die markierten Änderungen an exakt den gleichen Stellen im Quelltext der MainActivity-Klasse durchgeführt habt.

In Android Studio sollte die MainActivity.java Klassendatei nun wie folgt aussehen:

Hinweis: Der untere Quellcode ist Teil des geschützten Bereichs von ProgrammierenLernenHQ. Durch Freischalten unserer Android Online-Kurse erhältst du Zugriff auf alle geschützten Inhalte.
Erfahre mehr über unsere Android Online-Kurse.

android_sqlite_lektion8_blurry

Der obere Quellcode ist Teil des geschützten Bereichs von ProgrammierenLernenHQ. Durch Freischalten unserer Android Online-Kurse erhältst du Zugriff auf alle geschützten Inhalte.
Erfahre mehr über unsere Android Online-Kurse.

In der oberen Abbildung ist die MainActivity.java Klassendatei dargestellt. Es sind nur die Methoden aufgeklappt, an denen Änderungen vorgenommen wurden. Es wurden die folgenden 6 Änderungen vorgenommen:

  1. A – Importieren der benötigten Klassen.
  2. B – Deklarieren des ListViews als Membervariable.
  3. C – Einfügen des initializeShoppingMemosListView() Methodenaufrufs in die onCreate() Methode.
  4. D – Definieren der initializeShoppingMemosListView() Methode, durch die unser ListView initialisiert wird.
  5. E – Überarbeiten der showAllListEntries() Methode, so dass der einmal erzeugte ArrayAdapter wiederverwendet wird.
  6. F – Anpassen der createEditShoppingMemoDialog() Methode an die neue Tabellenstruktur. Dazu haben wir den updateShoppingMemo() Methodenaufruf überarbeitet.

Mit diesen Änderungen im Quellcode der MainActivity-Klasse haben wir unserer SQLite App eine weitere nützliche Funktion hinzugefügt. Wir können jetzt Listeneinträge abhaken. Um diese Funktion zu implementieren, musste die Tabellenstruktur der SQLite Datenbank durch ein Upgrade geändert werden.

Im nächsten Abschnitt werden wird unsere SQLite App testen und Einträge der Einkaufsliste durchstreichen.

6. Starten und Testen unserer SQLite App

Wir werden nun unserer Android App auf einem Android Virtual Device im Emulator ausführen lassen und überprüfen, ob der SQLite Datenbank Upgrade korrekt ausgeführt wird. Zusätzlich prüfen wir auch, ob die Einträge korrekt in dem ListView der MainActivity abgehakt werden können.

Hinweis: Wenn ihr Probleme beim Ausführen der App im Android Emulator oder auf einem Android Gerät haben solltet, könnt ihr unseren großen Android Apps Programmieren Kurs als Hilfe nutzen. Darin zeigen wir, wie eine Android App im Emulator oder auf einem physikalischen Android Gerät ausgeführt wird.

Unsere App starten wir dazu wie gewohnt über den Run > Run 'app' Menüeintrag, den wir über die obere Menüleiste erreichen.

Unsere SQLite App sollte jetzt auf dem Android Gerät gestartet worden sein. Die grafische Benutzeroberfläche der Anwendung hat sich auch diesmal nicht geändert, daher sieht auf dem Gerät alles genau so aus wie in der vorherigen Lektion.

Hinweis: Das Emulator-Fenster mit dem darin laufenden Android Virtual Device muss für die nächsten Arbeitsschritte geöffnet bleiben. Zudem muss in dem AVD unsere eigene App ausgeführt werden. Sollte dies nicht der Fall sein, muss die App erneut im Emulator gestartet werden.

Um die Log-Meldungen zu überprüfen, öffnen wir in Android Studio das Logcat Tool Window während unsere App auf dem AVD im Emulator ausgeführt wird.

Dazu führen wir die folgenden Schritte aus:

  1. Wir öffnen das Logcat Tool Window mit einem Klick auf den Logcat-Tab am unteren Bildschirmrand.
  2. Anschließend wählen wir Emulator Nexus_9 als AVD in der Drop-Down Liste aus.
  3. Nun wählen wir unsere App de.codeyourapp.shoppinglist in der Liste rechts daneben aus.
  4. Danach stellen wir die Prioritätsstufe auf Verbose ein.
  5. Und geben in das anschließende Suchfeld MainActivity|ShoppingMemo als Suchbegriff ein.
  6. Dann wählen wir den Eintrag Show only selected application in Liste ganz rechts aus.
android_sqlite_upgrade_start

Überprüfen der Log-Meldungen unserer App im Logcat Tool Window von Android Studio

Uns werden die folgenden Log-Meldungen ausgegeben:

01: MainActivity﹕ Das Datenquellen-Objekt wird angelegt.
02: ShoppingMemoDataSource﹕ Unsere DataSource erzeugt jetzt den dbHelper.
03: ShoppingMemoDbHelper﹕ DbHelper hat die Datenbank: shopping_list.db erzeugt.
04: MainActivity﹕ Die Datenquelle wird geöffnet.
05: ShoppingMemoDataSource﹕ Eine Referenz auf die Datenbank wird jetzt angefragt.
06: ShoppingMemoDbHelper﹕ Die Tabelle mit Versionsnummer 1 wird entfernt.
07: ShoppingMemoDbHelper﹕ Die Tabelle wird mit SQL-Befehl: CREATE TABLE shopping_list(_id INTEGER PRIMARY KEY AUTOINCREMENT, product TEXT NOT NULL, quantity INTEGER NOT NULL, checked BOOLEAN NOT NULL DEFAULT 0); angelegt.
08: ShoppingMemoDataSource﹕ Datenbank-Referenz erhalten. Pfad zur Datenbank: /data/user/0/de.codeyourapp.shoppinglist/databases/shopping_list.db
09: MainActivity﹕ Folgende Einträge sind in der Datenbank vorhanden:

Die meisten Log-Meldungen sind bereits bekannt. Neu sind die beiden Meldungen 6 und 7, die über den durchgeführten SQLite Datenbank Upgrade informieren (Markierung A).

Die alte Tabelle wir aus der SQLite Datenbank entfernt und eine neue Tabelle mit dem oben aufgeführten SQL-Befehl erstellt. Wir können auch erkennen, dass die vierte Spalte vom Datentyp boolean sein soll und den Standardwert 0 für jeden neuen Eintrag besitzt.

Der Standardwert ist nicht false, da die SQLite Datenbank keinen echten boolean Datentyp beherrscht, sondern dafür die Integer-Zahlen 0 und 1 verwendet.

In dem unteren animierten GIF ist das Vorgehen für das Durchstreichen eines ListView-Eintrags in unserer App dargestellt:

android_sqlite_upgrade_demo

Einträge aus der SQLite Datenbank im ListView durchstreichen (abhaken)

6.1 Video – Funktionspräsentation unserer SQLite App

In dem unteren Video haben wir einen Eintrag aus dem ListView abgehakt und dadurch durchgestrichen. In der SQLite Datenbank wird dabei der Wert in der vierten Spalte entsprechend geändert. Im Video ist zu sehen, wie das Abhaken der Listeneinträge funktioniert.

Mit dieser weiteren Funktion ist die Einkaufslisten-App nun abgeschlossen und kann im Alltag verwendet werden. Es können natürlich noch zusätzliche Funktionen eingebaut werden, wie bspw. das Anlegen von verschiedenen Einkaufslisten.

Zusammenfassung

In diesem letzten Teil unseres Android SQLite Tutorials haben wir noch einmal Veränderungen an allen Klassen unseres Projekts vorgenommen. Dies war notwendig, da wir die Datenstruktur erweitert hatten.

Wir haben folgende Änderungen durchgeführt:

  1. Erweitern der ShoppingMemo-Klasse um ein zusätzliches Attribut.
  2. Vorbereiten der ShoppingMemoDbHelper-Klasse für den SQLite Datenbank Upgrade.
  3. Anpassen der ShoppingMemoDataSource-Klasse an die neue Datenstruktur der SQLite Datenbank und des Datenmodells.
  4. Erweitern der MainActivity-Klasse um einen OnItemClickListener, mit dessen Hilfe wir Einträge des ListViews abhaken (durchstreichen) können.

Nun sind wir am Ende des SQLite Tutorials angelangt. Wir hoffen es hat bei euch alles so wie beschrieben funktioniert und ihr hattet Spaß bei der Entwicklung einer Android SQLite App.

Vielen Dank für euer Durchhaltevermögen und viel Erfolg bei euren zukünftigen Android Projekten.



Comments 56

  1. hallo chris,
    danke erstmal für diesen hervorragenden Kurs hier.
    Hier meine Frage, da ich die Appidee leicht abgeändert habe(richtung Sporttracker) würde ich noch gerne wissen wie man in sqlite sortiert bzw. auf einzelne werte zugreift ( für HighscoreListe). Im Netz hab ich da bisher wenig gut erklärtes Material gefunden. Es wäre schön, wenn du mir dabei helfen könntest.
    Gruss
    Timo

  2. „In dieser Methode verändern die die Eigenschaften des TextView-Elements,…“

    *In dieser Methode verändern WIR die Eigenschaften…

    1. Post
      Author

      Hallo Daniel,

      danke für deinen Hinweis! Ich habe den Fehler korrigiert.

      Viele Grüße, Chris

  3. Ein wirklich tolles Tutorial – Für einen Laien wie mich perfekt aufbereitet. Es hat bei mir auchalles einwandfrei funktioniert! Darauf lässt sich aufbauen und nun selber experimentieren 🙂

    Eine Frage habe ich nur für das in Lektion 1 eingebaute Menu. Für was haben wir den Menupunkt „Settings“ implementiert? Dieser hat keine Funktion oder habe ich was überlesen.

    1. Post
      Author

      Hallo Marko,

      danke für’s Lob! Der Settings-Menüeintrag ist ein Überbleibsel aus einer alten Version des Tutorials. Er besitzt in der Tat keine Funktion.

      Viele Grüße, Chris

  4. Super Tutorial!

    Aber eine Frage, auf die Settings die wir im Options Menu Angelegt haben, sind wir gar nicht eingegangen oder hab ich was verpasst?

    Viele Grüße

    1. Post
      Author

      Hallo Paul,

      der Settings-Eintrag stammt aus einer alten Version dieses Tutorials. In der aktuellen Version besitzt er keine Funktion.

      Viele Grüße, Chris

  5. Hallo Chris,

    wirklich das beste Tutorial im Web!
    Mir hat es den Einstieg in die App Entwicklung sehr erleichtert.

    Vielleicht hättest du noch einen Tip für die Anwendung.
    Wie könnte man die Listview nach dem Markieren der Checkbox sortieren.
    Also die markierten Produkte unten anzeigen?

    Beste Grüße
    Dirk

  6. Mein Android Studio meckert beim Build
    in Zeile 59: List emptyListForInitialization = new ArrayList();
    meckert das Build einen Fehler:
    error: cannot find symbol class ArrayList
    uses unchecked or unsafe operations

    ich kann keinen fehler im quellcode im vergleich zu dem obigen entdecken.

    P.S. das ist die Coolste Seite die ich in Bezug auf Android Programmierung bisher gefunden habe. Ich persönlich finde, das man Programmieren am besten anhand eines Projektes lernen kann, welche die verschiedenen Aspekte der Programmiersprache beleuchtet.

    Insgesamt Java an sich ist ja recht simpel, ähnlich wie C. Aber die Klassen und Interfaces etc.. da muss man sich schon sehr tief reinwühlen. Das finde ich immer noch sehr schwierig nachzuvollziehen.

    Bei der Implementation des Codes und der Beschreibung hätte ich mir vielleicht gewünscht eine Grafik auf die Tabelle mit einzubinden die den Sachverhalt mit den Zeigern verdeutlicht. Dennoch großes Lob, das beste was ich bisher sah.

  7. Hallo Chris

    und gleich noch eine Frage zur 8. Lektion:
    Wenn ich ein Item „abhake“ wird ja in dem „OnItemClickListener“ definiert, dass „memo.checked“ umgedreht wird.
    Wenn ich dein Beispiel richtig verstehe, hast du keine Arraylist mitlaufen, wo der Wert geändert wird, und über adapter.notifyDataSetChanged() der Adapter angewiesen wird, die View zu aktualisieren, oder?

    Daher leerst Du den Adapter, und befüllst ihn aus der Datenbank immer komplett neu.
    Ginge das auch anders? Könnte mir vorstellen, dass es bei sehr großen DB’s dadurch langsamer wird.

    Gruß
    Carsten

    1. Post
      Author

      Hallo Carsten,

      ja dies sollte man bei größeren Datenbanken unbedingt optimieren. Im Tutorial habe ich versucht den Code so einfach und kurz wie möglich zu halten.

      Viele Grüße, Chris

  8. Hallo Chris

    ich bin jetzt mit deinem Tutorial auch komplett durch. Und schließe mich den anderen an.
    Das Tutorial ist sehr gut aufgbaut, sehr gut strukturiert. Man kann jederzeit sehr gut nachvollziehen, wo was eingefügt werden muss.
    Auch die einzelnen Erklärungen der entsprechenden Zeilen ist sehr gut.

    Ein Verbesserungsvorschlag noch für die 8. Lektion:
    Wenn ein Upgrade der DB durchgeführt wird, wird in deinem Beispiel der komplette Inhalt der DB gelöscht. Eine Idee wäre, wenn der Inhalt vorher ausgelesen werden könnte in eine temporäre Arraylist? und nach dem erneuten createDB der Inhalt direkt zurückgeschrieben. Wäre das eine mögliche Lösung, um Datenverlust vorzubeugen?

    Viele Grüße
    Carsten

    1. Post
      Author

      Hallo Carsten,

      danke für dein Lob! Ja dein Vorschlag ist die deutlich bessere Lösung und sollte sich auch schnell und sicher umsetzen lassen.

      Viele Grüße, Chris

  9. Seit vielen Jahren beschäftige ich mich mit Programmierung und habe Dutzende Bücher und Tutorials zu unterschiedlichen Sprachen gelesen. Eine derart sorgfältige Dokumentation ist mir dabei noch nicht begegnet – meine Hochachtung!
    Hier stimmt nicht nur der Code vollständig (was durchaus nicht selbstverständlich ist), sondern die einzelnen Schritte sind hervorragend aufgebaut, verständlich beschrieben und großartig illustriert.
    Vielen Dank!

    1. Post
      Author
  10. Hallo Chris, mir hat das Tutorial sehr gut gefallen. Alles hat auf Anhieb wie beschrieben funktioniert. Ich hatte keine einzige Fehlermeldung. Das Tutorial lohnt sich wirklich. Für einen Anfänger wie mich war es klasse und sehr lehrreich.

    Im Teil 5 beschreibst Du zwei Tabellen. Eine mit „Primärschlüssel“ und eine zweite mit zusätzlichem Fremdschlüssel. Vielleicht könntest Du zu diesem Thema das Tutorial erweitern? Zum Beispiel um eine Tabelle mit Geschäften bei denen man die einzelnen Produkte erwerben kann. Dann könnte man auch eine 1:n Beziehung realisieren …

    Ich habe bisher noch keine Vorstellung wie man so etwas realisieren könnte. Im Netz fand ich bisher keine wirklich hilfreichen Hinweise.

    Gruß Alfred

    1. Post
      Author

      Hallo Alfred,

      danke für dein Lob! Mir gefällt dein Vorschlag sehr. In einer zukünftigen Aktualisierung dieses Tutorials könnte ich ein 1:n Beziehung einbauen. Dies wäre wahrscheinlich für einige Leser sehr interessant.

      Viele Grüße, Chris

  11. Hallo Chris,
    erstmal ein großes Dankeschön für das Tutorial, ganz tolle Arbeit. Dank deiner guten Beschreibung war es ein Kinderspiel, allerdings für dich, so
    glaube ich eine aufwendige Angelegenheit. Die LogCat Methoden kannte ich noch nicht. Da gibt es ja verschiedene:
    Log.v(); // Verbose
    Log.d(); // Debug
    Log.i(); // Info
    Log.w(); // Warning
    Log.e(); // Error
    Habe in deinem Programm die Log.i(); // Info benutzt, statt Log.d(); // Debug
    dann sieht man es besser.
    Nochmals vielen Dank, guten Rutsch und sechs richtige im Lotto
    Heinz-Dieter

    1. Post
      Author

      Hallo Heinz-Dieter,

      danke für dein Lob und den Logcat Hinweis! Ja, in der Tat. Der Aufwand für diese Art von Tutorials ist sehr hoch, gemessen an der Zeit, die man für das Absolvieren der Reihe benötigt. Aber der Aufwand hat sich für mich gelohnt. Man lernt am meisten selbst, wenn man anderen etwas lernt. Dann merkt man direkt, ob man manche Zusammenhänge wirklich schon verstanden hat oder selbst noch etwas Studium benötigt.

      Viele Grüße, Chris

  12. Hallo Chris
    Ich möchte mich dem Lob der Vorgänger anschließen.
    Nun habe ich diese App über BuildVariant als release eingestellt und über Build/Build APK(s) erzeugen lassen. Diese Datei habe ich zu Testzwecken auf das Smartphone übertragen und wollte diese dort installieren. Obwohl in der Einstellungen unbekannte Quellen aktiviert wurde, kommt nachdem ich den Installationsprozess ausgelöst habe (App wurde nicht installiert)
    Auch über das Internet habe ich keine funktionierte Lösung gefunden.
    Sollte es die Wertvolle Zeit zulassen, dann währe ich über jeden Hinweis dankbar.
    Gruß Willi

  13. Hallo Chris
    Auch ich möchte mich dem Lob der Vorgänger anschließen.
    App läuft:
    1. Wie kann ich den Hintergrund der contextual-activity-bar ändern
    2. Wo kann ich die apk Datei finden android Studio
    Gruß Willi

  14. Hallo Chris
    Danke für die tollen Kurse. Wirklich super gemacht.
    Hier noch ein Verbesserungsvorschlag: Wenn du ein UI-Element beschreibst (z.B. ein neues Menu) fände ich es sinnvoll, gleich am Anfang einen Screenshot davon zu zeigen. Dann wissen die Leser schon mal, um was es geht und können sich auf den Inhalt konzentrieren, anstatt sich erstmal vorstellen zu müssen, wie das denn genau aussehen wird.

    Gruss
    Klaus

    1. Post
      Author

      Hallo Klaus,

      danke für Dein Lob und den sehr hilfreichen Verbesserungsvorschlag! Den werde ich für zukünftige Tutorials berücksichtigen.

      Viele Grüße, Chris

  15. Hallo,
    erstmal vorweg, ein super Arbeit. Habe nach Deinem Beispiel mein erstes App soweit entwickelt, das es läuft und komme nun bei den Feinheiten nicht weiter. Wie kann ich die Daten der Datenbank in csv oder pdf wandeln um sie per mail zu verschicken. Für ein wenig Hilfe wäre ich dankbar. Habe einges im Netz gefunden, kann es aber nicht lauffähig einbinden.

    Gruß
    Josh

  16. Hey,

    ich habe eine Frage, kann ich es irgendwie hinbekommen das sich die APP mit anderen Handys Synct? Am besten mithilfe eines Raspberry Pi 3b!

    Bis dahin aber schonmal danke für das Tutorial…

    Liebe Grüße

  17. Ein Super Tutorial. Funktionert sogar auf meinem Chinesenhandy mit Android 7 tadellos.
    Die ActionBar habe ich um einige Funktionen erweitert – läuft.
    Auch die Anzahl der Felder habe ich auf 8 erweitert. Läuft ebenfalls.
    Allerdings versuche ich seit Tagen, einen Multi-Column Listview hinzubekommen und schaffe es nicht.
    Irgendeine Idee, wie ich mit den bestehenden Klassen sowas erzeugen kann?

    Es sieht nicht schön aus, wenn alle Felder einfach nur durch Leerzeichen getrennt hintereinander gepresst stehen.
    Ein Layout dafür habe ich schon…..es müssen nur die Felder dort hinein – und genau das geht irgendwie nicht.

    Danke,
    Oliver

    1. Post
      Author

      Hallo Oliver,

      danke für dein Lob!

      Man müsste einen Custom-Adapter implementieren, der den ListView mit der Datenquelle verbindet. Vielleicht zeige ich in einem neuen Tutorial einmal wie so etwas geht.

      Viele Grüße, Chris

  18. Vielen Dank für dein Tutorial, wie vielen anderen hat es auch mir sehr geholfen 🙂

    Zum Schluss würde ich noch gerne wissen, wie ich meine Quellcodes so schön anschaulich exportieren kann ;D

    Wenn ich meinen Quellcode über Android Studio drucke sieht es ziemlich besch***** aus.

    1. Post
      Author

      Hallo HamGo,

      ich habe oft mehrere Screenshots vom Quellcode gemacht. Also das Android Studio Fenster mehrmals per Screenshot abfotografiert. Diese Screenshots habe ich dann manuell wieder zusammengesetzt. Sehr aufwendig, aber auch sehr hilfreich für die Leser.

      Viele Grüße, Chris

  19. Hi Chris.

    Bei mir ist folgendes Problem aufgetaucht. Deine Anleitung ist super und funktioniert ohne Probleme! Dafür erstmal danke und Top geschrieben.

    Mein Problem fängt aber da an, in dem ich versuche einen Weiteren Wert (String) hinzuzufügen. Im Grunde habe ich immer mich an den Strings product orientiert, verdoppelt und in „nachname“ umgewandelt.

    Ich habe mal mein Log eingefügt. Ich weiß jetzt nicht woran es liegen könnte.
    Danke schonmal für die Hilfe!

    03-24 18:56:38.587 32579-32579/com.example.fabian.teamapp D/Team: Das Datenquellen-Objekt wird angelegt.
    03-24 18:56:38.587 32579-32579/com.example.fabian.teamapp D/TeamDatenDataSource: Unsere DataSource erzeugt jetzt den dbHelper.
    03-24 18:56:38.587 32579-32579/com.example.fabian.teamapp D/TeamDatenDbHelper: DbHelper hat die Datenbank: shopping_list.db erzeugt.
    03-24 18:56:38.597 32579-32579/com.example.fabian.teamapp D/Team: Die Datenquelle wird geöffnet.
    03-24 18:56:38.597 32579-32579/com.example.fabian.teamapp D/TeamDatenDataSource: Eine Referenz auf die Datenbank wird jetzt angefragt.
    03-24 18:56:38.607 32579-32579/com.example.fabian.teamapp D/TeamDatenDataSource: Datenbank-Referenz erhalten. Pfad zur Datenbank: /data/user/0/com.example.fabian.teamapp/databases/shopping_list.db
    03-24 18:56:38.607 32579-32579/com.example.fabian.teamapp D/Team: Folgende Einträge sind in der Datenbank vorhanden:

  20. Hallo
    zuerst, es ist ein super Tutorial, ich bin wirklich ein Anfänger.

    hab aber ein Problem,
    laut Log ist alles einwandfrei siehe unten, aber auf dem Handy stürzt die APP ab mit dem Fehler
    “ ShoppingListHQ angehalten“
    was mache ich Falsch?
    Viele Grüsse
    Vito

    01-27 14:02:36.349 29607-29607/de.programmierenlernenhq.shoppinglisthq D/MainActivity: Das Datenquellen-Objekt wird angelegt.
    01-27 14:02:36.349 29607-29607/de.programmierenlernenhq.shoppinglisthq D/ShoppingMemoDataSource: Unsere DataSource erzeugt jetzt den dbHelper.
    01-27 14:02:36.349 29607-29607/de.programmierenlernenhq.shoppinglisthq D/ShoppingMemoDbHelper: DbHelper hat die Datenbank: shopping_list.db erzeugt.
    01-27 14:02:36.359 29607-29607/de.programmierenlernenhq.shoppinglisthq D/MainActivity: Die Datenquelle wird geöffnet.
    01-27 14:02:36.359 29607-29607/de.programmierenlernenhq.shoppinglisthq D/ShoppingMemoDataSource: Eine Referenz auf die Datenbank wird jetzt angefragt.
    01-27 14:02:36.369 29607-29607/de.programmierenlernenhq.shoppinglisthq D/ShoppingMemoDbHelper: Die Tabelle wird mit SQL-Befehl: CREATE TABLE shopping_list(_id INTEGER PRIMARY KEY AUTOINCREMENT, product TEXT NOT NULL, quantity INTEGER NOT NULL, checkedBOOLEAN NOT NULL DEFAULT 0); angelegt.
    01-27 14:02:36.369 29607-29607/de.programmierenlernenhq.shoppinglisthq D/ShoppingMemoDataSource: Datenbank-Referenz erhalten. Pfad zur Datenbank: /data/user/0/de.programmierenlernenhq.shoppinglisthq/databases/shopping_list.db
    01-27 14:02:36.369 29607-29607/de.programmierenlernenhq.shoppinglisthq D/MainActivity: Folgende Einträge sind in der Datenbank vorhanden:

    1. Post
      Author

      Hallo Vito,

      momentan kann ich mich aus Zeitgründen leider kaum den Kommentaren widmen, daher kommt meine Antwort so spät 🙁

      Wenn du möchtest, kannst Du mir deine Projektdateien (den ganzen Android Studio Projektordner, aber OHNE die build-Unterordner) als ZIP per E-Mail zusenden. Die E-Mail Adresse kannst Du im Impressum finden. Ich schaue dann mal, ob ich etwas herausfinden kann.

      Viele Grüße, Chris

    2. @Vito
      Wenn das Protokoll am Ende deines Postings endet, dann sieht es auf den ersten Blick nach einem Problem in showAllLIstEntries() aus. Vermutlich liegt das Problem in ShoppingMemoDataSource.getAllShoppingMemos(), weil dort die Daten zusammengestellt werden.
      Wenn du an diesen Stellen jeweils einen Breakpoint setzt und die App im Debug Modus startest, kannst du genauer betrachten, welche Laufzeitwerte vorliegen und so (hoffentlich) den Fehler finden.
      Da mein Beitrag jetzt erst kommt, hast du vermutlich das Problem bereits beseitigt, aber wer weiß … 😉

  21. Hallo,

    das Tutorial ist sehr gut. Das Programm funktioniert sowohl auf einem Emulator als auch auf einem angeschlossenen Samsung Galaxy S4 Mini. Leider werden im Android Device Monitor keine Dateien und Unterverzeichnisse angezeigt. Ich sehe auf der linken Seite nur die oberste Verzeichnisebene beim Emulatorsystem. Wie kann ich das ändern?

    Viele Grüße,
    Hans-Jürgen

    1. Post
      Author
  22. Super Tutorial, wirklich eins der besten was es im web gibt.
    Sehr ausführlich, übersichtlich markiert und immer wieder der ganze Quellcode damit nichts schief gehen kann. Hat mir sehr geholfen, Danke!

    1. Post
      Author

      Hallo André,

      danke für’s Lob! Ja das Tutorial sollte so verständlich wie möglich sein, da beim Programmieren schon winzige Kleinigkeiten zu großen Problemen führen können. Diese frustrierenden Momente sollte unseren Lesern erspart bleiben.

      Viele Grüße, Chris

  23. Hallo zusammen,
    ich habe ein neues Problem: Die App läuft, wenn ich die Methode „public void activateAddButton“ und den entsprechenden Methodenaufruf auskommentiere. Aber mit der Methode wird mir an genau diesen Stellen ein Fehler gemeldet mit einer NullPointerException: java.lang.NullPointerException: Attempt to invoke virtual method ‚void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)‘ on a null object reference
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3319)

    Ich weiß nicht, wo ich einen Fehler gemacht habe.

    Ich hoffe es kann mir jemand schnell helfen.

    Vielen Dank, Katja

    1. Post
      Author

      Hallo Katja,

      wie es scheint, wird versucht auf einem leeren Objekt eine Methode aufzurufen. Die Ursache des Fehlers findet man am besten mit dem Debugger. Wenn du möchtest, kannst Du mir deine Projektdateien (den ganzen Android Studio Projektordner, aber OHNE die build-Unterordner) als ZIP per E-Mail zusenden. Die E-Mail Adresse kannst Du im Impressum finden. Ich schaue dann mal, ob ich etwas herausfinden kann.

      Viele Grüße, Chris

  24. Hallo Chris,

    ich bin begeistert. Mir geht es ähnlich, wie deinem Fan Norbert Schomburg. Ich habe die 66 auch schon überschritten und mich an die Android-Programmierung gewagt. Dank deines ausgezeichneten Tutorials habe ich die App sogar auf meinem SAMSUNG Galaxy S5 zum Laufen gebracht. Jetzt würde ich nur noch wissen wollen, wie man die Checkbox mit einem Häkchen füllen bzw. wieder leeren kann.

    Hast du da einen Tipp?

    1. Post
      Author

      Hallo Wilfried,

      danke für Dein Lob!

      Im SQLite-Tutorial nehmen wir für die Checkboxen ein Standardlayout (simple_list_item_multiple_choice). Damit ist es schwierig zu erklären, wie eine Checkbox in Android funktioniert.

      Ich habe mich mal im Web umgesehen und ein sehr anschauliches Tutorial gefunden, das Checkboxen im Detail behandelt. Auch der Beitrag auf der Android Developer Seite ist zu empfehlen.

      https://www.mkyong.com/android/android-checkbox-example/
      https://developer.android.com/reference/android/widget/CheckBox.html

      Ich hoffe Dir helfen die Links weiter.

      Viele Grüße, Chris

  25. Hi, sehr gutes Tutorial!
    Hat mir sehr weiter geholfen 🙂

    Ich möchte nun eine weitere Tabelle anlegen, die die gleiche Struktur wie die erste besitzt. Wie kann ich das am einfachsten machen, ohne DataSource, Helper und Entry neu anlegen zu müssen?

    Viele Grüße und weiter so!

    1. Post
      Author

      Hallo Rob,

      in der Helper-Klasse einfach eine neue Tabelle definieren (Tabellenname als Konstante, SQL-Create Anweisung, in onCreate() die Tabelle erstellen lassen) und nicht vergessen die DB-Version um 1 zu erhöhen.

      In der DataSource-Klasse dann die benötigten zusätzlichen Methoden, die mit der neu angelegten Tabelle (mittels Tabellennamen) arbeiten, implementieren und von der Activity wie gehabt auf die DataSource-Methoden zugreifen.

      Viele Grüße, Chris

    2. @Rob:
      Wenn du eine weitere Tabelle mit der gleichen Struktur anlegen willst, ist es vermutlich sehr viel zweckmäßiger, dass du die bereits existierende Tabelle um eine Spalte erweiterst, die eine Art Kategorie abbildet. Solche Kategorien könnten bspw. sein: Supermarkt, Lebensmittel, Baumarkt, Internetbestellung, Kleidung, …
      Auch Anlässe wären als Kategorien geeignet.

      @Chris:
      Sehr gut zusammengestelltes Tutorial. Ich habe es sehr ambitioniert und gerne durchgearbeitet. Einzig anfangs aufzuführende Lernvoraussetzungen seitens der Lernenden fehlen. Für Anfänger ist es nicht geeignet, weil recht viele Inhalte aus anderen Bereichen zusammenkommen. Insbesondere die SQL-Anweisungen dürften bei Nicht-Datenbank-Vorkenntnissen schwer zu verstehen sein.
      Noch ein Hinweis: Die Grafik in der anfänglichen Übersicht zu Lektion 2 (MainActivity … DataObjekt) finde ich sehr aufschlussreich. Ähnliche Grafiken in späteren Lektionen, die die Zusammenhänge der Erweiterungen zeigen, könnten die Schritte vermutlich noch besser verdeutlichen.

  26. Hallo lieber Chris,

    Dankeschön für so einen tollen Beitrag, das hat mir wirklich mit Begeisterung geholfen. Bleibe gesund!

    Gruß aus Berlin

    Amir

    1. Post
      Author

      Hallo Amir,

      Danke für Dein Lob! Es freut mich sehr, wenn der Beitrag hilfreich und zugleich interessant war.

      Viele Grüße, Chris

    1. Post
      Author
  27. Hallo Chris, was du auf deiner Seite anbietest ist aussergewöhnlich. Für einen Android- Anfänger wie mich (73) mit einiger Erfahrung in PASCAL ist das Projekt ziemlich anspruchsvoll, aber ich habe mich durchgekämpft.Was ich nicht geschafft habe: ich will die erzeugte SQLITE-Datei von außen mit einem DB-Drowser manipulieren. Leider gelingt es mir nicht , diese Datei im Projekt zu finden, ich finde schon den Pfad nicht. Wenn du mir da helfen könntest, fiele mir die beabsichtigte Spende noch leichter !
    Nochmals danke ! Norbert

    1. Post
      Author

      Hallo Norbert,

      danke für Deine lobenden Worte!

      Es gibt mehrere Wege, um an die Datenbank einer Android App zu gelangen.

      Wenn man die App auf dem Emulator in einem AVD ausführt, kann man mit Hilfe des Android Device Monitors zur Datenbankdatei navigieren und die Datei dann mit „Pull a file from the device“ vom AVD herunterladen und auf der Festplatte speichern.

      Um dies zu tun, führt man zuerst die App auf dem AVD aus und startet anschließend den Android Device Manager (ADM) in Android Studio über Tools>Android>Android Device Manager. Im ADM wählt man als Device den Emulator aus und klickt danach auf den Reiter File Explorer. Dort kann man frei in dem Dateisystem des Android Virtual Devices navigieren. Die Datenbank liegt in dem Ordner data/data/de.programmierenlernenhq.shoppinglisthq/databases und trägt den Namen shopping_list.db.

      Die Datei wird nun ausgewählt und kann dann mit Hilfe des Buttons „Pull a file from the device“ auf die Festplatte kopiert werden. Der Button ist rechts oben im ADM-Fenster angebracht und sieht aus wie eine Diskette mit einem lila Pfeil (der nach links zeigt).

      Die überarbeitete Datenbankdatei kann auf diesem Wege auch wieder aus das AVD kopiert werden. Dazu muss der Button „Push a file onto the device“ rechts daneben verwendet werden.

      Möchte man die Datenbankdatei von einem physischen Android Gerät laden, geht der eben beschriebene Weg leider nicht, da man keine Leserechte auf die „data“-Verzeichnisse besitzt. Um dennoch auf die Datei zugreifen zu können, kann man sich die Datei auf eine SD Karte kopieren lassen. Bei StackOverflow.com gibt es dazu einige erfolgversprechende Anleitungen zu finden, bspw. hier: location of sqlite database on the device

      Man kann scheinbar die Datenbank auch direkt auf der SD-Karte erzeugen lassen. Ich habe dies aber noch nicht selbst ausprobiert. Der Quellcode könnte wie folgt aussehen:

      public ShoppingMemoDbHelper(Context context) {
              super(context, "/mnt/sdcard/database_name.db", null, DB_VERSION);
              Log.d(LOG_TAG, "DbHelper hat die Datenbank: " + getDatabaseName() + " erzeugt.");
      }
      

      Ich hoffe meine Ausführungen helfen Dir etwas weiter und wünsche noch viel Freude bei der Android Programmierung 🙂

      Viele Grüße, Chris

  28. Hallo,
    ich habe mit dem Code

    private ShoppingMemo cursorToShoppingMemo(Cursor cursor) {
            int idIndex = cursor.getColumnIndex(ShoppingMemoDbHelper.COLUMN_ID);
            int idProduct = cursor.getColumnIndex(ShoppingMemoDbHelper.COLUMN_PRODUCT);
            int idQuantity = cursor.getColumnIndex(ShoppingMemoDbHelper.COLUMN_QUANTITY);
            int idChecked = cursor.getColumnIndex(ShoppingMemoDbHelper.COLUMN_CHECKED);
    
            String product = cursor.getString(idProduct);
            int quantity = cursor.getInt(idQuantity);
            long id = cursor.getLong(idIndex);
            int intValueChecked = cursor.getInt(idChecked);
    
            Boolean isChecked = (intValueChecked != 0);
    
            ShoppingMemo shoppingMemo = new ShoppingMemo(product, quantity, id, isChecked);
    

    Folgenden Fehler:

    Error:(110, 37) error: constructor ShoppingMemo in class ShoppingMemo cannot be applied to given types;
    required: String,int,long
    found: String,int,long,Boolean
    reason: actual and formal argument lists differ in length

    Irgendeine Idee ?
    Das Tut ist nebenbei echt sehr gut erzählt, vielen Dank für die Mühe und Arbeit!

    MfG Kevin

    1. Post
      Author

      Hallo Kevin,

      Deine Fehlermeldung weist auf ein Problem mit dem Konstruktor der ShoppingMemo-Klasse hin. In Deinem Quellcode ist ein entsprechender Konstruktor mit 3 Parametern vorhanden, es wird aber ein Konstruktor mit 4 Parametern benötigt.

      In Schritt 2 von Teil 8 haben wir den Konstruktor der Klasse ShoppingMemo.java um einen vierten Parameter erweitert, von:

      public ShoppingMemo(String product, int quantity, long id) { ... }
      

      hin zu:

      public ShoppingMemo(String product, int quantity, long id, boolean checked) { ... }
      

      Vielleicht hast Du diesen Schritt übersprungen oder es hat sich ein kleiner Tippfehler in Deinem Quellcode eingeschlichen.

      Außerdem sollte die Variable isChecked vom primitiven Typ boolean sein. In Deinem Quellcode ist sie vom Objekt-Typ Boolean (java.lang.Boolean).

      Viele Grüße, Chris

    2. Hallo Chris,

      Vielen Dank für die schnelle Antwort, ein Semikolon hatte gefehlt, beim erneuten hinschauen viel es mir dann auf, vielen dank.

      Ist es möglich eine Datenbank online zu speichern und abzurufen ?

      Vielen Dank für die Hilfe
      MfG Kevin

    3. Post
      Author

      Hallo Kevin,

      schön wenn es jetzt funktioniert.

      Ja es ist möglich auf eine online Datenbank zuzugreifen. Man benötigt dafür einen Server auf dem die Datenbank ausgeführt wird und eine Schnittstelle über die man vom Android Gerät auf die Datenbank zugreifen kann. Als Anfrage-Ergebnisse kann man sich XML oder JSON Daten liefern lassen und diese in der App dann weiterverarbeiten.

      Viele Grüße, Chris

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.