【问题标题】:Filter jtree - keeping all nodes and children of nodes that match criteria过滤 jtree - 保留符合条件的所有节点和节点的子节点
【发布时间】:2016-11-17 01:46:15
【问题描述】:

我有一个 DefaultMutableTreeNodes 的 JTree,我想过滤它们。

当我进行过滤时,我想保留任何符合我的条件或具有符合我的条件的子节点的节点。

这里我包含了代码供您参考。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.border.EmptyBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;

public class FilteredJTreeExample extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPanel contentPane;
    private JTextField textField;
    private JTree tree;

    /**
     * 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("Search:");
        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);

        tree = new JTree();
        tree.setEditable( true );
        tree.setShowsRootHandles( false );
        tree.setInvokesStopCellEditing(true);
        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();
                boolean isMatched = false;
                while (e.hasMoreElements()) {
                    DefaultMutableTreeNode nextElement = e.nextElement(); 
                    if (matchesFilter(nextElement)) {
                        isMatched = true;
                    }
                }
                return isMatched;
            }
        });
    }
}

输出:

当我将搜索文本输入为“颜色”并单击 Enter 时。它只显示 JTree 和 Colors 节点。

问题:

  1. 我想展示颜色的孩子。即蓝色、紫色等,

  2. 我发现的另一个问题是执行搜索操作后,鼠标向上滚动不起作用,而向下滚动则正常。它可以通过搜索“e”并缩小框架并向下和向上滚动鼠标来复制。

注意:我使用的是 Ubuntu 14.04 LTS。

有什么想法吗?

【问题讨论】:

  • 关于 1.,您可以在 actionPerformed 的末尾执行 int r = 0; while (r &lt; tree.getRowCount()) { tree.expandRow(r); r++; },以完全展开(过滤的)树。我无法重现 2. ...
  • @Marco13 :我已经有一个展开所有操作,这里没有显示。我的问题是,我无法匹配符合标准的父母的孩子。在这里,我想显示 JTree > 颜色 > blue Purple red Yellow。
  • @Marco13 :问候问题 2:输入“e”(按 Enter 按钮)并将框架缩小到仅显示 3 条记录,然后向下滚动,您可以看到另外 3 条记录。但是你向上滚动,以前的记录没有显示。这将在鼠标滚动时发生(左键旁边)。如果我们拖动右侧的滚动条,它可以正常工作。
  • 关于滚动的问题:您应该检查过滤的树是否也会发生这种情况。如果是这样,请考虑将其作为一个自己的问题编写,并附上minimal reproducible example 以及有关操作系统和 Java 版本的详细信息。我无法像你描述的那样重现它(Win7,Java 1.8.XX)
  • 附注:过滤JTree 通常真的很困难。事实上,令我感到惊讶的是,您的方法似乎完全有效,它可能只是大小为 (0,0) 的渲染组件的“副作用”——这也可能是该行仍然存在的原因从根节点延伸到“不可见”节点——因此行为的细节也可能取决于 L&F。我认为正确的基于 view 的过滤几乎是不可能的。 (例如,使用您当前的解决方案,您仍然可以使用键盘浏览 invisible 键!)

标签: java swing awt jtree defaultmutabletreenode


【解决方案1】:

您还需要一个包含节点的方法,当它的一个父节点(祖先)包含所需的字符串时。例如,

private boolean containsMatchingParent(DefaultMutableTreeNode node) 
{
    DefaultMutableTreeNode current = node;
    while (current != null)
    {
        if (matchesFilter(current))
        {
            return true;
        }
        current = (DefaultMutableTreeNode) current.getParent();
    }
    return false;
}

(使用方式与containsMatchingChild相同)。

【讨论】:

    【解决方案2】:

    matchesFilter() 更改为:

    private boolean matchesFilter(DefaultMutableTreeNode node) 
    {
        TreeNode parent = node;
        while ( parent != null )
        {
            if ( parent.toString().contains(textField.getText()))
            {
                return true;
            }
            parent = parent.getParent();
    
        }
    
        return false;
    }
    

    这将返回颜色,以便您可以展开 colors 节点。

    【讨论】:

      猜你喜欢
      • 2020-08-28
      • 1970-01-01
      • 2012-07-13
      • 1970-01-01
      • 2019-12-04
      • 1970-01-01
      • 2014-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多