I am learning Java Swing. So times I see codes like following in the main:-
public static void main(String args[]) {
// create an example of the data structure
HashMap<String, String[]> attributes
= new HashMap<String, String[]> ();
attributes.put("fruits", new String[]{"apples", "peaches"});
attributes.put("drugs", new String[]{"euthanasipame", "aspirine", "analgine"});
// create a ComboFest
ComboFest fest = new ComboFest(attributes);
fest.setPreferredSize(new Dimension(400, 260));
// create a window and display the ComboFest in it
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.add(fest);
frame.pack();
java.awt.EventQueue.invokeLater(new Runnable() {
#Override public void run() { frame.setVisible(true); }
});
}
why does it need to run inside a thread by the Runnable? What is the pros and cons? Coz many times I saw people don't use it at all. Please let me know?
Related
I have 2 icons, and I want them to change every second. I also want it to always run and not stop. I am doing it with this code but I am not successful.
public static void main(String[] args) {
Timer timer = new Timer();
JFrameLeds jframeLeds = new JFrameLeds();
jframeLeds.setVisible(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
Icon icono;
icono = new ImageIcon(getClass().getResource("camera.png"));
jframeLeds.jLabel1.setIcon(icono);
icono = new ImageIcon(getClass().getResource("target.png"));
jframeLeds.jLabel1.setIcon(icono);
}
};
timer.schedule(timerTask, 0, 1000);
}
Using Thread class, or TimerTask is not recommended in a Swing environment. You should be using Swing Timers or Swing Workers since component updates should only take place to the Event Dispatch Thread. Take a look at this example.
However, in your case a flag boolean might be required in order to achieve what you want. An example that changes icons to a label:
public class ChangeIconsTest extends JFrame {
private boolean icon1IsActive;
public ChangeIconsTest(Icon icon1, Icon icon2) {
super("test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JLabel label = new JLabel(icon1);
icon1IsActive = true;
Timer swingTimer = new Timer(1000, e -> {
label.setIcon(icon1IsActive ? icon2 : icon1);
icon1IsActive = !icon1IsActive;
});
swingTimer.start();
add(label);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
ImageIcon icon1 = new ImageIcon(
new URL("https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg"));
Icon icon2 = new ImageIcon(new URL("https://www.sample-videos.com/img/Sample-png-image-500kb.png"));
ChangeIconsTest test = new ChangeIconsTest(icon1, icon2);
test.setVisible(true);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
}
As many people here reminded, using TimerTask class from java.util is highly NOT recommended while working in Swing or JavaFX environment.
The Swing components aren't thread-safe, changing the state or repainting the components in different thread than the one used by Swing components may lead to unexpected behaviour and strange bugs.
The Swing and AWT components are using Event Dispach Thread as main background thread to process the events. Events are fired inside every component method that might cause the change of interface. The setIcon() and even setText() methods of JLabel are also firing an event to the EDT.
To avoid future bugs every component state change should be done undnder EDT. The EDT can be called through EventQueue.invokeLater(Runnable), but since you are using Swing, you can call the SwingUtilities.invokeLater(Runnable) which calls the EventQueue inside.
The invokeLater method schedules the task and returns, there's also a invokeAndWait which schedules the task and waits until it's finished before returning.
For the sample below I borrowed the icon urls from the George Z. answer.
Sample code for covering the timed icon change:
public class TimedIconChange {
static String ICON_1_URL = "https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg";
static String ICON_2_URL = "https://www.sample-videos.com/img/Sample-png-image-500kb.png";
static String ICON_3_URL = "http://www.frankieballard.com/sites/g/files/g2000005856/f/Sample-image10-highres.jpg";
public static void main(String[] args) throws MalformedURLException {
Icon icon1 = new ImageIcon(new URL(ICON_1_URL));
Icon icon2 = new ImageIcon(new URL(ICON_2_URL));
Icon icon3 = new ImageIcon(new URL(ICON_3_URL));
List<Icon> circularIcons = new ArrayList<>() {
int i = 0;
#Override
public Icon get(int index) {
return get();
}
private Icon get() {
if (i == size()) {
i = 0;
}
return super.get(i++);
}
};
circularIcons.add(icon3);
circularIcons.add(icon2);
circularIcons.add(icon1);
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
JLabel label = new JLabel();
label.setIcon(icon1);
frame.setLayout(new BorderLayout());
frame.add(label);
frame.pack();
frame.setVisible(true);
new Timer(1000, e -> label.setIcon(circularIcons.get(0))).start();
});
}
}
The sample contains a little implementation of circular list for circularIcons variable, to reduce the need of using boolean flag.
Additionaly, for longer tasks which are supposed to be working in the background using the SwingWorker class is recommended.
References and further reading on EDT:
https://en.wikipedia.org/wiki/Event_dispatching_thread
Why should I use a separate thread to show a GUI in JAVA
Why does my boilerplate Java desktop app JFrame use EventQueue.invokeLater in the main method?
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
I am used to being able to create an instance of another class by running
Config con = new Config();
con.setVisible(true);
However, this appears not to work with the way the WindowBuilder plugin has set up the gui in Config. When the previous command is run, it creates an empty, tiny JFrame. The main method of Config contains just the following:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Config window = new Config();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
the constructor just calls an initialize method which contains the content creation:
public Config(){
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
//other configuration here
}
How can I call Config from another class to run and be visible?
Alright, found the answer. First, to stop the tiny useless frame from being displayed, do not set visible true from the initial class. Instead just run:
Config con = new Config();
Then, in the constructor of Config, after initializing everything, add
frame.setVisible(true);
ANSWER
After create a new object of that class.
WindowShow ws = new WindowsShow();
ws.frame.setVisible(true);
And it will go perfect
I have the following Java Program which one starts in about 50% of all launch attempts. The rest of the time it seams to deadlock in the background without displaying any GUI. I traced the problem to the setText method of the JTextArea Object. Using another Class like JButton works with setText but JTextArea deadlocks. Can anyone explain to me why this is happening and what is wrong with the following code:
public class TestDeadlock extends JPanel {
private JTextArea text;
TestDeadlock(){
text = new JTextArea("Test");
add(text);
updateGui();
}
public static void main(String[] args){
JFrame window = new JFrame();
window.setTitle("Deadlock");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new TestDeadlock());
window.pack();
window.setVisible(true);
}
public synchronized void updateGui(){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
System.out.println("Here");
text.setText("Works");
System.out.println("Not Here");
}
});
}
}
your main method must be wrapped into invokeLater or invokeAndWait, that's basic Swing rule to create Swing GUI on EventDispashThread
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame window = new JFrame();
window.setTitle("Deadlock");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new TestDeadlock());
window.pack();
window.setVisible(true);
}
});
}
My program reads in files from a given directory on program start (each one containing an object), and adds each object to a Vector. updateList() is then called which loops through each of these objects one by one, adding their names(String property) to a JList with a DefaultListModel.
The issue is that very rarely when the program starts, the list appears empty. I have performed many checks such as getting the number of entries in the list as reported by the list model and everything would appear to be correct.
Has anybody seen this before? Am I missing something important here?
Thanks, updateList() below:
private void updateList(){
for (int i=0; i < calculators.size(); i++){
listModel.addElement(calculators.get(i).getName());
}
}
Has anybody seen this before?
Random errors generally happen because you are not updating Swing components on the Event Dispatch Thread. Read the section from the Swing tutorial on Concurrency for more information.
In particular you would use the invokeLater() method when starting your GUI. The Swing tutorial has plenty of examples. The basic structure the tutorial uses is like:
import java.awt.*;
import javax.swing.*;
public class SSCCE extends JPanel
{
public SSCCE()
{
add( new JLabel("Label") );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new SSCCE() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
1) I was able to add a JTextField to the JFrame, and I initialized the class Java2sAutoTextField as given in Auto Complete JTextField (Swing / AWT / SWT / JFace forum at JavaRanch).
2) I initialized the list and field inside the JFrame constructor, as shown below.
List possible = new ArrayList();
possible.add("Austria");
possible.add("Italy");
possible.add("Croatia");
possible.add("Hungary");
Java2sAutoTextField autoCompleter = new Java2sAutoTextField(possible);
3) The problem that arises is this: Even though I have initialized the Java2sAutoTextField, how can I apply auto completing to the JTextField?
Adding the main() method below to Java2sAutoTextField produced the expected result after typing "H". It's not crucial for this example, but Swing GUIs should be constructed on the EDT.
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
List<String> list = new ArrayList<String>(Arrays.asList(
"Austria", "Croatia", "Hungary", "Italy"));
JFrame f = new JFrame("AutoTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Java2sAutoTextField(list));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}