Stavo lavorando con lo stato dell'albero (nodi espansi/selezionati) salvando e creando una classe di utilità che può salvare e ripristinare gli stati dei nodi. Funziona bene.Ripristino degli stati del nodo albero espanso/compresso
Ma c'è ancora un problema con JTree stesso - mentre l'utente sta lavorando con qualche istanza JTree (espansione/compressione di nodi) ci potrebbe essere una situazione in cui un nodo (nascosto sotto un altro nodo compresso) viene espanso. Niente di speciale, va bene.
JTree conserva i record dei nodi espansi/compressi in un separato expandedState
Hashtable utilizzando il percorso del nodo come chiave e valore booleano come stato espanso. Quindi, quando quel nodo espanso sotto nodo principale compresso diventerà visibile, sarà comunque espanso poiché c'è un record per esso in expandedState
Hashtable con valore true
.
Situazione spiegato immagini ...
1. Expand radice ed espandere qualche nodo (cartella "glassfish4") sotto root:
2. Collapse radice:
3. espandersi radice e vediamo ancora il nodo figlio (cartella "glassfish4") ampliato:
pensate che io sia salvato stato albero al momento screenshot # 2, quando radice è crollato - il problema è che se voglio ripristinare tutti gli stati dei nodi dell'albero (anche per quelli nascosti) non riesco ad espandere un nodo sotto un altro nodo compresso perché questo costringerà tutti i nodi genitore ad espandersi. Inoltre non posso accedere a expandedState
Hashtable per modificare gli stati espansi direttamente all'interno di esso poiché è dichiarato privato in JTree e non ci sono buoni modi per accedervi. Quindi non posso riprodurre completamente lo stato dell'albero iniziale.
quindi quello che posso fare è:
- con forza di accesso che Hashtable attraverso la riflessione - pessima idea nodi
- Rewrite JTree ampliano la logica - questa è anche una cattiva idea
- Ripristinare tutti gli stati espansi prima quindi ripristina tutti gli stati compresso, che obbligherà l'albero a eseguire ulteriori ridisegni inutili e un sacco di rendering aggiuntivo, quindi è una soluzione veramente brutta che non voglio usare
Forse mi manca qualcos'altro?
Quindi la domanda è:
C'è un altro modo per espandere i nodi figlio senza causare l'espansione dei nodi padre?
È possibile trovare alcune classi che utilizzo per salvare/ripristinare lo stato dell'albero di seguito.
È sufficiente chiamare TreeUtils.getTreeState(tree)
per recuperare lo stato JTree e TreeUtils.setTreeState(tree,treeState)
per ripristinare lo stato JTree. Si noti che l'albero deve utilizzare UniqueNode, altrimenti tali metodi genereranno ClassCastException - è possibile semplicemente sostituire DefaultMutableTreeNode con UniqueNode se si dispone di nodi propri che estendono DefaultMutableTreeNode.
UniqueNode.java - semplice nodo con il proprio ID univoco
public class UniqueNode extends DefaultMutableTreeNode implements Serializable
{
/**
* Prefix for node ID.
*/
private static final String ID_PREFIX = "UN";
/**
* Unique node ID.
*/
protected String id;
/**
* Costructs a simple node.
*/
public UniqueNode()
{
super();
setId();
}
/**
* Costructs a node with a specified user object.
*
* @param userObject custom user object
*/
public UniqueNode (Object userObject)
{
super (userObject);
setId();
}
/**
* Returns node ID and creates it if it doesn't exist.
*
* @return node ID
*/
public String getId()
{
if (id == null)
{
setId();
}
return id;
}
/**
* Changes node ID.
*
* @param id new node ID
*/
public void setId (String id)
{
this.id = id;
}
/**
* Changes node ID to new random ID.
*/
private void setId()
{
this.id = TextUtils.generateId (ID_PREFIX);
}
/**
* {@inheritDoc}
*/
public UniqueNode getParent()
{
return (UniqueNode) super.getParent();
}
/**
* Returns TreePath for this node.
*
* @return TreePath for this node
*/
public TreePath getTreePath()
{
return new TreePath (getPath());
}
}
TreeUtils.java - classe di utilità che salva/carichi TreeState da/in JTree
public class TreeUtils
{
/**
* Returns tree expansion and selection states.
* Tree nodes must be instances of UniqueNode class.
*
* @param tree tree to process
* @return tree expansion and selection states
*/
public static TreeState getTreeState (JTree tree)
{
return getTreeState (tree, true);
}
/**
* Returns tree expansion and selection states.
* Tree nodes must be instances of UniqueNode class.
*
* @param tree tree to process
* @param saveSelection whether to save selection states or not
* @return tree expansion and selection states
*/
public static TreeState getTreeState (JTree tree, boolean saveSelection)
{
TreeState treeState = new TreeState();
List<UniqueNode> elements = new ArrayList<UniqueNode>();
elements.add ((UniqueNode) tree.getModel().getRoot());
while (elements.size() > 0)
{
UniqueNode element = elements.get (0);
TreePath path = new TreePath (element.getPath());
treeState.addState (element.getId(), tree.isExpanded (path), saveSelection && tree.isPathSelected (path));
for (int i = 0; i < element.getChildCount(); i++)
{
elements.add ((UniqueNode) element.getChildAt (i));
}
elements.remove (element);
}
return treeState;
}
/**
* Restores tree expansion and selection states.
* Tree nodes must be instances of UniqueNode class.
*
* @param tree tree to process
* @param treeState tree expansion and selection states
*/
public static void setTreeState (JTree tree, TreeState treeState)
{
setTreeState (tree, treeState, true);
}
/**
* Restores tree expansion and selection states.
* Tree nodes must be instances of UniqueNode class.
*
* @param tree tree to process
* @param treeState tree expansion and selection states
* @param restoreSelection whether to restore selection states or not
*/
public static void setTreeState (JTree tree, TreeState treeState, boolean restoreSelection)
{
if (treeState == null)
{
return;
}
tree.clearSelection();
List<UniqueNode> elements = new ArrayList<UniqueNode>();
elements.add ((UniqueNode) tree.getModel().getRoot());
while (elements.size() > 0)
{
UniqueNode element = elements.get (0);
TreePath path = new TreePath (element.getPath());
// Restoring expansion states
if (treeState.isExpanded (element.getId()))
{
tree.expandPath (path);
// We are going futher only into expanded nodes, otherwise this will expand even collapsed ones
for (int i = 0; i < element.getChildCount(); i++)
{
elements.add ((UniqueNode) tree.getModel().getChild (element, i));
}
}
else
{
tree.collapsePath (path);
}
// Restoring selection states
if (restoreSelection)
{
if (treeState.isSelected (element.getId()))
{
tree.addSelectionPath (path);
}
else
{
tree.removeSelectionPath (path);
}
}
elements.remove (element);
}
}
}
TreeState.java - classe contenitore per la mappa che contiene gli stati dei nodi
public class TreeState implements Serializable
{
/**
* Tree node states.
*/
protected Map<String, NodeState> states = new LinkedHashMap<String, NodeState>();
/**
* Constructs new object instance with empty states.
*/
public TreeState()
{
super();
}
/**
* Constructs new object instance with specified states.
*
* @param states node states
*/
public TreeState (Map<String, NodeState> states)
{
super();
if (states != null)
{
setStates (states);
}
}
/**
* Returns all node states.
*
* @return all node states
*/
public Map<String, NodeState> getStates()
{
return states;
}
/**
* Sets all node states.
*
* @param states all node states
*/
public void setStates (Map<String, NodeState> states)
{
this.states = states;
}
/**
* Adds node state.
*
* @param nodeId node ID
* @param expanded expansion state
* @param selected selection state
*/
public void addState (String nodeId, boolean expanded, boolean selected)
{
states.put (nodeId, new NodeState (expanded, selected));
}
/**
* Returns whether node with the specified ID is expanded or not.
*
* @param nodeId node ID
* @return true if node with the specified ID is expanded, false otherwise
*/
public boolean isExpanded (String nodeId)
{
final NodeState state = states.get (nodeId);
return state != null && state.isExpanded();
}
/**
* Returns whether node with the specified ID is selected or not.
*
* @param nodeId node ID
* @return true if node with the specified ID is expanded, false otherwise
*/
public boolean isSelected (String nodeId)
{
final NodeState state = states.get (nodeId);
return state != null && state.isSelected();
}
}
NodeState.java - espansione singolo nodo/selezione stato
public class NodeState implements Serializable
{
/**
* Whether node is expanded or not.
*/
protected boolean expanded;
/**
* Whether node is selected or not.
*/
protected boolean selected;
/**
* Constructs empty node state.
*/
public NodeState()
{
super();
this.expanded = false;
this.selected = false;
}
/**
* Constructs node state with the specified expansion and selection states.
*
* @param expanded expansion state
* @param selected selection state
*/
public NodeState (boolean expanded, boolean selected)
{
super();
this.expanded = expanded;
this.selected = selected;
}
/**
* Returns whether node is expanded or not.
*
* @return true if node is expanded, false otherwise
*/
public boolean isExpanded()
{
return expanded;
}
/**
* Sets whether node is expanded or not.
*
* @param expanded whether node is expanded or not
*/
public void setExpanded (boolean expanded)
{
this.expanded = expanded;
}
/**
* Returns whether node is selected or not.
*
* @return true if node is selected, false otherwise
*/
public boolean isSelected()
{
return selected;
}
/**
* Sets whether node is selected or not.
*
* @param selected whether node is selected or not
*/
public void setSelected (boolean selected)
{
this.selected = selected;
}
}
proposito, setTreeState
metodo evita che ristabilisce stati espansi sotto nodi collassati al momentaneamente
// Restoring expansion states
if (treeState.isExpanded (element.getId()))
{
tree.expandPath (path);
// We are going futher only into expanded nodes, otherwise this will expand even collapsed ones
for (int i = 0; i < element.getChildCount(); i++)
{
elements.add ((UniqueNode) tree.getModel().getChild (element, i));
}
}
else
{
tree.collapsePath (path);
}
metodo che blocca nodi figli solo chiamato se il nodo genitore è espanso. Quindi tutti i nodi figli sotto nodi compressi vengono ignorati. Se cambi questo comportamento, vedrai il problema che ho descritto all'inizio di questa domanda: i nodi principali verranno espansi.
+1 prega è possibile raggiungere quegli eventi, traccia da TreeModelListener, utilizzando TreeExpansionListener e TreeWillExpandListener (o bug nel L & F, problemi standard con JList, JComboBox, JSpinner e JTree e la sua Model_To_View) – mKorbel
buona domanda - difficile rispondere : afair, anche il JTree è solo uno schiavo dello stato di espansione, il vero controller è la AbstractLayoutCache creata e utilizzata dal delegato ui. Quindi sospetto (mai provato) che una soluzione reale coinvolga un layoutCache personalizzato che richiede uis personalizzati ... – kleopatra
@mKorbel è davvero un'opzione per ascoltare l'espansione dell'albero ed espandere i nodi nascosti quando diventeranno visibili, ma quello è un soluzione alternativa: una migliore, ma ancora una soluzione alternativa. Quello che voglio è ripristinare lo stato dell'albero allo stesso tempo senza lasciare tracce (come ascoltatori di espansione pigri) di presenza di "strumenti" di ripristino. –