Java Swing:需要一个质量很好的JTree带有复选框 [英] Java Swing: Need a good quality developed JTree with checkboxes

查看:288
本文介绍了Java Swing:需要一个质量很好的JTree带有复选框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在寻找aa JTree实现,包含复选框,其中:




  • 当您选择一个节点时,自动选择树中的后继


  • 当您取消选择一个节点时,树中的所有后继都会自动取消选择

    $ b
  • 当父节点已经被选择,并且选择被从它的后续节点之一删除时,节点颜色将被改变,以使得直观地,尽管选择了该父节点,但不是其所有选择后继者(例如当您选择要安装在公共安装程序中的组件时)


  • 点击节点可导致(不需要按住Ctrl ):




    • 如果节点已被选择,它将变为未选择状态及其所有后续

    • 如果未选择节点,则它将成为选择状态及其所有后继




<



有没有人知道这样的树的一个很好的实现?

解决方案

回答自己:



我决定与大家分享我的代码。



这是结果的屏幕截图:





实施细节:




  • 创建了扩展JTree的新类


  • 用创建的新类替换了TreeCellRenderer,并显示一个复选框和一个标签。复选框选择将更改,而不是标签背景和边框。


  • 完全终止了选择机制。将选择模型替换为DefaultTreeSelectionModel重写的inline,并具有空实现




    • 创建新事件类型以检查复选框


    • 创建可帮助快速指示每个节点状态的特殊数据结构




      public class Main extends JFrame {

    private static final long serialVersionUID = 4648172894076113183L;

    public Main(){
    super();
    setSize(500,500);
    this.getContentPane()。setLayout(new BorderLayout());
    final JCheckBoxTree cbt = new JCheckBoxTree();
    this.getContentPane()。add(cbt);
    cbt.addCheckChangeEventListener(new JCheckBoxTree.CheckChangeEventListener(){
    public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event){
    System.out.println(event);
    TreePath [ ] path = cbt.getCheckedPaths();
    for(TreePath tp:paths){
    for(Object pathPart:tp.getPath()){
    System.out.print(pathPart + ,);
    }
    System.out.println();
    }
    }
    });
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    public static void main(String args []){
    Main m = new Main();
    m.setVisible(true);
    }
    }

    这是类本身的源代码: / p>

      import java.awt.BorderLayout; 
    import java.awt.Component;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.util.EventListener;
    import java.util.EventObject;
    import java.util.HashMap;
    import java.util.HashSet;

    import javax.swing.JCheckBox;
    import javax.swing.JPanel;
    import javax.swing.JTree;
    import javax.swing.event.EventListenerList;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeSelectionModel;
    import javax.swing.tree.TreeCellRenderer;
    import javax.swing.tree.TreeModel;
    import javax.swing.tree.TreeNode;
    import javax.swing.tree.TreePath;

    public class JCheckBoxTree extends JTree {

    private static final long serialVersionUID = -4194122328392241790L;

    JCheckBoxTree selfPointer = this;



    //定义将启用快速检查的数据结构 - 指示每个节点的状态
    //它完全替换了选择机制JTree
    private class CheckedNode {
    boolean isSelected;
    boolean hasChildren;
    boolean allChildrenSelected;

    public CheckedNode(boolean isSelected_,boolean hasChildren_,boolean allChildrenSelected_){
    isSelected = isSelected_;
    hasChildren = hasChildren_;
    allChildrenSelected = allChildrenSelected_;
    }
    }
    HashMap< TreePath,CheckedNode> nodeCheckingState;
    HashSet< TreePath> checkedPaths = new HashSet< TreePath>();

    //为检查机制和准备事件处理机制定义一个新的事件类型
    protected EventListenerList listenerList = new EventListenerList();

    public class CheckChangeEvent extends EventObject {
    private static final long serialVersionUID = -8100230309044193368L;

    public CheckChangeEvent(object source){
    super(source);
    }
    }

    public interface CheckChangeEventListener extends EventListener {
    public void checkStateChanged(CheckChangeEvent event);
    }

    public void addCheckChangeEventListener(CheckChangeEventListener listener){
    listenerList.add(CheckChangeEventListener.class,listener);
    }
    public void removeCheckChangeEventListener(CheckChangeEventListener listener){
    listenerList.remove(CheckChangeEventListener.class,listener);
    }

    void fireCheckChangeEvent(CheckChangeEvent evt){
    Object [] listeners = listenerList.getListenerList();
    for(int i = 0; i< listeners.length; i ++){
    if(listeners [i] == CheckChangeEventListener.class){
    (CheckChangeEventListener)listeners [i + 1])。checkStateChanged(evt);
    }
    }
    }

    //覆盖
    public void setModel(TreeModel newModel){
    super.setModel(newModel);
    resetCheckingState();
    }

    //新方法只返回检查路径(完全忽略原始选择机制)
    public TreePath [] getCheckedPaths(){
    return checkedPaths .toArray(new TreePath [checkedPaths.size()]);
    }

    //如果节点被选中,返回true,有子节点但不是全部被选中
    public boolean isSelectedPartially(TreePath path){
    CheckedNode cn = nodesCheckingState.get(path);
    return cn.isSelected&&& cn.has儿童&& !cn.allChildrenSelected;
    }

    private void resetCheckingState(){
    nodesCheckingState = new HashMap< TreePath,CheckedNode>();
    checkedPaths = new HashSet< TreePath>();
    DefaultMutableTreeNode node =(DefaultMutableTreeNode)getModel()。getRoot();
    if(node == null){
    return;
    }
    addSubtreeToCheckingStateTracking(node);
    }

    //为检查机制创建当前模型的数据结构
    private void addSubtreeToCheckingStateTracking(DefaultMutableTreeNode node){
    TreeNode [] path = node.getPath ();
    TreePath tp = new TreePath(path);
    CheckedNode cn = new CheckedNode(false,node.getChildCount()> 0,false);
    nodesCheckingState.put(tp,cn);
    for(int i = 0; i addSubtreeToCheckingStateTracking((DefaultMutableTreeNode)tp.pathByAddingChild(node.getChildAt(i))。getLastPathComponent());
    }
    }

    //通过忽略原始选择机制的类覆盖单元格渲染器
    //它决定如何显示节点检查机制
    private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer {
    private static final long serialVersionUID = -7341833835878991719L;
    JCheckBox checkBox;
    public CheckBoxCellRenderer(){
    super();
    this.setLayout(new BorderLayout());
    checkBox = new JCheckBox();
    add(checkBox,BorderLayout.CENTER);
    setOpaque(false);
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree,Object value,
    boolean selected,boolean expanded,boolean leaf,int row,
    boolean hasFocus){
    DefaultMutableTreeNode node =(DefaultMutableTreeNode)value;
    Object obj = node.getUserObject();
    TreePath tp = new TreePath(node.getPath());
    CheckedNode cn = nodesCheckingState.get(tp);
    if(cn == null){
    return this;
    }
    checkBox.setSelected(cn.isSelected);
    checkBox.setText(obj.toString());
    checkBox.setOpaque(cn.isSelected&& cn.hasChildren&&!cn.allChildrenSelected);
    return this;
    }
    }

    public JCheckBoxTree(){
    super();
    //通过双击
    禁用切换this.setToggleClickCount(0);
    //用上面定义的新的重写细胞渲染器
    CheckBoxCellRenderer cellRenderer = new CheckBoxCellRenderer();
    this.setCellRenderer(cellRenderer);

    //用空的一个重写选择模型
    DefaultTreeSelectionModel dtsm = new DefaultTreeSelectionModel(){
    private static final long serialVersionUID = -8190634240451667286L;
    //完全禁用选择机制
    public void setSelectionPath(TreePath path){
    }
    public void addSelectionPath(TreePath path){
    }
    public void removeSelectionPath(TreePath path){
    }
    public void setSelectionPaths(TreePath [] pPaths){
    }
    };
    //鼠标点击调用检查机制
    this.addMouseListener(new MouseListener(){
    public void mouseClicked(MouseEvent arg0){
    TreePath tp = selfPointer.getPathForLocation get(tp).isSelected(tp);
    if(tp == null){
    return;
    }
    boolean checkMode =!nodesCheckingState.get ;
    checkSubTree(tp,checkMode);
    updatePredecessorsWithCheckMode(tp,checkMode);
    //触发检查更改事件
    fireCheckChangeEvent(new CheckChangeEvent(new Object()));
    //更新数据结构后重绘树
    selfPointer.repaint();
    }
    public void mouseEntered(MouseEvent arg0){
    }
    public void mouseExited(MouseEvent arg0){
    }
    public void mousePressed(MouseEvent arg0){
    }
    public void mouseReleased(MouseEvent arg0){
    }
    });
    this.setSelectionModel(dtsm);
    }

    //当一个节点被检查/取消选中时,更新前辈的状态
    protected void updatePredecessorsWithCheckMode(TreePath tp,boolean check){
    TreePath parentPath = tp.getParentPath();
    //如果是根,停止递归调用并返回
    if(parentPath == null){
    return;
    }
    CheckedNode parentCheckedNode = nodesCheckingState.get(parentPath);
    DefaultMutableTreeNode parentNode =(DefaultMutableTreeNode)parentPath.getLastPathComponent();
    parentCheckedNode.allChildrenSelected = true;
    parentCheckedNode.isSelected = false;
    for(int i = 0; i TreePath childPath = parentPath.pathByAddingChild(parentNode.getChildAt(i));
    CheckedNode childCheckedNode = nodesCheckingState.get(childPath);
    //这是足够的,即使一个子树没有被完全选择
    //来确定父项未被完全选择
    if(!childCheckedNode.allChildrenSelected){
    parentCheckedNode。 allChildrenSelected = false;
    }
    //如果至少选择了一个子项,也选择父项
    if(childCheckedNode.isSelected){
    parentCheckedNode.isSelected = true;
    }
    }
    if(parentCheckedNode.isSelected){
    checkedPaths.add(parentPath);
    } else {
    checkedPaths.remove(parentPath);
    }
    //转到上层前导
    updatePredecessorsWithCheckMode(parentPath,check);
    }

    //递归检查/取消检查子树
    protected void checkSubTree(TreePath tp,boolean check){
    CheckedNode cn = nodesCheckingState.get(tp);
    cn.isSelected = check;
    DefaultMutableTreeNode node =(DefaultMutableTreeNode)tp.getLastPathComponent();
    for(int i = 0; i checkSubTree(tp.pathByAddingChild(node.getChildAt(i)),check);
    }
    cn.allChildrenSelected = check;
    if(check){
    checkedPaths.add(tp);
    } else {
    checkedPaths.remove(tp);
    }
    }

    }


    I was looking for a a JTree implementation, that contains checkboxes and which:

    • When you select one node, all its successors in the tree are automatically selected

    • When you unselect one node, all its successors in the tree are automatically unselected

    • When a parent node is already selected, and the selection was removed from one of its successors, the node color will be changed, to make it intuitive that although this parent node is selected, not all of its successors are selected (Like when you select components to install in common installers)

    • A click on a node leads to (No need to hold 'Ctrl' key!):

      • If the node is already selected, it becomes unselected, with all its successors
      • If the node is not selected, it becomes selected, with all its successors

    I searched the net for something simple, but could not find something as simple as I wanted.

    Does anyone know a good implementation of such tree?

    解决方案

    Answering myself:

    I decided to share my code with everyone.

    Here's a screenshot of the result:

    The implementation details:

    • Created a new class that extends JTree

    • Replaced the 'TreeCellRenderer' by a new class I created, that shows a checkbox and a label. The checkbox selection is changed instead of the label background and border.

    • Totally terminated the selection mechanism. Replaced the 'Selection Model' by a 'DefaultTreeSelectionModel' overridden inline, that has empty implementation

      • Created new event type for checking of the checkboxes

      • Created special data structures that help to indicate fast the state of each node

    Enjoy!!

    Here's a usage example:

    public class Main extends JFrame {
    
        private static final long serialVersionUID = 4648172894076113183L;
    
        public Main() {
            super();
            setSize(500, 500);
            this.getContentPane().setLayout(new BorderLayout());
            final JCheckBoxTree cbt = new JCheckBoxTree();
            this.getContentPane().add(cbt);
            cbt.addCheckChangeEventListener(new JCheckBoxTree.CheckChangeEventListener() {
                public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event) {
                    System.out.println("event");
                    TreePath[] paths = cbt.getCheckedPaths();
                    for (TreePath tp : paths) {
                        for (Object pathPart : tp.getPath()) {
                            System.out.print(pathPart + ",");
                        }                   
                        System.out.println();
                    }
                }           
            });         
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        }
    
        public static void main(String args[]) {
            Main m = new Main();
            m.setVisible(true);
        }
    }
    

    Here is the source code of the class itself:

    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.util.EventListener;
    import java.util.EventObject;
    import java.util.HashMap;
    import java.util.HashSet;
    
    import javax.swing.JCheckBox;
    import javax.swing.JPanel;
    import javax.swing.JTree;
    import javax.swing.event.EventListenerList;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeSelectionModel;
    import javax.swing.tree.TreeCellRenderer;
    import javax.swing.tree.TreeModel;
    import javax.swing.tree.TreeNode;
    import javax.swing.tree.TreePath;
    
    public class JCheckBoxTree extends JTree {
    
        private static final long serialVersionUID = -4194122328392241790L;
    
        JCheckBoxTree selfPointer = this;
    
    
    
        // Defining data structure that will enable to fast check-indicate the state of each node
        // It totally replaces the "selection" mechanism of the JTree
        private class CheckedNode {
            boolean isSelected;
            boolean hasChildren;
            boolean allChildrenSelected;
    
            public CheckedNode(boolean isSelected_, boolean hasChildren_, boolean allChildrenSelected_) {
                isSelected = isSelected_;
                hasChildren = hasChildren_;
                allChildrenSelected = allChildrenSelected_;
            }
        }
        HashMap<TreePath, CheckedNode> nodesCheckingState;
        HashSet<TreePath> checkedPaths = new HashSet<TreePath>();
    
        // Defining a new event type for the checking mechanism and preparing event-handling mechanism
        protected EventListenerList listenerList = new EventListenerList();
    
        public class CheckChangeEvent extends EventObject {     
            private static final long serialVersionUID = -8100230309044193368L;
    
            public CheckChangeEvent(Object source) {
                super(source);          
            }       
        }   
    
        public interface CheckChangeEventListener extends EventListener {
            public void checkStateChanged(CheckChangeEvent event);
        }
    
        public void addCheckChangeEventListener(CheckChangeEventListener listener) {
            listenerList.add(CheckChangeEventListener.class, listener);
        }
        public void removeCheckChangeEventListener(CheckChangeEventListener listener) {
            listenerList.remove(CheckChangeEventListener.class, listener);
        }
    
        void fireCheckChangeEvent(CheckChangeEvent evt) {
            Object[] listeners = listenerList.getListenerList();
            for (int i = 0; i < listeners.length; i++) {
                if (listeners[i] == CheckChangeEventListener.class) {
                    ((CheckChangeEventListener) listeners[i + 1]).checkStateChanged(evt);
                }
            }
        }
    
        // Override
        public void setModel(TreeModel newModel) {
            super.setModel(newModel);
            resetCheckingState();
        }
    
        // New method that returns only the checked paths (totally ignores original "selection" mechanism)
        public TreePath[] getCheckedPaths() {
            return checkedPaths.toArray(new TreePath[checkedPaths.size()]);
        }
    
        // Returns true in case that the node is selected, has children but not all of them are selected
        public boolean isSelectedPartially(TreePath path) {
            CheckedNode cn = nodesCheckingState.get(path);
            return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected;
        }
    
        private void resetCheckingState() { 
            nodesCheckingState = new HashMap<TreePath, CheckedNode>();
            checkedPaths = new HashSet<TreePath>();
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)getModel().getRoot();
            if (node == null) {
                return;
            }
            addSubtreeToCheckingStateTracking(node);
        }
    
        // Creating data structure of the current model for the checking mechanism
        private void addSubtreeToCheckingStateTracking(DefaultMutableTreeNode node) {
            TreeNode[] path = node.getPath();   
            TreePath tp = new TreePath(path);
            CheckedNode cn = new CheckedNode(false, node.getChildCount() > 0, false);
            nodesCheckingState.put(tp, cn);
            for (int i = 0 ; i < node.getChildCount() ; i++) {              
                addSubtreeToCheckingStateTracking((DefaultMutableTreeNode) tp.pathByAddingChild(node.getChildAt(i)).getLastPathComponent());
            }
        }
    
        // Overriding cell renderer by a class that ignores the original "selection" mechanism
        // It decides how to show the nodes due to the checking-mechanism
        private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer {     
            private static final long serialVersionUID = -7341833835878991719L;     
            JCheckBox checkBox;     
            public CheckBoxCellRenderer() {
                super();
                this.setLayout(new BorderLayout());
                checkBox = new JCheckBox();
                add(checkBox, BorderLayout.CENTER);
                setOpaque(false);
            }
    
            @Override
            public Component getTreeCellRendererComponent(JTree tree, Object value,
                    boolean selected, boolean expanded, boolean leaf, int row,
                    boolean hasFocus) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
                Object obj = node.getUserObject();          
                TreePath tp = new TreePath(node.getPath());
                CheckedNode cn = nodesCheckingState.get(tp);
                if (cn == null) {
                    return this;
                }
                checkBox.setSelected(cn.isSelected);
                checkBox.setText(obj.toString());
                checkBox.setOpaque(cn.isSelected && cn.hasChildren && ! cn.allChildrenSelected);
                return this;
            }       
        }
    
        public JCheckBoxTree() {
            super();
            // Disabling toggling by double-click
            this.setToggleClickCount(0);
            // Overriding cell renderer by new one defined above
            CheckBoxCellRenderer cellRenderer = new CheckBoxCellRenderer();
            this.setCellRenderer(cellRenderer);
    
            // Overriding selection model by an empty one
            DefaultTreeSelectionModel dtsm = new DefaultTreeSelectionModel() {      
                private static final long serialVersionUID = -8190634240451667286L;
                // Totally disabling the selection mechanism
                public void setSelectionPath(TreePath path) {
                }           
                public void addSelectionPath(TreePath path) {                       
                }           
                public void removeSelectionPath(TreePath path) {
                }
                public void setSelectionPaths(TreePath[] pPaths) {
                }
            };
            // Calling checking mechanism on mouse click
            this.addMouseListener(new MouseListener() {
                public void mouseClicked(MouseEvent arg0) {
                    TreePath tp = selfPointer.getPathForLocation(arg0.getX(), arg0.getY());
                    if (tp == null) {
                        return;
                    }
                    boolean checkMode = ! nodesCheckingState.get(tp).isSelected;
                    checkSubTree(tp, checkMode);
                    updatePredecessorsWithCheckMode(tp, checkMode);
                    // Firing the check change event
                    fireCheckChangeEvent(new CheckChangeEvent(new Object()));
                    // Repainting tree after the data structures were updated
                    selfPointer.repaint();                          
                }           
                public void mouseEntered(MouseEvent arg0) {         
                }           
                public void mouseExited(MouseEvent arg0) {              
                }
                public void mousePressed(MouseEvent arg0) {             
                }
                public void mouseReleased(MouseEvent arg0) {
                }           
            });
            this.setSelectionModel(dtsm);
        }
    
        // When a node is checked/unchecked, updating the states of the predecessors
        protected void updatePredecessorsWithCheckMode(TreePath tp, boolean check) {
            TreePath parentPath = tp.getParentPath();
            // If it is the root, stop the recursive calls and return
            if (parentPath == null) {
                return;
            }       
            CheckedNode parentCheckedNode = nodesCheckingState.get(parentPath);
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parentPath.getLastPathComponent();     
            parentCheckedNode.allChildrenSelected = true;
            parentCheckedNode.isSelected = false;
            for (int i = 0 ; i < parentNode.getChildCount() ; i++) {                
                TreePath childPath = parentPath.pathByAddingChild(parentNode.getChildAt(i));
                CheckedNode childCheckedNode = nodesCheckingState.get(childPath);           
                // It is enough that even one subtree is not fully selected
                // to determine that the parent is not fully selected
                if (! childCheckedNode.allChildrenSelected) {
                    parentCheckedNode.allChildrenSelected = false;      
                }
                // If at least one child is selected, selecting also the parent
                if (childCheckedNode.isSelected) {
                    parentCheckedNode.isSelected = true;
                }
            }
            if (parentCheckedNode.isSelected) {
                checkedPaths.add(parentPath);
            } else {
                checkedPaths.remove(parentPath);
            }
            // Go to upper predecessor
            updatePredecessorsWithCheckMode(parentPath, check);
        }
    
        // Recursively checks/unchecks a subtree
        protected void checkSubTree(TreePath tp, boolean check) {
            CheckedNode cn = nodesCheckingState.get(tp);
            cn.isSelected = check;
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
            for (int i = 0 ; i < node.getChildCount() ; i++) {              
                checkSubTree(tp.pathByAddingChild(node.getChildAt(i)), check);
            }
            cn.allChildrenSelected = check;
            if (check) {
                checkedPaths.add(tp);
            } else {
                checkedPaths.remove(tp);
            }
        }
    
    }
    

    这篇关于Java Swing:需要一个质量很好的JTree带有复选框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆