Java: 系统 L&F 仅第二次正确显示
Java: system L&F only appearing correctly for second time
我正在使用这段代码来设置程序的外观:
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());
我只在程序运行时显示的 JPanel(在 JFrame 内)上使用系统外观。我第一次显示 JPanel 时,L&F 是错误的(看起来它是针对旧版本的 windows 或其他内容,与我之前在其他组件上使用的不同)。我隐藏 JPanel 然后再次打开它,L&F 现在是正确的。
可悲的是,我无法生成可重现的示例,但问题仍然存在于原始程序中。
它由多个类组成,并且有一个面向对象编写的UI,这是负责有问题的JPanel的代码:
package almanah;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SeriesProperties extends JPanel {
JButton button_close = new JButton();
JPanel container = new JPanel();
JScrollPane scPane = new JScrollPane(container);
public SeriesProperties(ItemLib lib) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedLookAndFeelException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
}
this.setLayout(new BorderLayout());
this.add(button_close, BorderLayout.EAST);
button_close.addActionListener((e) -> {
Almanah.frame.swapToTiles();
});
scPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(scPane, BorderLayout.CENTER);
//container
container.setBackground(Color.red);
container.setLayout(new WrapLayout());
for (int i = 0; i < 100; i++) {
JPanel panel=new JPanel();
panel.setPreferredSize(new Dimension(100, 100));
container.add(panel);
}
this.updateUI();
}
}
在创建任何 UI 元素之前先确定外观状态。在运行时切换 L&F 的能力实际上是一种副作用,通常不鼓励这样做,因为它从来都不是系统的一个特性。
相反,可能在您的 main
方法中,设置外观,然后在其后构建您的 UI。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame();
// I don't know what ItemLib, as the source is incomplete
frame.add(new SeriesProperties(...));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SeriesProperties extends JPanel {
JButton button_close = new JButton();
JPanel container = new JPanel();
JScrollPane scPane = new JScrollPane(container);
public SeriesProperties(ItemLib lib) {
this.setLayout(new BorderLayout());
this.add(button_close, BorderLayout.EAST);
button_close.addActionListener((e) -> {
// This seems like a bad idea
// You should consider using a observer/delegate
// pattern instead, reducing the coupling of your code
Almanah.frame.swapToTiles();
});
scPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(scPane, BorderLayout.CENTER);
//container
container.setBackground(Color.red);
container.setLayout(new WrapLayout());
for (int i = 0; i < 100; i++) {
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(100, 100));
container.add(panel);
}
//this.updateUI();
}
}
}
解决方案:
Error13660,你的问题解决了吗?这是我改变外观的解决方案。
并查看 This answer。
package snippet;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
var frame = new JFrame();
var panel = new JPanel();
{ // Button 0: onclick change theme 0
var btn = new JButton("Theme 0");
btn.addActionListener((e) -> {
System.out.println("actionPerformed: change theme 0");
Test.changeLaF(frame, "javax.swing.plaf.metal.MetalLookAndFeel");
});
panel.add(btn);
}
{ // Button 1: onclick change theme 1
var btn = new JButton("Theme 1");
btn.addActionListener((e) -> {
System.out.println("actionPerformed: change theme 1");
Test.changeLaF(frame, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
});
panel.add(btn);
}
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public static final void changeLaF(final JFrame frame, final String nameLookAndFeel) {
try {
UIManager.setLookAndFeel(nameLookAndFeel);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack ();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
24-02-21 之前的版本:
您尝试过使用SwingUtilities.updateComponentTreeUI(frame);
Changing the Look and Feel After Startup: You can change the L&F with setLookAndFeel even after the program's GUI is visible. To make existing components reflect the new L&F, invoke the SwingUtilities updateComponentTreeUI method once per top-level container. Then you might wish to resize each top-level container to reflect the new sizes of its contained components. For example:
UIManager.setLookAndFeel(lnfName);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack();
更新:
/!\ 如果成功加载 L&F,所有组件都使用该 L&F。 lookandfeel/plaf#Steps
我正在使用这段代码来设置程序的外观:
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());
我只在程序运行时显示的 JPanel(在 JFrame 内)上使用系统外观。我第一次显示 JPanel 时,L&F 是错误的(看起来它是针对旧版本的 windows 或其他内容,与我之前在其他组件上使用的不同)。我隐藏 JPanel 然后再次打开它,L&F 现在是正确的。
可悲的是,我无法生成可重现的示例,但问题仍然存在于原始程序中。
它由多个类组成,并且有一个面向对象编写的UI,这是负责有问题的JPanel的代码:
package almanah;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SeriesProperties extends JPanel {
JButton button_close = new JButton();
JPanel container = new JPanel();
JScrollPane scPane = new JScrollPane(container);
public SeriesProperties(ItemLib lib) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedLookAndFeelException ex) {
Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
}
this.setLayout(new BorderLayout());
this.add(button_close, BorderLayout.EAST);
button_close.addActionListener((e) -> {
Almanah.frame.swapToTiles();
});
scPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(scPane, BorderLayout.CENTER);
//container
container.setBackground(Color.red);
container.setLayout(new WrapLayout());
for (int i = 0; i < 100; i++) {
JPanel panel=new JPanel();
panel.setPreferredSize(new Dimension(100, 100));
container.add(panel);
}
this.updateUI();
}
}
在创建任何 UI 元素之前先确定外观状态。在运行时切换 L&F 的能力实际上是一种副作用,通常不鼓励这样做,因为它从来都不是系统的一个特性。
相反,可能在您的 main
方法中,设置外观,然后在其后构建您的 UI。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame();
// I don't know what ItemLib, as the source is incomplete
frame.add(new SeriesProperties(...));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SeriesProperties extends JPanel {
JButton button_close = new JButton();
JPanel container = new JPanel();
JScrollPane scPane = new JScrollPane(container);
public SeriesProperties(ItemLib lib) {
this.setLayout(new BorderLayout());
this.add(button_close, BorderLayout.EAST);
button_close.addActionListener((e) -> {
// This seems like a bad idea
// You should consider using a observer/delegate
// pattern instead, reducing the coupling of your code
Almanah.frame.swapToTiles();
});
scPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(scPane, BorderLayout.CENTER);
//container
container.setBackground(Color.red);
container.setLayout(new WrapLayout());
for (int i = 0; i < 100; i++) {
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(100, 100));
container.add(panel);
}
//this.updateUI();
}
}
}
解决方案:
Error13660,你的问题解决了吗?这是我改变外观的解决方案。
并查看 This answer。
package snippet;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
var frame = new JFrame();
var panel = new JPanel();
{ // Button 0: onclick change theme 0
var btn = new JButton("Theme 0");
btn.addActionListener((e) -> {
System.out.println("actionPerformed: change theme 0");
Test.changeLaF(frame, "javax.swing.plaf.metal.MetalLookAndFeel");
});
panel.add(btn);
}
{ // Button 1: onclick change theme 1
var btn = new JButton("Theme 1");
btn.addActionListener((e) -> {
System.out.println("actionPerformed: change theme 1");
Test.changeLaF(frame, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
});
panel.add(btn);
}
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public static final void changeLaF(final JFrame frame, final String nameLookAndFeel) {
try {
UIManager.setLookAndFeel(nameLookAndFeel);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack ();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
24-02-21 之前的版本:
您尝试过使用SwingUtilities.updateComponentTreeUI(frame);
Changing the Look and Feel After Startup: You can change the L&F with setLookAndFeel even after the program's GUI is visible. To make existing components reflect the new L&F, invoke the SwingUtilities updateComponentTreeUI method once per top-level container. Then you might wish to resize each top-level container to reflect the new sizes of its contained components. For example:
UIManager.setLookAndFeel(lnfName);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack();
更新: /!\ 如果成功加载 L&F,所有组件都使用该 L&F。 lookandfeel/plaf#Steps