JTree的:自定义设置开启/关闭图标单独组JTree的:自定义设置开启/关闭图标单独组(JTree

2019-05-12 06:10发布

  1. 我知道如何设置自定义叶图标的JTree
  2. 我知道如何设置自定义关闭/打开图标所有组节点

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

我试图通过重写做到这一点getTreeCellRendererComponent类的方法DefaultTreeCellRenderer

但改变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);
  }  
}

Answer 1:

在你TreeCellRenderer ,你可以使用setOpenIcon()setClosedIcon()在与相关模型中定义的参数和谓词结合必需的。 鉴于其默认树JTree模式, TreeRenderer下面将使用closedopen的图标sports节点:

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

另见本相关的例子 。



Answer 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不影响当前渲染器,但未来渲染器。 那是。 如果你打电话setOpenIcon后,你已经叫super.getTreeCellRendererComponent ,它不会影响当前的渲染器,但是它将在接下来的通话效果,以super.getTreeCellRendererComponent的设置方法,则只需设置类变量的值。

额外

Trashgod取得了约依托实施和现在的工作方式的宝贵意见。

而不是调用的DefaultTreeCellRenderer#setXxxIcon的内getTreeCellRendererComponent方法,你其实应该简单地调用DefaultTreeCellRenderer#setIcon ,使用基于传递给它的参数所需要的图标。

这意味着你可以打电话super.getTreeCellRendererComponent先后覆盖图标的行为。

你也可以抓住一个参考Object的价值和覆盖DefaultTreeCellRenderer#getXxxIcon方法和基于价值,改变了这些方法的返回值。 我个人,也不会有勇气这个,因为它改变了渲染器的记录的行为



文章来源: JTree: Set custom open/closed icons for individual groups