I need to display a BufferedImage in one JTable column. I overwrote JTable method
@Override
public Class<?> getColumnClass(int column) {
if (column == 1){
return BufferedImage.class;
}
return super.getColumnClass(column);
}
But I am still obtaining String representation of the object instead of image itself.Does anyone have idea what I am missing?
I'd populate the column that needs to show an image with ImageIcons and have the getColumnClass()
method return Icon.class, and then render it with a JLabel that displays the Icon. In fact, I believe that the DefaultCellRenderer really is a JLabel, and so it should already know how to handle Icons.
Yep, all the model needs is to know that it holds Icons. For example, this code below works in the program below:
DefaultTableModel model = new DefaultTableModel(COL_NAMES, 0) {
@Override
public Class<?> getColumnClass(int column) {
if (getRowCount() > 0) {
return getValueAt(0, column).getClass();
}
return super.getColumnClass(column);
}
};
For example:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class ImageColumnTest2 {
public static final String IMAGE_SHEET_PATH = "http://speckycdn.sdm.netdna-cdn.com/"
+ "wp-content/uploads/2010/08/flag_icons_04.jpg";
public static final String[] COUNTRIES = {
"Denmark", "China", "Chile", "Canada", "Belgium", "Austria",
"Argentina", "France", "Malaysina", "Lebanon", "Korea", "Japan",
"Italy", "Ireland", "India", "Hong Kong", "Greece", "Germany"
};
public static final int COLS = 6;
public static final int ROWS = 3;
private static final String[] COL_NAMES = {"Country", "Flag"};
private JTable table = new JTable();
private JScrollPane mainPane = new JScrollPane(table);
public ImageColumnTest2() throws IOException {
DefaultTableModel model = new DefaultTableModel(COL_NAMES, 0) {
@Override
public Class<?> getColumnClass(int column) {
if (getRowCount() > 0) {
return getValueAt(0, column).getClass();
}
return super.getColumnClass(column);
}
};
URL url = new URL(IMAGE_SHEET_PATH);
BufferedImage img = ImageIO.read(url);
int x1 = 15; // sorry about the magic numbers
img = img.getSubimage(x1, 0, img.getWidth() - 2 * x1, img.getHeight());
int y1 = 20 ; // ditto!
int w = img.getWidth() / COLS;
int h = img.getHeight() / ROWS;
for (int row = 0; row < ROWS; row++) {
int y = (row * img.getHeight()) / ROWS;
for (int col = 0; col < COLS; col++) {
int x = (col * img.getWidth()) / COLS;
BufferedImage subImg = img.getSubimage(x, y, w, h);
subImg = subImg.getSubimage(x1, 0, subImg.getWidth() - 2 * x1, subImg.getHeight() - y1);
ImageIcon icon = new ImageIcon(subImg);
String country = COUNTRIES[col + row * COLS];
Object[] rowData = {country, icon};
model.addRow(rowData);
}
}
table.setModel(model);
table.setRowHeight(((ImageIcon)model.getValueAt(0, 1)).getIconHeight());
}
public JComponent getMainComponent() {
return mainPane;
}
private static void createAndShowGui() {
ImageColumnTest2 imgColumnTest = null;
try {
imgColumnTest = new ImageColumnTest2();
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("ImageColumnTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(imgColumnTest.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
There is no default TableCellRenderer
for BufferedImage
, you're going to have to supply one
Create yourself a new class that extends from DefaultTableCellRenderer
. Override the getTableCellRendererComponent
method
In this method, check the value been passed in is a BufferedImage
, if it is, create a instance of ImageIcon
, passing the BufferedImage
into it.
Use the cell renderes setIcon
method, passing the new instance of ImageIcon
to it
With your table instance, use the setDefaultRenderer
method to associate the cell renderer with the BufferedImage
class
table.setDefaultRenderer(BufferedImage.class, myInstanceOfBufferedImageCellRenderer)
Check out Using Custom Renderers for more info
Added example
So a threw a quick example together using both ideas from myself and Hovercraft.
My personally feeling is that Hovercraft's idea will use less resources and be quicker then using a cell renenderer, so long as you create a ImageIcon
once for each BufferedImage
and maintain that reference.
You could get the custom cell renderer to do the same, but you would need to dabble with WeakHashMap
s to maintain a reference between the BufferedImage
and Icon
and there is still the risk that the BufferedImage
in question will never be collected, leaving the Icon
reference hanging around.
If you weren't doing anything special with the BufferedImage
in the way of rendering, I would use Hovercraft's suggest, purly from a ease of use and resource management perspective.
public class BufferedImageTableCellRenderer {
public static void main(String[] args) {
new BufferedImageTableCellRenderer();
}
public BufferedImageTableCellRenderer() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
File[] files = new File("some folder some where").listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(".gif") || name.endsWith(".jpg") || name.endsWith(".png");
}
});
ImageTableModel model = new ImageTableModel();
for (File file : files) {
try {
model.add(ImageIO.read(file));
} catch (IOException ex) {
ex.printStackTrace();
}
}
JTable table = new JTable(model);
table.setRowHeight(100);
table.setDefaultRenderer(BufferedImage.class, new BufferedImageCellRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BufferedImageCellRenderer extends 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 BufferedImage) {
setIcon(new ImageIcon((BufferedImage)value));
setText(null);
} else {
setText("Bad image");
}
return this;
}
}
public class ImageTableModel extends AbstractTableModel {
private List<BufferedImage> images = new ArrayList<>(25);
private List<Icon> icons = new ArrayList<>(25);
@Override
public int getRowCount() {
return images.size();
}
public void add(BufferedImage image) {
images.add(image);
icons.add(new ImageIcon(image));
fireTableRowsInserted(images.size() - 1, images.size() - 1);
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value = null;
switch (columnIndex) {
case 0:
value = images.get(rowIndex);
break;
case 1:
value = icons.get(rowIndex);
break;
}
return value;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
Class clazz = String.class;
switch (columnIndex) {
case 0:
clazz = BufferedImage.class;
break;
case 1:
clazz = Icon.class;
break;
}
return clazz;
}
@Override
public String getColumnName(int column) {
String name = null;
switch (column) {
case 0:
name = "BufferedImage";
break;
case 1:
name = "Icon";
break;
}
return name;
}
}
}