I cannot seem to force a layout in Swing. I have a JComponent added to a JLayeredPane and I set a border on the JComponent. Then, I want to immediately re-draw everything - not in the sense of "please do this asap" like invalidate(), but synchronously and immediately. Any help? I cannot seem to find the right method of doing this, and all my reading about invalidate(), validate(), repaint(), doLayout(), etc is just confusing me more!
According to this (see the section titled "Synchronous Painting") the paintImmediately() method should work.
The most reliable way to get Swing to update a display is to use SwingUtilities.invokeLater. In your case, it would look something like
SwingUtilities.invokeLater(new Runnable {
public void run() {
somecomponent.repaint();
}
});
I realize the 'invokelater' does not exactly sound like it does anything immediate, but in practice, events posted in this way tend execute pretty quickly compared to just calling e.g. somecomponent.repaint() directly. If you absolutely must make your control code wait for the GUI to update, then there is also invokeAndWait, but my experience is that this is rarely necessary.
See also: document on event dispatch in Swing.
This is super old, but I found a simple solution, which I'll show with a JPasswordField:
var pw = new JPasswordField();
...
pw.paint(pw.getGraphics()); // paints immediately
Related
Inside the actionPerformed method of a jButton, I have the following code:
btnLogin.setText("Logging In...");
btnLogin.setPreferredSize(new Dimension(110, 29));
btnLogin.setEnabled(false);
//more stuff here, irrelevant to this
This works, however it only takes visual effect (is repainted) once the method is complete.
If in the //more stuff here area I have code that takes a long time to complete, the effects of the btnLogin changes do not take effect until that code is complete.
I have tried typing:
this.revalidate();
this.repaint();
Directly after the first 3 lines, and multiple other solutions, to try to force the damn thing to repaint DURING the method, but no matter what, it only happens at the end!
Another thing I've noticed is that if I call a JOptionPane in the middle of the method, the frame WILL repaint (in the background), so that's interesting.
What is is that's automatically happening in the end of the method that I need to call to make it happen during the method?
Thanks in advance!
You're blocking the Swing event thread with the long-running code, and this prevents Swing from drawing the text changes. The solution:
Do the long-running code in a background thread such as in a SwingWorker's doInBackground method.
But make sure to make most all Swing calls on the Swing event thread.
Read the Concurrency in Swing tutorial to learn the details on the Swing event thread and threading issues.
I had this question while setting a JLabel visible when a button is clicked it is like a loading icon. The p.make() method is executed but the Label is still invisible after the Method returns the Label is visible.
Can someone explain what is happening?
ActionPerformed:
String[] args = {jTextFieldDrgzusatzVariable.getText(),jTextFieldAusgabe.getText(),"C:\\CPOracle",jTextFieldKatalog.getText()};
this.jLblLoading.setVisible(true);
if(jLblLoading.isVisible()){
try{
new P21Make(args[0],args[1],args[2],args[3]).make();
}catch(Exception e){
e.printStackTrace();
}
}
The reason is very simple: Swing is single threaded (see the Swing concurrency tutorial for more information).
What happens is that the actionPerformed method is called on the Swing thread (the E(vent)D(ispatch)T(hread)). When the
this.jLblLoading.setVisible(true);
statement is reached, it will immediately mark the jLblLoading as visible. However, this has no effect yet on the UI. The UI needs to be repainted before the change in visibility has any effect. This repaint is scheduled on the EDT (which is not the same as immediately executed).
This explains why your
if(jLblLoading.isVisible()){
check succeeds, and you still do not see the difference in the UI. The component is marked as visible, but the repaint is still pending. The repaint will remain pending until the EDT becomes available again. Since the thing that is currently occupying the EDT is your actionPerformed call, the rest of the code in that actionPerformed method will be executed before the repaint (meaning before you see a change in the UI).
Your solution using a different thread can indeed fix this. You can however only use that if the new P21Make(...).make() does not affect the UI. If that statement interacts with Swing components in any way, it should be executed on the EDT. In that case, an alternative is to wrap the statement in a SwingUtilities#invokeLater call.
You should probably look at using SwingUtilities.invokeLater to allow actions which modify the gui to complete.
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
I have a dialog containing several buttons. When a particular button is clicked, it's ActionListener iniates a process that takes several seconds to complete. During this time I want to provide some feedback to the user. To take a simple approach, I have a label in the dialog "Computing..." which is initially not visible. A code segment looks like this
button_OpenHoursReport.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
lbl_Computing.setVisible(true);
new runAndRenderReport();
RunAndRenderReport.main(null);
lbl_Computing.setVisible(false);
}
});
The problem is, the lbl_Computing text does not become visible until the RunAndRenderReport is completed. Obviously, that's not much help to the user. Don't know where to go from here. Does this have to do with threads? If so, I could use some guidance on how to get started.
actionPerformed() is executed on the GUI-thread (EDT), so avoid executing intensive operations on it. Instead use SwingWorker.
See How SwingWorker works.
A trick which is much easier than using SwingWorker is to call paintImmediately on your label after calling setVisible(true). You should see the effects - immediately.
lbl_Computing.paintImmediately(0, 0, lbl_Computing.getWidth(), lbl_Computing.getHeight());
But SwingWorker is the way to go if you want your GUI to be responsive in other ways as well while the reporting is running.
there is a lot of similar topics that I've seen here but couldn't really find solution to my little problem.
My application is searching through a file and showing the results inside a jtree. and I have a problem with that. When I add new nodes to a tree using insertNodeInto(...); i can search through found items while still searching but there is a problem with visualization. I mean there is a problem with rendering the nodes - I really can't explain that properly so I'm including this image.
When I use reload on jtree at the end of searching everything is back to normal - rendering is ok, unfortunately this closes all tabs that user opened.
I'm a student - sorry for my poor english. I hope someone know why this problem appears.
The way that your GUI is displayed, it certainly looks as if you are adding components to your Model outside of the painting thread (the Event Dispatch Thread (EDT)). This in turn will trigger painting outside of this thread, which will result in erratic painting.
Please take a look at this tutorial on threads in Swing
Looking at DefaultTreeModel, it is clear that the insertNodeInto(..) method will trigger the GUI updates, so if not done on the EDT, you are prone the painting issues depicted in your example.
A quick fix would be to add a method similar to the (uncompiled) code below:
public void safeInsertNodeInto(final MutableTreeNode newChild,
final MutableTreeNode parent, final int index) {
SwingUtilities.invokeLater(
new Runnable(){
public void run(){
model.insertNodeInto(newChild,parent,index);
}
}
);
}
then call that method instead of directly calling on you model.
That said, I heavily recommend reading the tutorial cited above. There are more advanced ways of dealing with the EDT constraints.
Apologies for the somewhat unclear question - couldn't think of a better way of putting it.
I use a JXTaskPane (from the Swing labs extension API) to display some information.
The user can "click" the title to expand the panel. The JXTaskPane is in a container JPanel, which is then added to a JFrame, my main application window.
I want my application window to resize to the size of the expanded task pane. To achieve this, I added a component listener to my container JPanel which would set size to the now expanded panel.
panel.addComponentListener(new ComponentListener()
{
public void componentResized(ComponentEvent e)
{
Dimension newSize = ((JXTaskPane)e.getSource()).getSize();
reSizeFrame(newSize);
}
}
private void reSizeFrame(Dimension newSize)
{
if ((newSize.height < maxSize.height) && (newSize.width < maxSize.width))
{
containerPanel.setSize(newSize);
appFrame.setSize(containerPanel.getSize());
appFrame.pack();
}
}
The problem is that the componentResized method is called as the task pane expands, as a result the resizeFrame method is called lots of times, and looks really awful on the screen.
How can I detect when the JXTaskpane has finished resizing? I thought of two approaches:
Put the resizeFrame() method in a SwingUtilities.invokeLate(..) call.
Put in a timer resizeFrame call, so any subsequent calls do not do anything until the timer fires. This should give enough time for the panel to resize.
What is the best way forward?
Also - This is my first serious Java GUI app after years of server side program. StackOverflow has been very helpful. So thanks!
I know you've already selected an answer, but overriding the paint method is definitely not correct, and while you may be able to hack something in place, it won't be ideal.
Looking at the source for JXTaskPane and specifically looking in setExpanded() (line 387), you can see it calls JXCollapsiblePane.setCollapsed(...) and then fires a property change event for expanded. A listener on that property won't be correct, because it'll fire before the animation is complete. So, if you go into JXCollapsiblePane and look at setCollapsed(...) (line 470) you'll see that if it's animated, it sets the paramaters and starts a timer. We want to know when the animation ends, so in that file, look at the animator (line 620, and specifically 652-667), which shows that when the animation ends, it fires a property change for ANIMATION_STATE_KEY with a value of "collapsed" or "expanded". This is the event you actually want. However, you don't have access to JXCollapsiblePane, so go back to JXTaskPane and search for ANIMATION_STATE_KEY, and you find line 208, which shows that JXTaskPane creates a listener on JXCollapsiblePane.ANIMATION_STATE_KEY and refires it as it's own event.
Since you do have access to JXTaskPane, you can listen for that event, so doing ...
taskPane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if(e.getNewValue().equals("expanded") {
...
}
else if(e.getNewValue().equals("collapsed") {
...
}
}
}
should get your event exactly when you want it.
The correct way to listen for events in Swing is through property listeners. Unfortunately, the only way to find out what the correct properties and values are is by digging through source code.
As a suggestion, have you tried overriding the paint method, first calling super and then putting your resize code at the end of that if (and only if) the size has changed significantly.
I'm not familiar with JXTaskPane, but my first reaction is that maybe you're handling the wrong event. You want the frame to resize when the user clicks on the header - so why not handle that event (perhaps using EventQueue.invokeLater() to resize the frame after the task pane has been resized)?
But if that doesn't work and you need to use the approach you've outlined above, using a javax.swing.Timer is probably best. Set it for 200 milliseconds or so and just restart() it every time componentResized() fires.