6

我正在开发一个使用 Nimbus 外观和感觉的应用程序。有一个表格,其中一列包含按钮(使用Rob Camick 的表格按钮列)。这确实有效,但结果不是我所期望的。我试图修复外观,但无济于事。

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

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

背景不正确的按钮

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

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;
}

是 default的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);
        }

这会产生:

按钮背景也不正确

所以这次在第一张图片中看起来不错的按钮现在是坏的,反之亦然。按钮的内部背景(圆角矩形内的区域)似乎确实根据背景颜色属性(这是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);
        }

结果:

仍然不正确的按钮

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

4

2 回答 2

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;
    }

}

屏幕截图:使用 unwrap hack

在此处输入图像描述

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

在此处输入图像描述

非 hacky 的出路可能是(这里没有尝试过,但记得做过一次)用样式注册一个区域,类似于 NimbusDefaults 在内部所做的:

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

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

于 2013-08-13T09:48:12.857 回答
1

不要将背景设置为 JButton。使用 JPanel 包装 JButton 并将背景设置为 JPanel。如果您在一个 JTable 列中使用更多按钮,这可能会很明显。

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

  1. 保持对原始渲染器的引用
  2. 让原始渲染器渲染它自己的组件(每次渲染)!
  3. 使用渲染组件的背景来设置 JPanel 的背景(每次渲染)!

这样您就不必自己选择正确的颜色

您还必须重写paintComponent 才能正确绘制JPanel 的白色背景:

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

编辑:正如@kleopatra 建议您不必覆盖paintComponent,只需将背景颜色设置为非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();
}
}

结果:

结果

于 2013-08-13T06:19:38.287 回答