Rob*_*bin 34 java swing jtree swingx
对a应用过滤JTree以避免某些节点/叶子出现在渲染版本中JTree.理想情况下,我正在寻找一个允许动态过滤器的解决方案,但如果我能让静态过滤器工作,我会很高兴.
为了使它更容易一些,让我们假设JTree只支持渲染,而不是编辑.应该可以移动,添加,删除节点.
一个例子是a上面的搜索字段JTree,而在键入时,JTree只显示带有匹配的子树.
一些限制:它将用于可以访问JDK和SwingX的项目中.我想避免包含其他第三方库.
我已经想到了一些可能的解决方案,但这些都不是理想的
基于模型的过滤
TreeModel以过滤掉一些值.快速而简单的版本很容易编写.过滤掉节点,并且在过滤器或委托TreeModel的每次更改时,装饰器都可以触发整个树发生变化的事件(treeStructureChanged根节点作为节点).将它与恢复选择状态和扩展状态的侦听器相结合JTree,你会得到一个或多或少有效的版本,但是来自它的事件TreeModel都搞砸了.这或多或少是这个问题中使用的方法TreeModel但尝试点燃正确的事件.我(还)没有设法提出这个的工作版本.它似乎需要委托的副本,TreeModel以便能够在从委托模型中删除节点时使用正确的子索引触发事件.我认为有更多的时间我可以让它工作,但它只是感觉不对(过滤感觉像视图应该做的事情,而不是模型)TreeModel.但是,这完全是不可重用的,并且可能和编写装饰器一样困难TreeModel基于视图的过滤
这似乎是要走的路.过滤不应影响模型,只影响视图.
我看了RowFilter上课.虽然javadoc似乎建议你可以结合使用它JTree:
当与JTree相关联时,条目对应于节点.
我找不到RowFilter(或RowSorter)和JTree班级之间的任何联系.RowFilterSwing教程的标准实现似乎表明RowFilter只能直接使用JTable(参见参考资料JTable#setRowSorter).没有类似的方法可供选择JTree
JXTreejavadoc.它有一个ComponentAdapter可用的,并且javadoc ComponentAdapter表示RowFilter可以与目标组件进行交互,但我看不出如何建立RowFilter和之间的链接JTreeJTable使用RowFilters 处理过滤,也许可以在修改后的版本上完成相同的操作JTree.简而言之:我不知道解决这个问题的最佳方法是什么
注意:这个问题可能是这个问题的重复,但这个问题仍然没有答案,问题相当简短,答案似乎不完整,所以我想发布一个新问题.如果没有这样做(常见问题解答没有提供明确的答案),我将更新这个3年的问题
基于视图的过滤绝对是最佳选择.您可以使用类似我在下面编写的示例.过滤树时的另一种常见做法是在过滤树时切换到列表视图,因为列表不需要您显示需要显示其后代的隐藏节点.
这是绝对可怕的代码(我试图在刚刚鞭打它的过程中削减每一个角落),但它应该足以让你开始.只需在搜索框中键入您的查询,然后按Enter键,它将过滤JTree的默认模型.(仅供参考,前90行只是生成的样板和布局代码.)
package com.example.tree;
import java.awt.BorderLayout;
public class FilteredJTreeExample extends JFrame {
private JPanel contentPane;
private JTextField textField;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
FilteredJTreeExample frame = new FilteredJTreeExample();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public FilteredJTreeExample() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JPanel panel = new JPanel();
contentPane.add(panel, BorderLayout.NORTH);
GridBagLayout gbl_panel = new GridBagLayout();
gbl_panel.columnWidths = new int[]{34, 116, 0};
gbl_panel.rowHeights = new int[]{22, 0};
gbl_panel.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
gbl_panel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
panel.setLayout(gbl_panel);
JLabel lblFilter = new JLabel("Filter:");
GridBagConstraints gbc_lblFilter = new GridBagConstraints();
gbc_lblFilter.anchor = GridBagConstraints.WEST;
gbc_lblFilter.insets = new Insets(0, 0, 0, 5);
gbc_lblFilter.gridx = 0;
gbc_lblFilter.gridy = 0;
panel.add(lblFilter, gbc_lblFilter);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
final JTree tree = new JTree();
scrollPane.setViewportView(tree);
textField = new JTextField();
GridBagConstraints gbc_textField = new GridBagConstraints();
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
gbc_textField.anchor = GridBagConstraints.NORTH;
gbc_textField.gridx = 1;
gbc_textField.gridy = 0;
panel.add(textField, gbc_textField);
textField.setColumns(10);
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
TreeModel model = tree.getModel();
tree.setModel(null);
tree.setModel(model);
}
});
tree.setCellRenderer(new DefaultTreeCellRenderer() {
private JLabel lblNull = new JLabel();
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean arg2, boolean arg3, boolean arg4, int arg5, boolean arg6) {
Component c = super.getTreeCellRendererComponent(tree, value, arg2, arg3, arg4, arg5, arg6);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (matchesFilter(node)) {
c.setForeground(Color.BLACK);
return c;
}
else if (containsMatchingChild(node)) {
c.setForeground(Color.GRAY);
return c;
}
else {
return lblNull;
}
}
private boolean matchesFilter(DefaultMutableTreeNode node) {
return node.toString().contains(textField.getText());
}
private boolean containsMatchingChild(DefaultMutableTreeNode node) {
Enumeration<DefaultMutableTreeNode> e = node.breadthFirstEnumeration();
while (e.hasMoreElements()) {
if (matchesFilter(e.nextElement())) {
return true;
}
}
return false;
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
当你真实地实现它时,你可能想要创建自己的TreeNode和TreeCellRenderer实现,使用一个不那么愚蠢的方法来触发更新,并遵循MVC分离.请注意,"隐藏"节点仍然呈现,但它们太小,以至于您无法看到它们.但是,如果您使用箭头键导航树,您会注意到它们仍在那里.如果你只是需要一些有用的东西,这可能就足够了.

编辑
以下是Mac OS中未过滤和过滤的树版本的屏幕截图,显示在Mac OS中可以看到空白:


看看这个实现:http://www.java2s.com/Code/Java/Swing-Components/InvisibleNodeTreeExample.htm
它创建DefaultMutableNode的子类,添加"isVisible"属性,而不是实际从TreeModel中删除/添加节点.我觉得很甜蜜,它整齐地解决了我的过滤问题.
旧问题,我偶然发现...对于所有想要快速简便解决方案的人
我知道它不如过滤模型那么干净,并带有可能的回退,但是如果您只想为小型应用程序提供快速解决方案,请执行以下操作:
扩展DefaultTableCellRenderer,重写getTreeCellRendererComponent-调用super.getTreeCellRendererComponent(...),然后将要隐藏的所有节点的Preferred Height设置为ZERO。构造JTree时,请确保设置setRowHeight(0);。-因此它将尊重每行的“首选高度” ...
瞧-所有过滤的行都看不见!
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
public class JTreeExample
{
public static void main( final String[] args ) throws Exception
{
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
// The only correct way to create a SWING Frame...
EventQueue.invokeAndWait( new Runnable()
{
@Override
public void run()
{
swingMain();
}
} );
}
protected static void swingMain()
{
final JFrame f = new JFrame( "JTree Test" );
f.setLocationByPlatform( true );
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
final int items = 5;
final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( "JTree", true );
final DefaultTreeModel myModel = new DefaultTreeModel( rootNode );
final Box buttonBox = new Box( BoxLayout.X_AXIS );
for( int i = 0; i < items; i++ )
{
final String name = "Node " + i;
final DefaultMutableTreeNode newChild = new DefaultMutableTreeNode( name );
rootNode.add( newChild );
final JButton b = new JButton( "Show/Hide " + i );
buttonBox.add( b );
b.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( final ActionEvent e )
{
// If the node has a Text, set it to null, otherwise reset it
newChild.setUserObject( newChild.getUserObject() == null ? name : null );
myModel.nodeStructureChanged( newChild.getParent() );
}
} );
}
final JTree tree = new JTree( myModel );
tree.setRowHeight( 0 );
tree.setCellRenderer( new JTreeExample.TreeRenderer() );
f.add( tree, BorderLayout.CENTER );
f.add( buttonBox, BorderLayout.SOUTH );
f.setSize( 600, 500 );
f.setVisible( true );
}
public static class TreeRenderer extends DefaultTreeCellRenderer
{
@Override
public Component getTreeCellRendererComponent( final JTree tree, final Object value, final boolean selected,
final boolean expanded, final boolean leaf, final int row, final boolean hasFocus )
{
// Invoke default Implementation, setting all values of this
super.getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, hasFocus );
if( !isNodeVisible( (DefaultMutableTreeNode)value ) )
{
setPreferredSize( new Dimension( 0, 0 ) );
}
else
{
setPreferredSize( new Dimension( 200, 15 ) );
}
return this;
}
}
public static boolean isNodeVisible( final DefaultMutableTreeNode value )
{
// In this example all Nodes without a UserObject are invisible
return value.getUserObject() != null;
}
}
Run Code Online (Sandbox Code Playgroud)