1

I have a column in JTable which should display two types: String OR ImageIcon, not both. Each cell in that column has own thread which calculates data. In the beginning I put to each cell an image(like waiting logo), then REPLACE(not append) the image with a string of calculated data. I tried to extend default TableCell renderer, but it displays image like object address(javax.swing.ImageIcon@342...) and then replaces with string. Another variant, it displays the image correctly, but replaces it with empty string(or it is not visible?).

How to set it up so the table displays cell content correctly according to type?

Here is what I have at the moment:

class IconAndStringRenderer extends DefaultTableCellRenderer {

private static final long serialVersionUID = 3606788739290618405L;

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 Icon) {

      setIcon((Icon) value);
      setText("");
    } 
    return this;
  }
}

Here is JTable:

table = new JTable(model) {

        private static final long serialVersionUID = 8058795799817761161L;

        public Class<?> getColumnClass(int column) {
            if (column == TARGET_COLUMN)
                return ImageIcon.class;
            else
                return super.getColumnClass(column);
        }
    };

A few more questions:

  1. How to set it so the text replaces the image, not write text after image(even if it's not visible);

  2. How to set text color, I gonna use setForebackground(Color c), but if I use it, the image is not dislayed.

  3. Is it possible to make it working with Jlabel? Set up required Jlabel(with image or text) in the thread which modifies a cell and just setValueAt(label, row, column);

Swat Zp
  • 11
  • 1
  • 5
  • 1
    See http://stackoverflow.com/questions/4941372/how-to-insert-image-into-jtable-cell – Reimeus Feb 03 '13 at 11:27
  • unrelated: never-ever let the view decide about model properties - returning the correct class is the exclusive responsibility of the model. – kleopatra Feb 03 '13 at 14:04
  • repeating @trashgod for emphasis: for effective help sooner you should provide an SSCCE _and_ explain your requirement completely. Until now, feels like you are doing something fundamentally wrong if the answer doesn't solve the problem. – kleopatra Feb 03 '13 at 14:10

3 Answers3

2

You need a custom renderer which can understand both types you use.

For example

public class IconAndStringRenderer extends DefaultTableCellRenderer {
  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 Icon) {
      setText("");
      setIcon((Icon) value);
    }
    return this;
  }
}

Try to set this class as your column renderer

Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • Thanks. Is there any way to change color of the input text? I tried setForeground(Color c), but this method overwrites all images, they just disappear – Swat Zp Feb 03 '13 at 11:59
  • Or is it possible to work with JLabel. I will put empty Jlabel to each cell, then update needed cells with JLabel(with Icon) and then update needed cells with another JLabel(with text with needed color). – Swat Zp Feb 03 '13 at 12:05
  • Tried this, but it returns Jlabel object address: if (value instanceof JLabel) { setValue(value); } – Swat Zp Feb 03 '13 at 12:06
  • The `DefaultTableCellRenderer` already handles text and icons, as shown [here](http://stackoverflow.com/a/14672312/230513); you shouldn't need `instanceof`. – trashgod Feb 03 '13 at 12:40
2

The default renderer knows how to display both strings and icons. All you have to do is ensure that your TableModel returns the correct class from getColumnClass(), String.class and Icon.class, respectively. Examples may be found here.

Addendum: Here's a minimal example to illustrate the principle, based on default implementations.

Addendum: Not sure if it works when needed to return different classes for the same column.

If you really need to choose the renderer on a per-cell basis, override prepareRenderer(), as shown here.

image

import java.awt.EventQueue;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;

/** @see https://stackoverflow.com/a/14672312/230513 */
public class Test {

    private static final Icon YES = UIManager.getIcon("InternalFrame.maximizeIcon");
    private static final Icon NO = UIManager.getIcon("InternalFrame.closeIcon");

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        DefaultTableModel model = new DefaultTableModel(
            new Object[]{"Name", "Icon"}, 0) {

            @Override
            public Class<?> getColumnClass(int col) {
                if (col == 1) {
                    return Icon.class;
                } else {
                    return super.getColumnClass(col);
                }
            }
        };
        model.addRow(new Object[]{"One", YES});
        model.addRow(new Object[]{"Two", NO});
        final JTable table = new JTable(model);
        table.setRowHeight(YES.getIconHeight() +2);
        f.add(table);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Test().display();
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Not sure, if it works when needed to return different classes for the same column – Swat Zp Feb 03 '13 at 12:40
  • Please edit your question to include that requirement; also add an [sscce](http://sscce.org/) such as the one above. – trashgod Feb 03 '13 at 12:43
  • That requirement was mentioned from the beginning: 'I have a column in JTable which should display two types: String or ImageIcon.' which means handling different classes in the same column – Swat Zp Feb 03 '13 at 12:51
  • Please edit your question to _clarify_ that requirement; I've elaborated above; for additional guidance, include an [sscce](http://sscce.org/). – trashgod Feb 03 '13 at 12:56
  • I've cited several _complete_ examples that address your update. Please edit your question to include an [sscce](http://sscce.org/) that shows your current approach. – trashgod Feb 03 '13 at 13:30
  • BTW, is it possible to make it working with Jlabel? Set up required Jlabel(with image or text) in the thread which modifies a cell and just setValueAt(label, row, column); – Swat Zp Feb 03 '13 at 13:32
  • 1
    @SwatZp _return different classes for the same column_ don't know what you mean: the getColumnClass must return the exact same class on each call. If the instances contain different types for different rows, the type returned by the method must be their common super type, at the worst that would be Object.class. In that case, it's up to the renderer to decide how to ... render them – kleopatra Feb 03 '13 at 14:26
  • okay, thanks kleopatra, that's one of the things I wanted to know – Swat Zp Feb 03 '13 at 14:29
2

To take advantage of the default renderers you can override the getCellRenderer(...) method of JTable to return the appropriate renderer. Something like:

public TableCellRenderer getCellRenderer(int row, int column)
{
    int modelColumn = convertColumnIndexToModel(column);

    if (modelColumn == ???)
    {
        Class rowClass = getModel().getValueAt(row, modelColumn).getClass();
        return getDefaultRenderer( rowClass );
    }
    else
        return super.getCellRenderer(row, column);
}
camickr
  • 321,443
  • 19
  • 166
  • 288