javaswingcolorsjtabletablecellrenderer

TablecellRenderer setForeground only on certain characters in cell


I'm trying to create a JTable where in one column certain characters have a different color than the rest.

Text1test

Text2test

Text4test

In this example, I want the numbers to be blue, the rest black (the lines represent the cells of the column). Is this possible? I only found answers how to color the complete text of a cell with TableCellRenderer.


Solution

  • A TableCellRenderer may return any AWT Component, so you could subclass JComponent and overwrite paintComponent to tokenize your text and paint each token in an appropriate color. And indeed, this might be the most efficient way to solve your problem.

    A less involved solution would be leveraging JLabel's support for rendering HTML-formatted text. Again, the idea would be tokenizing your text and coloring the tokens as appropriate. E.g.

    class MarkupRenderer implements TableCellRenderer {
      private final DefaultTableCellRenderer dtcr;
      private final Pattern digits;
    
      MarkupRenderer() {
        dtcr = new DefaultTableCellRenderer();
        digits = Pattern.compile("\\d+");
      }
    
      @Override
      public Component getTableCellRendererComponent(
        JTable table,
        Object value,
        boolean isSelected,
        boolean hasFocus,
        int row,
        int column
      ) {
        return dtcr.getTableCellRendererComponent(
          table, format((String)value), isSelected, hasFocus, row, column);
      }
    
      private String format( String s ) {
        final StringBuilder sb = new StringBuilder();
        sb.append("<html><head></head><body>");
    
        int lastIdx = 0;
        final Matcher m = digits.matcher(s);
        while (m.find()) {
          final int idx1 = m.start();
          final int idx2 = m.end();
          sb.append(s, lastIdx, idx1);
          sb.append("<span style=\"color: #0000ff;\">");
          sb.append(s, idx1, idx2);
          sb.append("</span>");
    
          lastIdx = idx2;
        }
        if (lastIdx < s.length()) {
          sb.append(s.substring(lastIdx));
        }
    
        sb.append("</body></html>");
        return sb.toString();
      }
    }
    
    

    The above assumes that your numbers are always non-negative integral numbers. If you need to support negative numbers or decimal numbers as well, you need to adjust the tokenizing logic in format accordingly.