Tutorial – greenDAO from Scratch – Teil 2

In Teil 1 haben wir uns nur mit DaoGenerator beschäftigt und haben erstmal alle direkt mit Android in ZUsammenhang stehende Dinge aussen vor gelassen – wir haben bisher nicht eine Activity oder ein Fragment erstellt. In diesem Teil wird sich das ändern: wir werden den zentralen RecyclerView erstellen(mit allen benötigten Klassen) und die Liste unserer Leases laden. Für die Ungeduldigen: am Ende werden wir ein paar Einträge generieren, so dass wir sehen können, ob alles wie geplant funktioniert.



Ich muss leider mit ein paar Warnungen beginnen:
Das Package, in dem unsere generierten Klassen sind, befindet sich im normalen Quellverzeichnis unserer Anwendung. Wie fast alles hat dies sowohl Vorteile, als auch Nachteile. Auf der einen Seite müssen wir nicht erst einen Ordner importieren, auf der anderen Seite dürfen wir dies bei Änderungen nicht vergessen.Um es kurz zu machen:

  • Ja, ihr könnt manuelle Änderungen an den generierten Klassen vornehmen.
  • Ja, ihr könnt eigene Klassen / Dateien / Ordner erstellen.
  • Nein, eure Änderungen überleben den nächsten Lauf des Generators nicht.

 

Inhalt Teil 2:

  1. Hinzufügen von Bibliotheken
  2. Anpassung des Build-Files der App
  3. Erstellen der zentralen Activity
  4. Implementieren des RecyclerView
  5. RecyclerView funktionstüchtig machen

Hinzufügen von Bibliotheken

Im ersten Teil haben wir den Generator und die Freemarker Bibliothek zur Generator-Applikation hinzugefügt. Fügt diesmal bitte (per copy + paste) die GreenDAO-Bibliothek zu folgendem Ordner hinzu:

Ort des libs Ordners

Anpassung des Build-Files der App

Wahrscheinlich werdet ihr euch erinnern, dass wir im letzten Teil schonmal Änderungen an der Gradle-Build-Datei vorgenommen haben. Wir müssen aber genau darauf achten was wir tun, denn wir haben 2 BUILD DATEIEN! Letztes Mal legten wir Hand an die Build-Datei der Generator-Applikation – dieses Mal arbeiten wir an der App. Die richtige Datei ist folgende:

Ort der Gradle Build-Datei

Im „dependencies“-Bereich (Abhängigkeiten), fügt bitte die folgende Zeile für GreenDAO ein:

compile files('libs/greendao-1.3.7.jar')
compile 'com.android.support:recyclerview-v7:21.0.0'

Alle Abhängigkeiten sind dadurch erfüllt, aber ist euch der gelbe Balken am oberen Rand aufgefallen? Android Studio merkt sofort, dass Änderungen an der Build-Datei vorgenommen wurden und fordert einen „Sync“ – macht dies, indem ihr auf „sync now“ klickt

Erstellen der zentralen Activity

Macht einen Rechtsklick auf den „app“-Ordner und wählt „New…“ -> „Activity“ -> „Blank Activity“ aus. Android Studio hat leider keine vorbereiteten Activities für RecyclerViews – keine Panik, darum kümmern wir uns selbst. In dem Wizard könnt ihr alles lassen wie es ist (es sei denn, ihr habt den Package-Namen angepasst) :

Erstellen einer neuen, leeren Activity

Android Studio versucht es euch nun einfach zu machen und öffnet die XML View-Definition im grafischen Editor – wir reagieren mit einem freundlichen, aber bestimmten „Nein, Danke“ und wechseln in die Code-Ansicht:

Wechsel vom grafischen Editor zum Texteditor

Implementieren des RecyclerView

Passt die XML-Datei wie u.a. an:

	
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.devteam83.tutorials.leasegreendao.MainActivity">

    <android.support.v7.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main_activity_recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ffffff"
        />

</FrameLayout>

RecyclerView funktionstüchtig machen

Wenn ihr euch bisher noch nicht mit RecyclerViews beschäftigt habt, dann sucht am besten nach einem entsprechenden Tutorial, weil wir hier nicht detailliert darauf eingehen werden. Zusätzlich sei darauf hingewiesen, dass unsere Implementierung nicht in einer produktiven Anwendung eingesetzt werden sollte – sie dient nur der Demonstration von GreenDao. Wenn ihr mehr über RecyclerView lesen möchtet: ich füge am Ende dieser Serie ein paar interessante Links ein.

Wir beginnen jetzt damit, eine neue Layout-Datei zu erstellen, da wir diese später brauchen werden – wir werden es für die einzelnen Elemente verwenden. Macht einen Rechtsklick auf den layout Ordner der App und wählt „New“-> „Layout resource file“ aus. Wir geben dem Layout den Namen „activity_main_item“ und lassen „LinearLayout“ wie voreingestellt als Wurzelelement – schön wird unser Design vermutlich nicht, aber das könnt ihr ja zu einem späteren Zeitpunkt an euren persönlichen Geschmack anpassen. Bitte passt die Datei wie folgt an:

	
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- item -->
    <TextView
        android:id="@+id/activity_main_item_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16dp" />
    <!-- person name -->
    <TextView
        android:id="@+id/activity_main_item_person_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16dp" />

</LinearLayout>

Als nächstes erstellen wir einen Adapter, der das Bindeglied zwischen RecyclerView und unseren DAO-Klassen bildet. Bitte das Package „com.devteam83.leasegreendao“ (bzw. euren eigenen Package-Namen) mit der rechten Maustaste anklicken und „New“ -> „Java class“ auswählen. Ein sinnvoller Name für die Klasse ist „MainActivityListAdapter“

Dieser Klasse geben wir eine Membervariable „mData“ (wenn Android Studio uns vorschlägt, Importe durchzuführen, dann einfach zustimmen), sowie eine Methode, die die Anzahl Elemente hiervon zurückgibt:

	private List<Lease> mData;

        @Override
        public int getItemCount() {
            return mData.size();
        }
	

„@Override“ wird dabei rot unterstrichen, weil die Klasse (noch !) gar nicht explizit von einer Elternklasse abgeleitet ist – kein Problem, das ändern wir etwas später. Bevor wir uns darum kümmern erstellen wir eine innere Klasse, die Verweise auf alle Elemente im RecyclerView hält (bitte beachten: Elemente in der Sicht, nicht in der Datenbank!)

    public static class ViewHolder extends RecyclerView.ViewHolder {

        public TextView textViewItem;
        public TextView textViewPersonName;

        public ViewHolder(View itemLayoutView) {
            super(itemLayoutView);
            textViewItem = (TextView) itemLayoutView.findViewById(R.id.activity_main_item_item);
            textViewPersonName = (TextView) itemLayoutView.findViewById(R.id.activity_main_item_person_name);
        }
    }

Wir müssen noch zwei weitere Methoden überschreiben, die später von RecyclerView aufgerufen werden. Diese werden jedes mal benutzt, wenn ein ViewHolder erstellt wird (onCreateViewHolder) oder neue Daten eingefügt werden (bevor er dargestellt wird, unabhängig davon, ob er gerade erzeugt wurde oder recycled wurde):

	@Override
    public MainActivityListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        View itemLayoutView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.activity_main_item, null);

        // create ViewHolder

        ViewHolder viewHolder = new MainActivityListAdapter.ViewHolder(itemLayoutView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {

        viewHolder.textViewItem.setText(mData
                                .get(position)
                                .getItem());
        viewHolder.textViewPersonName.setText(mData
                                .get(position)
                                .getPerson()
                                .getName());
    }

Sollte nicht zu schwer zu erraten sein, was diese tun, oder? An dieser Stelle brauchen wir auch das vorhin erstellte Layout. Das letzte fehlende Stück im Adapter ist der Konstruktor – er ist nicht sehr kompliziert aufgebaut, bekommt die Liste mit den Elementen als Parameter übergeben und reicht diese an die Membervariable weiter.
Wie vorhin schon versprochen kümmern wir uns nun tatsächlich um die unterstrichenen „@Override“ Annotationen – wir leiten unsere Klasse ab von

RecyclerView.Adapter<MainActivityListAdapter.ViewHolder>

Die gesamte Klasse inklusive Importe sieht nun so aus:

package com.devteam83.tutorials.leasegreendao;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.devteam83.tutorials.leasegreendao.model.Lease;

import java.util.List;

public class MainActivityListAdapter extends RecyclerView.Adapter<MainActivityListAdapter.ViewHolder>{
    private List<Lease> mData;

    public MainActivityListAdapter(List<Lease> data) {
        this.mData = data;
    }

    @Override
    public MainActivityListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        View itemLayoutView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.activity_main_item, null);

        // create ViewHolder

        ViewHolder viewHolder = new MainActivityListAdapter.ViewHolder(itemLayoutView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {

        viewHolder.textViewItem.setText(mData
                                .get(position)
                                .getItem());
        viewHolder.textViewPersonName.setText(mData
                                .get(position)
                                .getPerson()
                                .getName());
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {

        public TextView textViewItem;
        public TextView textViewPersonName;

        public ViewHolder(View itemLayoutView) {
            super(itemLayoutView);
            textViewItem = (TextView) itemLayoutView.findViewById(R.id.activity_main_item_item);
            textViewPersonName = (TextView) itemLayoutView.findViewById(R.id.activity_main_item_person_name);
        }
    }
}

Da unser ListAdapter nun fertig ist, doppelklicken wir MainActivity.java und fügen folgendes in der onCreate-Methode ein:

	DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "lease-db", null);
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        DaoSession daoSession = daoMaster.newSession();
        
	LeaseDao leaseDao = daoSession.getLeaseDao();
        List<lease> leaseList = leaseDao.loadAll();

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_activity_recyclerView);
	recyclerView.setLayoutManager(new LinearLayoutManager(this));
        MainActivityListAdapter adapter = new MainActivityListAdapter(leaseList);
        recyclerView.setAdapter(adapter);

Langsam kommen wir einer lauffähigen App immer näher, nur noch ein Schritt fehlt! Fügt MainActivity noch einen Intent-Filter im AndroidManifest.xml hinzu, danach sollte die Datei so aussehen:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.devteam83.tutorials.leasegreendao" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Ändert danach bitte die Ausführungskonfiguration (run config) auf „app“:

Wechsel der Konfiguration auf app

… und probiert einfach einmal aus, ob alles funktioniert! Drückt einfach den grünen Play-Knopf!

Erstes Ausführen der App

Langweilig, oder? Alles leer!

Wie am Anfang versprochen werden wir das aber ändern und ein paar Testdaten einfügen. Fügt die folgende Methode am Ende von MainActivity.java ein:

	public void insertSampleData(DaoSession daoSession) {
        Person person = new Person();
        person.setName("John Doe");
        PersonDao personDao = daoSession.getPersonDao();
        personDao.insertOrReplace(person);

        Lease lease = new Lease();
        lease.setItem("My Nexus 6");
        lease.setPerson(person);
        LeaseDao leaseDao = daoSession.getLeaseDao();
        leaseDao.insertOrReplace(lease);
    }

Diese Methode müssen wir jetzt noch aufrufen – am Ende von „onCreate“ direkt nach der Zeile „DaoSession daoSession = daoMaster.newSession();“ machen wir genau das mit „insertSampleData(daoSession);“. Das war es auch schon! Jedes Mal, wenn ihr die App nun startet, werden neue Entitäten eingefügt.

unsere App mit Testdaten

Das war der zweite Teil – nächstes Mal werden wir nicht nur Testdaten einfügen, sondern CRUD-Operationen implementieren. Wenn ihr Fragen oder Probleme mit diesem Teil hattet / habt, dann lasst gern, wie immer, einen Kommentar da.

zu Teil 1
zu Teil 3