Programmation Java Swing/Apparence de l'interface

L'apparence et le comportement (look and feel en anglais) des composants Swing d'une application sont modifiables et configurables par le gestionnaire d'apparence et de comportement. L'apparence et le comportement varient plus largement pour les boîtes de dialogue standards telle celle de l'ouverture de fichiers où les composants utilisés diffèrent :

Apparence de l'application modifier

Les composants Swing ont une apparence et un comportement (look and feel en anglais) modifiables définis par le gestionnaire d'apparence et de comportement. L'apparence peut ensuite être modifiée pour chaque composant en appelant les méthodes communes :

  • setBackground(Color) : Changer la couleur de fond.
  • setForeground(Color) : Changer la couleur de premier plan (texte, ligne en général).
  • setFont(Font) : Changer la police de caractères, si le composant affiche du texte.
  • setBorder(Border) : Changer la bordure autour du composant (aucune par défaut en général).

Les méthodes get correspondantes existent également permettant d'obtenir la valeur courante.

Le gestionnaire d'apparence et de comportement par défaut est Metal, disponible quel que soit la plateforme. La classe UIManager permet de changer l'apparence et le comportement en appelant la méthode setLookAndFeel et en lui passant le nom de la classe implémentant l'apparence et le comportement voulus.

Si l'application effectue un changement, il faut le faire avant la création des fenêtres concernées. Par exemple dans la méthode main :

protected static String laf_selected = null;

// Sélection par nom de classe
private static boolean selectClass(String classname)
{
	try
	{
		UIManager.setLookAndFeel(classname);
		laf_selected = classname;
		return true;
	}
	catch (Exception e)
	{
		e.printStackTrace();
	}
	return false;
}

// Sélection par nom d'affichage, recherche parmi les looks & feels disponibles.
public static boolean select(String name)
{
	for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels() )
	{
		if (name.equalsIgnoreCase(laf.getName()))
			return selectClass(laf.getClassName());
	}
	return false;
}

public static void main(String[] args)
{
	// Choisir l'apparence avant de créer une fenêtre
	select("Nimbus");
	//selectClass(UIManager.getSystemLookAndFeelClassName()); // Apparence de l'OS
	//selectClass(UIManager.getCrossPlatformLookAndFeelClassName()); // Apparence cross-plateforme (Metal)
	EventQueue.invokeLater(new Runnable()
	{
		public void run()
		{
			DemonstrationApparence frame = new DemonstrationApparence();
			frame.setVisible(true);
		}
	});
}

Si l'application effectue le changement après création des fenêtres, la mise à jour doit se faire explicitement en appelant la méthode statique SwingUtilities.updateComponentTreeUI pour chaque fenêtre à mettre à jour en argument.

Aperçu modifier

Les copies d'écran ci-dessous montrent l'apparence de divers composants Swing selon l'apparence sélectionnée. Les apparences disponibles dépendent du système d'exploitation sous lequel tourne l'application Java. L'apparence peut modifier les couleurs, la police de caractère par défaut, la taille, les marges des différents composants. Par contre, l'apparence du bord de la fenêtre ne change pas.

Sous windows 7 :


 
Apparence Metal pour les bords d'une fenêtre Swing (JFrame)

L'aspect de tous les composants changent, mais celui du bord de la fenêtre ne change pas par défaut. Le changement de l'aspect du bord de fenêtre n'est pas supporté par toutes les apparences. La seule apparence à le supporter est le look&feel Metal. Pour activer le changement d'apparence de décoration des fenêtres et boîtes de dialogue, il faut appeler ces méthodes (les deux en général) :

JDialog.setDefaultLookAndFeelDecorated(true);
JFrame.setDefaultLookAndFeelDecorated(true);

Ces deux méthodes doivent être appelées avant la création des fenêtres et boîtes de dialogue. On peut obtenir le même effet sur une fenêtre individuelle :

JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);

Pour une boîte de dialogue :

JDialog dialog = new JDialog();
dialog.setUndecorated(true);
dialog.getRootPane().setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);

La personnalisation de l'apparence des bords de fenêtres est effectivement géré par JRootPane, dans une fenêtre sans décoration. Si le changement d'apparence de décoration des fenêtres et boîtes de dialogue est activé avec une apparence qui ne le supporte pas, la fenêtre n'a plus de bord.

 
Apparence Metal (bords de fenêtre inclus), avec gras désactivé

L'apparence peut être configurée avant la création de fenêtre.

Par exemple, il est possible de désactiver les polices en gras de l'apparence Metal :

UIManager.put("swing.boldMetal", Boolean.FALSE);

Code source modifier

Le code source ci-dessous est celui de l'application ayant permis les captures d'écrans de ce chapitre.

package org.wikibooks.fr.swing;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

/**
 * Démonstration du changement d'apparence. 
 * @author fr.wikibooks.org
 */
public class DemonstrationApparence extends JFrame
{
	private String[] laf_classnames;
	private String[] laf_names;
	private int index_cross_platform = -1;
	private int index_system = -1;
	private int index_selected = -1;

	private void changerApparence(int index)
	{
		if (index != index_selected)
		{
			index_selected = index;
			if (selectClass(laf_classnames[index]))
				SwingUtilities.updateComponentTreeUI(this);
			else
				JOptionPane.showMessageDialog(this,
					"Impossible de changer l'apparence à "+laf_names[index],
					"Erreur", JOptionPane.ERROR_MESSAGE);
		}
	}

	public DemonstrationApparence()
	{
		// Lister les apparences disponibles
		UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels();
		laf_classnames = new String[lafs.length];
		laf_names = new String[lafs.length];
		String laf_system = UIManager.getSystemLookAndFeelClassName();
		String laf_cross_platform = UIManager.getCrossPlatformLookAndFeelClassName();
		index_selected = -1;
		for(int i=0 ; i<lafs.length ; i++)
		{
			UIManager.LookAndFeelInfo laf = lafs[i];
			String classname = laf.getClassName();
			laf_classnames[i] = classname;
			laf_names[i] = laf.getName();
			if (laf_selected!=null && laf_selected.equals(classname))
				index_selected = i;
			if (laf_system.equals(classname))
				index_system = i;
			if (laf_cross_platform.equals(classname))
			{
				index_cross_platform = i;
				if (laf_selected==null) index_selected = i;
			}
		}

		// Création de la fenêtre
		setTitle("Démonstration de l'apparence des composants Swing");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Dimension d = new Dimension(450, 440);
		setMinimumSize(d);
		setSize(d);
		Container c = getContentPane();
		c.setLayout(new BorderLayout());

		JPanel p_main = new JPanel();
		p_main.setLayout(new BoxLayout(p_main, BoxLayout.Y_AXIS));
		p_main.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		c.add(p_main, BorderLayout.WEST);

		JLabel l_apparence = new JLabel("Choisissez l'apparence :");
		p_main.add(l_apparence);

		// Un seul bouton radio appartenant au groupe ne pourra être sélectionné
		ButtonGroup group = new ButtonGroup();
		JRadioButton rb_sel = null;
		for(int i=0 ; i<lafs.length ; i++)
		{
			String name = laf_names[i];
			if (i==index_cross_platform) name = name+" (Cross-platform)";
			if (i==index_system) name = name+" (System)";
			JRadioButton rb = new JRadioButton(name);
			p_main.add(rb);
			group.add(rb);
			if (i == index_selected) rb_sel = rb;
			final int rb_index = i;
			rb.addItemListener(new ItemListener()
			{
				@Override
				public void itemStateChanged(ItemEvent e)
				{
					if (rb.isSelected())
						changerApparence(rb_index);
				}
			});
		}
		if (rb_sel!=null) rb_sel.setSelected(true);

		JPanel p_comp = new JPanel();
		p_comp.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		p_comp.setLayout(new BoxLayout(p_comp, BoxLayout.Y_AXIS));
		c.add(p_comp, BorderLayout.CENTER);

		// Quelques composants pour montrer leur apparence
		// espacés de 10 pixels verticalement
		p_comp.add(new JCheckBox("Case à cocher"));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JSpinner(new SpinnerNumberModel(50, 0, 100, 1)));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JComboBox<String>(new String[]{"Choix 1", "Choix 2", "Choix 3"}));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JTextField("Champ de saisie"));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JScrollPane(new JTextArea("Zone de saisie\nSur plusieurs lignes\n1 2 3 4 5 6 7 8 9\n...")));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JButton("Un bouton"));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JToggleButton("Un bouton basculable"));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(new JScrollPane(new JTable(
			new Object[][]
				{
				{ "pi",  3.14159, true },
				{ "phi", 1.61803, false },
				{ "e",   2.71828, true },
				{ "square root of 2", 1.41421, false },
				{ "square root of 3", 1.73205, false },
				{ "square root of 5", 2.23607, false },
				{ "square root of 7", 2.64575, false },
				},
			new Object[]{ "Nom", "Valeur", "Utiliser" })));
		p_comp.add(Box.createVerticalStrut(10));
		p_comp.add(Box.createVerticalGlue());
	}

	protected static String laf_selected = null;

	private static boolean selectClass(String classname)
	{
		try
		{
			UIManager.setLookAndFeel(classname);
			laf_selected = classname;
			return true;
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return false;
	}

	public static boolean select(String name)
	{
		for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels() )
		{
			if (name.equalsIgnoreCase(laf.getName()))
				return selectClass(laf.getClassName());
		}
		return false;
	}

	public static void main(String[] args)
	{
		// Choisir l'apparence avant de créer une fenêtre
		select("Nimbus");
		//selectClass(UIManager.getSystemLookAndFeelClassName());
		//selectClass(UIManager.getCrossPlatformLookAndFeelClassName());
		EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				DemonstrationApparence frame = new DemonstrationApparence();
				frame.setVisible(true);
			}
		});
	}
}