如何只显示 JColorChooser 的 HSV 框?

How do I show only the HSV box of a JColorChooser?

在此图片中,您可以看到默认的 Java Swing 颜色选择器,JColorChooser:

我想要的只是红色框内的颜色方块和垂直滑块:

我能够删除所有选项卡(色板、HSL 等)和预览,但无法删除所有滑块。我该怎么做?

查看 javax.swing.colorchooser internals reveals that ColorChooserComponentFactory creates subclasses of AbstractColorChooserPanel using ColorChooserPanelColorModel 的合适子类。 ColorChooserPanel 将您想要的部分保存为 DiagramComponent 的两个实例:左侧名为 diagram,右侧名为 slider

与其从现有 ColorChooserPanel 中删除组件,不如考虑仅包含所需元素的 Creating a Custom Chooser Panel

class MyChooserPanel extends AbstractColorChooserPanel implements PropertyChangeListener {

    private final ColorModel model;
    private final DiagramComponent diagram;
    private final DiagramComponent slider;

    MyChooserPanel(ColorModel model) {
        this.model = model;
        this.diagram = new DiagramComponent(this.panel, true);
        this.slider = new DiagramComponent(this.panel, false);
    }
    …
}

当然,您必须重述一些(包)私有代码,但结果会不那么脆弱。

我的解决方案:

        colorChooser.setPreviewPanel(new JPanel());
        AbstractColorChooserPanel[] panels = colorChooser.getChooserPanels();
        for (AbstractColorChooserPanel accp : panels) {
            if(!accp.getDisplayName().equals("HSV")) {
                colorChooser.removeChooserPanel(accp);
            } 
        }
        JComponent current = (JComponent) colorChooser.getComponents()[0];
        while( !current.getClass().toString().equals( "class javax.swing.colorchooser.ColorChooserPanel" ) ){
            current = (JComponent) current.getComponents()[0]; 
        }
        for(Component jc : current.getComponents()){
            if(!jc.getClass().toString().equals( "class javax.swing.colorchooser.DiagramComponent" )){
                current.remove(jc);
            }
        }  

这会删除我不想要的所有内容,但我认为定制一个会是更好的解决方案。
我打算试试 trashgod 的,看看它是否更好用。

您可以使用 Swing Utils class 在面板上搜索给定类型的组件。

在下面的示例中,您可以找到所有滑块,然后使它们不可见:

import java.awt.*;
import java.util.List;
import javax.swing.*;
import javax.swing.colorchooser.*;

public class ColorChooserPanel extends JPanel
{
    ColorChooserPanel()
    {
        JColorChooser chooser = new JColorChooser();
        AbstractColorChooserPanel[] panels = chooser.getChooserPanels();

        for (AbstractColorChooserPanel panel: panels)
        {
            if ("HSL".equals(panel.getDisplayName()))
            {
                add( panel );

                List<JSlider> sliders = SwingUtils.getDescendantsOfType(JSlider.class, panel, true);

                for (JSlider slider: sliders)
                {
                    slider.setVisible( false );
                }

            }
        }
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("ColorChooserPanel");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new ColorChooserPanel());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );

    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}

好吧,为了开心,我决定从头开始创建您想要的 JPanel,将其连接到一种 View-Controller 设置中,因为这里的模型很简单——控制器中的一个 Color 字段。要使用此组件,您需要创建该组件的一个实例,将其添加到需要的 GUI 中,然后简单地向其添加一个 PropertyChangeListener 以监听其 COLOR 属性:

hsvChooserPanel.addPropertyChangeListener(HsvChooserPanel.COLOR, pce -> {
    Color c = (Color) pce.getNewValue();
    // do what you want with Color value, c, here
});

测试class:


版本 2:这允许使用 HSV panel/bar 组合中的任意 3 种,可以是带有侧重于色相、饱和度或值(此处为亮度)的条的组合。它使用具有 3 个值的枚举 ColorProperty,ColorProperty.HUE、ColorProperty.SATURATION 和 ColorProperty.BRIGHTNESS,并在枚举项本身内计算如何创建颜色条和颜色方形缓冲图像。欢迎评论:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TestHsvChooser2 {
    // ***** choose which color panel / color bar you'd like to test:
    // private static final ColorProperty COLOR_PROP = ColorProperty.HUE;
    // private static final ColorProperty COLOR_PROP = ColorProperty.BRIGHTNESS;
    private static final ColorProperty COLOR_PROP = ColorProperty.SATURATION;

    private static void createAndShowGui() {
        HsvChooserPanel2 hsvChooserPanel = new HsvChooserPanel2(COLOR_PROP);

        JPanel testPanel = new JPanel();
        JPanel outerPanel = new JPanel(new BorderLayout());
        outerPanel.add(testPanel);
        outerPanel.setBorder(BorderFactory.createTitledBorder("Test Panel"));

        hsvChooserPanel.addPropertyChangeListener(HsvChooserPanel2.COLOR, pce -> {
            Color c = (Color) pce.getNewValue();
            testPanel.setBackground(c);
        });

        JPanel gridPanel = new JPanel(new GridLayout(0, 1, 10, 10));
        gridPanel.add(hsvChooserPanel);
        gridPanel.add(outerPanel);

        JFrame frame = new JFrame("HSV Chooser");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(gridPanel);
        frame.setResizable(false);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

整个shebang:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.event.SwingPropertyChangeSupport;

@SuppressWarnings("serial")
public class HsvChooserPanel2 extends JPanel {
    public static final String COLOR = "color";
    private static final int PREF_W = 180;
    private static final int PREF_H = PREF_W;
    private static final int PREF_W_CB = 20;
    private static final int GAP = 10;
    private MyColorPanel2 colorPanel = null;
    private MyColorBar2 colorBar = null;
    private InnerControl2 innerControl = null;

    public HsvChooserPanel2(ColorProperty colorProp) {
        colorPanel = new MyColorPanel2(PREF_W, PREF_H, colorProp);
        colorBar = new MyColorBar2(PREF_W_CB, PREF_H, colorProp);
        innerControl = new InnerControl2(this, colorPanel, colorBar);
        colorPanel.setColorPropertyValue(Color.RED); // !! magic value
        setLayout(new BorderLayout(GAP, GAP));
        setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
        add(colorPanel, BorderLayout.CENTER);
        add(colorBar, BorderLayout.LINE_END);

        // propagate COLOR changes in the inner controller
        // to this component's property change support
        innerControl.addPropertyChangeListener(COLOR, pce -> {
            firePropertyChange(COLOR, pce.getOldValue(), pce.getNewValue());
        });
    }


}

// listens for changes to both the color bar and the color panel
class InnerControl2 {
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);

    private HsvChooserPanel2 hsvChooser;
    private MyColorPanel2 colorPanel;
    private MyColorBar2 colorBar;
    private Color color; // This is all there is to the model, a "bound"
                         // property

    public InnerControl2(HsvChooserPanel2 hsvChooser, MyColorPanel2 colorPanel, MyColorBar2 colorBar) {
        this.hsvChooser = hsvChooser;
        this.colorPanel = colorPanel;
        this.colorBar = colorBar;

        // listen for changes
        colorPanel.addPropertyChangeListener(ColorPanelParent.CURSOR, new ColorPanelListener());
        colorBar.addPropertyChangeListener(ColorPanelParent.CURSOR, new ColorBarListener());
    }

    public Color getColor() {
        return color;
    }

    // classic setter method for a "bound" property
    public void setColor(Color color) {
        Color oldValue = this.color;
        Color newValue = color;
        this.color = color;
        // notify listeners of the change
        pcSupport.firePropertyChange(HsvChooserPanel2.COLOR, oldValue, newValue);
    }

    // allow outside classes the ability to listen
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(name, listener);
    }

    public HsvChooserPanel2 getHsvChooser() {
        return hsvChooser;
    }

    // inner listeners
    private class ColorPanelListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            Point p = colorPanel.getCursorP();
            Color c = colorPanel.getColor(p);
            colorBar.setColorPropertyValue(c);
            setColor(c); // this will fire the prop change
                                              // support
        }
    }

    private class ColorBarListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            // get hue from the color bar and use it to set the main hue
            // of the color panel
            Point p = colorBar.getCursorP();
            Color c = colorBar.getColor(p);
            colorPanel.setColorPropertyValue(c);
        }
    }
}

// parent of both color bar panel and color panel
@SuppressWarnings("serial")
abstract class ColorPanelParent extends JPanel {
    public static final String CURSOR = "cursor";
    private int prefW;
    private int prefH;
    private Point cursorP = new Point(0, 0);
    private BufferedImage img = null;
    private ColorProperty colorProperty;
    private boolean panel;

    public ColorPanelParent(int prefW, int prefH, ColorProperty colorProperty, boolean panel) {
        this.prefW = prefW;
        this.prefH = prefH;
        this.colorProperty = colorProperty;
        this.panel = panel;
        MyMouse myMouse = new MyMouse();
        addMouseListener(myMouse);
        addMouseMotionListener(myMouse);
        setBorder(BorderFactory.createLineBorder(Color.BLACK));
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(prefW, prefH);
    }

    public int getPrefH() {
        return prefH;
    }

    public int getPrefW() {
        return prefW;
    }

    public ColorProperty getColorProperty() {
        return colorProperty;
    }

    public BufferedImage getImg() {
        return img;
    }

    public Point getCursorP() {
        return cursorP;
    }

    public void setImg(BufferedImage img) {
        this.img = img;
        repaint();
    }

    public Color getColor(Point p) {
        Color c = null;
        if (getImg() != null) {
            int rgb = getImg().getRGB(p.x, p.y);
            c = new Color(rgb);
        }
        return c;
    }

    // when the main hue is changed, we have to create a new
    // background image to reflect the new main color
    public void setColorPropertyValue(Color color) {
        int w = getPrefW();
        int h = getPrefH();
        BufferedImage img = getColorProperty().createImage(color, w, h, panel);
        setImg(img);
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (img != null) {
            g.drawImage(img, 0, 0, this);
        }
    }

    // change the cursor point and then
    // notify prop change support of changes
    private class MyMouse extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            mouseResponse(e);
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            mouseResponse(e);
        }

        private void mouseResponse(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();
            if (!contains(e.getPoint())) {
                x = Math.max(0, x);
                x = Math.min(prefW - 1, x);
                y = Math.max(0, y);
                y = Math.min(prefH - 1, y);
            }
            Point oldValue = cursorP;
            cursorP = new Point(x, y);
            firePropertyChange(CURSOR, oldValue, cursorP);
            repaint();
        }
    }

}

// color bar on the right side of the HsvChooser JPanel.
// Controller action: Changing selection point on this JPanel
// will change the hue of the color panel
@SuppressWarnings("serial")
class MyColorBar2 extends ColorPanelParent {

    public MyColorBar2(int prefW, int prefH, ColorProperty colorProperty) {
        super(prefW, prefH, colorProperty, false);

        // create and set the background image
        setColorPropertyValue(Color.RED); // fix the magic number?
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // draws the background image

        // this draws a line at the cursor's location
        g.setXORMode(Color.WHITE);
        int y = getCursorP().y;
        g.drawLine(0, y, getPrefW(), y);
    }

}

// color panel on the left side of the HsvChooser JPanel.
// Controller action: Changing selection point on this JPanel
// will notify listeners of a new COLOR selection
@SuppressWarnings("serial")
class MyColorPanel2 extends ColorPanelParent {
    private static final int CURSOR_RADIUS = 8;

    public MyColorPanel2(int prefW, int prefH, ColorProperty colorProperty) {
        super(prefW, prefH, colorProperty, true);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // draws the background image

        // draws the cross hatch indicating color selection point
        g.setXORMode(Color.WHITE);
        int x = getCursorP().x;
        int y = getCursorP().y;
        int x1 = x - CURSOR_RADIUS;
        int y1 = y - CURSOR_RADIUS;
        int x2 = x + CURSOR_RADIUS;
        int y2 = y + CURSOR_RADIUS;
        g.drawLine(x1, y, x2, y);
        g.drawLine(x, y1, x, y2);
    }
}

enum ColorProperty {
    HUE {
        @Override
        public BufferedImage createImage(Color color, int w, int h, boolean panel) {
            BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

            if (panel) {
                int red = color.getRed();
                int green = color.getGreen();
                int blue = color.getBlue();
                // float array is HSB
                float hue = Color.RGBtoHSB(red, green, blue, null)[0]; 

                for (int i = 0; i < h; i++) {
                    for (int j = 0; j < w; j++) {
                        float s = ((float) j) / (float) w;
                        float b = (h - (float) i) / (float) h;
                        int rgb = Color.getHSBColor(hue, s, b).getRGB();
                        img.setRGB(j, i, rgb);
                    }
                }
            } else {
                for (int i = 0; i < h; i++) {
                    for (int j = 0; j < w; j++) {
                        float hue = (h - (float) i) / (float) h;
                        int rgb = Color.getHSBColor(hue, 1f, 1f).getRGB();
                        img.setRGB(j, i, rgb);
                    }
                }
            }
            return img;
        }
    },
    SATURATION {
        @Override
        public BufferedImage createImage(Color color, int w, int h, boolean panel) {
            BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            int red = color.getRed();
            int green = color.getGreen();
            int blue = color.getBlue();
            // float array is HSB
            float[] hsb = Color.RGBtoHSB(red, green, blue, null);

            return panel ? createPanelImg(w, h, img, hsb) : createBarImg(w, h, img, hsb);
        }

        private BufferedImage createBarImg(int w, int h, BufferedImage img, float[] hsb) {
            float hue = hsb[0];
            // float brightness = hsb[2];
            float brightness = 1f;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    float saturation = (h - (float) i) / (float) h;
                    int rgb = Color.getHSBColor(hue, saturation, brightness).getRGB();
                    img.setRGB(j, i, rgb);
                }
            }
            return img;
        }

        private BufferedImage createPanelImg(int w, int h, BufferedImage img, float[] hsb) {
            float saturation = hsb[1]; 
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    float hue = ((float) j) / (float) w;
                    float b = (h - (float) i) / (float) h;
                    int rgb = Color.getHSBColor(hue, saturation, b).getRGB();
                    img.setRGB(j, i, rgb);
                }
            }            
            return img;
        }
    },
    BRIGHTNESS {
        @Override
        public BufferedImage createImage(Color color, int w, int h, boolean panel) {
            BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            int red = color.getRed();
            int green = color.getGreen();
            int blue = color.getBlue();
            // float array is HSB
            float[] hsb = Color.RGBtoHSB(red, green, blue, null);

            return panel ? createPanelImg(w, h, img, hsb) : createBarImg(w, h, img, hsb);
        }

        private BufferedImage createBarImg(int w, int h, BufferedImage img, float[] hsb) {
            float hue = hsb[0];
            // float saturation = hsb[1];
            float saturation = 1f;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    float brightness = (h - (float) i) / (float) h;
                    int rgb = Color.getHSBColor(hue, saturation, brightness).getRGB();
                    img.setRGB(j, i, rgb);
                }
            }
            return img;
        }

        private BufferedImage createPanelImg(int w, int h, BufferedImage img, float[] hsb) {
            float brightness = hsb[2]; 
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    float hue = ((float) j) / (float) w;
                    float saturation = (h - (float) i) / (float) h;
                    int rgb = Color.getHSBColor(hue, saturation, brightness).getRGB();
                    img.setRGB(j, i, rgb);
                }
            }            
            return img;
        }
    };

    public abstract BufferedImage createImage(Color color, int w, int h, boolean panel);
}