I have read several postings and several articles on lazy loading a JTree (using either the WillExpand event or writing a new TreeModel). One reason I want to lazy load is that I am populating the tree from a database over a somewhat slow network link. I am having difficulty wrapping my head around using a SwingWorker (or some other thread) to do the database access on demand - and my readings haven't cleared it up for me.
I don't know that I need to get the data until the user clicks on the tree. If I, at that point, go off and get the data synchronously the Event Dispatch Tread will be hung up until the query returns. If I use a thread to get the data, what do I tell the tree while I'm waiting for the query to return? Do I tell the tree that the selected node has zero children, and then asynchronously add the children and programatically open up the tree? ... I haven't seen any example code doing something like that.
Ron Alby wrote:Do I tell the tree that the selected node has zero children, and then asynchronously add the children and programatically open up the tree?
Did you already tell the tree that the selected node has zero children? Or did you tell the tree that the selected node does have children (so the user knows they can expand that node)? Clearly the latter is what you want to do.
But then what happens if it turns out there are no children, when you ask the database? At that point you're going to have to tell the tree the truth, that there are in fact no children. So it might be a good idea if your design included some visual clue that a node might or might not have children, but you don't know until the user asks.
There are several strategies you can employ, such as:
1. If you think you are likely to use mots of the data you can start loading it all in the background and if the user selects something that hasn't been loaded yet then load that now.
2. You can load the current tree level in the background and wait until the user selects a node before loading the next level of the tree. Again any node that the user selects that hasn't been loaded yet must be loaded immediately.
3. You could get really clever and keep track of the recent data requests and if the same data is being repeatedly requested by this user load that first.
4. You can wait until a user selects a node and then load just that node.
If I, at that point, go off and get the data synchronously the Event Dispatch Tread will be hung up until the query returns. If I use a thread to get the data, what do I tell the tree while I'm waiting for the query to return?
First of all don't use the EDT to get the data, use another thread or use SwingWorker which is perfect for any task that requires a background action which updates the GUI.
As for what to do with the GUI, if the user is waiting for a node to load you need to reflect this in the GUI, there are many ways of doing this, such as
1. Display a progress bar.
2. Change the mouse to an hour glass.
3. Change the JTree node to display an image that informs the user the opening action is waiting for the data to be loaded ie a an hour glass, arrows in a circle etc
Action type 1 stops the user doing anything else until the data is loaded
Action type 2 suggest to the user they can't do anything else until the data is loaded (it's up to you if you allow further mouse clicks whilst the mouse is an hour glass but it's not very intuitive to do so).
Action type 3 allows the user to continue doing other things whilst the data is being loaded. Of course this assumes there are other things the user can do whilst waiting for the data.
Joined: May 31, 2011
Let me be more specific ... perhaps the issue is how I do the lazy loading.
The user clicks on a node to expand it. The JTree calls the TreeModel method getChildCount, followed by a series of getChild. I currently do the loading in getChildCount. I must return out of this method with a number that the JTree will then use when it is laying itself out. Since I have no idea how many children there will be (if any), what number do I return?
Lets say I get fancy and restructure my code so that I am fetching one level of children in advance. Again, based on network traffic, I may still be in a situation where they have clicked on a node that has not yet had its children loaded and still have to deal with the getChildCount issue.
Ron Alby wrote:I currently do the loading in getChildCount. I must return out of this method with a number that the JTree will then use when it is laying itself out. Since I have no idea how many children there will be (if any), what number do I return?
If you have to do it that way (and I would rather do it some other way, like the TreeNodeWillExpand event you mentioned earlier), then the answer is that you start the SwingWorker to load the children, and return zero immediately. The SwingWorker will create nodes and add them to the TreeModel; the model should be designed so that adding a node causes a standard event to be sent to all of its listeners, which would include the JTree.
Joined: May 31, 2011
Thank you Paul.
I moved to the listener and implemented the worker as you suggested.
Its all good now.