onClickListener auf Buttons die im Code erzeugt wurden setzen.

SchokoMuh

Mitglied
Erst mal ein ganz freundlich Hallo,

dann hoffe ich das ich meine Frage im richtigen Forum stelle.
Dann sollte ich vielleicht noch erwähnen das ich erst seit 3 Monaten durch Selbststudium mit Java und Android arbeite also bitte ich um ein bisschen nachsicht.

So nun mal zu meinem Problem.
ich habe mir ein Programm geschrieben mit dem ich aus einer CSV Datei daten auslese und die Überschriften dann in Buttens als Text zuweise.
Ich habe auch 2 feste Buttons die ich schon über den onClickListener (Activity als Listener) abfrage. Soweit so gut.
Meine frage nun ist wie kann ich einen onClickListener für die im Code erzeugten Buttons anlegen, ich gebe ihnen ja mit .setId eine int iD.

[CODE lang="java" highlight="43-55"]public class DataInputActivity extends AppCompatActivity implements View.OnClickListener {

private static final int requCode = 7;
private List csvReaded = new ArrayList();
private int iDZaehler =0;
private String[] spalten;
private int spaltenAnzahl = 0;

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

Button bTSuchen = findViewById(R.id.bTSuchen);
Button bTHinzufuegen = findViewById(R.id.bTHinzufuegen);

bTSuchen.setOnClickListener(this);
bTHinzufuegen.setOnClickListener(this);
}
@Override
public void onClick(View v) {

switch (v.getId()){
case R.id.bTSuchen:
goToSuchen();
return;
case R.id.bTHinzufuegen:

return;
default:
return;
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.data_input_menu, menu);
return super.onCreateOptionsMenu(menu);

}

public Button erzeugeButton(String text, int id){
LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,200);
lllp.setMargins(10,10,10,0);
Button button = new Button(getApplicationContext());
button.setLayoutParams(lllp);
button.setGravity(Gravity.CENTER);
button.setText(text);
button.setBackgroundColor(Color.GRAY);
button.setTextSize(TypedValue.COMPLEX_UNIT_SP,20);
button.setId(id);

return button;
}


public void erweitereLL(){
LinearLayout linearLayout = findViewById(R.id.dA_LLv);

for (int i = 0;i <= spalten.length-1;i++){
linearLayout.addView(erzeugeButton(spalten, i));
}


}[/CODE]

Währe echt super wenn mir jemand einen tip oder einen link zu einer Seite geben könnte die sich mit dem Thema beschäftigt.
 
Beste Antwort
K
Also was ich gezeigt habe, war erst einmal eine Methodenreferenz. Das ist kein Lambda Ausdruck. Aber wenn man sich irgendwas durchliest, dann wird das alles in einem Rutsch behandelt und sowohl ein Lambda Ausdruck als auch eine Methodenreferenz setzen ein sogenanntes Funktionales Interface voraus.

Einfach einmal als schneller Überblick:

Die Methode setOnClickListener erwartet einen Parameter vom Typ View.onClickListener.
View.OnClickListener ist ein Interface mit genau einer zu implementierenden Methode. (==> Funktionale Interfaces haben genau eine zu implementierende Methode!)

Das kann man auf mehrere Wege bekommen:
a) Eine Klasse implementiert das Interface. Das ist der Weg, den Du derzeit verwendet hast:
public class...
Also zum einen muss sich ein Spinner öffnen aus dem ich dann die Spalte einer Datenbank auswählen kann in welche dann die vorher schon ausgelesenen Daten schreibe und der Button sollte seine Farbe ändern. Das ist auch soweit kein Problem nur weiß ich nicht wie ich die Buttons für den onClickListener Initialisiere. Ich weiß ja jetzt noch nicht wie viele Buttons zur Laufzeit erstellt werden. Ich hab da sowas wie eine Schleife im Kopf aber leider überhaupt keine Idee wie ich das umsetzten muss.

Oder kann ich den onClickListener bei der Erstellung der Buttons gleich drauf setzen? Probier ich gleich mal.
 
Ich verstehe die Frage nicht ganz, das funktioniert genauso wie mit den "festen" Buttons. Was soll denn beim Click auf so einen Button passieren?
Also zum einen muss sich ein Spinner öffnen aus dem ich dann die Spalte einer Datenbank auswählen kann in welche dann die vorher schon ausgelesenen Daten schreibe und der Button sollte seine Farbe ändern. Das ist auch soweit kein Problem nur weiß ich nicht wie ich die Buttons für den onClickListener Initialisiere. Ich weiß ja jetzt noch nicht wie viele Buttons zur Laufzeit erstellt werden. Ich hab da sowas wie eine Schleife im Kopf aber leider überhaupt keine Idee wie ich das umsetzten muss.

Oder kann ich den onClickListener bei der Erstellung der Buttons gleich drauf setzen? Probier ich gleich mal.
 
Ist schon ab und zu lustig wie es läuft. Ich mach jetzt seit gestern an dem ding rum und komm auf keine Lösung und beim schreiben fällt es mir dann auf.
Update:
den onClicklistener zu setzten hab ich jetzt dann auch verstanden aber wie bring ich das mit der iD jetzt der Switch Case anweisung bei?
 
den onClicklistener zu setzten hab ich jetzt dann auch verstanden aber wie bring ich das mit der iD jetzt der Switch Case anweisung bei?
Wenn Du da Aktionen haben willst abhängig von der id, dann kann Du statt einer starren switch Anweisung z.B. eine Map nutzen.

Nur was baust Du da für eine Map? Du könntest Aufrufe speichern, also von der id verweist Du in der Map auf z.B. einen Consumer von der View des Click events.

Aber dann macht das natürlich keinen Sinn, denn statt einer gemeinsamem onClick Methode, die dann die jeweilige Unterroutine aufruft, könntest Du direkt die entsprechende Methode einbinden in dem Listener.

Du machst also kein .setOnClickListener(this); (Was ich so eh für schlecht halte) sondern Du gibst eine direkte Methodenreferenz:
.setOnClickListener(this::onClick); sollte deinen bisherigen Aufrufen das Gleiche machen.

Und wenn Du dann Methoden entsprechend dem, was sie machen, benennst, dann kannst da sauberen, lesbaren Code schreiben.
(Und natürlich verschwindet das implements View.OnClickListener bei der Klasse!)
 
Also was ich gezeigt habe, war erst einmal eine Methodenreferenz. Das ist kein Lambda Ausdruck. Aber wenn man sich irgendwas durchliest, dann wird das alles in einem Rutsch behandelt und sowohl ein Lambda Ausdruck als auch eine Methodenreferenz setzen ein sogenanntes Funktionales Interface voraus.

Einfach einmal als schneller Überblick:

Die Methode setOnClickListener erwartet einen Parameter vom Typ View.onClickListener.
View.OnClickListener ist ein Interface mit genau einer zu implementierenden Methode. (==> Funktionale Interfaces haben genau eine zu implementierende Methode!)

Das kann man auf mehrere Wege bekommen:
a) Eine Klasse implementiert das Interface. Das ist der Weg, den Du derzeit verwendet hast:
public class DataInputActivity extends AppCompatActivity implements View.OnClickListener {

Das ist prinzipiell ok, aber was mir etwas missfällt ist, dass dieses Interface ja kein Verhalten der Activity aufzeigt. Wenn es ein Interface LautGeben gäbe, dann könnte ein Hund das Interface implementieren. Denn der Hund hat ein Verhalten, bei dem er Laut gibt (er bellt). Das sehe ich hier nicht so wirklich.
Und zum anderen hast du eine Methode onClick in der Activity. Was macht onClick? Das sagt nichts aus. Eine Methode wäre gotoSearch -> was die macht, ist eher klar.

b) Anonyme Klasse:
Du kannst natürlich jederzeit eine anonyme Klasse erzeugen. Das wäre dann etwas wie (im Editor geschrieben - Tippfehler bitte verzeihen:
Java:
someButton.setOnClickListener(new View.ClickListener() {
    @Override
    public void onClick(View v) {
        // Whatever
    }
})
Viel Code und wieder ein onClick, das nicht sagt, WAS es macht sondern nur, WANN es ausgeführt wird.

Das waren die beiden "alten" Wege. Mit Java 8 ist dann ein Bereich dazu gekommen, der eine Art funktionale Programmierung mit einbringen sollte. Damit verbunden war, dass es Funktionale Interfaces gibt. Und das Interface hat nur eine Methode onClick, daher ist es funktional. Und damit gehen Lambda Ausdrücke und Methodenreferenzen:

c) Lambda Ausdruck
someButton.setOnClickListener(v -> doSomething(v));
Hier hast Du erst den Parameter (bei mehreren kommen die in Klammern, also z.B. "(a, b, c)"), dann den Pfeil ("->") und dann den Code, der den Parameter nutzen kann. Der Code kannauch ein ganzer Block mit { .. } sein, wobei das etwas ist, das als unschön angesehen wird, da schnell unleserlich. Lieber in eine Methode packen und dann die Methode aufrufen.

d) Methoden Referenz
someButton.setOnClickListener(this::doSomething);
Hier wird nur die Methode angegeben, die aufgerufen werden soll. In der aktuellen Instanz soll doSomething aufgerufen werden. Damit das funktionieren kann, muss doSomething natürlich von der Signatur her passen - Der Name darf natürlich unterschiedlich sein, aber die Parameter und Rückgabetyp müssen passen, damit das Interface erfüllt ist.

Das wäre auf die Schnelle und Kürze eine Übersicht zu dem Thema.
 
Beste Antwort

Zurück
Oben