Java GUI friert ein beim drücken auf Button

XHotSniperX

Lt. Junior Grade
Registriert
Jan. 2008
Beiträge
474
Hallöschen :)

ich möchte ein Fenster machen, wo es Buttons gibt und wenn einer geklickt wird, dann soll einfach der Inhalt des Fensters sich ändern. Quasi ein neues JPanel drauf und das alte löschen. Leider friert der Knopf ein und bleibt stecken...
Habe gelesen, dass invokeLater helfen soll mit neuem Runnable und so weiter. Bringt aber nichts. Warum friert es bei mir immer ein?
Ich würde gerne wissen, wie man das macht und wie man durch den Buttonklick Methoden anderer Klassen ausführt, ohne, dass es einfriert. Danke

Hier mal ein Beispiel, wo ich den Inhalt des Fensters einfach entfernen möchte:

Code:
import ...

public class Test extends JFrame {

	private JPanel contentPane;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Test frame = new Test();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Test() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		JButton btnNewButton = new JButton("New button");
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				SwingUtilities.invokeLater(
					    new Runnable(){
					        public void run(){
					            getContentPane().removeAll();
					            validate();
					        }
					    }
					);
			}
		});
		contentPane.add(btnNewButton, BorderLayout.WEST);
	}

}
Ergänzung ()

achso repaint() hat nach validate() gefehlt xD
 
Hatte das Problem mal auch. Ist bissl länge her.. Soweit ich mich erinnern kann, packt Java alle GUI Elemente in eine Queue und nimmt erst das nächste Element, wenn Vorgänger abgearbeitet ist. Wie du schon richtig erkannt hast, braucht man hier einen neuen Thread. Ich habs damals so gemacht:

Code:
new Thread() {
	@Override
	public void run() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() { 
                        [B]hier was beim Klicken gemacht werden soll[/B]
                        }
                }
        }
}.start;

Hoffe es hilft dir ein bisschen.
Gruß
tshape
 
anders gesagt: ich möchte einfach ein Fenster haben und mehere Panels, die ich durch Buttonklicks tauschen kann.. Ich möchte aber kein CardLayout.
 
Halte dir doch einfach eine Collection bzw. pro "Layer" den du da ab und zu anzeigen möchtest eine Referenz und dann legst du das anzuzeigende Panel einfach oben drauf und entfernst es später einfach wieder und legst stattdessen das neue anzuzeigende Panel drauf.

z.B.
Code:
    private void addScreenOnTop(final Layer layerToAdd, final boolean focus)
    {
        Runnable addScreen = new Runnable()
        {
            @Override
            public void run()
            {
                window.getContentPane().add(layerToAdd, 0);

                if (focus)
                    layerToAdd.requestFocusInWindow();
                
                window.validate();
                window.repaint();
            }
        };

        if (SwingUtilities.isEventDispatchThread())
            addScreen.run();
        else
            SwingUtilities.invokeLater(addScreen);
    }
und entsprechend andersherum zum removen.

P.S.: Layer ist nur eine kleine Erweiterung von JPanel meinerseits und window ist ein JFrame.
 
Wenn ich jetzt aber zum Beispiel

Code:
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				removeScreenOnTop(contentPane, true);
				addScreenOnTop(new Test2(), true);
			}
		});

mache und Test2 ist eine JPanel Klasse, dann verschwindet zwar das alte aber es kommt nihts dazu. Warum nicht?
 
Kommt wirklich nichts dazu oder wird nur nicht repainted?

Mach mal sowas hier:
Code:
        for (Component c : window.getContentPane().getComponents())
            System.out.print(c.getClass().getSimpleName() + " ");

        System.out.println("\n");
wobei du den einzelnen layern irgendwas Einzigartiges gibst, um sie identifizieren zu können. Bei mir hat z.B. jeder layer eine eigene spezielle Implementierung von Layer, daher kann ich sie so unterscheiden. Aber es tut auch ein Name oder eine id, ist ja nur zum Debuggen. Dann siehst du zumindest welche layer an der content pane hängen und vor allem, in welcher Reihenfolge.
 
Ich habe am Anfang nur nen JButton drauf und dann nach dem Klick verschwindet der.

Vor dem Klick kommt als Ausgabe: JButton

und nach dem Klick kommt: Test2

Wird es also einfach nicht gezeichnet oder was sit hier los?
 
Klingt für mich so. Einfach mal ein validate(); und dann repaint(); danach probiert? Ansonsten poste mal etwas Code, wenn es viele Dateien sind, einfach gepackt und angehangen.
 
Macht keinen Unterschied, weil validiert und repaintet wird es ja in der Methode von dir schon.

Nene ist nicht viel Code.. sind halt nur die zwei Testklassen Test und Test2. Test ist die JFrame Klasse und Test2 ne JPanel Klasse. Oder soll ich zwei JFrame Klassen erstellen?

Ich möchte halt eben die verschiedenen Panels mit dem Eclipse Plugin von Google designen, weil das schon ne Menge Zeit erspart. Deswegen halt am besten in mehrere Klassen unterteilt.

Code:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;


public class Test extends JFrame {

	private JPanel contentPane;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Test frame = new Test();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Test() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		
		JButton btnNewButton = new JButton("New button");
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				removeScreenOnTop(contentPane, true);
				addScreenOnTop(new Test2(), true);
				validate();
				repaint();
				debug();
			}
		});
		GroupLayout gl_contentPane = new GroupLayout(contentPane);
		gl_contentPane.setHorizontalGroup(
			gl_contentPane.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_contentPane.createSequentialGroup()
					.addGap(159)
					.addComponent(btnNewButton)
					.addContainerGap(176, Short.MAX_VALUE))
		);
		gl_contentPane.setVerticalGroup(
			gl_contentPane.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_contentPane.createSequentialGroup()
					.addGap(104)
					.addComponent(btnNewButton)
					.addContainerGap(125, Short.MAX_VALUE))
		);
		contentPane.setLayout(gl_contentPane);
		
		debug();
	}
	
	private void addScreenOnTop(final JPanel layerToAdd, final boolean focus)
	{
		Runnable addScreen = new Runnable()
		{
			@Override
			public void run()
			{
				getContentPane().add(layerToAdd, 0);

				if (focus)
					layerToAdd.requestFocusInWindow();
				validate();
				repaint();
			}
		};

		if (SwingUtilities.isEventDispatchThread())
			addScreen.run();
		else
			SwingUtilities.invokeLater(addScreen);
	}
	
	private void removeScreenOnTop(final JPanel layerToRemove, final boolean focus)
	{
		Runnable removeScreen = new Runnable()
		{
			@Override
			public void run()
			{
				getContentPane().remove(0);

				if (focus)
					layerToRemove.requestFocusInWindow();
				validate();
				repaint();
			}
		};

		if (SwingUtilities.isEventDispatchThread())
			removeScreen.run();
		else
			SwingUtilities.invokeLater(removeScreen);
	}
	
	private void debug(){
		for (Component c : getContentPane().getComponents())
			System.out.print(c.getClass().getSimpleName() + " ");

		System.out.println("\n");
	}

}

Code:
import javax.swing.JPanel;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.LayoutStyle.ComponentPlacement;


public class Test2 extends JPanel {
	private JTextField txtHello;

	/**
	 * Create the panel.
	 */
	public Test2() {
		
		txtHello = new JTextField();
		txtHello.setText("Hello");
		txtHello.setColumns(10);
		
		JButton btnHiiii = new JButton("hiiii");
		GroupLayout groupLayout = new GroupLayout(this);
		groupLayout.setHorizontalGroup(
			groupLayout.createParallelGroup(Alignment.LEADING)
				.addGroup(groupLayout.createSequentialGroup()
					.addGap(126)
					.addComponent(txtHello, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
					.addContainerGap(238, Short.MAX_VALUE))
				.addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup()
					.addContainerGap(218, Short.MAX_VALUE)
					.addComponent(btnHiiii)
					.addGap(143))
		);
		groupLayout.setVerticalGroup(
			groupLayout.createParallelGroup(Alignment.LEADING)
				.addGroup(groupLayout.createSequentialGroup()
					.addGap(84)
					.addComponent(txtHello, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED, 96, Short.MAX_VALUE)
					.addComponent(btnHiiii)
					.addGap(77))
		);
		setLayout(groupLayout);

	}
}
 
removeScreenOnTop ignoriert das erste Argument und entfernt immer das erste Element in der Komponentenliste...

Code:
removeScreenOnTop(contentPane, true);
Du versuchst hier das das ContentPane von sich selbst zu entfernen?

Im übrigen liegt es nicht an validate & Co., das kannst du einfach überprüfen, indem du das Fenster in der Größe änderst (manuelles validate/repaint). Ich nehme mal an, dass das Problem bei dem GroupLayout liegt, denn ohne dieses funktioniert es.

Edit: Jap, es liegt indirekt am GroupLayout. Du musst deinen Button auf ein separates JPanel packen und dieses mit der Methode addScreenOnTop hinzufügen, genau so, wie du es mit Test2 machst. Sonst funktioniert dein Layer-System nicht.
 
Zuletzt bearbeitet:
Na, du bist mir ja einer...
Gibts deinem JFrame zuerst ein GroupLayout und willst aber dann in der addScreenOnTop-Methode einfach so ein JComponent ohne Constraints hinzufügen ^^
Schon klar, dass das nicht funktionieren kann.
Hab mal das Runnable in der addScreenOnTop angepasst. Sicherlich nicht die schönste Lösung, da ich keine Ahnung vom GroupLayout habe, aber es bringt dich sicherlich weiter.

Lieben Gruß!

Code:
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

public class Test extends JFrame {

	private JPanel contentPane;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {

			public void run() {
				try {
					Test frame = new Test();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Test() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		JButton btnNewButton = new JButton("New button");
		btnNewButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				removeScreenOnTop(contentPane, true);
				addScreenOnTop(new Test2(), true);
				validate();
				repaint();
				debug();
			}
		});
		GroupLayout gl_contentPane = new GroupLayout(contentPane);
		gl_contentPane.setHorizontalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(gl_contentPane.createSequentialGroup().addGap(159).addComponent(btnNewButton).addContainerGap(176, Short.MAX_VALUE)));
		gl_contentPane.setVerticalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(gl_contentPane.createSequentialGroup().addGap(104).addComponent(btnNewButton).addContainerGap(125, Short.MAX_VALUE)));
		contentPane.setLayout(gl_contentPane);
		debug();
	}

	private void addScreenOnTop(final JPanel layerToAdd, final boolean focus) {
		Runnable addScreen = new Runnable() {

			@Override
			public void run() {
				GroupLayout gl_contentPane = new GroupLayout(contentPane);
				gl_contentPane.setHorizontalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(gl_contentPane.createSequentialGroup().addGap(0).addComponent(layerToAdd).addContainerGap(0, Short.MAX_VALUE)));
				gl_contentPane.setVerticalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(gl_contentPane.createSequentialGroup().addGap(0).addComponent(layerToAdd).addContainerGap(0, Short.MAX_VALUE)));
				contentPane.setLayout(gl_contentPane);

				if (focus) layerToAdd.requestFocusInWindow();
				validate();
				repaint();
			}
		};

		if (SwingUtilities.isEventDispatchThread())
			addScreen.run();
		else
			SwingUtilities.invokeLater(addScreen);
	}

	private void removeScreenOnTop(final JPanel layerToRemove, final boolean focus) {
		Runnable removeScreen = new Runnable() {

			@Override
			public void run() {
				getContentPane().remove(0);

				if (focus) layerToRemove.requestFocusInWindow();
				validate();
				repaint();
			}
		};

		if (SwingUtilities.isEventDispatchThread())
			removeScreen.run();
		else
			SwingUtilities.invokeLater(removeScreen);
	}

	private void debug() {
		for (Component c : getContentPane().getComponents())
			System.out.print(c.getClass().getSimpleName() + " ");

		System.out.println("\n");
	}

}
 
Ja danke so funktioniert das. Aber wie kann ich jetzt zum Beispiel wieder zurück zum JFrame Fenster mit einem zurück-Button? Oder auf ein anderes JPanel?

Auf jeden Fall danke für das bis jetzt :)
 
Du musst dir Referenzen auf die layer halten, die du entfernst. So kannst du sie einfach wieder oben drauflegen. Ob du nun alle gemeinsam in einer collection hältst (macht eher bei vielen layern Sinn) oder einzelne Felder dafür anlegst, das ist dir überlassen.
Wenn es eine "Zurück-Funktion" geben soll (kommt auf den konkreten Anwendungsfall an, ob das sinnvoll ist), dann brauchst du eine history, die erneut entweder eine collection sein kann (für mehrere Sprünge zurück) oder ein einzelnes Feld, welches auf den zuletzt sichtbaren layer referenziert.
 
Zurück
Oben