覆盖Nimbus颜色

时间:2014-06-12 08:23:43

标签: java swing look-and-feel swingx nimbus

我们正在使用Nimbus LaF开发Swing应用程序。我们更改了许多Nimbus默认设置(控件,文本,NimbusLightBackground等)以使主题变暗。

现在我们在渲染JLists和JComboBox时遇到了很大的麻烦,因为渲染器显然使用了NimbusLightBackground颜色来选择文本前景。这导致深蓝色背景上的深灰色文本 - 不好。

我尝试通过UIManager全局覆盖Nimbus Defaults中的任何适用的键(" ComboBox:\" ComboBox.listRenderer \" [Selected] .textForeground"等等) .putDefault()和per-component-overrides,但根本无法获得任何更改。

即使SwingX荧光笔似乎也无法覆盖此行为,至少在组合框下拉列表中。

有关如何为J(X)列表和J(X)组合框下拉菜单设置所选文本前景色的任何想法?

我对每个组件覆盖的最新尝试:

JXComboBox comboBox = new JXComboBox();
UIDefaults comboBoxTheme = new UIDefaults();
comboBoxTheme.put("nimbusLightBackground", new Color(0xFFFAFA));
comboBoxTheme.put("ComboBox:\"ComboBox.listRenderer\"[Selected].textForeground", new Color(0xFFFAFA));
comboBox.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
comboBox.putClientProperty("Nimbus.Overrides", comboBoxTheme);
SwingUtilities.updateComponentTreeUI(comboBox);

应用程序范围的nimbus默认为:

ColorUIResource backgroundUI = new ColorUIResource(0x494949);
ColorUIResource textUI = new ColorUIResource(0xFFFAFA);
ColorUIResource controlBackgroundUI = new ColorUIResource(0x5F5F4D);
ColorUIResource infoBackgroundUI = new ColorUIResource(0x2f5cb4);
ColorUIResource infoUI = new ColorUIResource(0x2f5cb4);
ColorUIResource lightBackgroundUI = new ColorUIResource(0x5D5D5B);
ColorUIResource focusUI = new ColorUIResource(0x39698a);

UIManager.put("control", backgroundUI);
UIManager.put("text", textUI);
UIManager.put("nimbusLightBackground", lightBackgroundUI);
UIManager.put("info", infoUI);
UIManager.put("nimbusInfoBlue", infoBackgroundUI);
UIManager.put("nimbusBase", controlBackgroundUI);
UIManager.put("nimbusBlueGrey", controlBackgroundUI);
UIManager.put("nimbusFocus", focusUI);

所有这些都是在Java 7u55中实现的,尽管我怀疑这一点并不重要,因为似乎没有人维持Swing / Nimbus很长一段时间。

PS:我当然读过this question和其他人,但是没有找到有效的答案。

编辑:这是一个证明问题的SSCCE。它创建了一个JFrame,顶部有一个默认的组合框,中间是一个列表,底部是一个按组件覆盖的组合框。选择列表中的值或从框下拉列表中可以看到问题。

package sscce;

import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.ColorUIResource;

public class ForegroundProblemDemo extends JFrame {

public ForegroundProblemDemo() {
    super("Demo");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JComboBox<String> comboBoxWithDefaults = createComboBox();
    JComboBox<String> comboBoxWithOverrides = createComboBox();
    JList<String> list = createList();
    addOverrides(comboBoxWithOverrides);

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(new JScrollPane(list), BorderLayout.CENTER);
    getContentPane().add(comboBoxWithDefaults, BorderLayout.NORTH);
    getContentPane().add(comboBoxWithOverrides, BorderLayout.SOUTH);

    pack();
    setLocationRelativeTo(null);
    setVisible(true);
}

JComboBox<String> createComboBox() {
    JComboBox<String> comboBox = new JComboBox<>(new String[] {"A","B","C","D"});
    return comboBox;
}

JList<String> createList() {
    JList<String> list = new JList<>(new String[] {"A","B","C","D"});
    return list;
}

void addOverrides(JComponent component) {
    UIDefaults theme = new UIDefaults();
    theme.put("nimbusLightBackground", new Color(0xFFFAFA));
    theme.put("ComboBox:\"ComboBox.listRenderer\"[Selected].textForeground", new Color(0xFFFAFA));
    component.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
    component.putClientProperty("Nimbus.Overrides", theme);
    SwingUtilities.updateComponentTreeUI(component);
}


public static void main(String... args) throws Throwable {
    ColorUIResource backgroundUI = new ColorUIResource(0x494949);
    ColorUIResource textUI = new ColorUIResource(0xFFFAFA);
    ColorUIResource controlBackgroundUI = new ColorUIResource(0x5F5F4D);
    ColorUIResource infoBackgroundUI = new ColorUIResource(0x2f5cb4);
    ColorUIResource infoUI = new ColorUIResource(0x2f5cb4);
    ColorUIResource lightBackgroundUI = new ColorUIResource(0x5D5D5B);
    ColorUIResource focusUI = new ColorUIResource(0x39698a);
    UIManager.put("control", backgroundUI);
    UIManager.put("text", textUI);
    UIManager.put("nimbusLightBackground", lightBackgroundUI);
    UIManager.put("info", infoUI);
    UIManager.put("nimbusInfoBlue", infoBackgroundUI);
    UIManager.put("nimbusBase", controlBackgroundUI);
    UIManager.put("nimbusBlueGrey", controlBackgroundUI);
    UIManager.put("nimbusFocus", focusUI);

    for (LookAndFeelInfo lafInfo : UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(lafInfo.getName())) {
            UIManager.setLookAndFeel(lafInfo.getClassName());
            break;
        }
    }

    new ForegroundProblemDemo();
}

}

编辑2:对不起,之前应该已经提到过:对于列表,可以使用setSelectionForeground()方法轻松解决问题。对于ComboBoxes,我还没有找到一种缺少自定义渲染器的方法。所以我主要关注的是ComboBoxes。

2 个答案:

答案 0 :(得分:2)

我不喜欢这个答案,很乐意接受任何告诉我如何使用Nimbus设置

的人

我最终做了什么 - 如果我找到更好的解决方案,我将很高兴改变 - 是这样的:

我创建了一个ListCellRenderer实现,它包装了DefaultListCellRenderer,并且如果isSelected参数为true,则设置前景色。它有效,但由于以下原因我不喜欢它:

  1. 每个其他自定义渲染器(谢天谢地)都不得不实施相同的hack,这违反了DRY原则。
  2. 这会以这样的方式覆盖LookAndFeel,即设置另一个主题(或简单地改回灯光主题)将需要在代码中的多个位置进行更改。
  3. JList或JComboBox的每个实例都必须手动设置渲染器,这再次违反了DRY原则。
  4. 对于Java核心库中的一个错误来说,这是一个丑陋的解决方法,我很生气,尽管自2007年以来似乎没有发生任何关于该主题的错误报告。
  5. 无论如何,这里是那些可能有同样问题的孤独流浪者的代码。

    import java.awt.Color;
    import java.awt.Component;
    
    import javax.swing.DefaultListCellRenderer;
    import javax.swing.JList;
    import javax.swing.ListCellRenderer;
    
    
    @SuppressWarnings("rawtypes")
    public class ThemeCompliantListCellRenderer implements ListCellRenderer {
    
    private ListCellRenderer wrappedRenderer = new DefaultListCellRenderer();
    private Color textColor = new Color(0xFFFAFA); 
    
    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        @SuppressWarnings("unchecked")
        Component c = wrappedRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        if (isSelected) {
            c.setForeground(textColor);
        }
        return c;
    }
    
    public void setSelectedForeground(Color color) {
        textColor = color;
    }
    }
    

答案 1 :(得分:2)

  • 通过使用全矩形Painter,看看W8发生了什么,Win8.1 64b,Java7,JDK 1.7_021,

enter image description here

import java.awt.*;
import java.util.Vector;
import javax.swing.*;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.nimbus.AbstractRegionPainter;

public class MyComboBox {

    private Vector<String> listSomeString = new Vector<String>();
    private JComboBox someComboBox = new JComboBox(listSomeString);
    private JComboBox editableComboBox = new JComboBox(listSomeString);
    private JComboBox non_EditableComboBox = new JComboBox(listSomeString);
    private JFrame frame;

    public MyComboBox() {
        listSomeString.add("-");
        listSomeString.add("Snowboarding");
        listSomeString.add("Rowing");
        listSomeString.add("Knitting");
        listSomeString.add("Speed reading");
        someComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        someComboBox.setFont(new Font("Serif", Font.BOLD, 16));
        someComboBox.setEditable(true);
        someComboBox.getEditor().getEditorComponent().setBackground(Color.YELLOW);
        ((JTextField) someComboBox.getEditor().getEditorComponent()).setBackground(Color.YELLOW);
        editableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        editableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
        editableComboBox.setEditable(true);
        JTextField text = ((JTextField) editableComboBox.getEditor().getEditorComponent());
        text.setBackground(Color.YELLOW);
        /*JComboBox coloredArrowsCombo = editableComboBox;
         Component[] comp = coloredArrowsCombo.getComponents();
         for (int i = 0; i < comp.length; i++) {
         if (comp[i] instanceof MetalComboBoxButton) {
         MetalComboBoxButton coloredArrowsButton = (MetalComboBoxButton) comp[i];
         coloredArrowsButton.setBackground(null);
         break;
         }
         }*/
        non_EditableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        non_EditableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
        frame = new JFrame();
        frame.setLayout(new GridLayout(0, 1, 10, 10));
        frame.add(someComboBox);
        frame.add(editableComboBox);
        frame.add(non_EditableComboBox);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocation(100, 100);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        try {
            for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(laf.getName())) {
                    UIManager.setLookAndFeel(laf.getClassName());
                    UIManager.getLookAndFeelDefaults().put("ComboBox[Enabled].backgroundPainter",
                            new javax.swing.plaf.nimbus.AbstractRegionPainter() {
                        @Override
                        protected AbstractRegionPainter.PaintContext getPaintContext() {
                            return new AbstractRegionPainter.PaintContext(null, null, false);
                        }

                        @Override
                        protected void doPaint(Graphics2D g, JComponent c,
                                int width, int height, Object[] extendedCacheKeys) {
                            g.setColor(Color.MAGENTA);
                            g.fill(new Rectangle(0, 0, width, height));
                        }
                    });
                    UIManager.getLookAndFeelDefaults().put("ComboBox[Focused+Pressed].backgroundPainter",
                            new javax.swing.plaf.nimbus.AbstractRegionPainter() {
                        @Override
                        protected AbstractRegionPainter.PaintContext getPaintContext() {
                            return new AbstractRegionPainter.PaintContext(null, null, false);
                        }

                        @Override
                        protected void doPaint(Graphics2D g, JComponent c,
                                int width, int height, Object[] extendedCacheKeys) {
                            g.setColor(Color.CYAN);
                            g.fill(new Rectangle(0, 0, width, height));
                        }
                    });
                    UIManager.getLookAndFeelDefaults().put("ComboBox[Focused].backgroundPainter",
                            new javax.swing.plaf.nimbus.AbstractRegionPainter() {
                        @Override
                        protected AbstractRegionPainter.PaintContext getPaintContext() {
                            return new AbstractRegionPainter.PaintContext(null, null, false);
                        }

                        @Override
                        protected void doPaint(Graphics2D g, JComponent c,
                                int width, int height, Object[] extendedCacheKeys) {
                            g.setColor(Color.RED);
                            g.fill(new Rectangle(0, 0, width, height));
                        }
                    });
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MyComboBox aCTF = new MyComboBox();
            }
        });
    }
}
  • 没有使用XxxRenderer就没有任何事情发生,那么可以为可编辑和不可编辑的JComboBox创建漂亮的主题(具有相同的Color主题)

  • 我会看看Seaglas L&amp; F(注意要求在JDK 1.6._Xxx中编译)

enter image description here

import java.awt.*;
import java.util.Vector;
import javax.swing.*;
import javax.swing.UIManager;

public class MyComboBox {

    private Vector<String> listSomeString = new Vector<String>();
    private JComboBox someComboBox = new JComboBox(listSomeString);
    private JComboBox editableComboBox = new JComboBox(listSomeString);
    private JComboBox non_EditableComboBox = new JComboBox(listSomeString);
    private JFrame frame;

    public MyComboBox() {
        listSomeString.add("-");
        listSomeString.add("Snowboarding");
        listSomeString.add("Rowing");
        listSomeString.add("Knitting");
        listSomeString.add("Speed reading");
        someComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        someComboBox.setFont(new Font("Serif", Font.BOLD, 16));
        someComboBox.setEditable(true);
        someComboBox.getEditor().getEditorComponent().setBackground(Color.YELLOW);
        ((JTextField) someComboBox.getEditor().getEditorComponent()).setBackground(Color.YELLOW);
        editableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        editableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
        editableComboBox.setEditable(true);
        JTextField text = ((JTextField) editableComboBox.getEditor().getEditorComponent());
        text.setBackground(Color.YELLOW);
        non_EditableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        non_EditableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
        frame = new JFrame();
        frame.setLayout(new GridLayout(0, 1, 10, 10));
        frame.add(someComboBox);
        frame.add(editableComboBox);
        frame.add(non_EditableComboBox);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocation(100, 100);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel("com.seaglasslookandfeel.SeaGlassLookAndFeel");
        } catch (Exception e) {
            e.printStackTrace();
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MyComboBox aCTF = new MyComboBox();
            }
        });
    }
}