I am attempting to display an image file as soon as it is selected from a file chooser. The file chooser is restricted to .png and .jpg files with the selected files being stored in a variable of type File. To do this I have set up an ImageView, and I wish to set the image with this new file only problem is it is of type File not Image.
How can this be achieved? Code so far...
public void fileSelection(){
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Profile Picture");
fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Image Files", "*.png", "*jpg"));
File selectedFile = fileChooser.showOpenDialog(null);
File selectedFileInput = selectedFile;
if(selectedFile != null) {
selectedFileOutput.setText("File selected: " + selectedFile.getName());
previewPicture.setImage();
} else {
selectedFileOutput.setText("Please select a profile picture...");
}
}
You can simply create an image with
Image image = new Image(selectedFile.toURI().toString());
and then place it in the ImageView:
previewPicture.setImage(image);
Other constructors offer more control over resources required for loading the image. If you want to force the image to be a certain size, you can resize it on loading, which will save memory if the user chooses a large image but you only want to display a scaled-down version. Additionally, loading a large image may take time, so you should not load it on the UI thread. The Image constructors taking string versions of URLs have options to automatically load the image in a background thread. The following forces the width and height to be both no more than 240 pixels (while maintaining the original aspect ratio), and loads the image in the background (thus not blocking the UI):
Image image = new Image(selectedFile.toURI().toString(),
240, // requested width
240, // requested height
true, // preserve ratio
true, // smooth rescaling
true // load in background
);
See the documentation for other available constructors.
You create image and set to the ImageView as follows
Image image = new Image(new FileInputStream(selectedFile));
previewPicture.setImage(image);
I adding a late answer to demonstrate different image-file loading alternatives:
import java.io.File;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class ImagesFromFile extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Load Images From File");
primaryStage.setScene(new Scene(new ImagePanel().getPane()));
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
/**
<pre>
Folders structure:
-Project
| - src
| | - package
| | | - ImagePanel.java
| | | - red_dot.png
| |
| | - resources
| | - blue_dot.png
|
| - black_dot.png
</pre>
*/
class ImagePanel{
private Pane pane;
ImagePanel() {
try{
//not embedded resource. doesn't work when packaged in jar
File file = new File("black_dot.png");
Image imageFromProjectFolder = new Image(file.toURI().toString());//or new Image(new FileInputStream("black_dot.png"));
ImageView view1 = new ImageView(imageFromProjectFolder);
URL url = getClass().getResource("red_dot.png");
Image imageFromSourceFolder = new Image(url.openStream());
ImageView view2 = new ImageView(imageFromSourceFolder); //or new ImageView("/package/red_dot.png");
url = getClass().getResource("/resources/blue_dot.png");
Image imageFromReourceFolder = new Image(url.openStream());
ImageView view3 = new ImageView(imageFromReourceFolder); //or new ImageView("/resources/blue_dot.png");
pane = new TilePane(view1, view2, view3);
} catch (Exception ex) {ex.printStackTrace();}
}
Pane getPane(){
return pane;
}
}
Related
I am attempting to display an image file as soon as it is selected from a file chooser. The file chooser is restricted to .png and .jpg files with the selected files being stored in a variable of type File. To do this I have set up an ImageView, and I wish to set the image with this new file only problem is it is of type File not Image.
How can this be achieved? Code so far...
public void fileSelection(){
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Profile Picture");
fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Image Files", "*.png", "*jpg"));
File selectedFile = fileChooser.showOpenDialog(null);
File selectedFileInput = selectedFile;
if(selectedFile != null) {
selectedFileOutput.setText("File selected: " + selectedFile.getName());
previewPicture.setImage();
} else {
selectedFileOutput.setText("Please select a profile picture...");
}
}
You can simply create an image with
Image image = new Image(selectedFile.toURI().toString());
and then place it in the ImageView:
previewPicture.setImage(image);
Other constructors offer more control over resources required for loading the image. If you want to force the image to be a certain size, you can resize it on loading, which will save memory if the user chooses a large image but you only want to display a scaled-down version. Additionally, loading a large image may take time, so you should not load it on the UI thread. The Image constructors taking string versions of URLs have options to automatically load the image in a background thread. The following forces the width and height to be both no more than 240 pixels (while maintaining the original aspect ratio), and loads the image in the background (thus not blocking the UI):
Image image = new Image(selectedFile.toURI().toString(),
240, // requested width
240, // requested height
true, // preserve ratio
true, // smooth rescaling
true // load in background
);
See the documentation for other available constructors.
You create image and set to the ImageView as follows
Image image = new Image(new FileInputStream(selectedFile));
previewPicture.setImage(image);
I adding a late answer to demonstrate different image-file loading alternatives:
import java.io.File;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class ImagesFromFile extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Load Images From File");
primaryStage.setScene(new Scene(new ImagePanel().getPane()));
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
/**
<pre>
Folders structure:
-Project
| - src
| | - package
| | | - ImagePanel.java
| | | - red_dot.png
| |
| | - resources
| | - blue_dot.png
|
| - black_dot.png
</pre>
*/
class ImagePanel{
private Pane pane;
ImagePanel() {
try{
//not embedded resource. doesn't work when packaged in jar
File file = new File("black_dot.png");
Image imageFromProjectFolder = new Image(file.toURI().toString());//or new Image(new FileInputStream("black_dot.png"));
ImageView view1 = new ImageView(imageFromProjectFolder);
URL url = getClass().getResource("red_dot.png");
Image imageFromSourceFolder = new Image(url.openStream());
ImageView view2 = new ImageView(imageFromSourceFolder); //or new ImageView("/package/red_dot.png");
url = getClass().getResource("/resources/blue_dot.png");
Image imageFromReourceFolder = new Image(url.openStream());
ImageView view3 = new ImageView(imageFromReourceFolder); //or new ImageView("/resources/blue_dot.png");
pane = new TilePane(view1, view2, view3);
} catch (Exception ex) {ex.printStackTrace();}
}
Pane getPane(){
return pane;
}
}
I am having a error for my GUI. Trying to set title bar icon then be included in a Runnable JAR.
BufferedImage image = null;
try {
image = ImageIO.read(getClass().getClassLoader().getResource("resources/icon.gif"));
}
catch (IOException e) {
e.printStackTrace();
}
frame.setIconImage(image);
Here is the error I am getting:
Exception in thread "main" java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(Unknown Source)
at GUI.<init>(GUI.java:39)
at GUI.main(GUI.java:351)
The image is in the correct directory which "resources" folder is the root of the
project file
First of all, change this line :
image = ImageIO.read(getClass().getClassLoader().getResource("resources/icon.gif"));
to this :
image = ImageIO.read(getClass().getResource("/resources/icon.gif"));
More info, on as to where lies the difference between the two approaches, can be found on this thread - Different ways of loading a Resource
For Eclipse:
How to add Images to your Resource Folder in the Project
For NetBeans:
Handling Images in a Java GUI Application
How to add Images to the Project
For IntelliJ IDEA:
Right-Click the src Folder of the Project. Select New -> Package
Under New Package Dialog, type name of the package, say resources. Click OK
Right Click resources package. Select New -> Package
Under New Package Dialog, type name of the package, say images. Click OK
Now select the image that you want to add to the project, copy it. Right click resources.images package, inside the IDE, and select Paste
Use the last link to check how to access this file now in Java code. Though for this example, one would be using
getClass().getResource("/resources/images/myImage.imageExtension");
Press Shift + F10, to make and run the project. The resources and images folders, will be created automatically inside the out folder.
If you are doing it manually :
How to add Images to your Project
How to Use Icons
A Little extra clarification, as given in this answer's first
code example.
QUICK REFERENCE CODE EXAMPLE(though for more detail consider, A little extra clarification link):
package swingtest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
/**
* Created with IntelliJ IDEA.
* User: Gagandeep Bali
* Date: 7/1/14
* Time: 9:44 AM
* To change this template use File | Settings | File Templates.
*/
public class ImageExample {
private MyPanel contentPane;
private void displayGUI() {
JFrame frame = new JFrame("Image Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
contentPane = new MyPanel();
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private class MyPanel extends JPanel {
private BufferedImage image;
public MyPanel() {
try {
image = ImageIO.read(MyPanel.class.getResource("/resources/images/planetbackground.jpg"));
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(400, 300): new Dimension(image.getWidth(), image.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ImageExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
There's a much easier way to load and set an image as a frame icon:
frame.setIconImage(
new ImageIcon(getClass().getResource("/resources/icon.gif")).getImage());
And thats all :)! You don't even have to use a try-catch block because ImageIcon does not throw any declared exceptions. And due to getClass().getResource(), it works both from file system and from a jar depending how you run your application.
If you need to check whether the image is available, you can check if the URL returned by getResource() is null:
URL url = getClass().getResource("/resources/icon.gif");
if (url == null)
System.out.println( "Could not find image!" );
else
frame.setIconImage(new ImageIcon(url).getImage());
The image files must be in the directory resources/ in your JAR, as shown in How to Use Icons and this example for the directory named images/.
Is it somehow possible to render a GUI to a BufferedImage or another kind of memory image without displaying it on a screen ?
I know this will loose all kinds of hardware acceleration, but for a simple GUI that is refreshed only once or twice a second this should not be an issue.
Already tried to get JavaFX to output an image, but i can't find a way to leave out rendering on a screen first. Does anyone know a way to do this with JavaFX or Swing ?
It is no problem to draw a simple GUI myself using simple image manipulations, but then i would have to do it all by hand and using Swing or FX would make it much easier.
Edit:
To make it a bit more clear, i don't have an active display, but i can save an image which then gets displayed through other means. To be exact its a raspberry pi, but without a primary display device with a connected tft display using the GPIO port. So i can't render the UI directly to a display device, but need to create an image that i can save at a specific location. All methods i have tried so far need a primary display device.
Yes, it is possible to render a GUI to an image offscreen.
Here is a sample using JavaFX, with example image output as below:
The example works by rendering the chart to an scene which is not added to any window and no window (Stage in JavaFX terminology) is ever shown. The snapshot method is used to take a snapshot of the node and then ImageIO utilities are used to save the snapshot to disk.
Rendering of the offscreen scene will be hardware accelerated if the underlying hardware/software platform supports it.
import javafx.application.*;
import javafx.collections.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.*;
import javafx.scene.chart.PieChart;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.*;
public class OffscreenImageRecorder extends Application {
private static final Logger logger = Logger.getLogger(OffscreenImageRecorder.class.getName());
private static final String IMAGE_TYPE = "png";
private static final String IMAGE_FILENAME = "image." + IMAGE_TYPE;
private static final String WORKING_DIR = System.getProperty("user.dir");
private static final String IMAGE_PATH = new File(WORKING_DIR, IMAGE_FILENAME).getPath();
private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
private final Random random = new Random();
private final int CHART_SIZE = 400;
#Override
public void start(Stage stage) throws IOException {
Parent chart = createChart();
Image image = snapshot(chart);
exportPng(SwingFXUtils.fromFXImage(image, null), IMAGE_PATH);
Platform.exit();
}
private Parent createChart() {
// create a chart.
final PieChart chart = new PieChart();
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", random.nextInt(30)),
new PieChart.Data("Oranges", random.nextInt(30)),
new PieChart.Data("Plums", random.nextInt(30)),
new PieChart.Data("Pears", random.nextInt(30)),
new PieChart.Data("Apples", random.nextInt(30))
);
chart.setData(pieChartData);
chart.setTitle("Imported Fruits - " + dateFormat.format(new Date()));
// It is important for snapshots that the chart is not animated
// otherwise we could get a snapshot of the chart before the
// data display has been animated in.
chart.setAnimated(false);
chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
chart.setPrefSize(CHART_SIZE, CHART_SIZE);
chart.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
return chart;
}
private Image snapshot(final Parent sourceNode) {
// Note: if the source node is not in a scene, css styles will not
// be applied during a snapshot which may result in incorrect rendering.
final Scene snapshotScene = new Scene(sourceNode);
return sourceNode.snapshot(
new SnapshotParameters(),
null
);
}
private void exportPng(BufferedImage image, String filename) {
try {
ImageIO.write(image, IMAGE_TYPE, new File(filename));
logger.log(Level.INFO, "Wrote image to: " + filename);
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
launch(args);
}
}
It's a bit of a hack, but you could create a frame and position it on a invisible location (using Swing in this example):
frame = new JFrame("Invisible frame");
frame.setBounds(-1000, 100, 640, 480);
I am trying to add an image to a JButton and I'm not sure what I'm missing. When I run the following code the button looks exactly the same as if I had created it without any image attribute. Water.bmp is in the root of my project folder.
ImageIcon water = new ImageIcon("water.bmp");
JButton button = new JButton(water);
frame.add(button);
I think that your problem is in the location of the image. You shall place it in your source, and then use it like this:
JButton button = new JButton();
try {
Image img = ImageIO.read(getClass().getResource("resources/water.bmp"));
button.setIcon(new ImageIcon(img));
} catch (Exception ex) {
System.out.println(ex);
}
In this example, it is assumed that image is in src/resources/ folder.
#Rogach
and you may like to add:
// to remote the spacing between the image and button's borders
button.setMargin(new Insets(0, 0, 0, 0));
// to add a different background
button.setBackground( ... );
// to remove the border
button.setBorder(null);
It looks like a location problem because that code is perfectly fine for adding the icon.
Since I don't know your folder structure, I suggest adding a simple check:
File imageCheck = new File("water.bmp");
if(imageCheck.exists())
System.out.println("Image file found!")
else
System.out.println("Image file not found!");
This way if you ever get your path name wrong it will tell you instead of displaying nothing. Exception should be thrown if file would not exist, tho.
You put your image in resources folder and use follow code:
JButton btn = new JButton("");
btn.setIcon(new ImageIcon(Class.class.getResource("/resources/img.png")));
public class ImageButton extends JButton {
protected ImageButton(){
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Image img = Toolkit.getDefaultToolkit().getImage("water.bmp");
g2.drawImage(img, 45, 35, this);
g2.finalize();
}
}
OR use this code
class MyButton extends JButton {
Image image;
ImageObserver imageObserver;
MyButtonl(String filename) {
super();
ImageIcon icon = new ImageIcon(filename);
image = icon.getImage();
imageObserver = icon.getImageObserver();
}
public void paint( Graphics g ) {
super.paint( g );
g.drawImage(image, 0 , 0 , getWidth() , getHeight() , imageObserver);
}
}
I did only one thing and it worked for me .. check your code is this method there ..
setResizable(false);
if it false make it true and it will work just fine ..
I hope it helped ..
buttonB.setIcon(new ImageIcon(this.getClass().getResource("imagename")));
//paste required image on C disk
JButton button = new JButton(new ImageIcon("C:water.bmp");
This code work for me:
BufferedImage image = null;
try {
URL file = getClass().getResource("water.bmp");
image = ImageIO.read(file);
} catch (IOException ioex) {
System.err.println("load error: " + ioex.getMessage());
}
ImageIcon icon = new ImageIcon(image);
JButton quitButton = new JButton(icon);
For example if you have image in folder res/image.png you can write:
try
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("image.png");
// URL input = classLoader.getResource("image.png"); // <-- You can use URL class too.
BufferedImage image = ImageIO.read(input);
button.setIcon(new ImageIcon(image));
}
catch(IOException e)
{
e.printStackTrace();
}
In one line:
try
{
button.setIcon(new ImageIcon(ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("image.png"))));
}
catch(IOException e)
{
e.printStackTrace();
}
If the image is bigger than button then it will not shown.
eclipse example:
After reading the above, it still took me more research to understand, where and how to place image resources, using eclipse.
The outcome: in eclipse you need to store images underneath any "Source Folder" (such as "src") or below in a "Folder".
You create both by right-clicking on your project, "New"->"Source Folder" or "New"->"Folder". Any folder name is fine. Examples for "<Source Folder>/<image Folder>" are "src/images" or "resource/img".
Here's some fully functioning sample code, which expects two button images, "Button-Image-Up.png" and "Button-Image-Down.png" in folder "images":
import javax.swing.JDialog;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class ImageButtonDlg extends JDialog {
// define button images and fall-back text
private static gstrTxt="<Button Fall-back Text>"; // in case image does not load
private static String gstrImgUp="Button-Image-Up.png"; // regular image
private static String gstrImgDown="Button-Image-Down.png"; // button pressed image
private static String gstrImgFolder="images"; // image folder, "/images" is also fine
public static void main(String[] args) {
ImageButtonDlg dialog = new ImageButtonDlg();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
}
/**
* Create the dialog.
*/
public ImageButtonDlg() {
initializeDialog();
}
/**
* Create a new ImageIcon from an image resource.
* The input can be:
* - an image file name, e.g. <image.png>
* - a relative path, e.g. <image/image.png>
* - an absolute path, e.g. </home/user/dev/image.png>
* In eclipse, images can be stored below any project "Source folder",
* such as "src".
* ├── src
* │ ├── img1.png
* │ └── images
* │ └── img2.png
* ├── resources
* │ ├── img3.png
* │ └── images
* │ └── img4.png
* └── img5.png
* In above example img1.png to img4.png are found by
* image file name or relative path
* However, img5 is stored in the project root folder and
* needs an absolute path.
*
* #param strImagePath - image filename, absolute path or relative path
* #return new ImageIcon or 'null' if load fails
*/
public static ImageIcon getResourceImageIcon(String strFilepath) {
ClassLoader loader = null;
URL url = null;
Image img=null;
ImageIcon imgIcon=null;
loader = ImageButtonDlg.class.getClassLoader();
System.out.println(loader.toString());
try { // file location: <a relative path>
url = loader.getResource("images/"+strFilepath);
if(url==null) {
System.out.printf("ImageButtonDlg.class.getResource(%s)=>null\n", "images/"+strFilepath);
}
} catch (Exception e0) {
e0.printStackTrace();
}
if(url==null) {
try { // file location: <image filename>
url = loader.getResource(strFilepath);
if(url==null) {
System.out.printf("Util.class.getResource(%s)=>null\n", strFilepath);
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
try {
img = ImageIO.read(url);
} catch (Exception e2) {
e2.printStackTrace();
System.exit(0);
}
if (img!=null){
imgIcon = new ImageIcon(img);
}
return imgIcon;
}
/**
* Create JButton with Image
* In case Image load fails, we create a JButton with text
* #param strImgPath - path to Image
* #param strAltText - fall-back text
* #return
*/
public static JButton getNewJButtonWithImageIcon(String strImgPath, String strAltText) {
JButton btnReturn = null;
ImageIcon imgIcon = getResourceImageIcon(strImgPath);
if (imgIcon!=null){
btnReturn = new JButton(imgIcon);
}
else {
btnReturn = new JButton(strAltText);
}
return btnReturn;
}
/**
* Image button was pressed, display a message box
*/
private void actionImageButtonPressed() {
try {
JOptionPane.showMessageDialog(null, "Image Button was pressed", "Info", JOptionPane.INFORMATION_MESSAGE);
}
catch (Exception e) {
; // ignore
}
}
/**
* Initialize the dialog
* add a button panel and the image button to the window/content pane
*/
private void initializeDialog()
{
this.setTitle("Image Button Example");
this.setResizable(false);
this.setBounds(200, 200, 699, 601);
JPanel panelButton = new JPanel();
panelButton.setLayout(new FlowLayout(FlowLayout.RIGHT)); // all buttons in a row
getContentPane().add(panelButton, BorderLayout.SOUTH); // button pane at the bottom of the window
// create the Image Button
JButton btnImageButton = getNewJButtonWithImageIcon(gstrImgUp, gstrTxt);
btnImageButton.setToolTipText("<Explain what pressing the Button does>");
btnImageButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
actionImageButtonPressed();// The Action
}
});
// load button image when button clicked/released
btnImageButton.addMouseListener((MouseListener) new MouseAdapter() {
public void mousePressed(MouseEvent e) {
btnImageButton.setIcon(getResourceImageIcon(gstrImgDown));
}
public void mouseReleased(MouseEvent e) {
btnImageButton.setIcon(getResourceImageIcon(gstrImgUp));
}
});
btnImageButton.setActionCommand("ImageButtonAction");
// add button to button panel
panelButton.add(btnImageButton);
}
}
Have Fun!
Volker Fröhlich
P.S.:
Perhaps someone can share additional practical experience.
In eclipse "WindowBuilder Editor" such image buttons are shown, but they cannot be accessed - is there some workaround?
What is the NetBeans approach, if any different?
I wanted to create a JFileChooser with thumbnail view of image files.So I subclassed FileView and in the method which creates ImageIcon did some scaling sothat thumbnail images are shown.
However,the overall effect is that, the filechooser widget takes some time before opening a directory and showing thumbnails..In createImageIcon() below,I need to call new ImageIcon() twice-once with the image filepath and next with the resized image as constructor argument.I think this is what slows the widget .
Is there a more efficient alternative?Any suggestions/pointers most welcome.
thanks,
mark
public static void main(String[] args) {
JFileChooser chooser=new JFileChooser();
ThumbNailView thumbView=new ThumbNailView();
chooser.setFileView(thumbView);
}
class ThumbNailView extends FileView{
public Icon getIcon(File f){
Icon icon=null;
if(isImageFile(f.getPath())){
icon=createImageIcon(f.getPath(),null);
}
return icon;
}
private ImageIcon createImageIcon(String path,String description) {
if (path != null) {
ImageIcon icon=new ImageIcon(path);
Image img = icon.getImage() ;
Image newimg = img.getScaledInstance( 16, 16, java.awt.Image.SCALE_SMOOTH ) ;
return new ImageIcon(newimg);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
private boolean isImageFile(String filename){
//return true if this is image
}
I was actually surprised to see that, despite using the native look & feel in Windows, the file chooser indeed doesn't have a thumbnail view. I tried your example and you're going along the right lines, but I see how slow it was for folders with a lot of large images. The overhead is, of course, due to I/O when reading the file contents and then interpreting the image, which is unavoidable.
What's even worse, is that I found out that FileView.getIcon(File) is called a lot - before the file list is shown, when you mouse over an icon, and when the selection changes. If we don't cache the images after loading them, we'll be pointlessly reloading images all the time.
The obvious solution is to push all the image loading off onto another thread or a thread pool, and once we have our scaled-down result, put it into a temporary cache so it can be retrieved again.
I played around with Image and ImageIcon a lot and I discovered that an ImageIcon's image can be changed at any time by calling setImage(Image). What this means for us is, within getIcon(File), we can immediately return a blank or default icon, but keep a reference to it, passing it along to a worker thread that will load the image in the background and set the icon's image later when it's done (The only catch is that we must call repaint() to see the change).
For this example, I'm using an ExecutorService cached thread pool (this is the fastest way to get all images, but uses a lot of I/O) to process the image loading tasks. I'm also using a WeakHashMap as the cache, to ensure that we only hold onto the cached icons for as long as we need them. You could use another kind of Map, but you would have to manage the number of icons you hold onto, to avoid running out of memory.
package guitest;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.filechooser.FileView;
public class ThumbnailFileChooser extends JFileChooser {
/** All preview icons will be this width and height */
private static final int ICON_SIZE = 16;
/** This blank icon will be used while previews are loading */
private static final Image LOADING_IMAGE = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB);
/** Edit this to determine what file types will be previewed. */
private final Pattern imageFilePattern = Pattern.compile(".+?\\.(png|jpe?g|gif|tiff?)$", Pattern.CASE_INSENSITIVE);
/** Use a weak hash map to cache images until the next garbage collection (saves memory) */
private final Map imageCache = new WeakHashMap();
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFileChooser chooser = new ThumbnailFileChooser();
chooser.showOpenDialog(null);
System.exit(1);
}
public ThumbnailFileChooser() {
super();
}
// --- Override the other constructors as needed ---
{
// This initializer block is always executed after any constructor call.
setFileView(new ThumbnailView());
}
private class ThumbnailView extends FileView {
/** This thread pool is where the thumnnail icon loaders run */
private final ExecutorService executor = Executors.newCachedThreadPool();
public Icon getIcon(File file) {
if (!imageFilePattern.matcher(file.getName()).matches()) {
return null;
}
// Our cache makes browsing back and forth lightning-fast! :D
synchronized (imageCache) {
ImageIcon icon = imageCache.get(file);
if (icon == null) {
// Create a new icon with the default image
icon = new ImageIcon(LOADING_IMAGE);
// Add to the cache
imageCache.put(file, icon);
// Submit a new task to load the image and update the icon
executor.submit(new ThumbnailIconLoader(icon, file));
}
return icon;
}
}
}
private class ThumbnailIconLoader implements Runnable {
private final ImageIcon icon;
private final File file;
public ThumbnailIconLoader(ImageIcon i, File f) {
icon = i;
file = f;
}
public void run() {
System.out.println("Loading image: " + file);
// Load and scale the image down, then replace the icon's old image with the new one.
ImageIcon newIcon = new ImageIcon(file.getAbsolutePath());
Image img = newIcon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_SMOOTH);
icon.setImage(img);
// Repaint the dialog so we see the new icon.
SwingUtilities.invokeLater(new Runnable() {public void run() {repaint();}});
}
}
}
Known issues:
1) We don't maintain the image's aspect ratio when scaling. Doing so could result in icons with strange dimensions that will break the alignment of the list view. The solution is probably to create a new BufferedImage that is 16x16 and render the scaled image on top of it, centered. You can implement that if you wish!
2) If a file is not an image, or is corrupted, no icon will be shown at all. It looks like the program only detects this error while rendering the image, not when we load or scale it, so we can't detect this in advance. However, we might detect it if we fix issue 1.
Use fileDialog instead of JfileChooser for choising the image:
FileDialog fd = new FileDialog(frame, "Test", FileDialog.LOAD);
String Image_path
fd.setVisible(true);
name = fd.getDirectory() + fd.getFile();
image_path=name;
ImageIcon icon= new ImageIcon(name);
icon.setImage(icon.getImage().getScaledInstance(jLabel2.getWidth(),jLabel2.getHeight() , Image.SCALE_DEFAULT));
jLabel2.setIcon(icon);
You could use a default icon for each fileand load the actual icons in another thread (perhaps using a SwingWorker?). As the icons are loaded the SwingWorker could call back and update the FileView.
Not sure if a single SwingWorker would do the trick, or whether it would be better to use one for each icon being loaded.