Java 每个组件定制的 Nimbus 外观和感觉 ("Nimbus.Overrides") - 其他实例也受到影响
Java Nimbus Look and Feel per component customization ("Nimbus.Overrides") - other instances affected too
我是Java Nimbus 外观 的新手。我正在尝试使用 Nimbus 的功能 自定义单个组件实例 使用 putClientProperty("Nimbus.Overrides", overrides)
: https://docs.oracle.com/javase/8/docs/api/javax/swing/plaf/nimbus/package-summary.html
我遇到了以下问题(错误?):
不幸的是,"Nimbus.Overrides" 值显然不仅会影响明确设置的组件对象,还会影响其他对象。
似乎自定义属性 "inherited" 以某种方式与其他(后来 "styled",显然不是以前)相同类型的实例。我需要确保只对一个单独的实例进行更改(不影响任何其他对象)。示例 - 使用了 JButton
,但遇到了同样的问题,例如JTabbedPane
和自定义画家:
button 1
- 属性 A(内容边距)自定义。
button 2
- 仅 属性 B(字体)自定义 - 但 属性 A 也可见(更大的 manrgins),"somehow inherited" 来自 button 1
.
button 3
- 没有 属性 自定义(使用空 属性 地图) - 两个变化 (A+B) 似乎都是从 [= 继承的(更大的边距,更大的字体) 20=] 和 button2
button 4
- JButton 的默认外观(无自定义)
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.UIDefaults;
public class NimbusPerComponentTest extends JFrame {
public NimbusPerComponentTest() {
super("Test");
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
// 4 buttons to test
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JButton button3 = new JButton("Button 3");
JButton button4 = new JButton("Button 4");
add(button1);
add(button2);
add(button3);
add(button4);
pack();
// style
// button 1
UIDefaults overrides1 = new UIDefaults();
overrides1.put("Button.contentMargins", new Insets(10,10,10,10));
button1.putClientProperty("Nimbus.Overrides", overrides1);
// button 2
UIDefaults overrides2 = new UIDefaults();
overrides2.put("Button.font", new Font("Sans Serif", Font.BOLD, 15));
button2.putClientProperty("Nimbus.Overrides", overrides2);
// button 3
UIDefaults overrides3 = new UIDefaults();
// nothing = left empty
button3.putClientProperty("Nimbus.Overrides", overrides3);
// button 4
// no styling
}
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NimbusPerComponentTest.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NimbusPerComponentTest().setVisible(true);
}
});
}
}
知道为什么吗?我错过了什么?任何优雅的解决方法?
(Java 8, Windows 10)
编辑
经过一些回答的启发:
尝试在我的原始代码末尾重置外观(为 null 并再次返回 Nimbus,包括 SwingUtilities.updateComponentTreeUI),唯一的结果:
现在即使是 button4 也画错了(边距和字体都改变了),尽管通用默认值从未被触及过......很奇怪。
编辑 2
我设法找到了 单行解决方法 /hack。请参阅我自己对我的问题的回答...
如果我改变 UIDefaults
的创建,它对我有用。而不是
UIDefaults overrides1 = new UIDefaults();
我用过
UIDefaults overrides1 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
对 overrides2
和 overrides3
也这样做:
// style
// button 1
UIDefaults overrides1 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
overrides1.put("Button.contentMargins", new Insets(10, 10, 10, 10));
button1.putClientProperty("Nimbus.Overrides", overrides1);
// button 2
UIDefaults overrides2 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
overrides2.put("Button.font", new Font("Sans Serif", Font.BOLD, 15));
button2.putClientProperty("Nimbus.Overrides", overrides2);
// button 3
UIDefaults overrides3 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
// nothing = left empty
button3.putClientProperty("Nimbus.Overrides", overrides3);
// button 4
// no styling
这会在我的机器上产生以下输出:
请注意,以这种方式创建的 UIDefaults
大小可能会很大。
我怀疑这是由于 javax.swing.plaf.nimbus.NimbusLookAndFeel 的 shouldUpdateStyleOnEvent
方法中的错误所致。来自 the source:
protected boolean shouldUpdateStyleOnEvent(PropertyChangeEvent ev) {
String eName = ev.getPropertyName();
// These properties affect style cached inside NimbusDefaults (6860433)
if ("name" == eName ||
"ancestor" == eName ||
"Nimbus.Overrides" == eName ||
"Nimbus.Overrides.InheritDefaults" == eName ||
"JComponent.sizeVariant" == eName) {
JComponent c = (JComponent) ev.getSource();
defaults.clearOverridesCache(c);
return true;
}
return super.shouldUpdateStyleOnEvent(ev);
}
不用说,这不是比较字符串的有效方法。我无法在错误数据库中找到任何关于此的信息;也许以后有时间,我会提交错误报告。
经过相当长时间的调试,我似乎找到了一个单行解决方法/hack,这也可能暗示了问题的原因:
在使用 putClientProperty("Nimbus.Overrides", overrides)
设置每个组件的样式后,您可以立即通过以下代码阻止 "inheriting" 属性随后设置样式的组件:
button1.putClientProperty("Nimbus.Overrides", overrides1);
UIManager.getDefaults().putDefaults(new Object[0]);
// add after each "styling"
// - clears the compiledDefaults in NimbusLookAndFeel
它有什么作用?
在功能上,什么都没有(empy array = nothing put),但它触发了 PropertyChange 事件(参见来源 here), which is listened in DefaultsListener
private class in NimbusLookAndFeel
class (see source here),这是我设法找到 清除 compiledDefaults
NimbusLookAndFeel
中的缓存 (?),这在我看来是导致问题的原因:
if ("UIDefaults".equals(key)) {
compiledDefaults = null;
}
我在 getDefaultsForPrefix
NimbusLookAndFeel
class 中调试 getDefaultsForPrefix
方法(使用 compiledDefaults
)时注意到了这个问题 - 请参阅来源 here。 对于后来的组件,它不仅返回 "real defaults",而且出于某种原因还返回设置为先前组件的自定义属性。
澄清一下:我完全是个业余爱好者,对 Nimbus classes 的细节或架构没有详细的知识或了解。我可能是错的,但解决方案对我有用...
是否因为某些原因不适合?有什么风险吗?真的是bug吗?
一道题:
我注意到如果我 pack()
我的框架 之前 使用 putClientProperty 为组件设置样式(如我问题中的代码),但是 在之后,解决方案不起作用(没有任何帮助)...
我是Java Nimbus 外观 的新手。我正在尝试使用 Nimbus 的功能 自定义单个组件实例 使用 putClientProperty("Nimbus.Overrides", overrides)
: https://docs.oracle.com/javase/8/docs/api/javax/swing/plaf/nimbus/package-summary.html
我遇到了以下问题(错误?):
不幸的是,"Nimbus.Overrides" 值显然不仅会影响明确设置的组件对象,还会影响其他对象。
似乎自定义属性 "inherited" 以某种方式与其他(后来 "styled",显然不是以前)相同类型的实例。我需要确保只对一个单独的实例进行更改(不影响任何其他对象)。示例 - 使用了 JButton
,但遇到了同样的问题,例如JTabbedPane
和自定义画家:
button 1
- 属性 A(内容边距)自定义。button 2
- 仅 属性 B(字体)自定义 - 但 属性 A 也可见(更大的 manrgins),"somehow inherited" 来自button 1
.button 3
- 没有 属性 自定义(使用空 属性 地图) - 两个变化 (A+B) 似乎都是从 [= 继承的(更大的边距,更大的字体) 20=] 和button2
button 4
- JButton 的默认外观(无自定义)import java.awt.FlowLayout; import java.awt.Font; import java.awt.Insets; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.UIDefaults; public class NimbusPerComponentTest extends JFrame { public NimbusPerComponentTest() { super("Test"); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setLayout(new FlowLayout()); // 4 buttons to test JButton button1 = new JButton("Button 1"); JButton button2 = new JButton("Button 2"); JButton button3 = new JButton("Button 3"); JButton button4 = new JButton("Button 4"); add(button1); add(button2); add(button3); add(button4); pack(); // style // button 1 UIDefaults overrides1 = new UIDefaults(); overrides1.put("Button.contentMargins", new Insets(10,10,10,10)); button1.putClientProperty("Nimbus.Overrides", overrides1); // button 2 UIDefaults overrides2 = new UIDefaults(); overrides2.put("Button.font", new Font("Sans Serif", Font.BOLD, 15)); button2.putClientProperty("Nimbus.Overrides", overrides2); // button 3 UIDefaults overrides3 = new UIDefaults(); // nothing = left empty button3.putClientProperty("Nimbus.Overrides", overrides3); // button 4 // no styling } public static void main(String args[]) { try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(NimbusPerComponentTest.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new NimbusPerComponentTest().setVisible(true); } }); } }
知道为什么吗?我错过了什么?任何优雅的解决方法? (Java 8, Windows 10)
编辑
经过一些回答的启发:
尝试在我的原始代码末尾重置外观(为 null 并再次返回 Nimbus,包括 SwingUtilities.updateComponentTreeUI),唯一的结果:
现在即使是 button4 也画错了(边距和字体都改变了),尽管通用默认值从未被触及过......很奇怪。
编辑 2
我设法找到了 单行解决方法 /hack。请参阅我自己对我的问题的回答...
如果我改变 UIDefaults
的创建,它对我有用。而不是
UIDefaults overrides1 = new UIDefaults();
我用过
UIDefaults overrides1 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
对 overrides2
和 overrides3
也这样做:
// style
// button 1
UIDefaults overrides1 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
overrides1.put("Button.contentMargins", new Insets(10, 10, 10, 10));
button1.putClientProperty("Nimbus.Overrides", overrides1);
// button 2
UIDefaults overrides2 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
overrides2.put("Button.font", new Font("Sans Serif", Font.BOLD, 15));
button2.putClientProperty("Nimbus.Overrides", overrides2);
// button 3
UIDefaults overrides3 = (UIDefaults) UIManager.getLookAndFeelDefaults().clone();
// nothing = left empty
button3.putClientProperty("Nimbus.Overrides", overrides3);
// button 4
// no styling
这会在我的机器上产生以下输出:
请注意,以这种方式创建的 UIDefaults
大小可能会很大。
我怀疑这是由于 javax.swing.plaf.nimbus.NimbusLookAndFeel 的 shouldUpdateStyleOnEvent
方法中的错误所致。来自 the source:
protected boolean shouldUpdateStyleOnEvent(PropertyChangeEvent ev) {
String eName = ev.getPropertyName();
// These properties affect style cached inside NimbusDefaults (6860433)
if ("name" == eName ||
"ancestor" == eName ||
"Nimbus.Overrides" == eName ||
"Nimbus.Overrides.InheritDefaults" == eName ||
"JComponent.sizeVariant" == eName) {
JComponent c = (JComponent) ev.getSource();
defaults.clearOverridesCache(c);
return true;
}
return super.shouldUpdateStyleOnEvent(ev);
}
不用说,这不是比较字符串的有效方法。我无法在错误数据库中找到任何关于此的信息;也许以后有时间,我会提交错误报告。
经过相当长时间的调试,我似乎找到了一个单行解决方法/hack,这也可能暗示了问题的原因:
在使用 putClientProperty("Nimbus.Overrides", overrides)
设置每个组件的样式后,您可以立即通过以下代码阻止 "inheriting" 属性随后设置样式的组件:
button1.putClientProperty("Nimbus.Overrides", overrides1);
UIManager.getDefaults().putDefaults(new Object[0]);
// add after each "styling"
// - clears the compiledDefaults in NimbusLookAndFeel
它有什么作用?
在功能上,什么都没有(empy array = nothing put),但它触发了 PropertyChange 事件(参见来源 here), which is listened in DefaultsListener
private class in NimbusLookAndFeel
class (see source here),这是我设法找到 清除 compiledDefaults
NimbusLookAndFeel
中的缓存 (?),这在我看来是导致问题的原因:
if ("UIDefaults".equals(key)) {
compiledDefaults = null;
}
我在 getDefaultsForPrefix
NimbusLookAndFeel
class 中调试 getDefaultsForPrefix
方法(使用 compiledDefaults
)时注意到了这个问题 - 请参阅来源 here。 对于后来的组件,它不仅返回 "real defaults",而且出于某种原因还返回设置为先前组件的自定义属性。
澄清一下:我完全是个业余爱好者,对 Nimbus classes 的细节或架构没有详细的知识或了解。我可能是错的,但解决方案对我有用...
是否因为某些原因不适合?有什么风险吗?真的是bug吗?
一道题:
我注意到如果我 pack()
我的框架 之前 使用 putClientProperty 为组件设置样式(如我问题中的代码),但是 在之后,解决方案不起作用(没有任何帮助)...