Button (ActionListener) funktioniert nicht, wenn y-Koordinate verändert wird

Hallo,

ich habe angefangen ein kleines Programm zu schreiben.

Nun habe ich eine Art Hauptmenü, in dieser habe ich 1x den Button "Schwierigkeitsgrad" und den Button "Start".
Beide Buttons sollen zur Zeit das Spiel an sich aufrufen, das klappt auch.

Zudem haben beide Buttons eines ActionListener.

Wenn das Spiel aufgerufen wird, kann man einen Spielstein von links nach rechts bewegen und muss von oben kommenden Steinen ausweichen.

Nun das eigentliche Problem:

Wenn ich den "Start"-Button oberhalb des "Schwierigkeitsgrad"-Buttons positioniere, kann ich meinen Spielstein nicht bewegen. Mach ich es andersherum funktioniert alles wunderbar.
Wenn man auf "Schwierigkeitsgrad" klickt, klappt immer alles.

Es ist also praktisch so:

|Start| -> Spielstein bewegt sich nicht
|Schwierigkeitsgrad| -> Spielstein bewegt sich

|Schwierigkeitsgrad| -> Spielstein bewegt sich
|Start| -> Spielstein bewegt sich



Hier noch der Code:
Java:
package FallendeBlöcke;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class Haupt extends JFrame implements Runnable {
	JPanel spielfeld;
	JPanel jpHauptmenue;
	JButton[] button = new JButton[50];
	JButton spielStein;
	JLabel nL;
	JButton jbStart = new JButton("Start");
	JButton jbSchwierigkeitsgrad = new JButton("Schwierigkeitsgrad");
	int zufallsX = 0;
	int x = 0;
	int y = -50;
	int[] zufallsArray = new int[15];

	public Haupt() {
		this.setSize(600, 600);
		this.setLayout(null);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);

		spielfeld = new JPanel();
		spielfeld.setBounds(50, 50, 500, 500);
		spielfeld.setBackground(Color.BLUE);
		spielfeld.setLayout(null);

		jpHauptmenue = new JPanel();
		jpHauptmenue.setBounds(50, 50, 500, 500);
		jpHauptmenue.setBackground(Color.BLACK);
		jpHauptmenue.setLayout(null);

		spielStein = new JButton();
		spielStein.setBounds(250, 450, 50, 50);
		spielStein.setEnabled(false);
		spielfeld.add(spielStein);
		spielfeld.setVisible(false);

		jbStart.setBounds(200, 50, 100, 24);
		jbSchwierigkeitsgrad.setBounds(200, 90, 100, 24);

		jpHauptmenue.add(jbSchwierigkeitsgrad);
		jpHauptmenue.add(jbStart);

		this.getContentPane().add(jpHauptmenue);
		this.getContentPane().add(spielfeld);
		jbStart.addActionListener(new ActionClass());
		jbSchwierigkeitsgrad.addActionListener(new ActionClass());
		this.addKeyListener(new KeyAction());

	}

	public class ActionClass implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (e.getSource() == jbStart)
			{
				erstelleSteine();
			}
			else if (e.getSource() == jbSchwierigkeitsgrad)
			{
				erstelleSteine();
			}
			
		}
	}

	class KeyAction implements KeyListener {
		@Override
		public void keyTyped(KeyEvent e) {
			// TODO Auto-generated method stub

		}

		@Override
		public void keyPressed(KeyEvent e) {
			// TODO Auto-generated method stub
			if (e.getKeyCode() == e.VK_RIGHT) {
				if (spielStein.getLocation().x < spielfeld.getBounds().width
						- spielStein.getBounds().width) {
					spielStein.setLocation(spielStein.getLocation().x + 50,
							spielStein.getLocation().y);
				}
			} else if (e.getKeyCode() == e.VK_LEFT) {
				if (spielStein.getLocation().x > 0) {

					spielStein.setLocation(spielStein.getLocation().x - 50,
							spielStein.getLocation().y);
				}

			}
		}

		@Override
		public void keyReleased(KeyEvent e) {
			// TODO Auto-generated method stub

		}

	}

	public void erstelleSteine() {
		this.setFocusable(true);
		for (int u = 0; u < 10; u++) {
			zufallsArray[u] = 300;
		}
		for (int i = 0; i < 10; i++) {
			button[i] = new JButton();
			button[i].setBounds(x, y, 50, 50);
			button[i].setEnabled(false);
			spielfeld.add(button[i]);
			zufallsX = (int) (Math.random() * 500);
			while (zufallsX % 50 != 0) {
				zufallsX = (int) (Math.random() * 500);
			}
			x = zufallsX; // Sorgt dafür, dass die Steine zufällig spawnen

		}
		jbStart.setVisible(false);
		jbSchwierigkeitsgrad.setVisible(false);

		spielfeld.remove(jbStart);
		spielfeld.setVisible(true);
		jpHauptmenue.setVisible(false);
		new Thread(this).start(); // Lässt die Steine fallen

	}

	public static void main(String[] args) {
		new Haupt().setVisible(true);

	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i <= 10; i++) {
			int zufall = (int) (Math.random() * 10);
			for (int k = 0; k <= i; k++) {
				if (zufall == zufallsArray[k]) {
					while (zufallsArray[k] == zufall) {
						zufall = (int) (Math.random() * 10);
						k = 0;
					}
				}
			}
			zufallsArray[i] = zufall;
			while (button[zufall].getBounds().y < 500) {
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				button[zufall].setLocation(button[zufall].getLocation().x,
						button[zufall].getLocation().y + 50);
				if (button[zufall].getLocation().equals(
						spielStein.getLocation())) {
					JOptionPane.showMessageDialog(Haupt.this, "Verloren!");
				}
			}

		}

	}
}

Ich habe ehrlich gesagt keinen blassen Schimmer woran es liegen könnte, zumal es funktioniert, wenn man die Button umdreht.

Mit dem obigen Code tritt das Problem auf, es lässt sich beheben, wenn man die Bounds von den beiden Buttons an der y-koordinate austauscht.

Ich weiß, dass ich das Problem umgehen könnte, wenn ich auf jbSchwierigkeitsgrad "Start" schreiben würde. Aber das kann ja auch nicht Sinn der Sache sein und ich würde gerne wissen, wo das Problem liegt.
 
was läßt dich überhaupt vermuten dass es gesichert geregelt funktioniert, einen KeyListener direkt auf ein JFrame zu adden,
hast du je irgendwo ein Referenz-Beispiel gesehen?
zu empfehlen ist das nicht, Zufalls-Programmm-Reationen sind bei unsinnigen Zusammenstellungen üblich,
bei mir allerdings anscheinend keine Probleme

KeyListener besser nur bei Komponenten, die einen blinkenden Cursor haben, etwa TextFelder,
KeyBindings sind die bessere Lösung,

wenn es so sein muss wie bisher, dann zumindest:
- KeyListener auf ein JPanel,
- NACH setVisible(true) für dieses Panel requestFocus() aufrufen,
dann klappt es einigermaßen verlässlich am Anfang,
nicht unbedingt wenn du zwischen verschieden Programmen hin und her springst, Button klickst und wer weiß was alles Richtung Focus machst,

nur wer den Focus hat, kann auch auf Tastatur reagieren..

-----

How to Write a Key Listener (The Java™ Tutorials > Creating a GUI With JFC/Swing > Writing Event Listeners)
gleich am Anfang je einen Satz zu Focus + KeyBindings mit jeweils Link

----

Action ist übrigens ein eher unglücklicher Name für einen Listener,
gerade weil zwischen KeyBindings mit Action und Listener-Konzept zu unterscheiden ist,
freilich gibt es auch den 'ActionListener' für Buttons 😉

------

edit: dein Thementitel scheint völlig verfehlt oder?
die Buttons mit ihren Listenern gehen, y-Koordinate ist kein Thema,
nur die Frage nach dem KeyListener, nach Horizontalbewegung des Spielsteins?
 
Zuletzt bearbeitet von einem Moderator:
So also ich habe nun Key Bindings genommen.

Die Frage die sich mir nun stellt ist, brauch ich für jeden Button

Java:
spielfeld.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"),"doRightAction");
spielfeld.getActionMap().put("doRightAction", rightAction);

und dann

Java:
static class RightAction extends AbstractAction {
		public void actionPerformed(ActionEvent e) {
		//tue etwas
}}

?
Ich schätze eher nicht. Kann man irgendwie sagen, dass er auf alle Buttons reagieren soll aber nur bei Pfeil-Links/Pfeil-Rechts etwas tun soll?


Mal zum Threadtitel:
An sich finde ich ihn passend, denn wenn ich die Position meines Start-Buttons verändere, reagiert diese nicht mehr auf den KeyListener (Ok das ActionListener wäre demnach falsch).



E:
.... brauch ich für jeden Button ....

ich meine natürlich nicht für jeden Button, sondern für jede Bewegung
 
Zuletzt bearbeitet:
mit den mehreren Keys mag es schwierig sein, stimmt,
da habe ich glaube ich noch keine Variante zu gesehen, selber noch nichts gemacht

ich könnte mir theoretisch vorstellen:
- sammle alle benötigten Zeichen, Buchstaben etwa mit Schleife über char-Bereich, Pfeiltasten usw. wohl nur über einzelne Aufrufe wie KeyStroke.getKeyStroke("RIGHT"), sammle also KeyStroke-Objekte,
- erste per Schleife genauso viele Action-Objekte deiner Action-Klasse, setze das aktuelle KeyStroke in einer Variablen,
adde mit dem KeyStroke in die Maps usw.
- in der Klasse hat man dann irgendeine Methode und in einer Variablen das Zeichen, dann könnte danach
per if/else oder switch unterschieden werden

bisschen sehr viel Aufwand, wirklich nur als angestachelte Extremlösung sehen,
zwei Actions zu definieren ist wohl für einfachen Fall humaner 😉


-----

hier ist eine gewisse fertige Auflistung von Namen für KeyStrokes usw.
Listing the Key Bindings in a Component : ActionsSwing JFCJava

und das Durchlaufen der InputMap bringt mich bereits auf eine bessere Idee:
nur eine ActionKlasse, aber durchaus pro KeyStroke ein Objekt registrieren, eine Zeile also immer noch nötig,
im Zweifel ja immer Methodenaufruf und darin mehrere Befehle

in der ActionKlasse die InputMap durchsuchen, schauen welche registrierte Action == das eigene Action-Objekt ist,
damit den zugehörigen KeyStroke/ String bestimmen und über diesen zwischen den Buttons unterscheiden
 
Naja wie dem auch sei.

Das Problem besteht weiterhin, dass ich meinen Spielstein nicht bewegen kann, wenn ich den Start Button über den anderen setze, andersherum funktionierts nach wie vor.


E:... nun geht es.

Hab jetzt STRG+A und alle jbStart in jbSchwierigkeitsgrad umbenannt und das gleiche nochmal andersherum.

Weiß der Geier woran es lag..
 
Zuletzt bearbeitet:
deine Methode erstelleSteine() ist auch nicht ganz vorschriftsmäßig, zumindest nicht ideal,
einfach so Elemente removen, setVisible true/false..
CardLayout wäre geeigneter um sauber zwischen verschiedenen Sichten umzuschalten,
ansonsten hinter allen Layout-Änderungen einmal
validate();
repaint();
aufrufen

aber falls hier Fehler, würdest du eher was falsches sehen als falsche Tasten-Reaktion,
na schwieriges Feld
 

Zurück
Oben