java Swing JTable行边框在应用Nimbus LookAndFeel后消失
我有一个Java8程序,其中-Parent是一个JFrame,它有菜单、几个按钮、一个文本字段和一个具有固定数量的不可编辑行的JTable。无法动态更改行数和数据数
菜单中有UIManager.getInstalledLookAndFeels()
最初,JTable行边框可见
如果LookAndFeel更改为[Nimbus javax.swing.plaf.nimbus.NimbusLookAndFeel]
,然后尝试任何其他LookAndFeel,则行边框将消失
我正在使用SwingUtilities.updateComponentTreeUI(parentFrame)
应用LnF。LnF应用于包括JTable在内的所有组件,但一旦Nimbus LnF应用,然后选择任何其他LnF,rows border就消失了
作为一种选择repaint()
没有任何区别
形象地
- (1) 当程序启动时,行边框是可见的
- (2) 当应用Nimbus LnF时
- (3) LnF已更改为“金属”,但行边框不可见
请建议
示例代码:
package com.sv.runcmd;
import com.sv.core.logger.MyLogger;
import com.sv.swingui.SwingUtils;
import com.sv.swingui.component.AppExitButton;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static com.sv.core.Constants.SP_DASH_SP;
import static com.sv.swingui.UIConstants.EMPTY_BORDER;
public class LnFExample extends JFrame {
public static void main(String[] args) {
new LnFExample().initComponents();
}
private static final String APP_TITLE = "LnF";
private DefaultTableModel model;
private JTable tblCommands;
private JMenuBar mbarSettings;
public LnFExample() {
super(APP_TITLE);
SwingUtilities.invokeLater(this::initComponents);
}
/**
* This method initializes the form.
*/
private void initComponents() {
Container parentContainer = getContentPane();
parentContainer.setLayout(new BorderLayout());
JButton btnExit = new AppExitButton(true);
createTable();
JPanel topPanel = new JPanel(new GridLayout(2, 1));
topPanel.add(btnExit);
topPanel.setBorder(EMPTY_BORDER);
JPanel lowerPanel = new JPanel(new BorderLayout());
JScrollPane jspCmds = new JScrollPane(tblCommands);
lowerPanel.add(jspCmds);
parentContainer.add(topPanel, BorderLayout.NORTH);
parentContainer.add(lowerPanel, BorderLayout.CENTER);
btnExit.addActionListener(evt -> exitForm());
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
exitForm();
}
});
createAppMenu();
setPosition();
}
private final MyLogger logger = MyLogger.createLogger("rc.log");
private void createAppMenu() {
mbarSettings = new JMenuBar();
JMenu menuSettings = new JMenu("Settings");
menuSettings.add(getThemesMenu());
mbarSettings.add(menuSettings);
setJMenuBar(mbarSettings);
}
public UIManager.LookAndFeelInfo[] getAvailableLAFs() {
return UIManager.getInstalledLookAndFeels();
}
public JMenu getThemesMenu() {
JMenu menu = new JMenu("Theme");
int i = 'a';
int x = 0;
for (UIManager.LookAndFeelInfo l : getAvailableLAFs()) {
JMenuItem mi = new JMenuItem((char) i + SP_DASH_SP + l.getName());
if (i <= 'z') {
mi.setMnemonic(i);
}
int finalX = x;
mi.addActionListener(e -> applyTheme(finalX, l));
menu.add(mi);
i++;
x++;
}
return menu;
}
UIManager.LookAndFeelInfo themeToApply;
public void applyTheme(int idx, UIManager.LookAndFeelInfo lnf) {
themeToApply = lnf;
SwingUtilities.invokeLater(this::applyLnF);
}
public void applyLnF() {
try {
UIManager.setLookAndFeel(themeToApply.getClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
SwingUtilities.updateComponentTreeUI(this);
}
private void createTable() {
model = SwingUtils.getTableModel(new String[]{"Col1"});
createRows();
Border borderBlue = new LineBorder(Color.BLUE, 1);
tblCommands = new JTable(model);
}
private void createRows() {
for (int i = 1; i <= 10; i++) {
model.addRow(new String[]{"Row- " + i});
}
}
private void setPosition() {
// Setting to right most position
pack();
GraphicsConfiguration config = getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
int x = bounds.x + bounds.width - insets.right - getWidth();
int y = bounds.y + insets.top + 10;
setLocation(x, y);
setVisible(true);
}
/**
* Exit the Application
*/
private void exitForm() {
setVisible(false);
dispose();
logger.dispose();
System.exit(0);
}
}
# 1 楼答案
在Windows10上使用JDK11也可以得到同样的结果
我怀疑问题出在
UIResource
接口上。注意,这只是一个标记接口,没有实际的实现方法我的理解是,这个接口应该在Swing组件的属性上实现。例如关于字体、边框、颜色、图标属性的各种组件
然后,当您调用
SwingUtilities.updateComponentTreeUI(parentFrame)
时,实现UIResource
的所有属性都将替换为新LAF中相应的属性签出UIManager Defaults。它将列出每个Swing组件的所有属性
您将看到,对于大多数LAF,属性位于
FontUIResource
或ColorUIResource
等实例中然而,对于Nimbus LAF,许多属性缺少“…UIResource”
所以我建议这是一个灵气问题,我不知道如何解决它
编辑:
这是一个灵气问题
从上述链接下载代码,然后进行以下更改:
现在在非NImbus之间切换LAF,您将看到非NImbus LAF的默认表呈现器在类名中包含UIResource,这将向我指示它们实现UIResource接口
现在切换到Nimbus LAF。呈现程序在类名中没有UIResource
现在切换回任何其他LAF,渲染不正确,因为Nimbus渲染器尚未替换为正确的LAF渲染器
注意,这不是LAF问题。它就是这样设计的。这允许您创建可在所有LAF中使用的自定义渲染器(当然,除非您使用UIResource接口标记渲染器)
出于某些原因,Nimbus开发人员似乎没有使用UIResource接口标记渲染器,因此一旦设置了渲染器,它们就不会随LAF的其余部分而更改
因此,另一个解决方案是创建一个“包装器”呈现器,它只包装一个默认呈现器并调用其默认呈现器逻辑,但也将实现UIResource接口。然后需要用包装器渲染器替换每个默认的Nimbus渲染器
# 2 楼答案
我收到了一封邮件,回复了我从甲骨文公司提出的关于这个问题的bug,他们也能够将它复制为bug。 JDK-8258567
链接:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8258567
# 3 楼答案
正如评论和@camickr所讨论的,这可能是Nimbus或Metal LaF的一个bug
不过,我已经制定了一个解决方案,您现在可以使用
基本上,我覆盖了
JTable
的prepareRenderer
,并检查是否正在使用金属LaF(并且它在启动时未设置,或者它将在JTable
周围绘制两个边框),如果满足这些条件,我们只需将每行的边框设置为new MetalBorders.TableHeaderBorder()
:TestApp。java: