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 unserer Android Online-Kurse 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 unserer Android Kurse. 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. Dieser besteht aus 35 großen Lektionen und ist als Einstiegskurs konzipiert worden. 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 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 all unseren Android Online-Kursen. Wir werden in Zukunft weitere Android Kurse hinzufügen. Auch auf alle zukünftigen Kurse 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 unserer Android Online-Kurse 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

Schreibe einen Kommentar

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