简体   繁体   English

根据值Java Swing动态设置颜色

[英]Set Color dynamically based on value Java Swing

I am using Java Swing. 我正在使用Java Swing。 I would like to display colors based on a double values which I compute. 我想根据我计算出的双精度值显示颜色。

Edit - I need to fill the color of a Path2D object. 编辑-我需要填充Path2D对象的颜色。 Currently, this is how I do it 目前,这就是我的做法

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

Now I do not want the color to be fixed as Color.Red but need to set it based on a value I compute. 现在,我不希望将颜色固定为Color.Red而是需要根据我计算的值进行设置。 The double value can be negative or positive. 双精度值可以是负数或正数。 The more negative the value is, the darker the color should be. 值越负,颜色应该越深。 The color need not be red. 颜色不必是红色。 How can I do so? 我该怎么办?

As a concrete example of the TableCellRenderer approach suggested here , implement the Icon interface, as shown here . 作为一个具体的例子TableCellRenderer方法建议在这里 ,实现了Icon界面,如图所示这里 Instead of varying the size, choose a color based on varying saturation or brightness using Color.getHSBColor() , as shown here . 代替改变大小,选择基于使用不同的饱和度或亮度的颜色Color.getHSBColor()如图所示在这里

Addendum: Like the example cited , the render below assumes that the data values are normalized in the interval [0, 1) . 附录:像引用示例一样,以下渲染假定数据值在间隔[0, 1)进行了归一化。 You'll need to scale to your data model's minimum and maximum values instead. 您将需要缩放到数据模型的最小值和最大值。 If the model is updated frequently, it may be worth updating these values with each addition. 如果模型经常更新,那么每次添加这些值都值得更新。

图片

/**
 * @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;
    }
}

The question lacks context. 这个问题缺乏背景。 All Swing components have the setBackground method which can change the background of opaque components. 所有Swing组件都有setBackground方法,该方法可以更改不透明组件的背景。

If you want to change the background color of a JTable cell or a JList or a JTable or JComboBox , then you need to provide a custom cell renderer capable of perfoming this task, in which case you should have a look at... 如果要更改JTable单元格或JListJTableJComboBox的背景色,则需要提供一个能够执行此任务的自定义单元格渲染器,在这种情况下,您应该看一下...

Updated with example, based on updates to the OP's question 根据OP的问题更新了示例

The following is an example of color blending algorithm that allows you to specify the colors you want to use and at what ratio/fraction they should appear. 以下是颜色混合算法的示例,该算法使您可以指定要使用的颜色以及应显示的比例/分数。 The example is very simply, it uses only three colors spread evenly across the range, but you could put more colors in or adjust the weight according to your requirements. 该示例非常简单,它仅使用在整个范围内均匀分布的三种颜色,但是您可以根据需要添加更多颜色或调整权重。

The example takes a series of randomly generated values and normalises them, so that as the value tends towards 0 (or the lower normal range) it will get darker, as it approaches 1, it will get lighter. 该示例采用一系列随机生成的值并对它们进行归一化,以使该值趋向于0(或较低的正常范围)时将变得更暗,接近1时将变得更亮。

You could change the algorithm to normalise positive and negative values separately if you chose. 如果选择,可以更改算法以分别对正值和负值进行归一化。

吧台

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

}

You can see this color blend algorithm in use at: 您可以在以下位置看到正在使用的颜色混合算法:

You have to know the possible range of the double values. 您必须知道double值的可能范围。 (At least, this would make things easier to understand). (至少,这会使事情更容易理解)。 In any case, you can transform your double value to be in the range [0,1]. 无论如何,您都可以将double值转换为[0,1]范围。 And it's in many cases beneficial to do such a kind of normalization anyhow. 无论如何,进行这种标准化在很多情况下都是有益的。

So you can create a method 所以你可以创建一个方法

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

This method will convert any "value" between min and max to a value in [0,1]. 此方法会将介于min和max之间的任何“值”转换为[0,1]中的值。 In order to map this normalized value to a color, you can use 为了将此归一化值映射到颜色,可以使用

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

This method will convert a value between 0.0 and 1.0 into a color: 0.0 will be black, and 1.0 will be red. 此方法会将0.0到1.0之间的值转换为颜色:0.0将为黑色,而1.0将为红色。

So assuming you have a double "value" between "min" and "max", you can map it to a color like this: 因此,假设您在“最小”和“最大”之间有一个双“值”,则可以将其映射为如下颜色:

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

EDIT: Reply to the comment: I think there are basically two options: 编辑:回复评论:我认为基本上有两个选择:

  • You could constantly keep track of the current min/max values that you have encountered so far. 您可以不断跟踪到目前为止所遇到的当前最小/最大值。
  • Alternatively, you could map the interval of [-Infinity,+Infinity] to the interval [0,1] using a sigmoidal function 或者,您可以使用S形函数将[-Infinity,+ Infinity]的间隔映射到间隔[0,1]

The first option has the disadvantage that values that have previously been associated with a certain color may suddenly have a different color. 第一种选择的缺点是,先前与某种颜色关联的值可能突然具有不同的颜色。 For example assume that you mapped the interval [0,1] to the color range [black, red]. 例如,假设您将间隔[0,1]映射到颜色范围[黑色,红色]。 Now you obtain a new maximum value like 100000. Then you have to adjust your range, the values 0 and 1 will no longer have a visual difference: They will both be mapped to 'black'. 现在,您获得了一个新的最大值,例如100000。然后您必须调整范围,值0和1将不再具有视觉差异:它们都将被映射为“黑色”。

The second option has the disadvantage that small differences in the initial values will not have a noticable effect on the color, depending on the range in which these differences occur. 第二种选择的缺点是,初始值的微小差异将不会对颜色产生明显影响,具体取决于这些差异发生的范围。 For example, you will not be able to see a difference between 10000 and 10001 there. 例如,您将看不到10000和10001之间的差异。

So you have to be clear about what color mapping and behavior you want. 因此,您必须清楚所需的颜色映射和行为。

However, here is an example that uses a sigmoidal function to map the interval of [-Infinity,+Infinity] to the interval [0,1], and this interval to a color: 但是,下面是一个使用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);
    }    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM