【问题标题】:JTree: Set custom open/closed icons for individual groupsJTree:为各个组设置自定义打开/关闭图标
【发布时间】:2012-12-15 07:48:25
【问题描述】:
  1. 我知道如何在 JTree 中设置自定义叶子图标
  2. 我知道如何为所有组节点设置自定义关闭/打开图标

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

我试图通过覆盖 DefaultTreeCellRenderer 类的 getTreeCellRendererComponent 方法来做到这一点

但是将icon 更改为当前的node 只会影响下一个节点!

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

请看我的代码:

Employee.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);
  }  
}

【问题讨论】:

  • 试试 UIManager.put(“Tree.openIcon”,employeeNodes);
  • 如需尽快获得更好的帮助,请发帖SSCCE
  • 如果我没记错的话UIManager.put(“Tree.openIcon”,employeeNodes); 会影响所有节点而不是其中一些。
  • @ehsun7b:DefaultTreeCellRenderer 将使用您设置的任何内容,如here 所示。

标签: java swing icons jtree treecellrenderer


【解决方案1】:

在您的TreeCellRenderer 中,您可以根据需要将setOpenIcon()setClosedIcon() 与与您的模型相关的已定义参数和谓词一起使用。给定具有默认JTree 模型的树,下面的TreeRenderer 将为sports 节点使用closedopen 图标:

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;
    }
}

另见此相关example

【讨论】:

  • 我们不需要在覆盖版本的最开始调用super.getTreeCellRendererComponent吗?
  • 不,您可能正在考虑调用 superclass constructor,这对于 DefaultTreeCellRendererno-argument 构造函数是自动的。
  • 哦,我的错误是我在更改图标之前调用了super.getTreeCellRendererComponent,因此它仅对下一个节点有效。谢谢
  • 我实现了你的方法,但是 Java 不断抛出 Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: TreeRenderer cannot be cast to javax.swing.AbstractButton。该异常是 Java 内部的,即堆栈跟踪从未提到我的代码中存在问题的行,这是怎么回事?
  • 这个错误似乎完全是由于在 Windows 上使用了打开和关闭的图标,使用我自己的本地保存的图像加载为图标工作得很好......
【解决方案2】:

运行您的代码后,您尝试加载的图像似乎“意在”嵌入到您的应用程序中(也就是说,它们不驻留在应用程序上下文之外的磁盘上的某个位置) .

所以不要这样做......

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.getTreeCellRendererComponent 之后调用setOpenIcon,它不会影响当前渲染器,但会影响对super.getTreeCellRendererComponent 的下一次调用,因为set 方法只是设置类变量的值。

附加

Trashgod 对依赖实施及其现在的工作方式提出了宝贵意见。

您实际上应该简单地调用DefaultTreeCellRenderer#setIcon,根据传递给它的参数使用所需的图标,而不是在getTreeCellRendererComponent 方法中调用DefaultTreeCellRenderer#setXxxIcon

这意味着您可以先调用super.getTreeCellRendererComponent,然后覆盖其后图标的行为。

您还可以获取对Object 值的引用并覆盖DefaultTreeCellRenderer#getXxxIcon 方法,并根据该值更改这些方法的返回值。我个人不会鼓励这样做,因为它会改变渲染器的记录行为

【讨论】:

  • +1 因为有动力运行由 4 个类组成的代码。
  • @AndrewThompson 至少它跑了:P
  • 其实图片应该在项目根目录下的一个叫images的文件夹里(生产级就是jar文件外),反正不是问题,当然大家都知道怎么加载将图像放入 ImageIcon,请重点关注主要问题。
  • @ehsun7b:sscce 中的资源有问题;使用回收的UIManager 图标是一种方便的选择。
  • @MadProgrammer:对;我只是硬编码了一个(非叶)节点以进行特殊处理,但您可能希望 Map&lt;String, IconPair&gt; 在同一棵树中有多个不同的打开/关闭对。
【解决方案3】:

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

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;

        
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 2013-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    相关资源
    最近更新 更多