I've got a viewport, and I've attached a change listener to it. Whenever I scroll through my viewport, my change listener gets called about four times. I'm not sure how to avoid this; I only want the call to happen once?
-
Seems, when you scroll your `ViewPort` state changed several times, because of, you have multiple invocations of `stateChanged(ChangeEvent e)` method. – alex2410 Jan 23 '14 at 06:51
-
voting to close as off-topic (doesn't meet with standard on SO) – mKorbel Jan 23 '14 at 07:15
2 Answers
There's no way around this, JViewport
will fire several stateChanged
events because it's providing notification about changes to a number of properties...
From the JavaDocs...
Adds a
ChangeListener
to the list that is notified each time the view's size, position, or the viewport's extent size has changed.
At this point, it's kind of hard to know what to suggest as we don't know what it is you are trying to achieve, however, if you have to use a ChangeListener
, you could set up a coalescing mechanism. That is, rather then responding to each event, you basically wait until a long enough delay has occurred between events before responding to it...
For example...
public class DelayedChangeHandler implements ChangeListener {
private Timer timer;
private ChangeEvent last;
public DelayedChangeHandler() {
timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stableStateChanged();
}
});
timer.setRepeats(false);
}
@Override
public void stateChanged(ChangeEvent e) {
last = e;
timer.restart();
}
protected void stableStateChanged() {
System.out.println("Finally...");
}
}
Basically, this is a ChangeListener
implementation that uses a non-repeating javax.swing.Timer
with a short delay. Each time stateChanged
is called, the timer is restart. Finally, when the timer is allowed to "tick", it calls stableStateChanged
indicating that enough time has passed since the last event was raised.
This assumes that you don't so much care about what caused the event, only that the event occured...
A runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestViewport {
public static void main(String[] args) {
new TestViewport();
}
public TestViewport() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JPanel pane = new JPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(1000, 1000);
}
};
JScrollPane sp = new JScrollPane(pane);
sp.getViewport().addChangeListener(new DelayedChangeHandler());
sp.getViewport().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println(evt.getPropertyName());
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DelayedChangeHandler implements ChangeListener {
private Timer timer;
private ChangeEvent last;
public DelayedChangeHandler() {
timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stableStateChanged();
}
});
timer.setRepeats(false);
}
@Override
public void stateChanged(ChangeEvent e) {
last = e;
timer.restart();
}
protected void stableStateChanged() {
System.out.println("Finally...");
}
}
}

- 343,457
- 22
- 230
- 366
-
Thanks guys ill try out the suggestions, much appreciated. What im doing is firing the event from a changelistener on my scrollpane in order to draw new components when the user scrolls past a certain point. I cant add all the components up front, its too big, causing memory issues. – 3uPh0riC Jan 23 '14 at 08:19
-
O by the way mKorbel, i appreciate the feedback really i do, but i dont understand saying things like 'voting to close question'. I just needed some help from some smart guys and asked a basic question, got some great answers and ill try them out, not sure whats wrong with that. For me to go and create a runnable short version of the monster program i have here will really be lots of work. If you find my question stupid or have some other problems with it just dont answer it, there are other guys more than willing to help out a knob like me. Thanks – 3uPh0riC Jan 23 '14 at 08:21
-
@3uPh0riC `For me to go and create a runnable short version of the monster program i have here will really be lots of work.` == (equals for me:-) very brazenly abusing that there are wise head, isn't it???, standard could be an [MCVE](http://stackoverflow.com/help/mcve) or link to API/tutorial (e.g. as you can to see in answer by MadProgrammer) – mKorbel Jan 23 '14 at 09:05
-
@3uPh0riC trust me, mKorbel was being helpful, had your question been closed without comment, you would have been wondering why. The fact there are people who are willing to let you know why they think the question should be closed or improved is a god send, to many people downvote and vote for closure without having the guts to state why. I took a massive stab in the dark and was lucky to provide at least one possible solution that might work for you, to often it takes hours of comments, re-work, bribery, cohersion and I get to the point where I just want to re-tract my answer – MadProgrammer Jan 23 '14 at 10:14
You can try to use AdjustmentListener
for gettign scroll event once, try next:
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.io.UnsupportedEncodingException;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Example {
public static void main(String[] args) throws UnsupportedEncodingException {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JScrollPane pane = new JScrollPane(new JTextArea());
pane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if(e.getValueIsAdjusting()){
return;
}
System.out.println("vertical scrolled");
System.out.println("bar value = " + e.getValue());
}
});
frame.setContentPane(pane);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Here is another example.

- 10,904
- 3
- 25
- 41
-
+1 now I don't remember the reason why I'm used [ChangeListener instead of AdjustmentListener](http://stackoverflow.com/a/8249353/714968), ??? there was/were an issue(s) ??? – mKorbel Jan 23 '14 at 07:13
-
@mKorbel Really, I don't remember when I used that listener in the past, but for that issue seems that will be work. ChangeListener for more broad changes, maybe. – alex2410 Jan 23 '14 at 07:39
-
+1 I like this idea - But in my testing, it did raise a lot of events, nice non-the-less – MadProgrammer Jan 23 '14 at 08:21