# Android TaschenRechner



## Java xyrse123 (14. Apr 2018)

Hallo,
ich wollte eine TaschenRechner App machen. Ich habe jetzt einige Buttons in XML definiert. Gibt es für die Buttons sowas wie getActionCommand() in Java?
Also das ich nicht für jeden Button einen onClick Listener brauche.
Schonmal Danke im Vorraus.


----------



## Java xyrse123 (14. Apr 2018)

Habe jetzt folgendes probiert:

```
public void updateText(View v){       
        Button btn = (Button) findViewById(v.getId());
        btn.setOnClickListener( this);
        String text = btn.getText().toString();
        TextView textview = (TextView) findViewById(v.getId());
        textview.setText(text);
    }

@Override
    public void onClick(View v) {
       updateText(v);
}
```
Aber bei der TextView wird nichts hinzugefügt.


----------



## Robat (14. Apr 2018)

Das Konstrukt macht auch recht wenig Sinn.
Der OnClickListener wird bei dir erst hinzugefügt, wenn die Methode `updateText(..)` aufgerufen wird. Die Methode wird aber erst aufgerufen wenn man auf einen Button clickt.

Hier mal anhand eines kleinen Beispielcodes gezeigt (Als Art Pseudocode zu sehen):


```
public class FooActivity extends AppCompatActivity implements View.OnClickListener {
    private Button fooBtn;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         fooBtn = (Button) findViewById(R.id.fooBtnId);
         fooBtn.setOnClickListener(this);
    }

     @Override
      public void onClick(View v) {
           if(this.fooBtn == v.getId()) {
                   // foo button clicked..
           }
      }
}
```


----------



## Java xyrse123 (14. Apr 2018)

Ok, danke. Aber ich habe 20 Buttons in der App und jetzt über 50 Zeilen alleine für die Buttons. Kann man das noch vereinfachen?


----------



## Robat (14. Apr 2018)

Das geht schon einfacher.
Sagen wir mal du hast die Buttons nach folgendem Schema benannt: `button_0, button_1, button_2, ..` könntest du dir ein Array für die Buttons machen:


```
Button[] button = new Button[N];
for (int i = 0; i < N; i++) {
   int id = getResources().getIdentifier("button_"+i, "id", getPackageName());
   button[i] = (Button) findViewById(id);
}
```

So könntest du immer über das Array iterieren um bspw den Buttons den OnClickListener zuzuweisen.


----------



## Java xyrse123 (14. Apr 2018)

Ok, danke. Ich wollte jetzt in der MainActivity die ganzen Buttons erstellen usw. und in der Klasse TaschenRechner die Rechenlogik machen. Doch jetzt hängt sich die App im Emulator auf und dort steht: TaschenRechner has stopped. Woran liegt das?


```
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

public static EditText TextFeld;

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

        Button b1= (Button) findViewById(R.id.button1); b1.setOnClickListener(this);      
        TextFeld = (EditText) findViewById(R.id.editText1);
        ...
        new TaschenRechner();

    }

    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.button1:  TextFeld.append("7");  break;           
            case R.id.button17: TextFeld.setText("");  break; // Enter, auf Stack
            case R.id.button18: TextFeld.setText("");  break; // Clear
        }
    }

    public static  EditText getTextFeld() { // TextFeld bekommen
        return TextFeld;
    }
   
}


public class TaschenRechner { // RechenLogik

     EditText TextFeld;
Stack<String> stack = new Stack<String>();
    public TaschenRechner() {

       
        TextFeld = MainActivity.getTextFeld();
        TextFeld.setText("Test");
    }

}
```


----------



## Robat (14. Apr 2018)

Ich würde mal vermuten, dass es an deinem Konstrukt liegt wie du dir das Textfeld holst. (ist aber ohne genauen Stacktrace/Exception nur geraten)
Du erstellst in der `onCreate(..)` Methode doch eh eine Instanz deiner TaschenRechner-Klasse. 
Dort kannst doch eine Instanz deiner MainActivity mitgeben, um auf das Textfeld zuzugreifen.
Über diese Instanz kannst du dann deine Methode `getTextFeld()` aufrufen.


----------



## Java xyrse123 (14. Apr 2018)

Achso ich habe eben das falsche gepostet. Das in #6 funktioniert. Die TaschenRechner Klasse hat noch eine Methode rechne() die nach dem Klicken vom Enter Button ausgeführt wird: 

```
public void rechne() {
        String s = TextFeld.getText().toString();
        stack.push(s);
}
```

Dort bekomme ich dann diese Exception und die App stürtz ab:


```
java.lang.NullPointerException: Attempt to invoke virtual method 'android.text.Editable android.widget.EditText.getText()' on a null object reference
        at com.example.admin.taschenrechner.TaschenRechner.rechne(TaschenRechner.java:24) (Zeile 24 ist die mit dem String s= ...)
```


----------



## Robat (14. Apr 2018)

Zeig noch mal den kompletten Code..


----------



## Java xyrse123 (14. Apr 2018)

HAbe eigentlich nicht viel verändert:

```
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

public static EditText TextFeld;
    TaschenRechner r = new TaschenRechner();

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

        Button b1= (Button) findViewById(R.id.button1); b1.setOnClickListener(this);
        ...
        TextFeld = (EditText) findViewById(R.id.editText1);


    }

    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.button1:  TextFeld.append("7");  break;
           ...
            case R.id.button17: TextFeld.setText(""); r.rechne(); break; // Enter, auf Stack
            case R.id.button18: TextFeld.setText("");   break; // Clear

        }
    }

    public static  EditText getTextFeld() {
        return TextFeld;
    }

}

public class TaschenRechner { // RechenLogik

    EditText TextFeld;
    Stack<String> stack = new Stack<String>();

    public TaschenRechner() {

        EditText TextFeld;
        Stack<String> stack = new Stack<String>();

            TextFeld = MainActivity.getTextFeld();

        }


  public void rechne() {
         String s = TextFeld.getText().toString();
         stack.push(s);
 }
}
```


----------



## Robat (14. Apr 2018)

Prinzipiell ist es das gleiche Problem wie vorhin.
Wenn du deine Instanz der TaschenRechner erstellst musst du das aktuelle MainActivity Objekt übergeben um darüber den Getter für das Textfeld aufzurufen. 
So wie du es jetzt hast wird dein Textfeld immer null sein.


----------



## Java xyrse123 (14. Apr 2018)

Robat hat gesagt.:


> Wenn du deine Instanz der TaschenRechner erstellst musst du das aktuelle MainActivity Objekt übergeben um darüber den Getter für das Textfeld aufzurufen.
> So wie du es jetzt hast wird dein Textfeld immer null sein.



Verstehe ich nicht wirklich, kannst du vielleicht ein Beispiel geben?

Ich habe es jetzt so gelöst(jedenfalls kommt keine Fehlermeldung mehr):

```
public static String getTextvonTextFeld() {
    return TextFeld.getText().toString();
}
```

Aber das erscheint mir irgendwie nicht ganz sauber gelöst.


----------



## Robat (14. Apr 2018)

Hier mal an deinem Beispiel kurz veranschaulicht.

```
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText textFeld;
    private TaschenRechner r ;

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

         ....
        this.textFeld = (EditText) findViewById(R.id.editText1);
        this.r = new TaschenRechner(this);

    }

    public void onClick(View v) {
        ...
    }

    public String getTextfeldText() {
        return TextFeld.getText().toString();
    }

}

public class TaschenRechner { // RechenLogik
    private MainActivity activity;

    public TaschenRechner(MainActivity activity) {
        this.acitivity = activity;
     }

  public void rechne() {
         String s = activity.getTextfeldText();
 }
}
```


----------



## Java xyrse123 (14. Apr 2018)

Ein Problem habe ich noch bei der RechnenMethode:
Die rechnen() Methode wird aufgerufen wenn der Enter Button gedrückt wird. Wenn eine Zahl eingeben wurde soll sie auf den Stack gepusht werden und wenn das + Zeichen eingegeben wird( nach dem 2 Zahlen auf dem Stack sind) soll die Summe ausgegeben werden.


```
public void rechne() {
    String eingabe = activity.getTextvonTextFeld();

    try {
        double zahl = Double.parseDouble(eingabe);
        stack.push(zahl);
     // activity.setTextFeldText(stack.peek().toString); kann ich mir anzeigen lassen
} catch (NumberFormatException e) {
    
    
        if (eingabe.equals("+") ){
            double zahl1=stack.pop();
            double zahl2=stack.pop();
            stack.push(zahl1+zahl2);
            zahl2=zahl1+zahl2;
            stack.push(zahl2);
            String tmp = Double.toString(zahl2);
            activity.setTextFeldText(tmp);
        }
    }
}
```
Aber nach dem ich + eingeben habe bekomme ich eine EmptyStack Exception obwohl der Stack ja eigentlich gar nicht leer sein kann.


----------



## Robat (14. Apr 2018)

Hmm also am Code seh ich jetzt eigentlich kein Problem. Interessant wäre vielleicht noch mal dein gesamter Code aus der Logik-Klasse.


----------



## Java xyrse123 (14. Apr 2018)

Das ist ja der gesamte Code aus der Logik-Klasse.

Ich habe jetzt folgendes probiert:
Z.B wenn ich das eingebe : 3.4.+ kommt als Ergebnis 7, was auch funktioniert.

```
public void rechne() {
    String text =activity.getTextvonTextFeld();
    for (String zeichen : text.split("\\.")) {
        switch (zeichen) {
            case "+":
                stack.push(stack.pop() + stack.pop());
                break;
            default:
                stack.push(Double.parseDouble(zeichen));
                break;
               }
            }
          activity.setTextFeldText("");
          activity.setTextFeldText(stack.peek().toString());
           }
```

Aber ich möchte, dass man eine Zahl eingibt, Enter drückt und die Zahl verschwindet und auf den Stack kommt.
Hier kommt jetzt wieder eine EmptyStackException :

```
public void rechne() {
    String text =activity.getTextvonTextFeld();
  
        switch (text) {
            case "+":
                stack.push(stack.pop() + stack.pop());
                activity.setTextFeldText(stack.peek().toString());
                break;
            default:
                stack.push(Double.parseDouble(text));
                break;
               }        
           }

Und in der onClick(): case R.id.button17: r.rechne(); TextFeld.setText(""); break; // Enter, auf Stack
```


----------



## Robat (14. Apr 2018)

Ehrlich gesagt durchblick ich deine Herangehensweise noch nicht so richtig.
Du hast ein Edittext und einen Button ("Enter").

Der Nutzer soll folgendes machen:
Zahl eingeben -> Enter  -> Zahl auf Stack pushen
Zahl eingeben -> Enter  -> Zahl auf Stack pushen
'+' eingeben -> Enter    -> Ergebnis ausrechnen.

Hab ich das soweit richtig verstanden?


----------



## Java xyrse123 (14. Apr 2018)

Ja, später kommen natürlich noch andere Rechenzeichen hinzu


----------



## Robat (14. Apr 2018)

Und gibst auch wirklich nur das oben ein? 2 Zahlen und ein +? Und drückst dansch nicht mehr auf ENTER?


----------



## Java xyrse123 (14. Apr 2018)

Vielleicht war es etwas verwirrend: 1. CodeBeispiel in # 16 ich gebe 4.3.+ ein und dann ENTER, es wird an den Punkte gesplittet  und als Ergebnis kommt 7.0 in TextFeld.

2. CodeBeispiel: ich gebe 2 ein -> ENTER, 3->ENTER und + -> ENTER( so wollte ich es haben) , bekoome dann aber eine Empty StackException und die App stürzt ab.


----------



## Robat (15. Apr 2018)

Also anhand deines gezeigten Codes kann ich mir den Fehler nicht erklären.
Ich könnte mir noch vorstellen, dass du die Logik-Instanz falsch erstellst und daher immer ein neuer Stack erstellt wird..

Hier mal ein funktionierendes Beispiel damit wir uns nicht mehr ewig im Kreis drehen 


```
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText edittext;
    private Button button;
    private Logic logic;

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

        this.edittext = findViewById(R.id.editText);
        this.button = findViewById(R.id.button);
        this.button.setOnClickListener(this);

        this.logic = new Logic(this);
    }

    @Override
    public void onClick(View view) {
        logic.calculate();
    }

    public String getText() {
        return edittext.getText().toString();
    }

    public void setText(String text) {
        this.edittext.setText(text);
    }

    public void clear() {
        edittext.setText("");
    }
}
```


```
public class Logic {
        private final MainActivity mainActivity;
        private final Stack<Double> stack;

        public Logic(final MainActivity mainActivity) {
            this.stack = new Stack<>();
            this.mainActivity = mainActivity;
        }

        public void calculate() {
            final String input = mainActivity.getText();

            switch(input) {
                case "+":
                    stack.push(stack.pop() + stack.pop());
                    mainActivity.clear();
                    mainActivity.setText("Ergebnis: " + stack.pop());
                    break;
                default:
                    stack.push(Double.parseDouble(input));
                    mainActivity.clear();
                    break;
            }
        }
    }
```


----------



## Java xyrse123 (15. Apr 2018)

Danke für die Mühe, ich habe den Fehler jetzt auch selbst gefunden.
In der onClick() Methode habe ich immer einen neuen TaschenRechner erzeugt.
Das war echt ein dämlicher Fehler


----------



## Robat (15. Apr 2018)

Es gibt keine dämlichen Fehler - man lernt aus Fehlern und ohne Fehler würdest du nichts lernen


----------

