Nimbus PLAF: and a double call to repaint() inside a runnable interface
ok let me put the codes first because I might be stating everything as bizarre as i see it
Code :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class DoubleCallToRepaintWithNimbusPLAFBizzareProblem {
private java.util.List<String> words;
private boolean stopSlide;
private int wordX;
private int head;
private int tail;
private JButton slide;
public DoubleCallToRepaintWithNimbusPLAFBizzareProblem() {
words = new ArrayList<String>();
initWordList();
tail = words.size() - 1;
stopSlide = true;
}
public void initWordList() {
words.add("F I R S T S L I D E");
words.add("S E C O N D S L I D E");
words.add("T H I R D S L I D E");
words.add("F O U R T H S L I D E");
}
public void showWindow() {
Panel p = new Panel();
JFrame f = new JFrame();
slide = new JButton("Slide");
slide.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stopSlide = false;
slide.setEnabled(false);
}
});
p.add(slide);
p.setPreferredSize(new Dimension(700, 450));
f.getContentPane().add(p);
f.pack();
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
f.setVisible(true);
}
private class Panel extends JPanel implements Runnable {
private Thread runner;
private boolean stopThread;
public Panel() {
runner = new Thread(this);
runner.start();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(new Font("Monospaced", Font.BOLD, 35));
g2d.drawString(words.get(head), wordX, 220);
g2d.drawString(words.get(tail), wordX + 900, 220);
}
@Override
public void run() {
while (!stopThread) {
try {
Thread.sleep(5);
if (!stopSlide) {
if (wordX >= -900) {
wordX -= 5;
}
if (wordX < -900 && !stopSlide) {
wordX = 0;
head = tail;
tail--;
stopSlide = true;
// set enabled again, and i see in the API
// there is a call to repaint() inside of it
slide.setEnabled(true);
// when tail becomes negative 1,
// make it the size() - 1 of the list
// imediately before the repaint() call below
if (tail < 0) {
tail = words.size() - 1;
}
}
}
repaint();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
try {
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}
catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
new DoubleCallToRepaintWithNimbusPLAFBizzareProblem().showWindow();
}
}
i tried to reproduce the thing i encoutered as small as possible , so this is a far as it goes.
- when i do a slide, the int values(head and tail) that calls the values in the word lists, decrements from size() - 1 to 0,
- when it reaches the critical point -1 (tail variable), inside the run method, i have an if part that turns the tail into a positive value that i want immediately before the call to repaint() below it, so i will be able to avoid a -1 out of bounds exception.
- why do i get a -1 exception?
- i tried to debug it for a couple of hours then i pin-point the problem(first assessment)
FIRST ASSESSMENT:
- every time I slide, I disable the slide button when a sliding is occuring, for every succesfull slide animations, i enable the slide button again, as you can see, i put that setEnabled(true) call inside the run() method, JUST BEFORE the if-part that turns the tail variable to something positive before a repainting again, repaint()
- from there i manage to see that there is another repaint() call inside the JComponent setEnabled() method - I dig in
- and from there i realize that when i have a -1 tail, that setEnabled() call calls a repaint() inside of it that repaints everything, and i thought that it might affect the other painting events(my sliding words), thats why i get a -1 outOfBounds(ONCE. only ONCE) it proceeds on the if-part that sets the tail a positive value(not a -1), then proceed to my own painting(the repaint() below of it) and good to go again.
- what i did is i placed the call to .setEnabled() (slide.setEnabled(true)) below the if-part that sets the tail to something positive value, and everything works fine, thats why i am, somehow confident with my understanding that there is a 2 call to repaint() that affects everything. one is inside the setEnabled() of JComponent and the other one is that i have
- now i tried to reproduce again the problem WITHOUT THE NIMBUS PLAF INSTALLATION, and everything i said above doesnt happen
- now the second assesment goes
SECOND ASSESSMENT:
- lets go back to square 1, all those things i said above, removing the installed NIMBUS PLAF, no error has occured
what is going on?, i might be wrong on something(maybe all) and i admit that Im doing a concurrency in an appropriate way. But please.. i need some assitance. Is using Nimbus plaf or installing another PLAF is risky? or calling a swing instance method with a repaint() inside of it in a run method with another repaint() is dangerous? , please ask me if my statements are hard to understand. ill try to explain as more clearly as possible. thank you in advance
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Quote:
why do i get a -1 exception?
Please post the full text of the error message.
Please explain how you get the error. When I execute the code there is no error. I pressed the SLide button many times and the text on the screen "slides".
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Quote:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.get(ArrayList.java:324)
at xError_Samplesx.DoubleCallToRepaintWithNimbusPLAFB izzareProblem$Panel.paintComponent(DoubleCallToRep aintWithNimbusPLAFBizzareProblem.java:94)
at javax.swing.JComponent.paint(JComponent.java:1029)
at javax.swing.JComponent.paintToOffscreen(JComponent .java:5124)
at javax.swing.RepaintManager$PaintManager.paintDoubl eBuffered(RepaintManager.java:1491)
at javax.swing.RepaintManager$PaintManager.paint(Repa intManager.java:1422)
at javax.swing.RepaintManager.paint(RepaintManager.ja va:1225)
at javax.swing.JComponent._paintImmediately(JComponen t.java:5072)
at javax.swing.JComponent.paintImmediately(JComponent .java:4882)
at javax.swing.RepaintManager.paintDirtyRegions(Repai ntManager.java:786)
at javax.swing.RepaintManager.paintDirtyRegions(Repai ntManager.java:714)
at javax.swing.RepaintManager.prePaintDirtyRegions(Re paintManager.java:694)
at javax.swing.RepaintManager.access$700(RepaintManag er.java:41)
at javax.swing.RepaintManager$ProcessingRunnable.run( RepaintManager.java:1636)
at java.awt.event.InvocationEvent.dispatch(Invocation Event.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.j ava:646)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:607)
at java.awt.EventQueue$1.run(EventQueue.java:605)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectio nPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java: 616)
at java.awt.EventDispatchThread.pumpOneEventForFilter s(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(E ventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarch y(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispa tchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispa tchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThre ad.java:122)
BUILD STOPPED (total time: 11 seconds)
oooh ok.. thank you for checking it.. hmmm.. i really dont have any more idea how did i get the error on my part.. thats the best thing i can do to reproduce it.. i got the exception thrown pointing at the paintComponent() body, when it is trying to access one of the strings inside the List
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
There must be a hole in your logic that allows the value of the arg to the get() method to be -1.
Look at line 94 to see if the variable with the bad value is head or tail.
Then look at the code to see how that variable can get a value of -1. Add some println statements that print out its value to see where/when it gets different values.
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
ok ok ill look into it even more, thank you... but does it also throw a -1 exception when you run it?
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Yes, I get the Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
some times.
Have you added some println statements to the code? What is printed out just before the AIOOBE is thrown?
Which variable has the -1 value? When does that variable have the value of -1?
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Code :
if (wordX < -900 && !stopSlide) {
wordX = 0;
head = tail;
tail--;
stopSlide = true;
slide.setEnabled(true);
System.out.println("Before the If : " + tail);
if (tail < 0) {
tail = words.size() - 1;
}
System.out.println("After the If but before repaint again: " + tail);
}
}
repaint(); // before calling this repaint make sure tail is positive
yes this is what i did before concluding those things i said above, i always think that the tail will always be something positive(i.e 3) before calling that repaint() below, so i wont get a indexOutOfBounds pointing to the paintComponent() method, i really dont get why do i get that exception
then i get this output
i excluded the same printed exception
Quote:
Before the If : -1
After the If but before repaint again: 3
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
What are the values of tail before the exception happens?
Please copy all of the debug print out including the text of the error message here.
Is the value of tail always the same when the exception happens?
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Quote:
What are the values of tail before the exception happens?
the tail starts to the size of the list which is 3(4 - 1), and it decrements as the panel repaints(), tail--;
Quote:
Is the value of tail always the same when the exception happens?
actually no, it only happens when it is negative 1, which i didnt expect will occur, because i have an if part that will change it from being negative just before the repaint
i added another println() before the slide.setEnabled(true) statement
Code :
if (wordX < -900 && !stopSlide) {
wordX = 0;
head = tail;
tail--;
System.out.println("Before enabling the button: " + tail);
stopSlide = true;
slide.setEnabled(true);
System.out.println("Before the If : " + tail);
if (tail < 0) {
tail = words.size() - 1;
}
System.out.println("After the If but before repaint again: " + tail);
System.out.println("");
}
}
repaint(); // before calling this repaint make sure tail is positive
Code :
Before enabling the button: 2
Before the If : 2
After the If but before repaint again: 2
Before enabling the button: 1
Before the If : 1
After the If but before repaint again: 1
Before enabling the button: 0
Before the If : 0
After the If but before repaint again: 0
Before enabling the button: -1
Before the If : -1
After the If but before repaint again: 3
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.get(ArrayList.java:324)
at textQuiz7Continents.TESTS.DoubleCallToRepaintWithNimbusPLAFBizzareProblem$Panel.paintComponent(DoubleCallToRepaintWithNimbusPLAFBizzareProblem.java:87)
at javax.swing.JComponent.paint(JComponent.java:1029)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5124)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1491)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1422)
at javax.swing.RepaintManager.paint(RepaintManager.java:1225)
at javax.swing.JComponent._paintImmediately(JComponent.java:5072)
at javax.swing.JComponent.paintImmediately(JComponent.java:4882)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:786)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:714)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:694)
at javax.swing.RepaintManager.access$700(RepaintManager.java:41)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1636)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:646)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:607)
at java.awt.EventQueue$1.run(EventQueue.java:605)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:616)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
BUILD STOPPED (total time: 10 seconds)
im puzzled that when i remove the statement everything seems to work fine..
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
It could be the call to setEnabled() lets the Swing thread call the paintComponent() method on another thread BEFORE the code corrects the value of trail away from -1. Move the setEnabled() call AFTER trail's value has been corrected.
You don't need to call repaint if no values have changed. Check that repaint is called only if a change in the locations has been made.
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Quote:
It could be the call to setEnabled() lets the Swing thread call the paintComponent() method on another thread BEFORE the code corrects the value of trail away from -1
thats it! thats also what i thought, im not just quite sure enough without having the same assesment, but.. how about the nimbus plaf? even if i dont move that setEnabled() else where, when i remove the nimbus plaf installation everything works fine.. i dont know if this is too much to ask..
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
something might be happening differently in the swing thread when a look-and-feel is installed. everything works fine if i just remove the plaf.
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Do not call Swing methods from a thread other than the EDT, unless the method is explicitly documented as being thread safe. Your call to setEnabled is done from a different thread. Dispatch this to the EDT using SwingUtilities. Some LAF's may be more tolerable of things such as this, others are not.
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
oh by the way, what do you mean by
Quote:
Dispatch this to the EDT using SwingUtilities
?
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface
Quote:
Originally Posted by
chronoz13
oh by the way, what do you mean by ?
You can send a Runnable to the EDT using SwingUtilties
Code :
public class Runner implements Runnable{
public void run(){
System.out.println("This is my thread");
SwingUtilities.invokeLater(new Runnable(){//the content within the passed Runnable will be executed on the Event Dispatch Thread
public void run(){
myButton.setEnabled(false);
System.out.println("Is this on the EDT? " + SwingUtilities.isEventDispatchThread());
}
}
}
}
Based upon the code you posted, you could also use a javax.swing.Timer
Re: Nimbus PLAF: and a double call to repaint() inside a runnable interface