For my program I have a JPane that as the game progresses it adds labels to the panel, however the only way I can make the panels show up is by using add(label) then revalidating and vice versa for removing labels.
My problem is that once it gets to the point that I have more than 40 labels on the screen the revalidate has to process too many things, so how can I override the revalidate(), ether works, so that it only revalidates the specific component that was added, and not every component on the screen. I know there is a loop somewhere within the revalidate() method that will run through a loop of all components, but I just can not for the life of me find it. I would like to be able to call revalidate(component-here) or validate (component-here) and have it only update that specific component.
I know there are other ways of writing this program but I am only interested in how to override revalidate() so no "you could have redone your whole code this way which should only take you like 6 hours >.<".
http://www.fileserve.com/file/jFdQ6nv/FINAL_PROJECT.zip a link to my eclipse project, if anyone who wants to help would like to see what im actually talking abouyt
I just tried this example. Adding 1000 text areas only takes a second, and updates are instant. Labels go even faster. You might want to look at something else slowing it down.
Also, you could look at CellRendererPane. It overrides invalidate() to do nothing.
public void invalidate() {}
Related
I've read through lots of the threads on paintComponent here, most of which making the point that it either is never or almost never necessary (or possible) to choose when paintComponent is called.
In my program, however, sometimes (only sometimes) paintComponent gets called before some of the objects it needs to paint have finished initializing or even sometimes before they've been created, triggering warnings- JOptionPane pop-ups, which surprisingly do not show any of the text they were hard-coded to display in their "message" area. I've read in other places that it's something to do with the EDT, and I've looked into some parts of that but I'm just getting confused. If the main purpose of the EDT is to update the gui, and by default pretty much everything will run in the EDT, then could I tell the program to run all the initialization and update functions in a different thread(s), which I somehow forcibly make run before the EDT runs?
What I'd ideally like to have happen is for paintComponent to wait until a certain point in my code to be run (after a bunch of update functions, regardless of what happens to the screen. After it does get called, it is followed by a pause in which relatively little is going on ( I had been using Thread.sleep() inside a while loop ) and which lasts until the user clicks something - at which point all the necessary functions are run again, followed by paintComponent afterwards, then the sleep() while loop, etc.
From what I understand, I think what I want isn't really possible, so my question is: Do you guys have any ideas of how to get around this?
EDIT:
So essentially the program is a college course planner, intended to make it easier for someone to plan out by semester all the courses they have to take before graduation, move those courses around (if possible), and see how all the courses are connected (prerequisites and such). When the program starts, it loads the list of necessary courses from a text file, then loads info about each course from a bunch of individual text files, and arranges them according to their prerequisites. Courses with no prerequisites go in the first semester, courses whose prerequisites have all been added to the first semester get added to the second, and so on until all the courses have been added. When paintComponent runs, it calls a function that assume all of each course's prerequisites exist on the schedule, and if it finds otherwise, it throws an error and displays a JOptionPane message box. When this happens during a normal run of the program (like if I manually add a course before adding its prerequisites), that all works and displays correctly. But sometimes that message box pops up when only some of the courses have been loaded (meaning control is still in the main constructor) and when it does so, the actual string message doesn't show up - only the actual pane, title and ok button do. Heres the line where I display the error box, so you can know that I'm not trying to display a string variable which has the potential of being empty.
JOptionPane.showMessageDialog(this,
"Course couldn't be loaded, partially >loaded\ncourses have been removed.",
"Error",
JOptionPane.OK_OPTION);
It is the "Course couldn't...been removed." part that doesn't get displayed. This is the only JOptionPane I display with the title "Error".
This post mentioned what sounds like the same thing happening, except I'm not using any of the things that poster had to fix. So possibly it's irrelevant but I'll add it just in case. JOptionPane.showMessageDialog() shows but without any message?
But to step back a bit, because that box popped up before all the courses had been added, it means that paintComponent was somehow called in the middle of the relevant JPanel's constructor, before a bunch of things had been initialized. I added a bunch of println() statements to make sure that that is true. Is it normal for that to happen, and if so, is there a way to fix it without simply using Andrew Thompson's advice?
After thinking though it a bit, I think that because the project is 3200 lines long and relies to a huge extent on text files, I'm really not sure how to ( or if I can) make a SSCCE for it..
If any specific pieces would be helpful I'll gladly add those but if this problem isn't clearly some standard issue I'm getting wrong, then I'll just add that flag and keep looking for bugs.
Thanks for your help
Declare a flag as a class attribute. Check it in the paint method. Change it at the end of the initialization.
class XandYandZ extends JComponent {
boolean initializationFinished = false;
public XandYandZ() {
// long initialisation..
initializationFinished = true;
}
public void paintComponent(Graphics g) {
if (!initializationFinished) return;
// .. paint ..
I have a project due in about an hour and I need to figure out the problem in my code before submitting.
In essence, there are two classes: one is a game and the other is a gui that should contain that game, some useless buttons, and a picture.
I'm having trouble making it all fit. I think I have a problem with the JPanels. (There might be some useless looking methods, but I had plans for them and I'll see if I have enough time for them to come into fruition)
This is life and death for my grade. I'd be eternally grateful if you could fix this code for me.
I'm positive the problem is not in the nitty gritty of the second link. It's been tested on its own and works.
Here's the code
http://pastebay.net/1236108
http://pastebay.net/1236109
Here's what I get when I run the code:
Get rid of the window in the Hangman constructor. Hangman is already frame.
Use pack instead of setSize
Git rid of game1 and game2 panels, they are adding nothing and causing you all the problems.
You will also find that if you override the getPreferredSize methods of DrawingDemoPanel and SubKillerPanel and return an appropriate size, that they will layout better.
Alternativly, you could create a single game panel and use a GridLayout to add the DrawingDemoPanel and SubKillerPanel to it, then add this to the CENTER position of the main window it will allow out better...
I have an application which depending on user input changes the entire content of the JFrame. I do this by removing the JPanel containing the current components and replace it by a new JPanel with new components.
After that one of the components needs to get focus and a JScrollpane should scroll to this component. In most cases this works properly.
Now one scenario leads to a JPanel beeing added, which itself contains more than 500 components. Rendering this takes some time and it seems that scrollRectToVisible() is called at a point, where the UI is not fully rendered. If I debug I can actually see that it first scrolls to the right position, but then further rendering is done and the component is moved out of the viewport again.
So I was trying find a Listener, which is called, when rendering is fully done. I tried with ComponentListener and AnchestorListener. Both didn't receive most of the events I was expecting. But even when they did the callback methods were called before any UI change was visible on the screen.
I swap the JPanels in EDT and call validate() on the JFrame afterwards. After that I do not process any further code. However, if I set a breakpoint in the last executed line and go one step further, the UI has not changed on screen. The EDT is actively doing something (I assume rendering the UI). And I would like to get notified, when the EDT has finished rendering.
Another thing I tried:
If I create another Thread that just sleeps for a few seconds (until the UI is definitely rendered) and call the scrollRectToVisible() then, everything works fine.
I'm sorry not to provide an SSCCE. I tried, but it seems to be rather complex. I really appreciate any idea on how I could get notified on the UI beeing fully rendered and visible to the user.
Thanks
If I create another Thread that just sleeps for a few seconds (until the UI is definitely rendered) and call the scrollRectToVisible() then, everything works fine.
Instead of sleeping, just wrap the scrollRectToVisibl() code in a SwingUtilities.invokeLater(). This will add the code to the end of the EDT so it should be processed after all the other rendering.
Add a property change listener:
jPanel.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(final PropertyChangeEvent evt) {
System.out.println(evt);
}
});
Produces something like:
java.beans.PropertyChangeEvent[propertyName=ancestor; oldValue=null; newValue=javax.swing.JPanel[null.contentPane,0,0,0x0,invalid,layout=javax.swing.JRootPane$1,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]; propagationId=null; source=javax.swing.JPanel[,0,0,0x0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]]
Maybe JComponent.addNotify() is what you need.
If you override that method, make sure that you call the super.addNotifty() as well!
I am going through a legacy application which is using Swing and i am struggling to figure out how the screens are changing when a user clicks a button. One of the reasons i cant figure this out is because this is the first time i am using Swing. I have read a book and got the basics but still struggling.
Basically, the screen i am looking at has a JSplitPane which has a number of shortcut buttons on the left and an empty pane on the right. When i click on the button, the right side pane is populated with a different screen depending on the button pressed.
Going through the code, i was expecting somewhere that there will be something that calls a setVisible() method depending on which button is pressed.
The actionPerformed method for each of the shortcut buttons looks something like this:
void shortCutBtn_actionPerformed(ActionEvent e) {
propertyChangeListeners.firePropertyChange("selectedShortCut", previousShortCutSel, currentShortCutSel);
mainPanel.updateUI();
}
I have gone through most of the code and came to a conclusion that the above code is what is causing the frame switching but i dont understand how that is happening.
Each screen is identified by a numeric constant. In the above code example, previousShortCutSel and previousShortCutSel refer to a numeric value that represents individual screens screen.
I have tried to look for documentation of how updateUI() works but i am lost. How does the above cause the content of the right panel of the JSplitPanel to be updated with a new screen?
This is not an appropriate use of updateUI(), which "Resets the UI property to a value from the current look and feel." As the example itself may be unreliable, consider studying another. GoogleOlympiad, for example, sets a label's icon using a (cached) image.
ImageIcon image = getImage(index);
imageLabel.setIcon(image);
(source: drjohnbmatthews at sites.google.com)
As per comments by ziggy (glad it helped)
Have a look at the PropertyChangeListeners that appear to be added in the code. In particular the propertyChange(PropertyChangeEvent e) method is where the code which changes the content will be present.
+1 to trashgod nice example/advice as always
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.