在 JTable 中制作 3 色标热图

Make 3 color scale heatmap in JTable

我有一个 JTable,比如说 100x100 的整数。 我将这个矩阵归一化,使相应的值从 0 到 1

现在我需要制作一个热图(就像我们在 excel 中做条件格式一样)/示例图

这是通过使用 3 向颜色缩放创建的,中间和最大值设置为百分位数 50 和 90。

现在,我正尝试在 JTable 中生成相同的内容,但我没有看到类似的颜色渐变。

我需要尽可能多地复制颜色的平滑度。我的代码是

int[][] color = { { 99, 190, 123 }, { 255, 235, 132 }, { 248, 105, 107 } };
    int idx1 = 0;
    int idx2 = 0;
    ArrayList<Integer> rgbList = new ArrayList<Integer>();

    if (value == 0.0)
    {
        idx1 = 0;
        idx2 = 0;
    }
    else if (value < fiftyPercentile)
    {
        idx1 = 0;
        idx2 = 1;
    }
    else if (value > nintyPercentile)
    {
        idx1 = 2;
        idx2 = 2;
    }
    else
    {
        idx1 = 1;
        idx2 = 2;
    }


    double r = ((color[idx2][0] - color[idx1][0]) * value + color[idx1][0]);
    double g = ((color[idx2][1] - color[idx1][1]) * value + color[idx1][1]);
    double b = ((color[idx2][2] - color[idx1][2]) * value + color[idx1][2]);

    rgbList.add((int) (r));
    rgbList.add((int) (g));
    rgbList.add((int) (b));
    return rgbList;

I need to replicate as much smoothness in the color as I can get.

使用Color.getHSBColor() to get a palette of N equally spaced hues, as shown here.

private List<Color> clut = new ArrayList<>(N); // color lookup table
…
for (int i = 0; i < n; i++) {
    clut.add(Color.getHSBColor((float) i / N, 1, 1));
}

您可以限制色调的范围,如下所示 here; because your palette goes from green through yellow to red, you'll want to invert 0.0 (red) to 0.33… (green). You can use them in TableCellRenderer, as shown here for alternating colors and here for a palette of brightness values. The variation of @aterai's 说明了 N– 颜色热图的效果,从绿色到黄色和橙色再到红色.

List<Color> palette = new ArrayList<>(N);
…
float gHue = 1 / 3f;
for (int i = 0; i < N; i++) {
    palette.add(Color.getHSBColor(gHue - ( i * gHue / N), 0.5f, 1));
}

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

/**
 * @see 
 * @see 
 */
public class TableTest {

    private static final int N = 100;

    public JComponent makeUI() {
        String[] columnNames = {"1", "2"};
        Object[][] data = {
            {0d, 1d}, {.5, .6}, {.66, .77},
            {.85, .89}, {.78, .99}, {.95, .88}
        };
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {
            @Override
            public Class<?> getColumnClass(int column) {
                return Double.class;
            }
        };
        Random r = new Random();
        double d = r.nextDouble();
        for (int i = 0; i < 100; i++) {
            model.addRow(new Double[]{r.nextDouble(), r.nextDouble()});
        }
        JTable table = new JTable(model);
        List<Color> palette = makeHSBPalette();
        table.setDefaultRenderer(Double.class, new DefaultTableCellRenderer() {
            @Override
            public Component getTableCellRendererComponent(
                JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
                super.getTableCellRendererComponent(
                    table, value, isSelected, hasFocus, row, column);
                if (value instanceof Double) {
                    setBackground(getColor(palette, (Double) value));
                }
                return this;
            }
        });
        JPanel p = new JPanel(new BorderLayout());
        p.add(new JScrollPane(table));
        return p;
    }

    private static List<Color> makeHSBPalette() {
        List<Color> palette = new ArrayList<>(N);
        float gHue = 1 / 3f;
        for (int i = 0; i < N; i++) {
            palette.add(Color.getHSBColor(gHue - ( i * gHue / N), 0.5f, 1));
        }
        return palette;
    }

    private static Color getColor(List<Color> palette, double v) {
        if (v < 0f || v > 1f) {
            throw new IllegalArgumentException("Parameter outside of expected range");
        }
        return palette.get((int) (Math.min(v * N, N - 1)));
    }

    public static void main(String... args) {
        EventQueue.invokeLater(() -> {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            f.getContentPane().add(new TableTest().makeUI());
            f.setSize(320, 240);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}

另一种选择是使用 LinearGradientPaint and PixelGrabber:

private static int[] makeGradientPallet() {
    BufferedImage image = new BufferedImage(100, 1, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2  = image.createGraphics();
    Point start    = new Point(0, 0);
    Point end      = new Point(99, 0);
    float[] dist   = {0.5f, 0.9f, 1.0f};
    Color[] colors = {new Color(99, 190, 123),
                      new Color(255, 235, 132),
                      new Color(248, 105, 107)};
    g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
    g2.fillRect(0, 0, 100, 1);
    g2.dispose();

    int width = image.getWidth(null);
    int[] pallet = new int[width];
    PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width);
    try {
        pg.grabPixels();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    return pallet;
}

截图

TableTest.java

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.*;

public class TableTest {
  public JComponent makeUI() {
    String[] columnNames = {"1", "2"};
      Object[][] data = {
          {0d, 1d}, {.5, .6}, {.66, .77},
          {.85, .89}, {.78, .99}, {.95, .88}
      };
    DefaultTableModel model = new DefaultTableModel(data, columnNames) {
      @Override public Class<?> getColumnClass(int column) {
        return Double.class;
      }
    };
    Random r = new Random();
    double d = r.nextDouble();
    for (int i = 0; i < 100; i++) {
      model.addRow(new Double[] {r.nextDouble(), r.nextDouble()});
    }
    JTable table = new JTable(model);
    int[] pallet = makeGradientPallet();
    table.setDefaultRenderer(Double.class, new DefaultTableCellRenderer() {
      @Override public Component getTableCellRendererComponent(
          JTable table, Object value, boolean isSelected,
          boolean hasFocus, int row, int column) {
        super.getTableCellRendererComponent(
          table, value, isSelected, hasFocus, row, column);
        if (value instanceof Double) {
          Color bgc = getColorFromPallet(pallet, (Double) value);
          setBackground(bgc);
        }
        return this;
      }
    });
    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    return p;
  }
  private static int[] makeGradientPallet() {
    BufferedImage image = new BufferedImage(100, 1, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2  = image.createGraphics();
    Point start    = new Point(0, 0);
    Point end      = new Point(99, 0);
    float[] dist   = {0.5f, 0.9f, 1.0f};
    Color[] colors = {
        new Color(99, 190, 123),
        new Color(255, 235, 132),
        new Color(248, 105, 107)
    };
    g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
    g2.fillRect(0, 0, 100, 1);
    g2.dispose();

    int width = image.getWidth(null);
    int[] pallet = new int[width];
    PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width);
    try {
      pg.grabPixels();
    } catch (InterruptedException ex) {
      ex.printStackTrace();
    }
    return pallet;
  }
  private static Color getColorFromPallet(int[] pallet, double v) {
    if (v < 0f || v > 1f) {
      throw new IllegalArgumentException("Parameter outside of expected range");
    }
    int i = (int)(pallet.length * v);
    int max = pallet.length - 1;
    int index = Math.min(Math.max(i, 0), max);
    return new Color(pallet[index]);
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TableTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}