Я пишу GUI Builder и хочу, чтобы пользователь мог изменить LookAndFeel графического интерфейса, который он создает. LookAndFeel следует изменять только для компонентов внутри области редактора. Остальная часть приложения должна оставаться с SystemLookAndFeel.
Большая проблема заключается в том, что LookAndFeel реализован как Singleton, и многократное изменение LookAndFeel во время приложения вызывает множество ошибок.
Я начал экспериментировать с кнопками: я попытался установить для ButtonUI значение MetalButtonUI, но они не отображались должным образом. Итак, я отладил метод paintComponent по умолчанию для JButton и увидел, что ButtonUI все еще нуждается в UIDefaults, которые не были полными, поскольку они были WindowsUIDefaults.
Мое текущее решение состоит в том, чтобы установить MetalLookAndFeel, сохранить UIDefaults, затем изменить LookAndFeel на SystemLookAndFeel и также сохранить эти UIDefaults, и каждый раз, когда я рисую кнопку внутри редактора, я меняю UIDefaults.
Вот код:
public class MainClass{
public static Hashtable systemUI;
public static Hashtable metalUI;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
metalUI = new Hashtable();
metalUI.putAll(UIManager.getDefaults());
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
systemUI = new Hashtable();
systemUI.putAll(UIManager.getDefaults());
} catch (Exception e) {
e.printStackTrace();
}
/* ...
* Generate JFrame and other stuff
* ...
*/
}
});
}
}
public class MyButton extends JButton {
public MyButton(String text) {
super(text);
ui = MetalButtonUI.createUI(this);
}
@Override public synchronized void paintComponent(Graphics g) {
UIManager.getDefaults().putAll(Application.metalUI);
super.paintComponent(g);
UIManager.getDefaults().putAll(Application.systemUI);
}
}
Как вы можете видеть здесь, результат довольно хороший. Слева показан MetalLaF, как он должен выглядеть, а справа — как он отображается в моем приложении. Градиент прорисован правильно, а Граница и Шрифт — нет.
Поэтому мне нужно знать, почему не все элементы LaF применяются к кнопке и как это исправить.
-
Изменить: я нашел некрасивое решение. LookAndFeel необходимо изменить перед созданием Button, потому что объект Graphics будет создан в Конструкторе. После вызова суперконструктора вы можете изменить LookAndFeel обратно.
Затем вам нужно изменить LookAndFeel до того, как компонент будет окрашен/перекрашен. Единственный момент, когда я заработал, был в paintComponent до вызова super. Вы можете изменить его обратно после вызова super.
Код:
import javax.swing.*;
import javax.swing.plaf.metal.MetalButtonUI;
import java.awt.*;
public class MainClass {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(f.getExtendedState() | JFrame.EXIT_ON_CLOSE);
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception ex) {
}
f.setLayout(new FlowLayout());
f.add(new MyButton("MetalButton"));
f.add(new JButton("SystemButton"));
f.pack();
f.setVisible(true);
}
});
}
}
class MyButton extends JButton {
public MyButton(String text) {
super(text);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
ui = MetalButtonUI.createUI(this);
}
@Override public synchronized void paintComponent(Graphics g) {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
super.paintComponent(g);
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
}
}
Редактировать 2: Не используйте это без крайней необходимости!
Это крайне нестабильно. После долгих тестов я обнаружил, что в нем меньше ошибок, когда я просто поменял местами UIDefaults вместо всего LookAndFeel, но я не рекомендую делать ничего из этого.
Редактировать 3. Лучшим решением, которое я нашел, было использование JavaFX в качестве графического интерфейса. Я вставил узел свинга в область редактора и теперь могу изменять внешний вид компонентов свинга так часто, как захочу, без каких-либо заметных побочных эффектов.
Rant: Если вы всегда можете выбрать JavaFX, если хотите изменить стиль своего приложения. CSS делает это максимально простым без каких-либо побочных эффектов!
Большое спасибо
Джонни
paintComponent
, это просто вызывает бесконечные проблемы. Вместо этого при необходимости используйтеUIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
иSwingUtiltiies#updateComponentTreeUI
и передайте в контейнер самого верхнего уровня. - person MadProgrammer   schedule 10.11.2015