I am putting a component ( derivative on JPanel ) inside a JScrollPane.
scrollPane = new JScrollPane(component);
since the component occasionally changes size, I have to occasionally do :
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
scrollPane.getViewport().setView(component);
component.repaint();
}
});
Also, the component can receive KeyEvents
component.addKeyListener(this);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
component.setFocusable(true);
component.requestFocusInWindow();
}
});
However, the component never receives any Key Events. Even the code in keyTyped() does not execute ( I put a System.out.println() there).
What is more baffling, is, in debug mode, I can pause the main thread, when the AWT event thread would accept key events. But during normal execution, it does not work.
Can anyone suggest what I am doing wrong ?
Making my component java.swing.Scrollable, and replacing scrollPane.getViewport().setView(component);
with component.revalidate solved my prioblem.
Thanks ordnungswidrig!
Related
I have a few shutdown steps which need to execute during a WindowClosing event before being disposed. Everything is executing correctly, but I'd like to add the capability to provide shutdown status messages in an existing JLabel within the closing JFrame. Is it possible to update the JLabel text during a WindowClosing event?
Sure. Just make sure component is not disposed before you start interacting with it.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// update label here
}
});
but I'd like to add the capability to provide shutdown status messages in an existing JLabel within the closing JFrame
label.setText(....);
label.paintImmediately(label.getBounds());
The code in the listener executes on the Event Dispatch Thread so the GUI can't repaint itself until all the listener code is executed and by that time the GUI will be closed.
The paintImmediately(...) will allow the component to bypass the RepaintManager and paint itself right away.
I used the following code to execute the shutdown steps in the background and then close the JFrame.
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
// shutdown steps go here and can update the JLabel text
frame.dispose();
return null;
}
};
worker.execute();
}
});
I have a JButton, lets call it "button" and added an ActionListener to it:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
call();
}
});
It is correctly added to my frame etc. In that JFrame I also have a JLabel, and I want to change its text while the JButton method is working(because it takes ~30 secs to finish). How do I do that? Do I have to use some multi-thread-thingy?
Here is the basic principle(the JLabel is called output):
public void call(){
output.setText("test1");
try { Thread.sleep(1000);
} catch (InterruptedException e) {}
output.setText("test2");
}
This will result in the "output" Label being changed to "test2" after one second. How can I make it instantly display "test1"?
Don't use Thread.sleep(). This will prevent the GUI from repainting itself.
Do I have to use some multi-thread-thingy?
Yes.
For a long running task you need to start a separate Thread, so the GUI can remain responsive.
In this case you can use a SwingWorker.
Read the section from the Swing tutorial on Concurrency for more information and an example of using a SwingWorker.
I have a JFrame. Frame was executed on EDT. A window closing event is being added to that frame using window adapter. What i need is; There is a background task that delete files generated by the application; and that task starts running when the close button of frame is being clicked. I want to show progressbar for that background tasks?
public class CloseApplication extends WindowAdapter{
#Override
public void windowClosing(WindowEvent we) {
new Thread(new Runnable() {
#Override
public void run() {
delete.deleteDirectory(a);
delete.deleteDirectory(b);
delete.deleteDirectory(c);
delete.deleteDirectory(d);
}
}).start();
}
}
I tried to add progress bar to the process but it didn't displayed. I then called it in new thread; still no success. Can you give me any idea that how this can be done?
Either way i use to call it in new thread it dont work. The reason is; background task executes in new thread and windowClosing comes to an end and close the application. If i call it without it; it makes the UI unresponsive.
Thanks in advance.
I have a JLabel that I require to change dynamically when a button is pressed. I am using JLabel.setText(s)
However, it is not working. I've tried:
JLabel.repaint()
JLabel.validate()
JLabel.revalidate()
The thing I find strange is that it works fine when I don't set the size of the JLabel using any of:
JLabel.setPrefferedSize()
JLabel.setMinimumSize()
JLabel.setSize()
Can anyone help me with why it isn't working after I set one of the size properties?
What your button is doing is probably a very long task and you want to keep the user informed about the process.
To do this you have to create a new Thread which performs the long task and when you set new text to your label, wrap the label.setText() statement into an invokeLater():
public void buttonAction()
{
new Thread(new Runnable()
{
public void run()
{
// Here your long task
// When you want to call Label.setText(), do it like this:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText("Loading 1/13...");
}
});
// Here another part of your task....
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText("Done!");
}
});
}
}).start();
}
For sizing components in Swing, ultimately the layout of the parent container has the final say on the size of the component.
For example, in FlowLayout, calling setPreferredSize(), setSize(), or setMaximumSize() on a component all have the result of giving the label that size, and not sizing over it. However, with setMinimumSize() it will start at that size, then size above it.
For BorderLayout, if the component is added to the center, none of the sizing methods affect the component - it just takes the maximum amount of space available.
If you set the layout of the parent container to null you can explicitly size components, however, you will lose the automatic resizing that you have with other layouts.
Figured out my problem,
I was using:
JLabel.setPreferredSize(100, JLabel.getheight());
I changed to:
JLabel.setPreferredSize(100, 22);
JLabel.setPreferredSize(100, JLabel.getheight());
As you are setting the preferred size here, the height of JLabel must not have been previously set :), if you try the same line after setting the height, I think it should work well.
Also sometimes JPanel<xx>.updateUI(); is required to refresh the displayed UI.enter code here
I'm working in Java, and I have a JPanel in a JFrame. In that JPanel, among other things, I have a JLabel that I want to make appear and disappear at will. I've tried setting visibility to true/false, adding and removing it from the JFrame and JPanel, and, having looked online, I tried validate()ing and revalidate()ing ad infinitum. What can be done here to solve this problem?
In general, calling the setVisible method is sufficient to make a Swing component to be shown or hidden.
Just to be sure that it works, I tried the following:
public class Visibility {
private void makeGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel l = new JLabel("Hello");
final JButton b = new JButton("Hide Label");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
l.setVisible(false);
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(l, BorderLayout.CENTER);
f.getContentPane().add(b, BorderLayout.SOUTH);
f.setSize(200, 200);
f.setLocation(200, 200);
f.validate();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Visibility().makeGUI();
}
});
}
}
The above program is able to affect the visibility by clicking on a JButton.
Could it be a Threading Issue?
My next suspicion was that perhaps a Thread that is not on the event dispatch thread (EDT) may not be affecting the display immediately, so I added the following after initializing the JLabel and JButton.
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
b.setVisible(!b.isVisible());
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception /* }
}
}
});
t.start();
With the new Thread running, it changed the toggled the visibility of the JLabel every 100 ms, and this also worked without a problem.
Calling a Swing component off the event dispatch thread (EDT) is a bad thing, as Swing is not thread-safe. I was a little surprised it worked, and the fact that it works may just be a fluke.
Repaint the JPanel?
If the JLabel's visibility is only being affected on resizing, it probably means that the JLabel is being drawn only when the JPanel is being repainted.
One thing to try is to call the JPanel's repaint method to see if the visibility of the JLabel will change.
But this method seems to be just a band-aid to a situation, if the main cause is due to a thread off the EDT is attempting to make changes to the GUI. (Just as a note, the repaint method is thread-safe, so it can be called by off-EDT threads, but relying on repaint is a workaround than a solution.)
Try using SwingUtilities.invokeLater
Finally, probably the thing I would try is the SwingUtilities.invokeLater method, which can be called (and should only be called) from a thread running separate from the EDT, if it wants to affect the GUI.
So, the earlier Thread example should be written as:
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
b.setVisible(!b.isVisible());
}
});
} catch (Exception e1) { /* Handle exception */ }
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception */ }
}
}
});
t.start();
If the change to the GUI is indeed occurring on a separate thread, then I would recommend reading Lesson: Concurrency in Swing from The Java Tutorials in order to find out more information on how to write well-behaving multi-threaded code using Swing.
setVisible() or removing it should work fine, make sure you are doing it from the event dispatch thread though. There are utility methods in EventQueue for running blocks in that thread.
http://helpdesk.objects.com.au/java/how-do-i-update-my-gui-from-any-thread
You would need to call revalidate() on the parent JPanel if you need its components to be re-laid out.
If you can post an example that demonstrates the problem I can have a look at it for you.