swing jtable行边框在应用nimbus lookandfeel后消失

cclgggtu  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(394)

我有一个Java8程序,其中-parent是一个jframe,它有菜单、几个按钮、一个文本字段和一个具有固定数量的不可编辑行的jtable。不能动态更改行数和数据数。
菜单中有 UIManager.getInstalledLookAndFeels() 最初jtable行边框可见
如果外观和感觉改变为 [Nimbus javax.swing.plaf.nimbus.NimbusLookAndFeel] ,然后尝试任何其他lookandfeel,行边框将消失。
我正在使用 SwingUtilities.updateComponentTreeUI(parentFrame) 应用lnf。lnf应用于包括jtable在内的所有组件,但一旦nimbus lnf应用,然后选择任何其他lnf,行边界就消失了。
作为一种选择 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);
    }

}
hkmswyz6

hkmswyz61#

正如评论和@camickr所讨论的,这可能是一个带有nimbus或metal laf的bug。
不过,我已经做了一个解决办法,你可以使用现在。
本质上,我超越了 prepareRendererJTable 并检查是否正在使用金属laf(并且它没有在启动时设置,或者它将在面板周围绘制两个边框) JTable )如果满足这些条件,我们只需将每行的边界设置为 new MetalBorders.TableHeaderBorder() :

@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
    JComponent component = (JComponent) super.prepareRenderer(renderer, row, column);
    if (UIManager.getLookAndFeel().getName().equals("Metal") && !wasMetalOnStartup) {
        component.setBorder(new MetalBorders.TableHeaderBorder());
    }
    return component;
}


testapp.java文件:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.UUID;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.metal.MetalBorders;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.OceanTheme;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class TestApp {

    private JTable table;
    private boolean wasMetalOnStartup = false;

    public TestApp() {
        setNimbusLookAndFeel();
        initComponents();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestApp::new);
    }

    private void initComponents() {
        wasMetalOnStartup = UIManager.getLookAndFeel().getName().equals("Metal");
        JFrame frame = new JFrame("TestApp");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.setBorder(new EmptyBorder(10, 10, 10, 10));

        // setup chmahe LaF button
        JButton refreshButton = new JButton("Change L&F");
        refreshButton.addActionListener((ActionEvent e) -> {
            try {
                if (!UIManager.getLookAndFeel().getName().equals("Nimbus")) {
                    setNimbusLookAndFeel();
                } else {
                    setMetalLookAndFeel();
                }
                SwingUtilities.updateComponentTreeUI(frame);
            } catch (Exception ex) {

            }
        });

        table = new JTable() {
            private static final long serialVersionUID = 1L;

            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }

            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
                JComponent component = (JComponent) super.prepareRenderer(renderer, row, column);
                if (UIManager.getLookAndFeel().getName().equals("Metal") && !wasMetalOnStartup) {
                    component.setBorder(new MetalBorders.TableHeaderBorder());
                }
                return component;
            }

        };

        // setup JTable and custom table model with intial data
        Object[][] data = getRandomData();
        String[] columnNames = {"Random Data"};
        DefaultTableModel model = new DefaultTableModel(data, columnNames);
        table.setModel(model);
        table.getColumnModel().getColumn(0).setPreferredWidth(300);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());

        // add components to the panel
        JScrollPane pane = new JScrollPane(table);
        panel.add(pane, BorderLayout.CENTER);
        panel.add(refreshButton, BorderLayout.SOUTH);

        frame.add(panel);
        frame.pack();
        frame.setVisible(true);

    }

    private void setNimbusLookAndFeel() {
        try {
            UIManager.setLookAndFeel(new NimbusLookAndFeel());
            wasMetalOnStartup = false;
        } catch (Exception ex) {

        }
    }

    private void setMetalLookAndFeel() {
        try {
            MetalLookAndFeel.setCurrentTheme(new OceanTheme());
            UIManager.setLookAndFeel(new MetalLookAndFeel());
            wasMetalOnStartup = false;
        } catch (Exception ex) {

        }
    }

    private Object[][] getRandomData() {
        Object[][] data = {{UUID.randomUUID()}, {UUID.randomUUID()}, {UUID.randomUUID()}, {UUID.randomUUID()}};
        return data;
    }
}
8fq7wneg

8fq7wneg2#

在windows10上使用jdk11也得到了相同的结果。
我怀疑问题出在 UIResource 接口。注意,这只是一个标记接口,没有实际的方法来实现。
我的理解是,这个接口应该在swing组件的属性上实现。例如关于字体、边框、颜色、图标属性的各种组件。
当你调用 SwingUtilities.updateComponentTreeUI(parentFrame) 实现 UIResource 将替换为新laf的相应属性。
查看uimanager默认值。它将列出每个swing组件的所有属性。
您将看到,对于大多数laf,属性都是示例中的 FontUIResource ,或 ColorUIResource 等等。
然而,对于nimbus laf,许多属性缺少“…uiresource”。
所以我建议这是一个光环问题,我不知道如何解决它。
编辑:
调试时很难判断这是否是金属laf问题。。。或是灵气问题
这是一个灵气问题。
从上述链接下载代码,然后进行以下更改:

SwingUtilities.updateComponentTreeUI( rootPane );
System.out.println(table.getDefaultRenderer(Object.class));

现在在非nimbus之间切换laf,您将看到非nimbus laf的默认表呈现器在类名中包含uiresource,这将向我指示它们实现uiresource接口。
现在切换到nimbus laf。呈现器在类名中没有uiresource。
现在切换回任何其他laf,渲染不正确,因为nimbus渲染器没有被正确的laf渲染器替换。
注意,这不是laf问题。它就是这样设计的。这允许您创建可在所有laf中使用的自定义呈现器(当然,除非您使用uiresource接口标记呈现器)。
出于某种原因,nimbus开发人员似乎没有用uiresource接口标记呈现器,因此一旦设置了呈现器,就不会用laf的其余部分更改它们。
因此,另一种解决方案是创建一个“ Package 器”呈现器,该呈现器只需 Package 一个默认呈现器并调用其默认呈现器逻辑,但也可以实现uiresource接口。然后需要用 Package 器渲染器替换每个默认的nimbus渲染器。

svgewumm

svgewumm3#

我收到了一封邮件,回复了我从甲骨文提出的关于这个问题的bug,他们也可以把它复制成bug。jdk-8258567。
链接:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=jdk-8258567

相关问题