根据Java Swing值动态设置Color

时间:2014-02-13 03:34:53

标签: java swing colors

我正在使用Java Swing。我想根据我计算的双值显示颜色。

编辑 - 我需要填充Path2D对象的颜色。目前,我就是这样做的

Path2D shape;
// some code here
g2d.setColor(Color.Red);
g2d.fill(shape);

现在我不想将颜色固定为Color.Red,但需要根据我计算的值来设置颜色。双值可以是负值或正值。值越负,颜色应越暗。颜色不必是红色。我怎么能这样做?

3 个答案:

答案 0 :(得分:3)

作为建议hereTableCellRenderer方法的具体示例,请实施Icon界面,如图所示here。不要改变大小,而是使用Color.getHSBColor()根据不同的饱和度或亮度选择颜色,如图here所示。

附录:与example cited一样,下面的渲染假定数据值在区间[0, 1)中标准化。您需要扩展到数据模型的最小值和最大值。如果模型经常更新,则每次添加都可能值得更新这些值。

image

/**
 * @see https://stackoverflow.com/a/21756629/230513
 * @see https://stackoverflow.com/a/2834484/230513
 */
private static class DecRenderer extends DefaultTableCellRenderer implements Icon {

    private static final int N = 256;
    private static final int SIZE = 32;
    private static List<Color> clut = new ArrayList<>(N);
    private DecimalFormat df;

    public DecRenderer(DecimalFormat df) {
        this.df = df;
        this.setIcon(this);
        this.setHorizontalAlignment(JLabel.RIGHT);
        this.setBackground(Color.lightGray);
        for (float i = 0; i < N; i++) {
            clut.add(Color.getHSBColor(1, 1, i / N));
        }
    }

    @Override
    protected void setValue(Object value) {
        setText((value == null) ? "" : df.format(value));
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        double v = Double.valueOf(this.getText());
        final int i = (int) (v * N - 1);
        g2d.setColor(clut.get(i));
        g2d.fillOval(x, y, SIZE, SIZE);
    }

    @Override
    public int getIconWidth() {
        return SIZE;
    }

    @Override
    public int getIconHeight() {
        return SIZE;
    }
}

答案 1 :(得分:2)

这个问题缺乏背景。所有Swing组件都有setBackground方法,可以更改不透明组件的背景。

如果要更改JTable单元格或JListJTableJComboBox的背景颜色,则需要提供自定义单元格渲染器执行此任务,在这种情况下,你应该看看......

根据OP问题的更新

更新了示例

以下是颜色混合算法的示例,它允许您指定要使用的颜色以及它们应显示的比例/分数。例如非常简单,它只使用三种颜色均匀分布在整个范围内,但您可以根据需要添加更多颜色或调整重量。

该示例采用一系列随机生成的值并对其进行归一化,因此当值趋于0(或较低的正常范围)时,它将变暗,当它接近1时,它将变得更亮。

如果选择,您可以更改算法以分别标准化正值和负值。

Bars

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ColorBars {

    public static void main(String[] args) {
        new ColorBars();
    }

    public ColorBars() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private PlotPane plotPane;
        private JSpinner valueAdd;

        public TestPane() {
            setLayout(new BorderLayout());
            plotPane = new PlotPane();
            add(plotPane);
        }

    }

    public class PlotPane extends JPanel {

        private Color[] colorRange = new Color[]{Color.BLACK, Color.RED, Color.WHITE};
        private float[] ratioRanges = new float[]{0f, 0.5f, 1f};

//        private Color maxColor = Color.WHITE;
//        private Color minColor = Color.RED;

        private List<Double> values;
        private double min = Double.MAX_VALUE;
        private double max = Double.MIN_VALUE;

        public PlotPane() {
            values = new ArrayList<>(25);
            Random rnd = new Random();
            for (int index = 0; index < 10; index++) {
                addValue((rnd.nextDouble() * 2000) - 1000);
            }
        }

        public void addValue(double value) {
            max = Math.max(max, value);
            min = Math.min(min, value);
            values.add(value);
            repaint();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics g2d = (Graphics2D) g.create();
            int height = getHeight();
            int width = getWidth();

            int barWidth = width / values.size();
            int x = 0;
            for (Double value : values) {
                double norm = value - min;
                norm /= (max - min);

                int barHeight = (int) (height * norm);
                System.out.println(NumberFormat.getInstance().format(norm));
                Color color = blendColors(ratioRanges, colorRange, (float)norm);
                g2d.setColor(color);
                g2d.fillRect(x, height - barHeight, barWidth, barHeight);
                x += barWidth;
            }
            g2d.dispose();
        }

    }

    public static Color blendColors(float[] fractions, Color[] colors, float progress) {
        Color color = null;
        if (fractions != null) {
            if (colors != null) {
                if (fractions.length == colors.length) {
                    int[] indicies = getFractionIndicies(fractions, progress);

                    float[] range = new float[]{fractions[indicies[0]], fractions[indicies[1]]};
                    Color[] colorRange = new Color[]{colors[indicies[0]], colors[indicies[1]]};

                    float max = range[1] - range[0];
                    float value = progress - range[0];
                    float weight = value / max;

                    color = blend(colorRange[0], colorRange[1], 1f - weight);
                } else {
                    throw new IllegalArgumentException("Fractions and colours must have equal number of elements");
                }
            } else {
                throw new IllegalArgumentException("Colours can't be null");
            }
        } else {
            throw new IllegalArgumentException("Fractions can't be null");
        }
        return color;
    }

    public static int[] getFractionIndicies(float[] fractions, float progress) {
        int[] range = new int[2];

        int startPoint = 0;
        while (startPoint < fractions.length && fractions[startPoint] <= progress) {
            startPoint++;
        }

        if (startPoint >= fractions.length) {
            startPoint = fractions.length - 1;
        }

        range[0] = startPoint - 1;
        range[1] = startPoint;

        return range;
    }

    public static Color blend(Color color1, Color color2, double ratio) {
        float r = (float) ratio;
        float ir = (float) 1.0 - r;

        float rgb1[] = new float[3];
        float rgb2[] = new float[3];

        color1.getColorComponents(rgb1);
        color2.getColorComponents(rgb2);

        float red = rgb1[0] * r + rgb2[0] * ir;
        float green = rgb1[1] * r + rgb2[1] * ir;
        float blue = rgb1[2] * r + rgb2[2] * ir;

        if (red < 0) {
            red = 0;
        } else if (red > 255) {
            red = 255;
        }
        if (green < 0) {
            green = 0;
        } else if (green > 255) {
            green = 255;
        }
        if (blue < 0) {
            blue = 0;
        } else if (blue > 255) {
            blue = 255;
        }

        Color color = null;
        try {
            color = new Color(red, green, blue);
        } catch (IllegalArgumentException exp) {
            NumberFormat nf = NumberFormat.getNumberInstance();
            System.out.println(nf.format(red) + "; " + nf.format(green) + "; " + nf.format(blue));
            exp.printStackTrace();
        }
        return color;
    }

}

您可以在以下位置看到此颜色混合算法:

答案 2 :(得分:2)

您必须知道double值的可能范围。 (至少,这会让事情变得更容易理解)。在任何情况下,您都可以将double值转换为[0,1]范围内的值。并且在许多情况下无论如何都要进行这样的规范化

所以你可以创建一个方法

private static double normalize(double min, double max, double value) {
    return (value - min) / (max - min);
}

此方法会将min和max之间的任何“值”转换为[0,1]中的值。要将此标准化值映射到颜色,可以使用

private static Color colorFor(double value) {
    value = Math.max(0, Math.min(1, value));
    int red = (int)(value * 255);
    return new Color(red,0,0);
}

此方法将0.0到1.0之间的值转换为颜色:0.0为黑色,1.0为红色。

假设你在“min”和“max”之间有一个双“值”,你可以将它映射到这样的颜色:

g2d.setColor(colorFor(normalize(min, max, value)));
g2d.fill(shape);

编辑:回复评论:我认为基本上有两种选择:

  • 您可以不断跟踪到目前为止遇到的当前最小/最大值。
  • 或者,您可以使用S形函数将[-Infinity,+ Infinity]的间隔映射到区间[0,1]

第一个选项的缺点是先前与某种颜色相关联的值可能会突然出现不同的颜色。例如,假设您将间隔[0,1]映射到颜色范围[黑色,红色]。现在你获得一个新的最大值,如100000.然后你必须调整你的范围,值0和1将不再有视觉差异:它们都将被映射到'黑色'。

第二种选择的缺点是,初始值的微小差异不会对颜色产生明显影响,具体取决于这些差异发生的范围。例如,您将无法看到10000和10001之间的差异。

因此,您必须清楚自己想要的颜色映射和行为。

但是,这是一个使用S形函数将[-Infinity,+ Infinity]的间隔映射到区间[0,1],并将此区间映射到颜色的示例:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

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


class ColorRange
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new ColorRangePanel());
        f.setSize(1000, 200);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}


class ColorRangePanel extends JPanel
{
    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;

        double values[] = {
            -1e3, -1e2, -1e1, -1, -0.5, 0.0, 0.5, 1, 1e1, 1e2, 1e3    
        };

        for (int i=0; i<values.length; i++)
        {
            double value = values[i];
            int w = getWidth() / values.length;
            int x = i * w;

            g.setColor(Color.BLACK);
            g.drawString(String.valueOf(value), x, 20);

            g.setColor(colorFor(value));
            g.fillRect(x, 50, w, 100);
        }
    }

    private static Color colorFor(double value) 
    {
        double v0 = value / Math.sqrt(1 + value * value); // -1...1
        double v1 = (1 + v0) * 0.5; // 0...1
        return colorForRange(v1);
    }    
    private static Color colorForRange(double value) 
    {
        value = Math.max(0, Math.min(1, value));
        int red = (int)(value * 255);
        return new Color(red,0,0);
    }    
}