Apps für Android programmieren leicht gemacht!
4.2 Kaffee Clicker

4.2 Kaffee Clicker

Wir fahren nun mit der Programmierung unserer ersten eigenen App fort.
Natürlich setzten wir die Schritte 4. Erste App und 4.1 Kaffee Clicker voraus. Solltet ihr bis dato, diese beiden Kapitel noch nicht gelesen haben solltet ihr dies nun nachholen.

 

Grundfunktionen:

Wie bereits angesprochen haben wir in Kapitel 4. und 4.1 bereits Vorarbeit geleistet und unserer App ein Aussehen und ein Grundgerüst verpasst.
Nun wollen wir uns ganz der Programmierung in Java widmen, um nicht nur eine App zu haben, die gut aussieht, sondern auch noch Funktionen hat.
Wir beginnen am besten mit den einfachsten Grundfunktionen von „Kaffee Clicker“ und arbeiten uns dann weiter durch alles Weitere. Am Ende kommen wir zu ein paar Optimierungstipps und zur Veröffentlichung der App im Google PlayStore.

Genug der schönen Worte um den heißen Brei herum.
Wir öffnen nun die Datei „MainActivity“, welche in der linken Dateiliste unter app -> java -> de.droid_lernen.kaffeeclicker zu finden ist.
Diese Datei sollte in etwa so aussehen:

package de.droid_lernen.kaffeeclicker;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

}

Der Ordnerpfad und der Text hinter „package“ könnte bei euch etwas anders lauten, wenn ihr bei der Einrichtung der App in Kapitel 4. Erste App einen anderen Domainnamen gewählt habt.

 

Was steht dort nun genau und was ist das für eine Datei?
Naja, diese Datei wird beim Start der App automatisch aufgerufen. Innerhalb der äußersten „{}“ kann man nun Methoden einfügen, die benutzt werden können. Die erste vorgefertigte Methode „onCreate“ wird automatisch, beim Start der App, aufgerufen. Hierbei handelt es sich also um unsere Startmethode. Alles was unsere App, bei ihrem Start tun soll, wird hier hineingeschrieben.

 

Variablen:

Wir müssen uns nun erst einmal ein paar Variablen erstellen, die später Informationen über unsere Geldbörse, unsere gekauften Verbesserungen, etc. enthalten sollen.

//Variablen
public float geld;
public float gewinn;
public int recycling;
public int shops;
public int rabatt;
public ImageView kaffeeBild;
public ImageView recyclingBild;
public ImageView shopsBild;
public ImageView rabattBild;
public TextView geldAnzeige;

Diese Variablen sollten für unsere App genügen. Wir fügen sie innerhalb der äußersten „{}“-Klammern ein, aber nicht innerhalb von „onCreate“. Wir erstellen uns also globale Variablen, auf die jede Methode zugreifen kann.

Nun müssen wir diese Variablen initialisieren und mit einem Standartwert beschreiben, dazu schreiben wir innerhalb von onCreate:

/* "super.onCreate(savedInstanceState);" sollte immer das Erste sein, was innerhalb von onCreate steht, also bitte darunter einfügen */

geld = 0F;      //Das "F" hinter der 0 steht für "Float", also eine Kommazahl
gewinn = 0.1F;    //Gewinn pro verkauftem Kaffee
recycling = 0;
shops = 0;
rabatt = 1;
kaffeeBild = (ImageView) findViewById(R.id.kaffeeBild);       //Definition von kaffeeBild
recyclingBild = (ImageView) findViewById(R.id.recyclingBild);
shopsBild = (ImageView) findViewById(R.id.shopBild);
rabattBild = (ImageView) findViewById(R.id.rabattBild);
geldAnzeige = (TextView) findViewById(R.id.geldAnzeige);

 

OnClick Listener:

Nachdem wir unsere Variablen erstellt und initialisiert haben benötigen wir einen Listener.
Also eine Methode die die gesamte Zeit über „zuhört“ und immer dann ausgeführt wird, wenn etwas geklickt wird/wurde.
Wir schalten diesen OnClick Listener auf unser ImageView mit der ID „kaffeeBild“ auf, um bei jedem Klick Kaffe verkaufen zu können.

Wir fügen in die Methode „OnCreate“, nachdem die Varaiablen initialisiert wurden, ein:

//Erstellen eines Listeners, für das ImageView mit der ID kaffeeBild, welches wir als gleichnamige Variable definiert haben (siehe oben).
kaffeeBild.setOnClickListener(new View.OnClickListener() {

       //Wird aufgerufen, wenn wir das ImageView anklicken
       @Override
       public void onClick(View view) {

           //Wird aufgerufen, wenn "onClick" aufgerufen wird.
           verkaufeKaffee();
       }
});

Alles was innerhalb der {}-Klammern von „onClick“ steht wird nun ausgeführt, wenn wir auf das ImageView mit der ID „kaffeeBild“ klicken.
Wie man sieht habe ich bereits in diese Methode den Aufruf einer neuen Methode veranlasst.
Klickt man nun also auf das ImageView mit der ID „kaffeeBild“ wird die Methode „onClick“ aufgerufen, welche dann wiederum die Funktion „verkaufeKaffee()“ aufruft.
Alles irgendwie sehr kompliziert, aber wir schaffen gleich mehr Klarheit.

 

verkaufeKaffee():

Nun haben wir bereits ein Grundgerüst, ein Aussehen und einen Listener, doch was nun?
Naja bisher zeigt unsere App etwas an und reagiert, wenn man auf den großen Kaffebecher klickt, aber noch fehlt uns eine Spielmechanik und die Methode „verkaufeKaffee()“.
Diese Methode wollen wir uns nun erstellen.

Sie soll uns zu der Variable „geld“ den Betrag hinzufügen, der unter „gewinn“ steht.
Wir vermehren also unser Geld, indem wir Kaffee verkaufen.
Bei jedem Klick/verkauftem Kaffee bekommen wir etwas mehr an Geld, in Höhe des Gewinns.

//Innerhalb der äußersten {}-Klammern einfügen
public void verkaufeKaffee(){

    //Unser neues Geld besteht aus unserem Geld + unserem Gewinn pro Kaffee
    geld = geld + gewinn;

    //Ändere die Textausgabe
    geldAnzeige.setText("Geld: " + geld + "€");

}

 

Fazit und Erläuterungen:

Nun kann unsere erste eigene App bereits Kaffee verkaufen und das verdiente Geld in der Textanzeige anzeigen lassen.
Damit beenden wir vorerst dieses Kapitel und führen unsere Programmierung im nächsten Kapitel fort.

Um noch einmal klarzustellen, wie nun eigentlich der Ablauf, beziehungsweise unsere Aufrufe aussehen findet ihr hier ein kleines Ablaufdiagramm:

  1. Start der App
  2. MainActivity wird von Android geöffnet
  3. Die Variablen werden erstellt, aber noch nicht beschrieben/initialisiert
  4. Alle Methoden werden zum Aufruf bereit gemacht
  5. Die Methode „onCreate“ wird von Android aufgerufen
    1. Die Variablen werden beschrieben/initialisiert
    2. Es wird ein Listener für das ImageView mit der ID „kaffeeBild“ erzeugt
  6. Der Listener wartet nun auf einen Klick, er „hört zu“
    1. Wenn der Listener einen Klick „hört“ führt er „onClick“ aus
      1. „onClick“ ruft die Methode „verkaufeKaffee“ auf
        1. „verkaufeKaffee“ errechnet nun das neue Geld und schreibt es in die Textanzeige

 

Kleine Information am Rande:
Es kann vorkommen, dass die Textanzeige einen Geldbetrag von z.B. „0.09999999994“ anzeigt, obwohl dort eigentlich 0.10 stehen sollte.
Dies kann vorkommen, da der Datentyp Float in Java die Zahlen nicht direkt speichert, sondern errechnet und dabei können Rundungsfehler passieren.
Dieses Problem ist aber nicht gravierend, da 0.09999999994 sehr nahe an 0.10 dran ist.
Auf Wikipedia findet ihr mehr Informationen dazu.

Marvin

Ich bin 23 Jahre jung und studiere zurzeit Wirtschaftsinformatik an der Georg-August-Universität in Göttingen. Ich bin ein Mensch, der sich neben der Programmierung noch für tausend andere Dinge interessiert, die mal mehr und mal weniger verrückt sind. Vor allem aber bin ich Feuer und Flamme mit der Programmierung von eigenen kleinen Apps und Programmen, die mein Leben bereichern.

18 Kommentare

*Pflichtfeld

  • Kleiner Fehler:
    geldAnzeige = (TextView) findViewById(R.id.geldAnzeige);

    Die Symbol id ist in der activity_main.xml als android:id=“@+id/GeldAnzeige“ definiert.
    Also wer dirket aus dem Tutorial kopiert…das große G in ein kleines g ändern!

    • Hallo Herr Pester,
      vielen Dank, für den Hinweis.

      Um die Einheitlichkeit zu wahren habe ich Kapitel 4.1 geändert.
      Nun sind alle Anfangsbuchstaben klein.

      Grüße,
      Marvin

  • Bei mir kommt es immer zu einer NullpointerException an folgender Stelle:

    kaffeeBild.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
    verkaufeKaffee();
    }
    });

  • Hallo Marvin,
    ich habe das gleiche Problem wie „SN“.
    ich glaube das liegt daran, da wir das immage nicht als Button definiert haben oder so ?
    Ich bin absolut neu und es kann sein, dass meine Vermutung auch völliger schwachsinn ist.

    Gruß Orhan.

    • public class MainActivity extends AppCompatActivity {

      //Variablen
      public float geld;
      public float gewinn;
      public int recycling;
      public int shops;
      public int rabatt;
      public ImageView kaffeeBild;
      public ImageView recyclingBild;
      public ImageView shopsBild;
      public ImageView rabattBild;
      public TextView geldAnzeige;

      //Methoden
      public void verkaufeKaffee(){
      //Unser neues Geld besteht aus unserem Geld + unserem Gewinn pro Kaffee
      geld = geld + gewinn;
      //Ändere die Textausgabe
      geldAnzeige.setText(„Geld: “ + geld + „€“);
      }

      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      geld = 0F; //Das „F“ hinter der 0 steht für „Float“, also eine Kommazahl
      gewinn = 0.1F; //Gewinn pro verkauftem Kaffee
      recycling = 0;
      shops = 0;
      rabatt = 1;
      kaffeeBild = (ImageView) findViewById(R.id.kaffeeBild); //Definition von kaffeeBild
      recyclingBild = (ImageView) findViewById(R.id.recyclingBild);
      shopsBild = (ImageView) findViewById(R.id.shopBild);
      rabattBild = (ImageView) findViewById(R.id.rabattBild);
      geldAnzeige = (TextView) findViewById(R.id.geldAnzeige);

      kaffeeBild.setOnClickListener(new View.OnClickListener() {
      //Wird aufgerufen, wenn wir das ImageView anklicken
      @Override
      public void onClick(View view) {
      //Wird aufgerufen, wenn „onClick“ aufgerufen wird.
      verkaufeKaffee();
      }
      });

      setContentView(R.layout.activity_main);
      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
      setSupportActionBar(toolbar);

      FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
      fab.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
      Snackbar.make(view, „Replace with your own action“, Snackbar.LENGTH_LONG)
      .setAction(„Action“, null).show();
      }
      });

      }

      • Hallo Orhan,

        Achte darauf, über „public class MainActivity …“ mindestens folgende Imports zu schreiben:
        import android.os.Bundle;
        import android.support.design.widget.FloatingActionButton;
        import android.support.v7.app.AppCompatActivity;
        import android.view.View;
        import android.widget.ImageView;
        import android.widget.TextView;
        import android.support.v7.widget.Toolbar;
        import android.support.design.widget.Snackbar;

        Ich weiß gerade leider nicht genau, was du mit SN meinst, aber einen onClickListener kann man auf Buttons und ImageViews anwenden.

        Gruß

  • Hallo Marvin,
    mit „SN“ meinte ich den Kommentator über mir.
    Ich glaube das in deiner Antwort sind Bibliotheken oder? die waren von anfang an schon drin, ich habe nur den restlichen code rauskopiert um zu fragen was da nicht stimmt.

    Hab halt auch den fehler wie der Kommentator über mir:

    „E/AndroidRuntime: FATAL EXCEPTION: main
    Process: helloworld.mrorhan.helloworld, PID: 4766
    java.lang.RuntimeException: Unable to start activity ComponentInfo{helloworld.mrorhan.helloworld/helloworld.mrorhan.helloworld.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method ‚void android.widget.ImageView.setOnClickListener(android.view.View$OnClickListener)‘ on a null object reference
    .
    .
    .“

    • Hallo,

      das habe ich glatt übersehen.
      Es gibt drei wichtige Dinge für einen onClickListener.
      Erstens muss die Variable definiert sein (wie in meiner Antwort auf SN).
      Dann müssen alle Imports geschehen sein.
      Als drittes musst du darauf achten, dass das Layout, welche gerade angezeigt wird (letzter setContentView(…)), auch das ImageView enthält.

      Öffne einfach mal die Datei activity_main.xml im Ordner res -> layouts und guck ob es dort ein ImageView gibt, dass die ID „kaffeeBild“ hat.

      Gruß

      • Hallo Marvin,
        ich hoffe ich gehe dir nicht zu sehr auf die Nerven.
        Das Problem ist, dass ich alle Images etc bei content_main.xml rein gefügt habe.
        Wenn ich sie in beide einfüge, bekomme ich alles doppelt.

        (Bei activity_main.xml wird die obere menüleiste, welche bei mir Blau ist, auch über bedeckt und man sieht alle icons nebeneinander. Sind nicht wie sie sein sollen hintereinander)

      • Ok es hat geklappt!!
        Ich hatte alle Deklerationen etc richtig, nur habe ich dummer weise nicht nachgedacht und nur copypaste mäßig, erst gesagt dass er auf kaffeeBild klicken soll und dann das layout aufgerufen 😀
        Vielen dank für deine mühe

    • Hallo Davdy,
      um welches ImageView handelt es sich genau?
      Also wie lautet die Zeile mit dem Fehler?

      Ich gehe mal davon aus, dass die Klasse „ImageView“ nicht importiert wurde oder eine der ID’s falsch ist.
      Wenn nur das Wort „ImageView“ rot ist, dann klicke auf die rote Lampe in der Zeile und wähle „Import XXX“ aus.

      Gruß

      • ja die schrift war rot habs jetzt mit Import gemacht und funzt auch allerdings sind immer noch fehler

        public void onClick(View view) {
        //Wird aufgerufen, wenn „onClick“ aufgerufen wird.
        verkaufeKaffee();

        View und verkaufeKaffee sind rot ich hab das gleiche mit View gemacht das hat aber nicht das problem gelöst

  • Hallo Marvin,
    wenn bei mir die setOnClickListener() Methode für das ImageView „kaffeebild“ ausgeführt wird, schmiert die App ab. Dann steht auf dem Handy „KaffeeClicker angehalten“.
    Wenn die Methode ausgeklammert ist, startet sie ganz normal.
    Merkwürdig ist auch, dass wenn der ich die App mit dem Debugger starte und nach den initialisieren der ImageViews und TextView mit findViewById, diese weiterhin null sind, obwohl ja eigentlich die Bilder hinterlegt worden sein müssen.

    Hoffentlich kannst du mir helfen.

    Mit freundlichen Grüßen Michael

    • Hallo Micheal,

      bitte prüfe doch einmal, ob in der Layout Datei die Zeile zum Kaffeebild die id „@+id/kaffeebild“ hat und in der Java Klasse ebenfalls „R.id.kaffeebild“. Bitte achte darauf, dass Groß- und Kleinschreibung beachtet wird.

      Ansonsten wäre es sehr hilfreich und in deinem Sinne, wenn du kurz die entsprechenden Zeilen Code aus der Java Klasse und dem Layout posten könntest.

      Gruß,
      Marvin