I have an implementation where I want to reload data while scrolling a JList after scrolling every 100 records. I have a large database, and it takes around 7 seconds to load the JList. Therefore, a wait dialog will be displayed to the user while data is loaded in the background.
I am facing two issues with the following code:
- For every single mouse scroll, three records are scrolled instead of scrolling a single record.
- If I continue scrolling while the reload is in progress (that is if I continued moving the mouse wheel), the scroll action event is stored somewhere, and once the reload is completed, the stored action event will be executed. For example, if I keep scrolling six times while records are reloading, the scrollbar won't move at the moment because the wait dialog is present, but once the reload is complete, it will scroll six records.
My goal is to remove whatever mouse scroll/wheel movement events waiting for execution once the data is loaded or even a call is made to reload the data.
JScrollPane calculateScrollPane = new JScrollPane();
JList calculateList = new JList();
MouseWheelListener mouseListener = new MouseWheelListener() {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
int firstVisibleIndex = calculateList.getFirstVisibleIndex();
WaitDialog wait = new WaitDialog();
wait.start();
if(firstVisibleIndex % 100 == 0)
reloadData(e);
wait.stop();
}
};
calculateScrollPane.addMouseWheelListener(mouseListener);
I tried storing the firstIndexAfterReload = firstVisibleIndex; and resetting the calculateList first visible index using calculateList.ensureIndexIsVisible(firstIndexAfterReload); This resets the firstVisibleIndex only once. If I have rigorously scrolled multiple times then firstIndexAfterReload is also reset.
Let's start with some obvious issues
MouseWheelListener(or any sort of mouse based listener) is a bad place to startSo, what's the answer?
Well, for me, I started with the
veriticalScrollBarof theJScrollPaneand added anAdjustmentListenerto it. This will notify me when the vertical scroll bar position changes. This might not be the "best" option, but it was the obvious one to me. This means we're not relient on the monitoring the keyboard and mouse, instead, we're monitoring the state (or part of the state) of the scroll pane itself.Next, I set up a "trigger" which was, in my test, 5 rows less then a full page of data. That is, in my example, a page was 20 rows, so my trigger was always 5 rows less (15/35/55/75/95), this means that we're attempting to load a new page slightly early.
When the
AdjustmentListenerwas triggered, I checked theJList#getLastVisibleIndexproperty and if the values mathched, I then used aSwingWorkerto off loading the loading of the data to a different thread, but which allows me an easy way to re-sync that data back to the EDT. See Worker Threads and SwingWorker for more detailsThis idea might look something like...
Now, please, understand. This is simplified solution. I've deliberately set the
setVisibleRowCountto a size less then my trigger row.You might be able to change it so that if the last row is
>=to the trigger row, you would start loading more pages, but, you'd probably need some way to determine if a loading is in progress first and possibly modify the logic which determines the next trigger point so that it's more efficient in a more dynamic environment.I'd also consider rolling a lot of this up into some kind of "pagination" workflow, so maybe a dedicated class you could pass the scroll pane and list into and have it manage all the core workflow and maybe trigger an observer to load the data, but that's me.
Oh, and might also consider making use of
JLayerto provide some kind of visual feedback, see How to Decorate Components with the JLayer Class for more details