我正在处理树状态(展开/选择的节点)保存,并创建了一个实用程序类,可以保存和恢复节点状态。工作很好。
但JTree本身仍然存在一个问题--当用户使用某个JTree实例(展开/折叠节点)时,可能会出现某个节点(隐藏在另一个折叠节点下)被展开的情况。没什么特别的--那很好。
JTree将有关展开/折叠节点的记录保存在单独的expandedstate
哈希表中,使用节点路径作为键,使用布尔值作为展开状态值。因此,当折叠的父节点下的展开节点变得可见时,它仍然会展开,因为expandedstate
Hashtable中有一个值为true
的记录。
所以我能做的是:
也许我错过了什么?
您可以在下面找到一些我用来保存/恢复树状态的类。
只需调用Treeutils.getTreeEstate(tree)
检索JTree状态,调用Treeutils.setTreeEstate(tree,TreeEstat)
还原JTree状态。注意,tree必须使用UniqueNode,否则这些方法将抛出ClassCastException-如果您有扩展DefaultMutableTreeNode的自己的节点,则可以简单地用UniqueNode替换DefaultMutableTreeNode。
java-具有自己唯一ID的简单节点
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 () );
}
}
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 );
}
}
}
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 ();
}
}
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;
}
}
顺便说一句,setTreeState
方法目前避免恢复折叠节点下的展开状态:
// 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 );
}
方法,该方法收集仅在父节点展开时调用的子节点。因此会忽略折叠节点下的所有子节点。如果您改变了该行为,您将看到我在本问题开头描述的问题-父节点将被扩展。
为什么不执行与所述相同的操作来恢复状态,首先将子节点设置为展开,然后根据需要将其父节点设置为折叠?
与当前代码的唯一区别是使用两次迭代而不是一次。首先在需要的地方进行迭代和展开,然后在需要的地方进行迭代和折叠。
由于重新绘制逻辑,树应该绘制一次。
我已展开treeView的某些项 我使用contextMenu向指定节点添加新项目 项已添加到我的结构(后端) 重新加载TreeView-它从一开始就显示(仅根目录) 我想有我的树视图的视图像之前添加项目(这个相同的项目折叠和展开)。我不知道怎么做。我正在考虑改变StructureNode的实现(add boolean field是扩展的并使用它,但它不起作用--我正在从文件中打开我的树视图,那里
折叠隐藏节点,可记录用户上次行为。 标题 内容 类型 通用 支持布局 responsive, fixed-height, fill, container, fixed 所需脚本 https://c.mipcdn.com/static/v2/mip-accordion/mip-accordion.js 使用说明 mip-accordion 是一个折叠隐藏节点的 MIP 组件,可以对满足特定结构的节点
问题内容: 我有一个带JSplitPane的JFrame,它是OneTouchExpandable。我想记住JSplitPane在JFrame上的最后一个Divider位置,如果重新打开JFrame,则恢复该位置。 它工作正常,但是如果用户通过oneTouchExpandable UI- Widget展开一侧,则我仅将’int’-Position放置在dispose上,然后再次将’int’-Pos
我有一个带有多个视图保持器的RecyclerView适配器。每个ViewHolder都有一个标题TextView和一个嵌套的RecyclerView,工作正常。但我想实现一个扩展/折叠函数,这样嵌套的RecyclerView就可以隐藏,直到单击标题为止。我使用此方法RecyclerView展开/折叠项目。它可以工作,但当我单击标题以展开嵌套的RecyleView时,recyclerview不会填充
我正在使用jsTree显示一个树。我想选择树中可以使用的所有节点。这工作得很好。 但是,这将展开所有节点,并且拥有一个大树将把其余的内容一直往下推。 我想在检查所有节点后折叠树,但使用不起作用。 有人有办法解决吗?
我正在使用Netbeans 7.2开发JavaFX2.2应用程序。我正在使用一个treeview,我扩展了TreeCell,为每个TreeItem提供一个上下文菜单,其中有一个MenuItem具有“collpase all”功能。treeview的最大深度级别为4。当用户右键单击级别2的TreeItem并单击“全部折叠”MenuItem时,我想使级别3的所有TreeItem折叠(setExpand