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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14096725/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 15:03:06  来源:igfitidea点击:

JTree: Set custom open/closed icons for individual groups

javaswingiconsjtreetreecellrenderer

提问by ehsun7b

  1. I know how to set custom leaf icons in JTree
  2. I know how to set custom closed/open icons for all group nodes
  1. 我知道如何在 JTree 中设置自定义叶子图标
  2. 我知道如何为所有组节点设置自定义关闭/打开图标

But I can not set custom open/closed icons based on the group node names, for example of node could be called Emails (so it is nice to have an envelop icon) or one group may be called tasks and so on.

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

I tried to do this by overriding the getTreeCellRendererComponentmethod of class DefaultTreeCellRenderer

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

But changing the iconfor the current nodewill affect for the next node only!

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

How to set custom open/closed icons for individual groups?

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

Please take a look at my code:

请看一下我的代码:

Employee.java

雇员.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

自定义树节点.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

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

测试1.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);
  }  
}

回答by trashgod

In your TreeCellRenderer, you can use setOpenIcon()and setClosedIcon()as required in conjunction with the defined parameters and predicates related to your model. Given a tree having the default JTreemodel, the TreeRendererbelow will use the closedand openicons for the sportsnode:

在你的TreeCellRenderer,你可以使用setOpenIcon(),并setClosedIcon()在与有关模型定义的参数和谓词结合必需的。给定具有默认JTree模型的树,TreeRenderer以下将使用节点的closedopen图标sports

iamge

图像

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

See also this related example.

另请参阅此相关示例

回答by MadProgrammer

Having run you code, it would appear that the images you are trying to load are "meant" to be embedded within you application (that is, they don't reside some where on the disk out side of the application context).

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

So instead of doing this...

所以,而不是这样做......

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

Try doing something like this...

尝试做这样的事情......

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

Instead. This will cause Java to look within it's class path (including any JAR resources) to find the images.

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

When I run you code without this fix, nothing worked, when I updated it to use this feature, it worked fine.

当我在没有此修复程序的情况下运行代码时,没有任何效果,当我更新它以使用此功能时,它运行良好。

NB: ImageIO#readthrows an IOExceptionso look out for it

注意:ImageIO#read抛出一个IOException所以要注意它

Updated

更新

After much head scratching...I changed the cell renderer to look like this...

经过多次挠头……我将单元渲染器更改为这样……

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

It it cleared it all up...

它清除了一切......

enter image description here

在此处输入图片说明

Calling setXxxIcondoesn't effect the current renderer, but the future renderer. That is. If you call setOpenIconAFTER you've already called super.getTreeCellRendererComponent, it will not effect the current renderer, but it will effect the next call to super.getTreeCellRendererComponentas the set method is simply setting the value of class variable.

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

Additional

额外的

Trashgod has made a valuable comment about relying on the implementation and the way it works now.

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

Instead of calling DefaultTreeCellRenderer#setXxxIconwithin the getTreeCellRendererComponentmethod, you should actually simply call DefaultTreeCellRenderer#setIcon, using the required icon based on the parameters passed to it.

而不是DefaultTreeCellRenderer#setXxxIcongetTreeCellRendererComponent方法内调用,您实际上应该简单地调用DefaultTreeCellRenderer#setIcon,使用基于传递给它的参数所需的图标。

This means you can call super.getTreeCellRendererComponentfirst and then override the behavior of the icons after it.

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

You could also grab a reference to Objectvalue and override the DefaultTreeCellRenderer#getXxxIconmethods and based on the value, change the return values of these methods. I personally, wouldn't courage this as it changes the documented behavior of the renderer

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