new JLabel("Это окно Windows L&F
TeMa Classic")); ifr2.setPreferredSize(new Dimension(200, 100)); ifr2.setVisible(true) ;
add(ifr2);
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.motif.MotifLookAndFeel"); }catch(Exception e){}
JInternalFrame ifr3 =
new JInternalFrame(" Окно CDE/Motif", true, true, true, true); i fr3.getContentPane().add(
new JLabelC^TO окно Solaris CDE L&F")); ifr3.setPreferredSize(new Dimension(200, 100)); ifr3.setVisible(true) ; add(ifr3);
setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new DiffLAF();
}
Дополнительные L&F
Кроме текущего L&F библиотека Swing может одновременно использовать
Дополнительные L&F обычно связаны с дополнительными устройствами ввода/вывода: клавиатурой Брайля, речевым вводом/выводом и тому подобными устройствами. Ничто не мешает дополнительному L&F выводить свои объекты на экран, но при этом могут возникнуть конфликты с текущим L&F. Поэтому рекомендуется не использовать в качестве дополнительных графических L&F классы, унаследованные от BasicLookAndFeel, а наследовать их прямо от MultiLookAndFeel.
Смена всего L&F
Разработчик GUI может сменить весь L&F своего приложения или его отдельные свойства. Для смены всего L&F сначала устанавливается новый L&F:
UIManager.setLookAndFeel(new MyCoolLookAndFeel());
Затем надо привести вид и поведение приложения в соответствие с новым L&F.
При каждом изменении какого-либо свойства, входящего в L&F, или всего L&F целиком, происходит событие класса PropertyChangeEvent. Класс UIManager присоединяет обработчик этого события обычным методом addPropertyChangeListener(PropertyChangeListener). В обработчике события следует сообщить всем компонентам приложения о смене L&F. Это удобно сделать статическим методом updateComponentTreeUI(Component) класса SwingUtilities. В аргументе данного метода достаточно указать контейнер верхнего уровня, метод передаст сообщение всем вложенным контейнерам и компонентам.
Итак, обработка изменения L&F выглядит следующим образом:
JFrame frame = JFrame("Главноe окно");
UIManager.addPropertyChangeListener( new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent e){
SwingUtilities.updateComponentTreeUI(frame);
}
});
Если при этом надо установить первоначальные размеры окна, то в обработчик события следует вставить обращение к методу frame.pack ().
Метод updateComponentTreeUI (frame) рекурсивно просматривает все вложенные контейнеры и компоненты. Для каждого компонента вызывается его метод updateUI () класса JComponent, который, в свою очередь, вызывает метод setUI(ComponentUI) с аргументом соответствующего типа, устанавливающий новый L&F для этого компонента.
import java.awt.*; import java.awt.event.*; import java.beans.*; import java.util.*; import javax.swing.*;
public class ChLAF extends JFrame{
ChLAF(){
super(" Смена L&F"); setLayout(new FlowLayout()) ;
JMenuBar mb = new JMenuBar(); setJMenuBar(mb);
JMenu serv = new JMenu("CepBHc"); mb.add(serv);
JMenu laf = new JMenu("Вид"); serv.add(laf);
ButtonGroup bg = new ButtonGroup();
UIManager.LookAndFeelInfo[] info =
UIManager.getInstalledLookAndFeels();
for (int i = 0; i < info.length; i++){
JRadioButtonMenuItem item =
new JRadioButtonMenuItem(info[i].getName());
item.addItemListener(new LAFChange(info[i].getClassName()));
bg.add(item); laf.add(item);
}
JButton b = new JButton("Кнопка");
add(b);
UIManager.addPropertyChangeListener( new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent e){ SwingUtilities.updateComponentTreeUI(c);
}
});
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new ChLAF();
}
class LAFChange implements ItemListener{
private String className;
public LAFChange(String className){ this.className = className;
}
public void itemStateChanged(ItemEvent e){
if(e.getStateChange() == ItemEvent.SELECTED) try{
UIManager.setLookAndFeel(className); }catch(Exception ex){}
}
}
}
Рис. 17.2. Смена L&F |
В составе Java SE, в каталоге $JAVA_HOME/demo/jfc/SwingSet2/, приведен пример, позволяющий менять L&F и темы во время работы приложения. Для его запуска достаточно перейти в указанный каталог и набрать в командной строке
java -jar SwingSet2.jar
Замена отдельных свойств L&F
Во время работы приложения можно заменить не весь текущий L&F, а только некоторые его свойства. Для этого надо воспользоваться статическим методом put(Object key, Object value) класса UIManager. Поскольку при этом меняется только модель данных — класс UIDefaults — к нему следует присоединить обработчик событий, получив ссылку на него методом getDefaults(). Потом нужно оповестить все заинтересованные компоненты о сделанных изменениях методом
updateComponentTreeUI(Component) .
import java.awt.*; import java.awt.event.*; import java.beans.*; import java.util.*; import javax.swing.*; import javax.swing.plaf.*;
public class PropCh extends JFrame{
PropCh(){
super(" Смена размера шрифта"); setLayout(new FlowLayout());
JMenuBar mb = new JMenuBar(); setJMenuBar(mb);
JMenu serv = new JMenu("Сервис"); mb.add(serv);
JMenu laf = new JMenu("Размер шрифта"); serv.add(laf);
ButtonGroup bg = new ButtonGroup();
FontChange fch = new FontChange();
for (int i = 10; i < 22; i += 2){
JRadioButtonMenuItem item = new JRadioButtonMenuItem(""+ i); item.addItemListener(fch); bg.add(item); laf.add(item);
}
JTextArea ta = new JTextArea(5, 20);
JTextField tf = new JTextField(20);
JPasswordField pf = new JPasswordField(20);
add(ta); add(tf); add(pf);
PropertyChangeListener pcl = new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent e){ SwingUtilities.updateComponentTreeUI(c);
}
};
UIManager.addPropertyChangeListener(pcl); UIManager.getDefaults().addPropertyChangeListener(pcl);
setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new PropCh();
}
class FontChange implements ItemListener{ public FontChange(){}
public void itemStateChanged(ItemEvent e){
if (e.getStateChange() == ItemEvent.SELECTED){
JMenuItem mi = (JMenuItem)e.getSource(); int n = Integer.parseInt(mi.getText());
Font f = UIManager.getFont("TextArea.font");
String name = f.getName(); int style = f.getStyle();
FontUIResource fr = new FontUIResource(name, style, n);
UIManager.put("TextArea.font", fr);
// UIManager.put("TextField.font", fr);
// UIManager.put("PasswordField.font", fr);
UIManager.put("EditorPane.font", fr);
UIManager.put("TextPane.font", fr);
UIManager.put("FormattedTextField.font", fr);
}
}
}
}
Рис. 17.3. Поля ввода с измененным размером шрифта |
Темы Java L&F
Вид и поведение Java L&F базируются на трех
Методы, возвращающие цвета и шрифты выбранной темы, частично определены абстрактным классом MetalTheme из пакета javax.swing.plaf.metal. Полное определение темы, выбираемой по умолчанию, дано классом DefaultMetalTheme. В этой теме основные цвета — темно-синий, синий и голубой, точнее, цвет primary1 в модели RGB равен (102, 102, 153), цвет primary2 — (153, 153, 204), цвет primary3 — (204, 204, 255). Дополнительные цвета — это темно-серый, серый и светло-серый, точнее, secondary1 равен (102, 102, 102), secondary2 — (153, 153, 153), secondary3 — (204, 204, 204). Цвет primary1 используется рамками активного компонента, надписями на компонентах. Цвет primary2 выделяет пункты меню, цвет primary3 выделяет текст в полях ввода. Цвет secondary1 оттеняет "выпуклые" компоненты, цвет secondary2 используется рамками неактивных компонентов, цвет secondary3 — цвет фона неактивных компонентов.
Кроме перечисленных цветов тема DefaultMetalModel определяет черный цвет для текста, вводимого в текстовые поля, и белый цвет для фона текстовых полей.
В теме класса OceanTheme, расширяющего класс DefaultMetalModel, основные цвета — это светло-синий, голубой и светло-голубой, точнее, в модели RGB это цвета (99, 130, 191), (163, 184, 204) и (184, 207, 229). Дополнительные цвета — серо-голубой, светло-голубой и светло-серый, точнее, (122, 138, 153), (184, 207, 229) и (238, 238, 238).
Для создания собственной темы достаточно расширить класс MetalTheme, определив методы, устанавливающие и возвращающие цвета: getPrimary1(), getPrimary2 ( ), getPrimary3(), getSecondary1(), getSecondary2(), getSecondary3(), и методы, задающие и возвращающие шрифты: getControlTextFont(), getMenuTextFont(), getSubTextFont (), getSystemTextFont ( ), getUserTextFont (), getWindowTitleFont (). Если нужно изменить только отдельный цвет или шрифт, то достаточно расширить класс DefaultMetalTheme или класс
OceanTheme.
Методы, создающие цвета, должны возвращать объект класса ColorUIResource. Для создания такого объекта есть четыре конструктора:
ColorUIResource(Color);
ColorUIResource(int red, int green, int blue);
ColorUIResource(float red, float green, float blue);
ColorUIResource(int rgb);
Поэтому метод, возвращающий первый основной цвет, может выглядеть так:
public ColorUIResource getPrimary1(){
return new ColorUIResource(36, 124, 225);
}
Методы, формирующие вид шрифтов, должны возвращать объект класса FontUIResource, для создания которого есть два конструктора:
FontUIResource(Font);
FontUIResource(String name, int style, int size);
Метод, создающий системный шрифт, может выглядеть так:
public FontUIResource getSystemTextFont(){
return FontUIResource("Times New Roman", Font.PLAIN, 10);
}
Хотя тема определяет главным образом шрифты, их цвета и цвета фона, но она может изменить любые свойства Java L&F. Для этого в классе MetalTheme есть метод
addCustomEntriesToTable (UIDefaults ), позволяющий занести в модель данных UIDefaults не только новые цвета и шрифты, но и какие-нибудь другие свойства. Достаточно переопределить этот метод в расширении класса MetalTheme, чтобы задать изменение свойств. В листинге 17.4 приведен пример метода, устанавливающего новые рамки текстовых полей.
После того как написан класс темы, расширяющий класс MetalTheme, класс DefaultMetalTheme или класс OceanTheme, надо установить новую тему статическим методом setCurrentTheme (MetalTheme) класса MetalLookAndFeel, как показано в листинге 17.4. В нем заданы только серые цвета, что удобно для печати иллюстраций в книге. Результат показан на рис. 17.4.
import java.awt.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.plaf.*; import javax.swing.plaf.metal.*;
public class ContTheme extends JFrame{
ContTheme(){
super(" Окно с серой темой");
JDesktopPane dp = new JDesktopPane(); setLayout(new FlowLayout()); setContentPane(dp);
JInternalFrame ifr1 = new JInternalFrame(
" Окно GrayMetalTheme", true, true, true, true); ifr1.getContentPane().setLayout(new FlowLayout()); i fr1.getContentPane().add(
new JLabelC^html^TO окно Java L&F
Cepan тема" )); ifr1.setBounds(0,0, 200,200); ifr1.setVisible(true); dp.add(ifr1); setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true);
public static void main(String[] args){
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true); MetalLookAndFeel.setCurrentTheme(new GrayMetalTheme()); new ContTheme();
}
}
class GrayMetalTheme extends DefaultMetalTheme{
public ColorUIResource getPrimary1(){ return getSecondary1();
}
public ColorUIResource getPrimary2(){ return getSecondary2();
}
public ColorUIResource getPrimary3(){ return getSecondary3();
}
public void addCustomEntriesToTable(UIDefaults table){ super.addCustomEntriesToTable(table);
BorderUIResource b = new BorderUIResource( new CompoundBorder(
new LineBorder(Color.gray), new LineBorder(Color.white))); table.put("TextField.border", b); table.put("PasswordField.border", b); table.put("TextArea.border", b); table.put("TextPane.font", b);
}
}
Рис. 17.4. Окна, оформленные в "серой" теме |
java -jar MetalWorks.jar
Вопросы для самопроверки
1. Что такое вид и поведение (Look and Feel) приложения?
2. Что такое тема (theme) оформления и чем она отличается от вида и поведения приложения?
3. Какие L&F входят в стандартную поставку графической библиотеки Swing?
4. Можно ли стандартными средствами Swing создать собственный L&F?
5. Какие темы предлагает Java L&F?
6. Можно ли создать свои темы в Java L&F?
ГЛАВА 18