Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
SwingWieso braucht man nach setVisible mal ein revalidate und mal nicht?
Nehmen wir an ich habe ein JPanel mit Border Layout.
Wird ein JPanel im EAST Bereich z.B. invisible gesetzt wird der CENTER Bereich automatisch vergrößert.
Habe ich jedoch ein z.B. JTextField im EAST Bereich und setze dieses invisible wird der CENTER Bereich nicht automatisch vergrößert sondern ich muss erst ein revalidate aufrufen.
Manipulationen am Layout zur Laufzeit macht man auch nicht
Und wenn doch dann sollte man immer revalidat() an der Container Komponente aufrufen.
Der Unterschied zwischen dem Verhalten zwischen JPanel und JTextField könnte darin liegen, dass JTextField getPreferredSize() überschreibt und JPanel die Implementierung von JComponent nutzt.
JComponent
hat revalidate
ist Eltern von JTextComponent --> JTextField
JPanel
JTextField hat
boolean isValidateRoot()
Calls to revalidate that come from within the textfield itself will be handled by validating the
textfield, unless the textfield is contained within a JViewport, in which case this returns false.
setFont public void setFont(Font f)
Sets the current font. This removes cached row height and column width so the new font will be reflected.
revalidate is called after setting the font.
Zur Laufzeit die GUI verändern geht nur sehr begrenzt.
In meinem Java-Audioplayer für Java SE und JApplet wird die GUI zur Laufzeit verändert.
Allerdings nur in den gültigen Varianten einer Klasse z.B. des Box-Layoutes.
(Player per audio, flash and java ansehbar).
Was ich mache (und das ist ja üblich) ist einige Komponenten wie z.B. Symbolleisten zur Laufzeit ein und ausblenden zu können.
Dazu verwende ich JGoodies Binding lib um den Visible Status der einzelnen Komponenten mit dem Programm Modell synchron zu halten. Das funktioniert auch wunderbar.
Setze ich nun während der Laufzeit (z.B. per Menü) eine Symbolleiste auf visible(false) funktionierts auch wunderbar. Die Symbolleiste (JToolbar) wird unsichtbar UND die Gesamt Toolbar aktualisiert (ordnet die Child Toolbars) automatisch neu. Sprich irgendwo wird ein revalidate auf das Parent aufgerufen.
In einem anderen JPanel habe ich als CHild aber Beispielsweise ein Textfeld. Dieses kann auch ein und ausgeblendet werden. Es wird auch unsichtbar ABER das dazugehörige JPanel (Parent des Textfield, mit BOrderLayout) aktualisiert sich nicht.
Ich wüsste gerne (Sorry aber ist mir immer noch nicht klar) Wieso in diesem Fall kein Revalidate auf dem JPanel ausgelöst wird.
Wie löst man so ein aus und einblenden denn sonst in einer Applikation? Das ist ja wirklich nichts ungewöhnliches. Vielleicht ist mein Ansatz auch einfach nicht so glücklich gewählt.
Hier noch ein Beispielcode. Zwar ohne Binding aber das Problem ist ja das selbe:
Java:
public class Main {
private JTextField textWest;
private JTextField textEast;
private JTextField textCenter;
private JPanel textPanel;
public Main() {
JCheckBoxMenuItem itemWest = new JCheckBoxMenuItem("Show left text");
itemWest.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textWest.setVisible(!textWest.isVisible());
}
});
JCheckBoxMenuItem itemEast = new JCheckBoxMenuItem("Show right text");
itemEast.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textPanel.setVisible(!textPanel.isVisible());
}
});
JMenuBar menuBar = new JMenuBar();
menuBar.add(itemWest);
menuBar.add(itemEast);
textCenter = new JTextField("Center");
textCenter.setHorizontalAlignment(SwingConstants.CENTER);
textWest = new JTextField("Left");
textEast = new JTextField("East");
textPanel = new JPanel();
textPanel.add(textEast);
JFrame frame = new JFrame();
JPanel panel = new JPanel(new BorderLayout());
panel.add(BorderLayout.WEST, textWest);
panel.add(BorderLayout.CENTER, textCenter);
panel.add(BorderLayout.EAST, textPanel);
frame.setJMenuBar(menuBar);
frame.add(panel);
frame.setSize(new Dimension(240, 120));
frame.setVisible(true);
}
public static void main(String[] args) {
new Main();
}
}
Im Beispiel sieht man schön, wie die Center Component die Größe ändert, wenn das East Panel visible(false) gesetzt wird.
Macht man das mit dem JTextField im West Bereich wird es zwar ausgeblendet, jedoch ändert das Layout nicht die Größe der Center Component. Irgendwie verstehe ich das nicht.
Nach meinem Verständnis erben doch JTextField und JPanel von den gleichen Elternklassen die fragliche Methode setVisible.
Das ist klar. Ich wüsste aber gerne wieso das unterschiedlich gehandhabt wird.
Das eine mal brauche ein revalidate (ausgelöst durch pack()) und das andere mal eben nicht. Die Frage war ja nicht wie bekomme ich das ans laufen sondern wieso ist es mal so und mal so.
Mein Problem ist zudem, dass ich das visible per binding setze. Ein manuelles revalidate ist da also nicht möglich.
Wie twseitex schon darauf hingewiesen hat überschreibt JTextField die
Code:
public boolean isValidateRoot()
von JComponent und liefert true falls sie nicht in einem JViewport verbaut ist.
Wenn man bei Deiner Demo das BorderLayout beobachtet, stellt man fest, dass bei setVisible() am JTextField keine Neuberechnung des Layouts stattfindet. Man kann zum Spaß die isValidateRoot des Textfeldes überschreibt, dann wird auch das Layout neu berechnet.
Allerdings ist eine Layoutänderung über die direkte Steuerung der Sichtbarkeit von Komponenten in Java nicht üblich. Sowas würde man wohl eher mittels JSplitPane bzw. beim Austauch von Komponenten mit CardLayout machen. Welches das zwar selbst über die Sichtbarkeit löst aber immer ein validate() am Container aufruft. Und das wäre auch die Empfehlung für jeden Anwender der direkt ins Layout eingreift.
Wie twseitex schon darauf hingewiesen hat überschreibt JTextField die
Code:
public boolean isValidateRoot()
von JComponent und liefert true falls sie nicht in einem JViewport verbaut ist.
Wenn man bei Deiner Demo das BorderLayout beobachtet, stellt man fest, dass bei setVisible() am JTextField keine Neuberechnung des Layouts stattfindet. Man kann zum Spaß die isValidateRoot des Textfeldes überschreibt, dann wird auch das Layout neu berechnet.
Allerdings ist eine Layoutänderung über die direkte Steuerung der Sichtbarkeit von Komponenten in Java nicht üblich. Sowas würde man wohl eher mittels JSplitPane bzw. beim Austauch von Komponenten mit CardLayout machen. Welches das zwar selbst über die Sichtbarkeit löst aber immer ein validate() am Container aufruft. Und das wäre auch die Empfehlung für jeden Anwender der direkt ins Layout eingreift.
Ah OK jetzt hab ichs glaub ich auch verstanden. Durch das Überschreiben der Methode isValidateRoot() wird das validate der Parent Component (in meinem Fall das JPanel) unterdrückt.
Die beiden anderen Vorschläge schau ich mir mal an. Hast du dazu vielleicht noch ein paar links?
EDIT: Ich finde irgendwie macht es doch keinen Sinn eine Splitpane zum aus oder einblenden eines Widgets zu verwenden. Zumal das was ich ein und ausblenden möchte immer eine feste Breite hat.
Könnte wirklich ein Beispiel gebrauchen, wie man Komponenten ein und ausblendet.
Wie "schalte" ich den z.B. Sichtbarkeit von Toolbars dann sinnvollerweise richtig um?