How to add an ImageIcon to a JToolBar - java

I am trying to add a icon to a toolbar but what is the best place to put it in? My desktop or should I make a new file in the project file or add all the pictures in because it is not showing and this is my code:
JToolBar toolBar = new JToolBar();
String[] iconFiles = {"pen-icon","",""};
String[] buttonLabels = {"New","Open","Save"};
icon = new ImageIcon[iconFiles.length];
Obutton = new JButton[buttonLabels.length];
for (int i = 0; i < buttonLabels.length; ++i) {
icon[i] = new ImageIcon(iconFiles[i]);
Obutton[i] = new JButton(icon[i]);
Obutton[i].setToolTipText(buttonLabels[i]);
if (i == 3)
toolBar.addSeparator();
toolBar.add(Obutton[i]);
}

I would use an Action. Here is the AbstractAction constructor
public AbstractAction(String name, Icon icon) - Creates an Action with the specified name and small icon.
Parameters:
name - the name (Action.NAME) for the action; a value of null is ignored
icon - the small icon (Action.SMALL_ICON) for the action; a value of null is ignored
The benefit of using an Action is that is can be reused for components with similar purposes. So say you want to have an icon button in the toolbar to open a file, and also have a JMenuItem in a JMenu that also opens a file. They could share the same action, thus sharing the same icon, action command, and action to perform.
Action action = new AbstractAction("someActionCommand", someIcon) {
#Override
public void actionPerformed(ActionEvent e) {
// do something.
}
};
toolbar.add(action);
The above will automatically put the icon for you, but not the String. In a JMenuItem it would put both the String and the icon.
Then just add the Action to the tool bar.
See more at How to use Actions
To answer you real question, as #MadProgrammer noted, you should be loading your images as an embedded resource, using
ImageIcon icon = new ImageIcon(MyClass.class.getResource("/resources/images/image.png"));
where the /resources/images directory is in the src, and getResource() returns a URL. Upon build, your IDE should copy the files into the class path for you.
ProjectRoot
src
resources
images
image.png
You'll come to find that when using a file from the file system, will not work upon time of deployment
Here's an example, where the JMenuItem and the JToolBar button share the same action. Notice that in the JToolBar all I have to do is add the Action, I don't need to create a button for it. The JToolBar automatically makes it a button, without the action command
I use this "open.gif" from the below file structure and use
ImageIcon icon = new ImageIcon(
ActionTest.class.getResource("/resources/image/open.gif"));
Here's the result
Here's the code. Enjoy!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class ActionTest {
public ActionTest() {
ImageIcon openIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/new.gif"));
Action openAction = new AbstractAction("Open", openIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Open File");
}
};
Action saveAction = new AbstractAction("Save", saveIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Save File");
}
};
Action newAction = new AbstractAction("New", newIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New File");
}
};
JMenuItem openMenuItem = new JMenuItem(openAction);
JMenuItem saveMenuItem = new JMenuItem(saveAction);
JMenuItem newMenuItem = new JMenuItem(newAction);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.add(openMenuItem);
fileMenu.add(saveMenuItem);
fileMenu.add(newMenuItem);
menuBar.add(fileMenu);
JToolBar toolBar = new JToolBar();
toolBar.add(Box.createHorizontalGlue());
toolBar.setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
JFrame frame = new JFrame("Toolbar and Menu Test");
frame.setJMenuBar(menuBar);
frame.add(toolBar, BorderLayout.PAGE_START);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ActionTest();
}
});
}
}

Resources of this type are typically best contained within the application context (such as a jar file). This reduces the chances of someone tampering with it as it's much more time consuming to unpack, modify and repack a jar file then simply replace a file. It also reduces what you need to distribute as it becomes self-contained.
These are known as embedded resources.
Where you would put them within this context is up to up, many people use a "resources" folder to store these types of files, but sometimes, you may want something that is relative to the context of the class. It's up to you.
This raises issues with loading these resources, as you can no longer reference them using something like File.
In general you can use Class#getResource(String), which returns a URL or Class#getResourceAsStream(String) which returns a InputStream. This provides you with all you need to load these embedded resources.
ImageIcon(String) expects the value to be a file reference, which means it won't work for embedded resources, but ImageIcon provides a constructor that takes a URL as a reference, this means you would need to use
icon[i] = new ImageIcon(getClass().getResource(iconFiles[i]));
To load your images.
Based on your example, the images would need to be relative to the class (ie within a directory structure the same as your package structure). How you achieve this will depend on your development environment.
Remember, you can also specify relative paths to getResource and even an absolute path in some contexts. An absolute path basic prefixes the elements of class path to the specified path when it searches for the resources.

Related

Wait for user input Java Swing

I am using JAVA Swing to create a very basic UI. When I run the program, a window will open with a message and browse button(using frame and JButtons for the same). On click of browse button, another window will open to navigate to the file. This I have achieved by calling a FileChooser on the click event of Browse button. However, my program does not wait for user input. The first window with browse button opens and program keeps on executing and ends up in an error as no file has been selected. How do I halt the execution till user input is provided?
In a forum it was advised to use showOpenDialog() method of browser but that straightway opens a browsing window, whereas I want to give the provision to user to click on Browse buttonbrowsewindow
pick file window
My code is below
frame.setLayout(new FlowLayout());
// set up a file picker component
JFilePicker filePicker = new JFilePicker("Pick a file", "Browse...");
filePicker.setMode(JFilePicker.MODE_OPEN);
filePicker.addFileTypeFilter(".jpg", "JPEG Images");
filePicker.addFileTypeFilter(".mp4", "MPEG-4 Videos");
// access JFileChooser class directly
JFileChooser fileChooser = filePicker.getFileChooser();
fileChooser.setCurrentDirectory(new File("C:/"));
// add the component to the frame
frame.add(filePicker);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(520, 100);
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
System.out.println();
JPicker is the custom class which creates a filechooser and sets things to be done on click of Browse button
Of Course, You set the JFrame visible at the end of its' initialization. You need to do this within the main() method of your startup class. Where is yours?
The JFilePicker (created by: Nam Ha Minh) is applied to a JFrame as a Java Component in order to save a little time in GUI development. I personally would just use the JFileChooser directly within a JButton ActionPerformed event. If you had followed the directions properly then you would see that you need a main() method which only makes sense. What your application Startup class should look like is something like this:
import java.awt.FlowLayout;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class TestJFilePicker extends JFrame {
private static final long serialVersionUID = 1L;
public TestJFilePicker() {
super("Test using JFilePicker");
setLayout(new FlowLayout());
// set up a file picker component
JFilePicker filePicker = new JFilePicker("Pick a file", "Browse...");
filePicker.setMode(JFilePicker.MODE_OPEN);
filePicker.addFileTypeFilter(".jpg", "JPEG Images");
filePicker.addFileTypeFilter(".mp4", "MPEG-4 Videos");
// access JFileChooser class directly
JFileChooser fileChooser = filePicker.getFileChooser();
fileChooser.setCurrentDirectory(new File("D:/"));
// add the component to the frame
add(filePicker);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(520, 100);
setLocationRelativeTo(null); // center on screen
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestJFilePicker().setVisible(true);
}
});
}
}
The above code (which is the work of Nam Ha Minh) of course assumes that you have already applied the JFilePicker and the FileTypeFilter class files to your project. Without them the above code will not work.

Why this GUI app does not show me the image?

I'm trying yo write a java app with a JFrame object that must show three label objects, with text and image, my text "North" and "South" shows up on execution, but my image does not, even I put the image file into src folder.
package deitel9;
import java.awt.BorderLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JFrame;
public class LabelDemo {
public static void main(String[] args)
{
//crate a label with a plain text
JLabel northLabel = new JLabel("North");
//crate an icon from an image so we can put it on a JLabel
ImageIcon labelIcon = new ImageIcon("maldive.jpg");
//crate a label with an Icon instead of text
JLabel centerLabel = new JLabel(labelIcon);
//create another label with an Icon
JLabel southLabel = new JLabel(labelIcon);
//set the label to display text (as well as an icon)
southLabel.setText("South");
//create a frame to hold the labels
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add the labels to the frame; the second argument specifies
//where on the frame to add the label
application.add(northLabel,BorderLayout.NORTH);
application.add(centerLabel,BorderLayout.CENTER);
application.add(southLabel,BorderLayout.SOUTH);
application.setSize(300,300);
application.setVisible(true);
}//end main
}//end class LabelDemo
Since your image stored in same package where LabelDemo is stored, try this,
ImageIcon labelIcon = new ImageIcon(LabelDemo.class.getResource("/deitel9/maldive.jpg").getFile());
or
private String getImage() {
return getClass().getResource("/deitel9/maldive.jpg").getFile();
}
ImageIcon labelIcon = new ImageIcon(new LabelDemo().getImage());
To figure out what you did wrong in a situation like this, you can simply call
File file = new File ("maldive.jpg");
System.out.println(file.getAbsolutePath());
This will print out the absolute path it is looking at for your file, which might give you an indication about what you did wrong.
Of course if you know how to use the debugger, you don't need the second line (and technically not even the first one, but that's a bit trickier ;))
According to the documentation, the ImageIcon constructor you chose expects a file name or file path, thus the image needs to be on file system rather than on the class path.
Creates an ImageIcon from the specified file. [...] The specified String can be a file name or a file path.
Given your description, when your project layout looks like this
\---src
\---deitel9
LabelDemo.java
maldive.jpg
then you should be able to retrieve the image as resource located on the class path like this:
ImageIcon labelIcon = new ImageIcon(LabelDemo.class.getResource("maldive.jpg"));

How do I make JFileChooser open in the current directory the user is in?

I do not want to specify a directory. I just want it to automatically "know" and open in the directory the user is working in. How do I do this?
This examples defaults to the user.dir on first showing. It then retains the instance of the chooser to automatically track the last load or save location, as suggested by MadProgrammer.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.io.*;
public class ChooserInCurrentDir {
// the GUI as seen by the user (without frame)
JPanel gui = new JPanel(new BorderLayout());
JFileChooser fileChooser;
private JTextArea output = new JTextArea(10, 40);
ChooserInCurrentDir() {
initComponents();
}
public final void initComponents() {
gui.setBorder(new EmptyBorder(2, 3, 2, 3));
String userDirLocation = System.getProperty("user.dir");
File userDir = new File(userDirLocation);
// default to user directory
fileChooser = new JFileChooser(userDir);
Action open = new AbstractAction("Open") {
#Override
public void actionPerformed(ActionEvent e) {
int result = fileChooser.showOpenDialog(gui);
if (result == JFileChooser.APPROVE_OPTION) {
try {
File f = fileChooser.getSelectedFile();
FileReader fr = new FileReader(f);
output.read(fr, f);
fr.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
};
Action save = new AbstractAction("Save") {
#Override
public void actionPerformed(ActionEvent e) {
int result = fileChooser.showSaveDialog(gui);
throw new UnsupportedOperationException("Not supported yet.");
}
};
JToolBar tb = new JToolBar();
gui.add(tb, BorderLayout.PAGE_START);
tb.add(open);
tb.add(save);
output.setWrapStyleWord(true);
output.setLineWrap(true);
gui.add(new JScrollPane(
output,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
}
public final JComponent getGui() {
return gui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
ChooserInCurrentDir cicd = new ChooserInCurrentDir();
JFrame f = new JFrame("Chooser In Current Dir");
f.add(cicd.getGui());
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Basically, you can't. You need to tell it.
When constructed with a null currentDirectory (such as the default constructor), it will use FileSystemView#getDefaultDirectory.
You could create a single instance of JFileChooser for each base task (one for saving, one for opening for example) and simply maintain that instance, which will "remember" the last directory that it was using, you'd still need to seed it with a starting directory though
Another choice would be to construct some kind of library call that could load and save the last directory the user used based on some unique key. This means you could simply do something like...
File toFile = MyAwesomeLibrary.getSaveFile(APPLICATION_DOCUMENT_SAVE_KEY);
Which would load the last known directory for the supplied key and show the JFileChooser configured with that value and would be capable of returning the selected File or null if the user canceled the operation...for example...
If you want to set the directory they were in at the previous opening of the file chooser you need to set the current directory.
//This will set the directory to the directory they previously chose a file from.
fileChooser.setCurrentDirectory(fileChooser.getCurrentDirectory());
Im not sure if this is what you're looking for, But when they select a file then go to select another file, it will maintain the directory they were in from the previous selection.
If you want your application to look for the user's working directory, you can try this:
String curDir = System.getProperty("user.dir");

refresh the contents of frame in real time [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
When does one need to call revalidate() on a swing component to make it refresh, and when not?
i am making a Explorer program in Java it works as follows
i input the path from the user, a textbox after the user presses enter the path is set and the list is computed of the folder and corresponding labels are created which are supposed to be displayed on the window but the application doesn't display the new contents of the folder, if i change the text then it directs to a new directory so when i press enter then it must show the contents of the new directory loaded but only after resizing the window does it show the new contents of the frame
the code is as follows
package explorer;
import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Explorer extends JFrame{
File file;
Scanner scan;
String path;
String [] listOfFiles;
JTextField txtPath;
JLabel lblLocation;
JLabel child[];
JPanel childPanel;
JPanel masterPanel;
public Explorer(){
lblLocation = new JLabel("Location: ");
/*
* the declaration of panels
*/
masterPanel = new JPanel();
childPanel = new JPanel();
JPanel panel = new JPanel();
/*declaration of other components*/
txtPath = new JTextField("",20);
/*addition of components to panel for layout*/
panel.add(lblLocation);
panel.add(txtPath);
/*adding to master panel, for sophisticated layout*/
masterPanel.add(panel, BorderLayout.NORTH);
masterPanel.add(childPanel, BorderLayout.SOUTH);
getContentPane().add(masterPanel);
/*this place from where address is fetched like /home/revolution/Desktop etc on ubuntu*/
txtPath.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ev){
childPanel.removeAll();
path = new String(txtPath.getText());//the absolute path
file = new File(path);
File childFiles[];
String name = path.substring(path.lastIndexOf('/')+1, path.length());//the name of the directory being displayed
setTitle(name);
if(!file.isDirectory()){
JOptionPane.showMessageDialog(null, "Error file is not a directory");
} else {
listOfFiles = file.list();
child = new JLabel[listOfFiles.length];// labels equal to the number fo files and with name of the coresponding file/folder
childFiles = new File[listOfFiles.length];//references to files
childPanel.setLayout(new GridLayout(listOfFiles.length/2,listOfFiles.length/2));//setting grid layout
for(int i=0; i<listOfFiles.length;i++){
childFiles[i] = new File(listOfFiles[i]);
child[i] = new JLabel(listOfFiles[i]);
child[i].setToolTipText(childFiles[i].isFile()?"File":"Folder");
childPanel.add(child[i]);
}
childPanel.setVisible(true);
}
}
});
}
}
what is wrong? how can i 'refresh' the contents of the window??
I think you need to revalidate() the panel.
More precisely at the end of your action listener you can add childPanel.revalidate();
childPanel.setVisible(true);
}
}
childPanel.revalidate();
});

Notifying objects about an event

I'm looking for an effective method to trigger a change in a set of objects after a change was done to the key object. What I want to do is this:
There is one object that if changed executes a method in other objects.
I was trying to use PropertyChangeListener to accomplish this:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BoundSample {
public static void main(String args[]) {
JFrame frame = new JFrame("PropertyChangeListener Sample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton button1 = new JButton("Open");
final JButton button2 = new JButton("Cancel");
final String[] languages = { "English", "Spanish" };
final JComboBox combo = new JComboBox(languages);
final JPanel panel = new JPanel();
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
JComboBox comboTmp = (JComboBox) actionEvent.getSource();
if (combo.getSelectedItem().equals("English") || combo.getSelectedItem().equals("Ingles")) {
String[] langs = { "English", "Spanish" };
comboTmp.setModel(new JComboBox(langs).getModel());
}
else if (combo.getSelectedItem().equals("Spanish") || combo.getSelectedItem().equals("Espanol")) {
String[] langs = { "Ingles", "Espanol" };
comboTmp.setModel(new JComboBox(langs).getModel());
}
}
};
PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
String property = propertyChangeEvent.getPropertyName();
JComboBox comboTmp = (JComboBox) propertyChangeEvent.getSource();
String language = (String) comboTmp.getItemAt(0);
System.out.println(language);
if ("model".equals(property)) {
if (language.equals("English")) {
button1.setLabel("Open");
button2.setLabel("Cancel");
} else if (language.equals("Ingles")) {
button1.setLabel("Abierto");
button2.setLabel("Cancelar");
}
}
}
};
combo.addPropertyChangeListener(propertyChangeListener);
combo.addActionListener(actionListener);
Container contentPane = frame.getContentPane();
panel.add(button1, BorderLayout.CENTER);
panel.add(button2, BorderLayout.SOUTH);
contentPane.add(combo, BorderLayout.NORTH);
contentPane.add(panel, BorderLayout.SOUTH);
frame.setSize(300, 100);
frame.setVisible(true);
}
}
The problem with this approach is that with the growing number of objects the propertyChange() method will expend and it will become difficult to manage. Moreover to add a new JComponent I will also have to modify propertyChange().
Is there a way to do it the other way around by making the objects "look" for a change in the key object and cause them to act accordingly instead of acting upon them by performing the action in the PropertyChangeListener of the key object? Or maybe other neat way to do this?
First off, a recommendation - always get a native speaker to do your translations. It helps to avoid certain types of errors. For instance, the word you've chosen as the replacement for 'open' is usually used as an adjective (as in, 'an open window') not as a verb, while you're using the inifinitive tense for 'cancel'. You also don't have accent marks, which may be confusing to some readers (some words are only different because of their accent marks...).
Next, you shouldn't hard-code language choices or uses; they should be loaded from .properties files or similar. This makes maintanence trivial, especially when adding additional languages. #Mort's answer about using the observer pattern is correct - that is how updates should be handled. Here's (really roughly) how to deal with the language aspects:
First, you need some .properties files.
spanishButtonNames.properties
==================================
openButton=Abrir
cancelButton=Cancelar
englishButtonNames.properties
==================================
opernButton=Open
cancelButton=Cancel
You'll want to wrap these up in some sort of PropertyManager that'll deal with loading the resource files behind the scenes, when the language is asked for (and possibly deallocating resources).
Next, when creating your buttons, name them:
JButton openButton = new JButton();
openButton.setName("openButton");
// And add them to a list
buttonList.add(openButton);
// More buttons....
Updating the buttons off of the button list is then trivial. You can have just the buttonList listen for (and propogate) updates:
for(JButton button : buttonList) {
button.setText(languagePropertyManager.getProperty(button.getName()));
}
(Note that with some clever coding, it's possible to load even the GUI layout from a text file, but I've never tried that. I believe there are frameworks to handle that, however.)
Do you know the observer pattern? It does what you want: objects will register at an object to get notified when something happens.
observer pattern

Categories