Search code examples
javaswingformattingjtabletablecellrenderer

How to change a cell in a jTable programmatically?


I'm currently writing an application where I present to the user amongst other things links to websites in a JTable. I already set up my JTable correctly to open up the corresponding website upon clicking the regarding cell. However I struggle with correctly formatting the cell so that users know they actually have the possibility of clicking the cell for instantly opening the website.

Hence what I want to achieve is to have the colour of the link being blue at least and even better also underlined. I searched through different articles on SO regarding this but couldn't quite grasp how the things explained there work together - despite I'm not entirely sure if these things would have even be what I'm actually looking for.

The way I fill my table is the following:

String[][] rowData = new String[entries.size() + 1][entries.get(0).length + 1];


rowData[0] = columnNames;

int i = 1;

Iterator<String[]> iterator = entries.iterator();
while (iterator.hasNext()) {
    rowData[i] = iterator.next();
    i++;
}

tblEntries = new JTable(rowData, columnNames);

entries in this case is an ArrayList that is passed over by the database handler and - as the name suggests - contains all entries for the table. After reading the ArrayList into the respective Array I initialize the table as seen in the last row. Now all the links are actually stored in all rows > 0 and the 4th column.

My first approach was doing something like this:

for (int j = 0; j < entries.size(); j++) {
    for (int j2 = 0; j2 < entries.get(0).length; j2++) {
        tblEntries.editCellAt(row, column, e);
    }
}

where e should be an event that checks wheter the conditions for a link are satisfied or not and execute the formatting accordingly. However I don't really now what kind of event is needed to pass it to the function.

An other approach I saw in a different SO article was to use the prepareRenderer method to specify the conditions for rendering the content correctly. However apparently this seems to be only possible for own implementations of a JTable which I'd like to avoid as tblEntries.prepareRenderer() and applying a new TableCellRenderer or DefaultTableCellRenderer doesn't give me the function that I need to override according to above mentioned SO article.

So, what would be the best and most convenient way to tackle this problem down? Thanks in advance for your any adivce and help.


SOLUTION:

For anyone facing a similar problem I'll put my solution here. As suggested by @camickr the best solution is a custom DefaultTreeCellRenderer the problem in this specific scenario however is that it will also render the specific table-header (which obviously doesn't contain any links) in the link format. Hence I searched a bit further and found this website where I found a working code for customising where the renderer should be applied.

In the end I came up with this code:

String[][] rowData = new String[entries.size() + 1][entries.get(0).length + 1];


rowData[0] = columnNames;

int i = 1;

Iterator<String[]> iterator = entries.iterator();
while (iterator.hasNext()) {
    rowData[i] = iterator.next();
    i++;
}

tblEntries = new JTable(rowData, columnNames) {
    @Override
    public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
        Component c = super.prepareRenderer(renderer, row, column);

        if (row > 0 && column == 4) {
            c = super.prepareRenderer(new LinkRenderer(), row, column);
        }

        return c;
    }
};

For reference for the LinkRenderer see accepted answer below.


Solution

  • what I want to achieve is to have the colour of the link being blue at least and even better also underlined.

    This is controlled by the renderer. The default renderer for the JTable is a JLabel.

    You can easily create a custom renderer to display the text in blue:

    DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
    renderer.setForeground( Color.BLUE );
    table.getColumnModel().getColumn(3).setCellRenderer( renderer );
    

    Unfortunately underlining the text will be more difficult. Underlining text in a component can be achieved by setting a property of the Font which is easy enough to do for a JLabel:

    JLabel label = new JLabel("Underlined label");
    Font font = label.getFont();
    Map<TextAttribute, Object> map = new HashMap<TextAttribute, Object>();
    map.put(TextAttribute.FONT, font);
    map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
    font = Font.getFont(map);
    label.setFont(font);
    

    However, you can't just set the Font for the renderer because when each cell is rendered the default renderer will reset the Font to be the Font used by the table.

    So if you want to implement a custom renderer with a custom Font, you need to extend the DefaultTableCellRenderer and override the getTableCellRendererComponent(….) method. The code might be something like:

    class LinkRenderer extends DefaultTableCellRenderer
    {
        private Font underlineFont;
    
        public LinkRenderer()
        {
            super();
            setForeground( Color.BLUE );
    
            underlineFont = .getFont();
            Map<TextAttribute, Object> map = new HashMap<TextAttribute, Object>();
            map.put(TextAttribute.FONT, underlineFont);
            map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
            underLinefont = Font.getFont(map);
        }
    
        @Override
        public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
        {
            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    
            setFont( underlineFont );
    
            return this;
        }
    }
    

    Read the section from the Swing tutorial on Renderers and Editors for more information.

    So the other approach is to NOT use a custom renderer but instead you can add HTML to the table model. A JLabel can display simple HTML.

    So the text you add to the model would be something like:

    String text = "<html><u><font color=blue>the link goes here</font></ul></html>";