Swing Look And Feel ändern

beastofchaos

Bekanntes Mitglied
Hallo Leute,
Ich habe ein Projekt, in dem ich auch das LaF verändern will. Normalerweise, benutzt man dafür folgende Zeile:
Code:
SwingUtilities.updateComponentTreeUI(main);
// main = das Hauptfenster

Nun werden dabei aber nicht Dialoge aktualisiert. Nun muss ich theoretisch für jeden Dialog diese Methode aufrufen. Noch schlimmer ist: Ich muss, um auf diese zu greifen zu wollen und nicht tausend Getter-Methode zu schreiben, diese "public" deklariert, wobei ich gerade in meinem Code versuche, das los zu werden. Ich weiß, Getter sind toll und ich mach das ja auch ganz brav, aber es wär deutlich praktischer, wenn ich die Möglichkeit hätte, in einer Zeile zu sagen: "Aktualisiere die ganze Anwendung und damit alle Fenster".

Freu mich auf Vorschläge,

Thomas 😉
 
Hey,
Also so ne richtig saubere Lösung ist da glaub ich recht schwierig. Du könntest aber zum Beispiel eine Liste der Komponenten pflegen, die sich nicht "selbst updaten", könnte man je nach Architektur deiner Anwendung statisch, oder in einem Singleton machen.
Oder du startest deine Anwendung neu, nach dem das LaF geändert wurde, das setzt natürlich vorraus, dass du hier bestimmte Informationen serialisieren musst (vorsicht falls du hiermit einen Singleton in Kombination benutzen solltest).
Nur für den Fall, dass der restart-Ansatz hilfreich für dich wäre, hier der Code wie das automatisiert geht (wird evtl. nicht aus einer IDE herraus funktionieren):

Java:
public static void restartApplication() throws URISyntaxException,
			IOException {
		final String javaBin = System.getProperty("java.home") + File.separator
				+ "bin" + File.separator + "java";
		final File currentJar = new File(Starter.class.getProtectionDomain()
				.getCodeSource().getLocation().toURI());

		if (!currentJar.getName().endsWith(".jar"))
			return;

		final ArrayList<String> command = new ArrayList<String>();
		command.add(javaBin);
		command.add("-jar");
		command.add(currentJar.getPath());
		final ProcessBuilder builder = new ProcessBuilder(command);
		builder.start();
		System.exit(0);
	}
 
Danke dafür, das werde ich vorallem bei der INternationalization gebrauchen können 🙂

Aber ich will bezüglich LaFs eigentlich direkt eines anzeigen, damit man nicht immer 10 Sekunden warten muss (großes Programm). So könnte man ein bisschen rumspielen mit dem Design. Einzige Alternative wäre, ich aktualisiere nur den dafür benutzten Dialog und das Hauptfenster und sage, dass beim Abschluss der Dialogs das Programm neugestartet werden muss.

Thomas
 
Musst du das LaF zwingend im laufenden Betrieb ändern, reichts nicht wenn du das am Programmstart nen einfaches
Java:
UIManager.setLookAndFeel("laf.name");
machst?
 
Ich würde neu starten lassen, genauso wie beim Ändern der Sprache. Ist das Einfachste, und wie oft ändert der Benutzer schon das Aussehen oder Sprache?
 
Entweder lässt du's neustarten (wie alle vor mir meinten), oder, falls du so eine Art Hauptmenü hast, zu welches du zurückkehren kannst, es nur dort zulassen. Sprich, wenn nur dieses Hauptmenüfenster offen ist.

Für das Neustarten: entweder als Property speichern oder als Parameter übergeben und dann von args[0] auslesen.
 
Guten Tag,

ich habe irgendwie noch nicht richtig verstanden, was du nun bezüglich des LaF machen möchtest.
Wenn ich dich doch richtig verstanden habe, möchtest du, dass das LaF deiner Anwendung zur Laufzeit geändert werden kann, oder ?

Du sagtest ja, dass die Dialoge deiner Anwendung bei der Aktualisierung des LaF nicht mit berücksichtigt werden. Ich habe eigentlich noch nie mit z.B. JOptionPane in Verbindung mit LaF gearbeitet und kann jetzt auch nicht sagen wie sich das verhält.

Aber du könntest dir ja mit JDialog eigene Dialoge zusammenbasteln, bei denen du dann jeweils das Look and Feel setzt, oder wenn du einen Dialog aufrufst, sagst du diesem, dass er das LaF mit dem gerade ausgewähltem LaF im Hauptprogramm aktualisieren soll.

Ich zum Beispiel habe mir immer eine MenuBar erstellt mit einem Menu. Nun habe ich eine Methode geschrieben, die mir 5 JRadioButtonMenuItem erstellt und die mit den im System vordefinierten Look and Feels belegt. Somit habe ich eine Menüleiste, in der ich den Reiter LaF habe und zwischen max. 5 Look and Feels wechseln kann. Beim Ändern der Auswahl müsste dann eigentlich auch das LaF des Programms komplett zur Laufzeit geändert werden.

Hier nun meine Methode:

Java:
private JMenuBar menuBar = new JMenuBar();
private JMenu lookAndFeelMenu = new JMenu("LaF");
private ButtonGroup buttongroup1 = new ButtonGroup();

public void createLaF() {
  
   menuBar.add(lookAndFeelMenu);
   JRadioButtonMenuItem[] item = new JRadioButtonMenuItem[5];
    for (int i = 0; i < UIManager.getInstalledLookAndFeels().length; i++) {
      UIManager.LookAndFeelInfo laf = UIManager.getInstalledLookAndFeels()[i];
      item[i] = new JRadioButtonMenuItem();
      buttongroup1.add(item[i]);
      lookAndFeelMenu.add(item[i]);
      item[i].setText(laf.getName());
      item[i].addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent evt)
        {
          try {
            JRadioButtonMenuItem item_i = (JRadioButtonMenuItem)evt.getSource();
            boolean found = false;
            UIManager.LookAndFeelInfo[] laf_array = UIManager.getInstalledLookAndFeels();
            for (int i = 0; (i < laf_array.length) && (!found); i++) {
              if (laf_array[i].getName().equals(item_i.getText())) {
                found = true;
                UIManager.setLookAndFeel(laf_array[i].getClassName());
              }
            }
            SwingUtilities.updateComponentTreeUI(this);
          } catch (java.lang.InstantiationException e) {
          }
        }
      });
    }

MfG
 
Zuletzt bearbeitet:
Naja, ich will ja die DIaloge aktualisieren und ich müsste ja dazu nur auf sie zugreifen und dann die entsprechende Methode von SwingComponents aufurfen, aber dafür müsst ich die ganzen Dialog, die in anderen Objekt wiederum gehalten werden "public" definieren. Wenn ich ein LookAndFeel verändere, werden zwar alle danach erzeugten Objekte dem automatisch angepasst, aber bereits vorhandene Dialog nicht.
 
Doch, eigentlich werden, sobald du das Look and Feel zur Laufzeit änderst, alle schon erstellten Fenster und Frames neu angepasst. Probiere mal den Quelltext, den ich gepostet habe, in dem du ein JMenu zur Laufzeit auf eine JMenuBar legst und diese mit den vom System bereitgestellten Look and Feels belegst. Die Methode createLaF musst du dann natürlich im Konstruktor aufrufen.

Bei meinem letzten Projekt, das war ein Download Manager, hat das Ändern des LaF zur Laufzeit eigentlich reibungslos funktioniert, wobei ich jedoch eigene Dialoge erstellt habe. Soweit ich mich erinnern kann, hatte ich beim Exception Handling doch JOptionPane benutzt, um Fehlernachrichten als Message Dialog auszugeben.

MfG
 
Also dein JMenu-Objekt ist doch noch in dem selben Frame drinnen, oder?

Ich weiß halt nur, dass es in meinem Fall nicht funktioniert:
Java:
public void setLookAndFeel(String lafName)
	{
		try
		{
			UIManager.setLookAndFeel(lafName);
			SwingUtilities.updateComponentTreeUI(main);
//			SwingUtilities.updateComponentTreeUI(main.menu.menuSettings.dialogSettings);
//			SwingUtilities.updateComponentTreeUI(main.menu.menuEffects.edContrast);
//			SwingUtilities.updateComponentTreeUI(main.menu.menuEffects.edLightness);
//			SwingUtilities.updateComponentTreeUI(main.north.dialogText);
//			SwingUtilities.updateComponentTreeUI(main.north.dialogStroke);
//			SwingUtilities.updateComponentTreeUI(main.actions.dialogFileOpen);
//			SwingUtilities.updateComponentTreeUI(main.actions.dialogFileSave);
//			SwingUtilities.updateComponentTreeUI(main.actions.fileSaveDirDialog);
//			SwingUtilities.updateComponentTreeUI(main.actions.layoutDialog);
//			SwingUtilities.updateComponentTreeUI(main.menu.menuSettings.dialogInfo);
//			SwingUtilities.updateComponentTreeUI(main.menu.menuSettings.dialogSettings);
//			SwingUtilities.updateComponentTreeUI(main.menu.menuEffects);
			Saves.settings.lookAndFeel = lafName;
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

Habe hier jetzt auch eine Dialog mal auskommentiert und tatsächlich werden zwar die Fenster an sich anscheinend angepasst, aber die Komponenten darin nicht richtig geladen. Z.B. bei einem Button in einem meiner Dialog sehe ich nur den Text, aber nicht den Button selber.

Gruß, Thomas
 
Was ist denn bei dir main ? Die Methode updateComponentTreeUI() braucht eine Referenz zu einem Frame oder Dialog, bzw. zu deinem Parent Frame. Falls deine Methode setLookAndFeel(), ein anderer Name wäre glaube ich besser, in der Klasse steht, wo du auch deine GUI definiert hast, dann ändere einmal dieses

Java:
 SwingUtilities.updateComponentTreeUI(main);

in

Java:
SwingUtilities.updateComponentTreeUI(this);
 
Zuletzt bearbeitet:
Java:
public class GUI() implements ActionListener {

    JDialog dialog1;
    JPanel panel1;
    JButton btn1, btn2;

    public GUI() {
        dialog1 = new JDialog("Look and Feel");
        panel1 = new JPanel(new BorderLayout()); 

        btn1 = new JButton("Windows");
        btn1.addActionListener(this)
        btn2 = new JButton("Metal");
        btn2.addActionListener(this);
       
        panel1.add(btn1);
        panel1.add(btn2);

        dialog1.setSize(300, 300);
        dialog1.add(panel1);
        dialog1.pack;
        dialog1.setVisible(true);
    }

    public void actionPerformed(ActionEvent evt) {
        if (evt.getSource().equals(btn1) {
            setLaF("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } else {
            setLaF("javax.swing.plaf.metal");
        }
    }

    public void setLaF(String lafName) {
       try {
            UIManager.setLookAndFeel(lafName);
            SwingUtilities.updateComponentTreeUI(dialog1);

       } catch (Exception e) {
            e.printStackTrace();
       }
   }
}

Ich hoffe, dass ich jetzt so schnell keine Fehler gemacht habe. 😛
Es ist nur ein Vorschlag, wie man es machen kann ;-) man kann noch weitere Look and Feels hinzufügen und anstatt das man JButton benutzt, kann man auch andere Komponente benutzen, die es einem erlauben einen ActionListener zu verwenden.

MfG
 
Zuletzt bearbeitet:
Tut mir leid ! Ich muss mich noch einmal korrigieren und zwar hatte ich geschrieben, dass du main in this ändern solltest, jedoch bin ich davon ausgegangen, dass du von einem Frame erbst. Nun habe ich mir dein ersten Post noch einmal durchgelesen und habe gesehen, dass main dein Objekt bzw. Frame ist. 😛

Du kannst dir diese Seite vielleicht auch noch einmal anschauen. 🙂

Javabeginners - LookAndFeel
 
Zuletzt bearbeitet:
Ich weiß, dass dabei der ganze Komponentenbaum von dem Hauptframe aktualisiert wird, aber hier ein Beispiel wo es nicht funktioniert und wie es in meinem Programm auch leider ist:

Java:
public CustomFrame extends JFrame implements ActionListener
{
    JDialog dialog;
    
    public CustomFrame()
    {
        super();
        dialog = new JDialog();
        JButton buttonAdopt = new JButton("Setzt LaF");
        buttonAdopt.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        try {
            UIManager.setLookAndFeel("javax.swing.plaf.metal");
            SwingUtilities.updateComponentTreeUI(this); // Frame aktualisieren
            SwingUtilities.updateComponentTreeUI(dialog); // Dialog aktualisieren
 
       } catch (Exception e) {
            e.printStackTrace();
       }
    }
}

Ich muss also Hauptfenster und Dialog getrennt aktualisieren, wobei das in meinem Programm mit ca. 30 Dialogen, die alle danke "private"-Deklaration nicht zugänglich sein sollten, ein viel größeres Ausmaß hat.
Deswegen hab ich auch gefragt, ob ich die Attribute eines Objekts ermitteln kann. Also ich würd in meinem Beispiel eine Methode
Code:
getAllAttributed(Object o)
auf das Fenster aufrufen und die Methode würde mir in diesem Fall ein Array mit diesem Dialog als einzige Komponente wiedergeben.

Aber wie gesagt, werde ich jetzt nur das Hauptfenster bei einem Wechsel aktualisieren und, wenn der Dialog zum Auswählen abgeschlossen ist, das Programm einfach neu starten.

Gruß, Thomas
 

Zurück
Oben