当前位置: 首页 > 知识库问答 >
问题:

JTree:为各个组设置自定义打开/关闭图标

黄浩涆
2023-03-14
  1. 我知道如何在JTree中设置自定义叶图标

但我无法根据组节点名称设置自定义打开/关闭图标,例如,节点可以称为电子邮件(因此有信封图标很好),或者一个组可以称为任务等等。

我试图通过重写类DefaultTreeCellRenderer的getTreeCellRendererComponent方法来实现这一点

但是更改当前节点的图标只会影响下一个节点!

如何为单个组设置自定义打开/关闭图标?

请看看我的代码

受雇者Java语言

package com.ehsunbehravesh.swing;

import java.util.Random;

public class Employee {

  public String name;
  public int id;
  public boolean isBoss;
  public Employee[] employees;

  public Employee(String name, boolean isBoss) {
    this.name = name;
    this.isBoss = isBoss;
    this.id = new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE);
  }  

  @Override
  public String toString() {
    return this.name;
  }

    static String randomName() {
    String chars = "abcdefghijklmnopqrstuvwxyz";
    StringBuilder builder = new StringBuilder();
    Random r = new Random(System.currentTimeMillis());
    int length = r.nextInt(10) + 1;
    for (int i = 0; i < length; i++) {
      builder.append(chars.charAt(r.nextInt(chars.length())));
    }

    return builder.toString();
  }
}

CustomTreeNode。Java语言

package com.ehsunbehravesh.swing;

import javax.swing.ImageIcon;
import javax.swing.tree.DefaultMutableTreeNode;

public class CustomTreeNode extends DefaultMutableTreeNode {

  /**
   * The icon which is displayed on the JTree object. open, close, leaf icon.
   */
  private ImageIcon icon;

  public CustomTreeNode(ImageIcon icon) {
    this.icon = icon;
  }

  public CustomTreeNode(ImageIcon icon, Object userObject) {
    super(userObject);
    this.icon = icon;
  }

  public CustomTreeNode(ImageIcon icon, Object userObject, boolean allowsChildren) {
    super(userObject, allowsChildren);
    this.icon = icon;
  }

  public ImageIcon getIcon() {
    return icon;
  }

  public void setIcon(ImageIcon icon) {
    this.icon = icon;
  }    
}

CustomeTreeCellRenderer.java

package com.ehsunbehravesh.swing;

import java.awt.Component;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;

class CustomeTreeCellRenderer extends DefaultTreeCellRenderer {

  public CustomeTreeCellRenderer() {
  }

  @Override
  public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
    super.getTreeCellRendererComponent(tree, value, leaf, expanded, leaf, row, hasFocus);

    if (!leaf) {
      CustomTreeNode node = (CustomTreeNode) value;
      System.out.println(((Employee) node.getUserObject()).name);

      if (node.getIcon() != null) {
        System.out.println(node.getIcon().toString());
        setClosedIcon(node.getIcon());
        setOpenIcon(node.getIcon());
      } else {
        setClosedIcon(getDefaultClosedIcon());
        setClosedIcon(getDefaultOpenIcon());
        setOpenIcon(getDefaultOpenIcon());
      }
    }

    return this;
  }
}

Test1.java

package com.ehsunbehravesh.swing;

import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeModel;

class TreeSample {
  public static void main(String args[]) {
    JFrame f = new JFrame("JTree Sample");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel pnlMain = new JPanel(new BorderLayout());
    pnlMain.setBackground(Color.white);

    createTree(pnlMain);

    f.setContentPane(pnlMain);

    f.setSize(300, 200);
    f.setVisible(true);
  }

  private static void createTree(JPanel pnlMain) {
    Employee bigBoss = new Employee(Employee.randomName(), true);
    Employee[] level1 = new Employee[5];    
    bigBoss.employees = level1;

    for (int i = 0; i < level1.length; i++) {
      level1[i] = new Employee(Employee.randomName(), true);      
    }


    for (int i = 0; i < level1.length; i++) {
      Employee employee = level1[i];
      if (employee.isBoss) {
        int count = 5;
        employee.employees = new Employee[count];

        for (int j = 0; j < employee.employees.length; j++) {
          employee.employees[j] = new Employee(Employee.randomName(), false);          
        }
      }
    }

    CustomTreeNode root = new CustomTreeNode(new ImageIcon("images/Circle_3.gif"), bigBoss);           
    DefaultTreeModel model = new DefaultTreeModel(root);

    for (Employee employee : bigBoss.employees) {
      CustomTreeNode boss = new CustomTreeNode(new ImageIcon("images/Circle_2.gif"), employee);
      root.add(boss);
      if (employee.isBoss) {                
        for (Employee employee1 : employee.employees) {
          CustomTreeNode emp = new CustomTreeNode(new ImageIcon("images/Circle_1.gif"), employee1);
          boss.add(emp);
        }
      }
    }

    JTree tree = new JTree(model);
    tree.setCellRenderer(new CustomeTreeCellRenderer());            
    pnlMain.add(tree, BorderLayout.CENTER);
  }  
}

共有3个答案

侯和惬
2023-03-14

如果我们需要组件(所以我们需要在第一步调用超级渲染器),设置所有树图标(打开、关闭、叶子)以使其正常工作是很重要的。

public java.awt.Component getTreeCellRendererComponent(javax.swing.JTree tree, Object value, boolean selected, boolean expanded, boolean isLeaf, int row, boolean focused) {
    Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused);

    DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
    MyTreeNodeWrapper treeNodeWrapper = (MyTreeNodeWrapper) node.getUserObject();
    Icon icon = treeNodeWrapper .getIcon();

    setOpenIcon(icon);
    setClosedIcon(icon);
    setLeafIcon(icon);

    if (!tree.isEnabled()) {
        if (isLeaf) {
            setDisabledIcon(getLeafIcon());
        } else if (expanded) {
            setDisabledIcon(getOpenIcon());
        } else {
            setDisabledIcon(getClosedIcon());
        }
    }
    else {
        if (isLeaf) {
            setIcon(getLeafIcon());
        } else if (expanded) {
            setIcon(getOpenIcon());
        } else {
            setIcon(getClosedIcon());
        }
    }
    
         
return c;

        
}

如果没有,如上所述,我们只需要设置3种图标类型:

public java.awt.Component getTreeCellRendererComponent(javax.swing.JTree tree, Object value, boolean selected, boolean expanded, boolean isLeaf, int row, boolean focused) {

    DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
    MyTreeNodeWrapper treeNodeWrapper = (MyTreeNodeWrapper ) node.getUserObject();
    Icon icon = treeNodeWrapper .getIcon();

    setOpenIcon(icon);
    setClosedIcon(icon);
    setLeafIcon(icon);

    
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused);
         
return c;

        
}
赫连泰宁
2023-03-14

运行代码后,您试图加载的图像似乎“意味着”嵌入到您的应用程序中(也就是说,它们并不位于应用程序上下文的磁盘外侧)。

所以与其这么做...

CustomTreeNode root = new CustomTreeNode(new ImageIcon("images/Circle_3.gif"), bigBoss); 

试着做这样的事情...

CustomTreeNode root = new CustomTreeNode(new ImageIcon(ImageIO.read(getClass().getResource("/images/Circle_3.gif"))), bigBoss); 

相反。这将导致Java在它的类路径(包括任何JAR资源)中查找图像。

当我在没有这个补丁的情况下运行代码时,一切都不起作用,当我更新它以使用这个特性时,它工作得很好。

注意:ImageIO#read引发IOException所以要小心

已更新

经过多次挠头...我改变了单元格渲染器,看起来像这样...

class CustomeTreeCellRenderer extends DefaultTreeCellRenderer {

    public CustomeTreeCellRenderer() {
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {

//            if (!leaf) {
        CustomTreeNode node = (CustomTreeNode) value;

        if (node.getIcon() != null) {
            System.out.println(node + " - " + node.getIcon());
            setClosedIcon(node.getIcon());
            setOpenIcon(node.getIcon());
            setLeafIcon(node.getIcon());
        } else {
            System.out.println(node + " - default");
            setClosedIcon(getDefaultClosedIcon());
            setLeafIcon(getDefaultLeafIcon());
            setOpenIcon(getDefaultOpenIcon());
        }
//            }

        super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

        return this;
    }
}

它把一切都弄清楚了。。。

调用setXxxIcon不会影响当前渲染器,但会影响未来的渲染器。就是这样。如果您在调用super之后调用setOpenIcon。GetTreeCellRenderComponent,它不会影响当前渲染器,但会影响下一次调用super。getTreeCellRendererComponent,因为set方法只是设置类变量的值。

附加

垃圾神(Trashgod)对依赖实现及其现在的工作方式发表了宝贵的评论。

与在方法getTreeCellRendererComponent中调用DefaultTreeCellRenderer#setXxxIcon不同,您实际上只需根据传递给它的参数使用所需的图标调用DefaultTreeCellRenderer#setIcon。

这意味着您可以调用super。首先是GetTreeCellRenderComponent,然后覆盖其后面图标的行为。

您还可以获取对对象值的引用,覆盖DefaultTreeCellRenderer#getXxxIcon方法,并根据该值更改这些方法的返回值。就我个人而言,我不敢这么做,因为它改变了渲染器记录的行为

贺兴昌
2023-03-14

在TreeCellRenderer中,可以根据需要使用与模型相关的已定义参数和谓词。给定一棵树具有默认的JTree模型,下面的TreeRenderer将为体育节点使用关闭和打开图标:

private static class TreeRenderer extends DefaultTreeCellRenderer {

    private static final Icon closed =
        (Icon) UIManager.get("InternalFrame.maximizeIcon");
    private static final Icon open =
        (Icon) UIManager.get("InternalFrame.minimizeIcon");

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value,
        boolean sel, boolean exp, boolean leaf, int row, boolean hasFocus) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
        String s = node.getUserObject().toString();
        if ("sports".equals(s)) {
            setOpenIcon(open);
            setClosedIcon(closed);
        } else {
            setOpenIcon(getDefaultOpenIcon());
            setClosedIcon(getDefaultClosedIcon());
        }
        super.getTreeCellRendererComponent(
            tree, value, sel, exp, leaf, row, hasFocus);
        return this;
    }
}

另请参见此相关示例。

 类似资料:
  • 我想为JTree中的每个节点设置一个不同的图标,实际上我是从数据库中加载每个节点,使用“while”,我将每个图标设置为根、叶或父。这样地: 我的所有声明都是全球性的: 这是我设置节点的代码: makeNode方法如下: 在用我的节点填充treemodel之后,我将模型设置为我的JTree: 但问题是。当我尝试设置图标时。我创建了一个名为myTreeRenler的子类,我使用它: 但它没有设置我想

  • 问题内容: 我想为JTree中的每个节点设置一个不同的图标,实际上是从数据库中加载每个节点,并带有“ while”,我将每个图标设置为根,叶或父级。像这样: 我所有的声明都是全球性的: 这是我设置节点的代码: 方法makeNode是这样的: 在用节点填充treemodel之后,将模型设置为JTree: 但是问题是。当我尝试设置图标时。我创建一个名为myTreeRenderer的子类,并使用以下代码

  • 问题内容: 我想将一列的Is Identity属性设置为off并在插入一个显式值后再次将其设置为on。我编写了以下查询: 尽管执行成功,但是表设计没有任何变化。请提出解决方案,这非常关键。 问题答案: 您提供的所有代码行都是禁用身份,以便您可以在身份列中插入特定的值- 通常,一次性完成此操作是必需的,例如,移动数据。标识仍然在列上,只是不起作用。从概念上讲,这类似于禁用和删除触发器之间的区别。

  • 自定义开关控件(UISwitch)的外观。 [Code4App.com]

  • 在ex-project中打开文件 在exvim中,你可以通过直接修改代码的形式自定义ex-project打开文件的行为. 主要通过编辑ex-project/autoload/exproject.vim, 找到exproject#confirm_select,添加你的代码. 在ctrlp中打开文件 ctrlp插件提供自定义的方式, 例子如下: let g:ctrlp_open_func = { 'f

  • 我在嵌套的代码周围看到一些奇怪的行为 鉴于: 在Chrome中,这会导致 也就是说,它关闭开始标记并打开结束标记。我(当然)听说过“自动关闭标签”;我从没听说过“自动打开标签”。 IE11采用了一种不同的(可以说是更错误的)方式: 因此,它不会自动关闭打开的标记,但它会自动打开关闭的标记,从而导致不平衡的DOM树。 无论如何,我很好奇解决这个问题的正确途径是什么;我应该打开Chrome和IE错误跟