对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使用RowFilter …我正在尝试在Java JTree中实现弹出菜单.我已经对DefaultTreeCellRenderer(更改节点外观)和DefaultTreeCellEditor(创建组件以附加事件侦听器)进行了细分,因为显然DefaultTreeCellRenderer.getTreeCellRendererComponent()返回的组件不能这样做吗?).我真的不想"编辑"节点,只需在节点右键单击时弹出菜单,但这是我现在想到的唯一方法......
下面是我到目前为止的代码 - 我只想弄清楚如何捕获MouseEvents.它有点工作,但很糟糕.有什么更好的方法来完成我在这里要做的事情?
private class My_TreeCellRenderer extends DefaultTreeCellRenderer {
My_TreeCellRenderer() {
super ();
}
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
// set label text and tool tips
setText(((My_Object)value).getTreeLabel());
setToolTipText(((My_Object)value).getTreeToolTip());
return this;
}
}
private class My_TreeCellEditor extends DefaultTreeCellEditor {
private MouseAdapter ma;
My_TreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
super (tree, renderer);
ma = new MouseAdapter() {
public void mousePressed(MouseEvent e) { …Run Code Online (Sandbox Code Playgroud) 我有一个非常基本的JTree.因为我匆忙,TreeModel如果不需要,我宁愿不使用.我写了一个SSCCE来揭露这个问题:
有时我会添加一个节点.其他时候我删除它们.当我推送时Add,正确添加了一个节点.当我推Remove,它应该删除节点,但它没有.此外,如果我尝试添加多个节点,树只会保留我添加的第一个节点.
我写了一个更新方法JTree,我首先擦除从根节点挂起的所有节点,然后我看看我必须创建哪些节点和子节点.
除了不使用TreeModel操作树之外,我在这里做错了什么?
import java.awt.Dimension;
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.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
public class TreeTest {
private JFrame myFrame;
private JTree myTree;
private JButton addButton, removeButton;
private int numberOfNodes;
private DefaultMutableTreeNode rootNode;
private ArrayList<String> graphicIDS;
private ArrayList<String> graphicInfo;
public static void main (String [ ] args){
new TreeTest();
}
public TreeTest() { …Run Code Online (Sandbox Code Playgroud) 我正在寻找一个包含复选框的JTree实现,其中包含:
选择一个节点时,将自动选择树中的所有后续节点
取消选择一个节点时,将自动取消选择树中的所有后续节点
如果已选择父节点,并且已从其后续节点之一中删除选择,则将更改节点颜色,以使其直观,即虽然选择了此父节点,但并未选择其所有后续节点(如选择时一样)要在通用安装程序中安装的组件)
点击节点即可(无需按住'Ctrl'键!):
我在网上寻找一些简单的东西,却找不到我想要的简单东西.
有谁知道这种树的良好实现?
由于JTree和TreeModel不提供开箱即用的工具提示,您如何看待,为JTree提供特定于项目的工具提示的最佳方法是什么?
编辑:(之后回答我自己的问题.)
@Zarkonnen:感谢getTooltipText的想法.
我发现了覆盖DefaultTreeCellRenderer的另一种(可能还是更好的)方法并且想要分享它:
public class JTreeWithToolTips {
private static class OwnRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
setToolTipText("foobar" + row);
return super.getTreeCellRendererComponent(tree, value, sel,
expanded, leaf, row, hasFocus);
}
}
public static void main(String[] args) {
JTree tree = new JTree(new Object[] { "foo", "bar", "foobar" });
tree.setCellRenderer(new OwnRenderer());
ToolTipManager.sharedInstance().registerComponent(tree);
JFrame frame = new JFrame();
frame.getContentPane().add(tree);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Run Code Online (Sandbox Code Playgroud) 我有自定义JTree和习惯JModel; 当我给它一个新的模型时,我会让JTree"自动扩展".目前,它只是将所有节点折叠到根目录.
这是一个例子:
private class CustomTree extends JTree {
@Override
public boolean isExpanded(TreePath path) {
return ((Person) path.getLastPathComponent).hasChildren();
}
private class CustomTreeModel extends TreeModel {
// ... omitting various implementation details
@Override
public boolean isLeaf(Object object) {
return !((Person) object).hasChildren();
}
}
Model model = new Model();
Person bob = new Person();
Person alice = new Person();
bob.addChild(alice);
model.setRoot(bob);
JTree tree = new CustomTree(new CustomTreeModel(model));
Run Code Online (Sandbox Code Playgroud)
此时,树正确显示:
- BOB
- ALICE
Run Code Online (Sandbox Code Playgroud)
Alice是Bob的孩子(在数据和可视化树中)
但是,如果我打电话:
tree.setModel(new CustomTreeModel(model));
Run Code Online (Sandbox Code Playgroud)
一切都崩溃了:
+ …Run Code Online (Sandbox Code Playgroud) 我需要一个例子来说明如何添加一个键盘处理程序来检测何时按下Ctrl+ C,Ctrl+ X,Ctrl+ .CJTree
之前我用菜单快捷键做了这个,但没有成功.
我有一个自定义树单元格渲染器,我用它来渲染自定义图标JTree,我真的很喜欢警告图标和JOptionPane分别为警告消息和错误消息显示的错误图标.显然,我可以使用下面的代码来获取我自己使用的图标,但这很重要,需要我实例化我永远不会使用的对话框:
public class ValidationCellRenderer extends DefaultTreeCellRenderer {
private Icon warnIcon;
private Icon errorIcon;
public ValidationCellRenderer() {
JOptionPane optionPane = new JOptionPane(new Object(),
JOptionPane.WARNING_MESSAGE);
warnIcon = optionPane.getIcon();
optionPane = new JOptionPane(new Object(),
JOptionPane.ERROR_MESSAGE);
errorIcon = optionPane.getIcon();
}
}
Run Code Online (Sandbox Code Playgroud)
必须有更好的方法将这些图标作为资源,但我找不到从Java API中执行此操作的简单方法.有人有什么建议吗?
首先,我要说我不使用DefaultTreeModel.我实现了自己的TreeModel,所以我不能使用DefaultXXX.问题是:通过我的模型定义的一些addStuff()方法,我将节点添加到底层数据结构.然后我通过调用addStuff()函数中的treeNodesChanged()来通知监听器(我知道有treeNodesInserted方法,但它是一样的.它只是用不同的方法通知监听器).现在,其中一个监听器是我的主窗体中的静态类,这个监听器可以告诉JTree,它也包含在我的主窗体中,用于刷新自身.如何告诉JTree从模型中"重新加载"其部分或全部节点?
更新:发现这个问题虽然不完全相同,但它给出了我想要的答案.
更新2:我的问题不是如何通知查看器(JTree),而是在模型通知后应该以什么方式重新加载jtree.
首先让我说,我知道刷新树以反映底层更改的唯一方法是调用updateUI(),或重用setModel()方法.基本上,我的问题是:
假设TreeModelListener刚刚通过(通过TreeModelListener API)通知模型中发生了更改.好的,现在呢?
我有这个问题,因为JTree没有实现TreeModelListener.因此,在我的情况下,监听器是JTree的容器,或实现监听器的内部类,与Jtree位于同一容器中.
所以假设我是一个TreeModelListener实现,和我的兄弟JTree一起幸福地生活在一个JForm中.突然我的方法treeNodesInserted(TreeModelEvent evt)被调用.现在我该怎么做?如果我从我内部调用Jtree.updateUI(),那么模型的侦听器List会抛出ConcurrentModification异常.我可以调用updateUI以外的其他东西吗?
我尝试了很多东西,但只有updateUI刷新了JTree.所以我在听众之外做了.从JForm,我只是调用模型的方法来改变不正常的结构,然后我调用updateUI.没有使用TreeModelListener.
UPDATE3:我发现注册了隐式TreeModelListeners.在我的模型的addTreeModelListener(TreeModelListener监听器)实现中,我放了一个debug system.out行:
System.out.println("listener added: " + listener.getClass().getCanonicalName());
Run Code Online (Sandbox Code Playgroud)
当我执行jTree.setModel(model)时,我看到了这个调试输出:
监听器添加:javax.swing.JTree.TreeModelHandler
监听器添加:javax.swing.plaf.basic.BasicTreeUI.Handler
引发ConcurrentModificationException是因为对jtree.updateUI()的调用会重新注册侦听器(只有plaf,而不是两者),因此当我在侦听器通知循环中调用updateUI时会抛出它.现在刷新树的唯一方法是在TreeModelListener之外进行.有关更好解决方案的任何意见或想法吗?我错过了什么吗?
我很难创建一个JTree,它允许通过在JTree中拖放节点来重组节点.这似乎应该相对简单.我在线查看了一些示例,但我似乎无法在自己的代码中实现它.
例如,sun提供的这允许在不同组件之间拖动到树中,而不是从树本身内拖动.
我还发现这可以让你将文本拖到JTree中,但不能在树中拖动.
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.tree.*;
public class DndTree {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame f = new JFrame("D-n-D JTree");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel top = new JPanel(new BorderLayout());
JLabel dragLabel = new JLabel("Drag me:");
JTextField text = new JTextField();
text.setDragEnabled(true);
top.add(dragLabel, BorderLayout.WEST);
top.add(text, BorderLayout.CENTER);
f.add(top, BorderLayout.NORTH);
final JTree tree = new JTree();
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel(); …