Add Jlabel with Image to Jlist to Show All the Images

Add JLabel with image to JList to show all the images

Note that I would not design the code this way, but I wanted to keep it as close to the original as practical, while making it work to display a list of images on a Windows based box.

ListView

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class ListView {

public static void main(String[] args) throws IOException {
String path = "C:/Documents and Settings/All Users/Documents/" +
"My Pictures/Sample Pictures";
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

File folder = new File(path);
File[] listOfFiles = folder.listFiles();
DefaultListModel listModel = new DefaultListModel();
int count = 0;
for (int i = 0; i < listOfFiles.length; i++)
{
System.out.println("check path"+listOfFiles[i]);
String name = listOfFiles[i].toString();
// load only JPEGs
if ( name.endsWith("jpg") ) {
ImageIcon ii = new ImageIcon(ImageIO.read(listOfFiles[i]));
listModel.add(count++, ii);
}
}
JList lsm=new JList(listModel);
lsm.setVisibleRowCount(1);

frame.add(new JScrollPane(lsm));

frame.pack();
frame.setVisible(true);
}
}

How to add image in jlist

You want to look as a custom ListCellRenderer. You can look at Provding a Custom Renderer for JComboBox. It the same for a JList. The tutorial over-complicates a bit for simple scenarios. They extends JLabel and implements ListCellRender where you have to implement a few unnecessary things if you just want basic functionality but with am image.

You can just instead extends or create a anonymous DefaultListCellRender and just get the JLabel render component and add to it, like setting Font and ImageIcon. Something like this

public class MarioListRenderer extends DefaultListCellRenderer {

Font font = new Font("helvitica", Font.BOLD, 24);

@Override
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
label.setIcon(imageMap.get((String) value));
label.setHorizontalTextPosition(JLabel.RIGHT);
label.setFont(font);
return label;
}
}

What happens is that each cell uses this renderer and calls the getListCellRendererComponent method. The value you see passed to the method is the value in each cell, in my case, one of the character names in the list. I then map that to the corresponding ImageIcon and set the Icon on the JLabel renderer component.

Sample Image

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class MarioList {

private final Map<String, ImageIcon> imageMap;

public MarioList() {
String[] nameList = {"Mario", "Luigi", "Bowser", "Koopa", "Princess"};
imageMap = createImageMap(nameList);
JList list = new JList(nameList);
list.setCellRenderer(new MarioListRenderer());

JScrollPane scroll = new JScrollPane(list);
scroll.setPreferredSize(new Dimension(300, 400));

JFrame frame = new JFrame();
frame.add(scroll);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public class MarioListRenderer extends DefaultListCellRenderer {

Font font = new Font("helvitica", Font.BOLD, 24);

@Override
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
label.setIcon(imageMap.get((String) value));
label.setHorizontalTextPosition(JLabel.RIGHT);
label.setFont(font);
return label;
}
}

private Map<String, ImageIcon> createImageMap(String[] list) {
Map<String, ImageIcon> map = new HashMap<>();
for (String s : list) {
map.put(s, new ImageIcon(
getClass().getResource("/marioscaled/" + s + ".png")));
}
return map;
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MarioList();
}
});
}
}

Side Note

  • AndrewThompson is correct about just checking the tutorial first. You could have easily found an example implementation, then tried it out. Swing tutorials can be found here. Look under the Using Swing Components for how to use different components.

  • Swing apps should be run on the Event Dispatch Thread (EDT). You can do so by wrapping your creatAndShowGui() in a SwinUtilities.invokeLater.... See more at Initial Threads


UPDATE with internet images.

Sample ImageSample ImageSample ImageSample ImageSample Image

new Code

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class MarioList {

private final Map<String, ImageIcon> imageMap;

public MarioList() {
String[] nameList = {"Mario", "Luigi", "Bowser", "Koopa", "Princess"};
imageMap = createImageMap(nameList);
JList list = new JList(nameList);
list.setCellRenderer(new MarioListRenderer());

JScrollPane scroll = new JScrollPane(list);
scroll.setPreferredSize(new Dimension(300, 400));

JFrame frame = new JFrame();
frame.add(scroll);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public class MarioListRenderer extends DefaultListCellRenderer {

Font font = new Font("helvitica", Font.BOLD, 24);

@Override
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
label.setIcon(imageMap.get((String) value));
label.setHorizontalTextPosition(JLabel.RIGHT);
label.setFont(font);
return label;
}
}

private Map<String, ImageIcon> createImageMap(String[] list) {
Map<String, ImageIcon> map = new HashMap<>();
try {
map.put("Mario", new ImageIcon(new URL("http://i.stack.imgur.com/NCsHu.png")));
map.put("Luigi", new ImageIcon(new URL("http://i.stack.imgur.com/UvHN4.png")));
map.put("Bowser", new ImageIcon(new URL("http://i.stack.imgur.com/s89ON.png")));
map.put("Koopa", new ImageIcon(new URL("http://i.stack.imgur.com/QEK2o.png")));
map.put("Princess", new ImageIcon(new URL("http://i.stack.imgur.com/f4T4l.png")));
} catch (Exception ex) {
ex.printStackTrace();
}
return map;
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MarioList();
}
});
}
}

Adding a list of images in jscrollpane and show it

Take a look at the section from the Swing tutorial on How to Use Combo Boxes. You find an example that does almost exactly what you want. The example uses a combo box, but the code for a JList will be very similar. That is the combo box contains a list of Strings and when you select an item the matching image is displayed.

more efficiently load images for JList, presumably with SwingWorker

One approach might be the following:

Whenever a cell renderer component for a certain element (Product) is requested, you check whether the matching image is already loaded. If not, you start a Swing worker that does the work of loading and processing the image in the background. When the worker is done, the image is placed into a cache for later lookup. In the meantime, you let the renderer just say "Loading..." or something.

A very quick implementation is here:

Sample Image

And as an MCVE:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingWorker;

public class LazyImageLoadingCellRendererTest
{

private JPanel mainPanel = new JPanel();
private JList<Product> list = new JList<Product>();
private JScrollPane scroll = new JScrollPane();

public LazyImageLoadingCellRendererTest()
{
mainPanel.setBackground(new Color(129, 133, 142));

scroll.setViewportView(list);
scroll.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setPreferredSize(new Dimension(80, 200));

list.setCellRenderer(new LazyImageLoadingCellRenderer<Product>(list,
LazyImageLoadingCellRendererTest::loadAndProcessImage));
DefaultListModel<Product> model = new DefaultListModel<Product>();

for (int i=0; i<1000; i++)
{
model.addElement(new Product("id" + i));
}
list.setModel(model);

mainPanel.add(scroll);
}

public static void main(String[] args) throws IOException
{

EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
JFrame frame = new JFrame("WorkerTest");
frame.setContentPane(
new LazyImageLoadingCellRendererTest().mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(300, 300);
frame.setMinimumSize(new Dimension(160, 255));
frame.setVisible(true);
}
});
}

private static final Random random = new Random(0);

private static BufferedImage loadAndProcessImage(Product product)
{
String id = product.getProductID();
int w = 100;
int h = 20;
BufferedImage image =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.GREEN);
g.fillRect(0, 0, w, h);
g.setColor(Color.BLACK);
g.drawString(id, 10, 16);
g.dispose();

long delay = 500 + random.nextInt(3000);
try
{
System.out.println("Load time of " + delay + " ms for " + id);
Thread.sleep(delay);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return image;
}

class Product
{
String productID;

public Product(String productID)
{
this.productID = productID;
}

public String getProductID()
{
return productID;
}
}

}

class LazyImageLoadingCellRenderer<T> extends JLabel
implements ListCellRenderer<T>
{
private final JList<?> owner;
private final Function<? super T, ? extends BufferedImage> imageLookup;
private final Set<T> pendingImages;
private final Map<T, BufferedImage> loadedImages;

public LazyImageLoadingCellRenderer(JList<?> owner,
Function<? super T, ? extends BufferedImage> imageLookup)
{
this.owner = Objects.requireNonNull(
owner, "The owner may not be null");
this.imageLookup = Objects.requireNonNull(imageLookup,
"The imageLookup may not be null");
this.loadedImages = new ConcurrentHashMap<T, BufferedImage>();
this.pendingImages =
Collections.newSetFromMap(new ConcurrentHashMap<T, Boolean>());
setOpaque(false);
}

class ImageLoadingWorker extends SwingWorker<BufferedImage, Void>
{
private final T element;

ImageLoadingWorker(T element)
{
this.element = element;
pendingImages.add(element);
}

@Override
protected BufferedImage doInBackground() throws Exception
{
try
{
BufferedImage image = imageLookup.apply(element);
loadedImages.put(element, image);
pendingImages.remove(element);
return image;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}

@Override
protected void done()
{
owner.repaint();
}
}

@Override
public Component getListCellRendererComponent(JList<? extends T> list,
T value, int index, boolean isSelected, boolean cellHasFocus)
{
BufferedImage image = loadedImages.get(value);
if (image == null)
{
if (!pendingImages.contains(value))
{
//System.out.println("Execute for " + value);
ImageLoadingWorker worker = new ImageLoadingWorker(value);
worker.execute();
}
setText("Loading...");
setIcon(null);
}
else
{
setText(null);
setIcon(new ImageIcon(image));
}
return this;
}
}

Note:

This is really just a quick example showing the general approach. Of course, this could be improved in many ways. Although the actual loading process is already pulled out into a Function (thus making it generically applicable for any sort of image, regardless of where it comes from), one major caveat is that: It will try to load all images. A nice extension would be to add some smartness here, and make sure that it only loads the images for which the cells are currently visible. For example, when you have a list of 1000 elements, and want to see the last 10 elements, then you should not have to wait for 990 elements to be loaded. The last elements should be priorized higher and loaded first. However, for this, a slightly larger infrastructure (mainly: an own task queue and some stronger connection to the list and its scroll pane) may be necessary. (I'll possibly tackle this one day, because it might be a nice and interesting thing to have, but until then, the example above might do it...)

How to change description image in JList java

I agree with trashgod (+1 to his suggestion), a JTable will be a simpler solution, here's why...

JList doesn't support editability, so you'd need to create it...

So, first, we'd need some kind of ListModel that provided some additional functionality, in particular, the ability to set the value at a particular index...

import javax.swing.ListModel;

public interface MutableListModel<E> extends ListModel<E> {

public void setElementAt(E value, int index);

public boolean isCellEditable(int index);

}

Next, we'd need some kind editor, in this case, following standard Swing API conventions, this asks for some kind of base interface

import java.awt.Component;
import javax.swing.CellEditor;
import javax.swing.JList;

public interface ListCellEditor<E> extends CellEditor {

public Component getListCellEditorComponent(
JList<E> list,
E value,
boolean isSelected,
int index);

public void applyEditorValue(E value);

}

Now, we need to create ourselves a custom JList capable of actually performing all the required functionality of editing a cell value...

Things like...

  • Recognising a "start editing" event
  • Determine if the cell can be edited
  • Managing the editing process, preparing and showing the editor, knowing when the editor has stopped or canceled and clean up appropriately...
  • Handling selection changes while editing is in progress
  • Handling component focus change (which I've not done cause that's an awesome amount of fun in itself...)

For example...

import java.awt.Component;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JList;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class EditableList<E> extends JList<E> {

private ListCellEditor<E> editor;
private int editingCell = -1;
private Component editorComponent;

private CellEditorHandler handler;

public EditableList(MutableListModel<E> model) {
this();
setModel(model);
}

public EditableList() {
InputMap im = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "editorCell");

ActionMap am = getActionMap();
am.put("editorCell", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Edit baby");
int cell = getSelectedIndex();
editCellAt(cell);
}
});

addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (isEditing()) {

if (!stopCellEditing()) {

cancelCellEditing();

}

}
}
});
}

public boolean isEditing() {

return editorComponent != null;

}

public void cancelCellEditing() {

getEditor().cancelCellEditing();

}

public boolean stopCellEditing() {

return getEditor().stopCellEditing();

}

public CellEditorHandler getCellEditorHandler() {
if (handler == null) {
handler = new CellEditorHandler();
}
return handler;
}

public void setEditor(ListCellEditor<E> value) {
if (value != editor) {
ListCellEditor old = editor;
editor = value;
firePropertyChange("editor", old, editor);
}
}

public ListCellEditor<E> getEditor() {
return editor;
}

public boolean isCellEditable(int cell) {

boolean isEditable = false;

ListModel model = getModel();
if (model instanceof MutableListModel) {

MutableListModel mcm = (MutableListModel) model;
isEditable = mcm.isCellEditable(cell);

}

return isEditable;

}

protected void editCellAt(int index) {

if (isCellEditable(index)) {

ListCellEditor<E> editor = getEditor();
if (editor != null) {

Rectangle cellBounds = getCellBounds(index, index);
E value = getModel().getElementAt(index);
boolean selected = isSelectedIndex(index);
editingCell = index;

editor.addCellEditorListener(getCellEditorHandler());
editorComponent = editor.getListCellEditorComponent(this, value, selected, index);
editorComponent.setBounds(cellBounds);

ensureIndexIsVisible(index);

add(editorComponent);

revalidate();

}

}

}

public int getEditingCell() {
return editingCell;
}

protected void editingHasStopped(ListCellEditor editor) {

editingCell = -1;

if (editorComponent != null) {
remove(editorComponent);
}

if (editor != null) {

editor.removeCellEditorListener(getCellEditorHandler());

}

}

public class CellEditorHandler implements CellEditorListener {

@Override
public void editingStopped(ChangeEvent e) {

E value = getModel().getElementAt(getEditingCell());

getEditor().applyEditorValue(value);
((MutableListModel) getModel()).setElementAt(value, getEditingCell());

editingHasStopped((ListCellEditor)e.getSource());

}

@Override
public void editingCanceled(ChangeEvent e) {

editingHasStopped((ListCellEditor)e.getSource());

}

}

}

Now, having done all that, you will need change the way you've structured your program, instead of using a List and ListModel to manage the descriptions and images separately, you should probably merge the concept into a single, manageable object, for example...

public class ImagePreview {

private String name;
private ImageIcon image;

public ImagePreview(String name, ImageIcon image) {
this.name = name;
this.image = image;
}

public String getDescription() {
return name;
}

public ImageIcon getImage() {
return image;
}

protected void setDescription(String description) {
this.name = description;
}

}

Even if you choose to use a JTable instead, you'll find this easier to manage...

Now we need some way to render and edit these values, to this end, I choose to start with a base component which could display the image and text...

public class ImagePreviewPane extends JPanel {

private JLabel imageLabel = new JLabel();
private JLabel descriptionLabel = new JLabel();

public ImagePreviewPane() {
setLayout(new BorderLayout());
Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5);
imageLabel.setBorder(emptyBorder);
descriptionLabel.setBorder(emptyBorder);
add(imageLabel, BorderLayout.CENTER);
add(descriptionLabel, BorderLayout.SOUTH);
}

protected JLabel getDescriptionLabel() {
return descriptionLabel;
}

protected JLabel getImageLabel() {
return imageLabel;
}

public void setImage(ImageIcon icon) {
imageLabel.setIcon(icon);
}

public void setDescription(String text) {
descriptionLabel.setText(text);
}

}

Create an extended version that could handle editing...

public static class ImagePreviewEditorPane extends ImagePreviewPane {

private JTextField editor;

public ImagePreviewEditorPane() {
super();
editor = new JTextField();
remove(getDescriptionLabel());
add(editor, BorderLayout.SOUTH);
}

@Override
public void setDescription(String text) {
editor.setText(text);
}

public String getDescription() {
return editor.getText();
}

public void setImagePreview(ImagePreview preview) {
setImage(preview.getImage());
setDescription(preview.getDescription());
}

@Override
public void addNotify() {
super.addNotify();
editor.requestFocusInWindow();
}

}

This is done to try and make it easier to modify the components later...

Next, a ListCellRenderer

public class ImageCellRenderer extends ImagePreviewPane implements ListCellRenderer {

public ImageCellRenderer() {
}

@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Color bg = null;
Color fg = null;

JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsert()
&& dropLocation.getIndex() == index) {

bg = DefaultLookup.getColor(this, getUI(), "List.dropCellBackground");
fg = DefaultLookup.getColor(this, getUI(), "List.dropCellForeground");

isSelected = true;
}

if (isSelected) {
setBackground(bg == null ? list.getSelectionBackground() : bg);
setForeground(fg == null ? list.getSelectionForeground() : fg);
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}

if (value instanceof ImagePreview) {
ImagePreview ip = (ImagePreview) value;
setImage(ip.getImage());
setDescription(ip.getDescription());
} else {
setImage(null);
setDescription("??");
}

setEnabled(list.isEnabled());
setFont(list.getFont());

return this;
}

}

And editor...

public class ImagePreviewListCellEditor extends AbstactListCellEditor<ImagePreview> {

private ImagePreviewEditorPane previewPane;

public ImagePreviewListCellEditor() {
previewPane = new ImagePreviewEditorPane();
InputMap im = previewPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "accept");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");

ActionMap am = previewPane.getActionMap();
am.put("accept", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
am.put("cancel", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
cancelCellEditing();
}
});
}

public void applyEditorValue(ImagePreview preview) {
preview.setDescription(previewPane.getDescription());
}

@Override
public Component getListCellEditorComponent(JList<ImagePreview> list, ImagePreview value, boolean isSelected, int index) {

Color bg = null;
Color fg = null;

JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsert()
&& dropLocation.getIndex() == index) {

bg = DefaultLookup.getColor(previewPane, previewPane.getUI(), "List.dropCellBackground");
fg = DefaultLookup.getColor(previewPane, previewPane.getUI(), "List.dropCellForeground");

isSelected = true;
}

if (isSelected) {
previewPane.setBackground(bg == null ? list.getSelectionBackground() : bg);
previewPane.setForeground(fg == null ? list.getSelectionForeground() : fg);
} else {
previewPane.setBackground(list.getBackground());
previewPane.setForeground(list.getForeground());
}

if (value instanceof ImagePreview) {
ImagePreview preview = (ImagePreview)value;
previewPane.setImagePreview(preview);
}


Related Topics



Leave a reply



Submit