设置Nimbus按钮的“背景”按钮

时间:2013-08-12 22:10:59

标签: java swing jbutton tablecellrenderer nimbus

我正在使用Nimbus Look and Feel开发应用程序。有一个表和一列包含按钮(使用Table Button Column from Rob Camick)。这确实有效,但结果并不像我预期的那样。我试图修复外观,但无济于事。

所以问题是:如何更改Nimbus按钮的“背景”(圆角矩形以外的区域)?最好以非黑客的方式: - )

使用默认的表列按钮,结果如下所示:

Buttons with incorrect background

正如您所看到的,背景(我的意思是按钮圆角矩形之外的区域)对于奇数(白色)行是错误的。产生此输出的代码是:

public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
    if (isSelected) {
        renderButton.setForeground(table.getSelectionForeground());
        renderButton.setBackground(table.getSelectionBackground());
    } else {
        renderButton.setForeground(table.getForeground());
        renderButton.setBackground(table.getBackground());
    }

    if (hasFocus) {
        renderButton.setBorder( focusBorder );
    } else {
        renderButton.setBorder( originalBorder );
    }

    // <snip some code>

    renderButton.setOpaque(true);

    return renderButton;
}

renderButton是默认JButton的实例。我已经尝试搞乱了按钮的背景颜色,但这并没有像我预期的那样有效:

        Color alternate = (Color)LookAndFeel.getDesktopPropertyValue("Table.alternateRowColor", Color.lightGray);
        Color normal = (Color)LookAndFeel.getDesktopPropertyValue("Table.background", Color.white);
        if (row % 2 == 0) {
            renderButton.setBackground(normal);
        } else {
            renderButton.setBackground(alternate);
        }

这会产生:

Also incorrect button background

所以这次在第一张图片中看起来不错的按钮现在很糟糕,反之亦然。按钮的内部背景(圆角矩形内的区域)似乎根据背景颜色属性(这是使用setBackground()调用真正修改的内容)具有正确的颜色。但外面的区域都是错的。好吧,让我们把两者结合起来:

        Color alternate = table.getBackground();
        Color normal = (Color)LookAndFeel.getDesktopPropertyValue("Table.background", Color.white);
        if (row % 2 == 0) {
            renderButton.setBackground(normal);
        } else {
            renderButton.setBackground(alternate);
        }

结果:

Still incorrect buttons

所以现在“背景”看起来确实正确,但按钮看起来不再像Nimbus按钮了。如何使“背景”具有正确的颜色,同时仍然看起来像Nimbus按钮?

2 个答案:

答案 0 :(得分:2)

下面是一个hacky方式,跟进@ Piro的建议:使用带按钮作为子组件的JPanel。鉴于我们并不真正想要触摸按钮的“内部”背景视觉效果,这本身就是一个好主意。

在强制Nimbus内部不使用 JPanel的默认背景来填充其区域但是使用给定面板实例的背景时,黑客就出现了这需要依赖于实现细节,特别是背景颜色的查找机制。这发生在SynthStyle.getColor():

// If the developer has specified a color, prefer it. Otherwise, get
// the color for the state.
Color color = null;
if (!id.isSubregion()) {
    if (type == ColorType.BACKGROUND) {
        color = c.getBackground();
    }
    ....
}

if (color == null || color instanceof UIResource) {
    // Then use what we've locally defined
    color = getColorForState(context, type);
}

翻译:它确实查询了实例的颜色,但如果实例颜色是UIResource则用默认值覆盖它 - 如果用作渲染器,通常就是这种情况。所以诀窍(由SynthBooleanRenderer尝试失败,但这是另一个故事;-)是使实例颜色成为UIResource。另一个怪癖是UIResource是必要的,以确保条纹颜色 - 不是UIResource类型,哈哈 - 应用...直观,不是它...

public class RendererPanel implements TableCellRenderer {

    private JComponent panel;
    private JButton button;
    public RendererPanel() {
        panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(3, 10, 2, 10));
        button = new JButton();
        panel.add(button);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table,
            Object value, boolean isSelected, boolean hasFocus, int row,
            int column) {
        // suggestion by Piro - use background of default
        DefaultTableCellRenderer dt = (DefaultTableCellRenderer) table.getDefaultRenderer(Object.class);
        dt.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        // first try: set the color as-is - doesn't work
        // panel.setBackground(dt.getBackground());
        // second try: set color as not ui-resource
        // that's working because at this point we already have the color that will be used
        // let's hinder synth background color searching to fall back to component defaults
        panel.setBackground(new Color(dt.getBackground().getRGB()));
        // hack: unwrap ui-resource as needed
        // updateBackground(isSelected ? table.getSelectionBackground() : table.getBackground(), row);
        button.setText(String.valueOf(value));
        return panel;
    }

    private void updateBackground(Color color, int row) {
        Color hack = row % 2 == 0 ? unwrap(color) : color;
        panel.setBackground(hack);
    }

    private Color unwrap(Color c) {
        if (c instanceof UIResource) {
            return new Color(c.getRGB());
        }
        return c;
    }

}

屏幕截图:with unwrap hack

enter image description here

屏幕截图:使用默认颜色(来自为Object.class安装的渲染器)

enter image description here

非hacky出路可能是(没有在这里尝试,但记得曾经做过一次)注册一个具有样式的Region,类似于NimbusDefaults在内部做的事情:

register(Region.PANEL, "Table:\"Table.cellRenderer\"");

这里的问题是没有公开的api这样做(或者可能是我对Synth的了解不够; - )

答案 1 :(得分:1)

不要将背景设置为JButton。使用JPanel包装JButton并将背景设置为JPanel。如果在一个JTable列中使用了更多按钮,这可能是显而易见的。

设置我所做的JPanel的正确背景颜色(你应该):

  1. 保持对原始渲染器的引用
  2. 让原始渲染器渲染自己的组件(对于每次渲染)!
  3. 使用渲染组件的背景来设置JPanel的背景(对于每个渲染)!
  4. 这样您就不必自己选择正确的颜色

    此外,您必须覆盖paintComponent才能正确绘制JPanel的白色背景:

    @Override
    protected void paintComponent(Graphics g) {
      Color background = getBackground();
      setBackground(new Color(background.getRGB()));
      super.paintComponent(g);
    }
    

    编辑:如@kleopatra建议您不必覆盖paintComponent,只将背景颜色设置为not-uiresource(显示在完整示例中

    这是完整的例子:

    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Graphics;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTable;
    import javax.swing.UIManager;
    import javax.swing.UIManager.LookAndFeelInfo;
    import javax.swing.table.TableCellRenderer;
    
    public class Test {
    public static void main(String[] args) throws Throwable {
        for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    
        String[] columnNames = new String[]{"c1"};
        Object[][] data = new Object[4][1];
        data[0][0] = "First";
        data[1][0] = "Second";
        data[2][0] = "Third";
        data[3][0] = "Fourth";
    
        JTable table  = new JTable(data, columnNames){
            @Override
            public javax.swing.table.TableCellRenderer getCellRenderer(int row, int column) {
                final TableCellRenderer ori = super.getCellRenderer(row, column);
                final TableCellRenderer mine = new TableCellRenderer() {
                    @Override
                    public Component getTableCellRendererComponent(JTable table, Object value,
                            boolean isSelected, boolean hasFocus, int row, int column) {
                        Component c = ori.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                        JPanel p = new JPanel();
                        if(value == null){
                            value = "";
                        }
                        p.add(new JButton(value.toString()));
                        p.setBackground(new Color(c.getBackground().getRGB()));
                        return p;
                    }
                };
                return mine;
            };
        };
        table.setRowHeight(50);
        JFrame f = new JFrame();
        f.add(table);
        f.setVisible(true);
        f.pack();
    }
    }
    

    结果:

    Result