Checkbox within a JXTreeTable

自作多情 提交于 2019-12-02 02:32:12

问题


Still a Java newbie here, trying to stuff far too advanced for my own good. Nevertheless, here's what I need done: A checkbox within a JXTreeTable. My main class:

package info.chrismcgee.sky.production;

import info.chrismcgee.sky.production.treetable.NoRootTreeTableModel;

import java.awt.Checkbox;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;

import net.miginfocom.swing.MigLayout;

import org.jdesktop.swingx.JXTreeTable;

public class TestFrame extends JFrame {

    /**
     * 
     */
    private static final long serialVersionUID = -1899673458785493250L;
    private JXTreeTable treeTable;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    TestFrame frame = new TestFrame();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public TestFrame() {
        setMinimumSize(new Dimension(600, 600));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 546, 600);
        JPanel contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new MigLayout("", "[grow][grow][grow][100px:n,grow][grow][grow][grow]", "[][251.00,grow][]"));

        List<Job> jobList = new ArrayList<Job>();

        List<ItemDetail> itemList = new ArrayList<ItemDetail>();
        itemList.add(new ItemDetail("N10", "N10", 2, 1000, PrintType.PAD, true));
        itemList.add(new ItemDetail("N13", "N13", 2, 2000, PrintType.PAD, true));

        // create and add the first job with its list of ItemDetail objects
        jobList.add(new Job(new Checkbox("Print Solutions"), "123456", ShipDate.getDate("02/28/14"), itemList));

        List<ItemDetail> itemList2 = new ArrayList<ItemDetail>();
        itemList2.add(new ItemDetail("P12", "Green", 1, 250, PrintType.SCREEN, true));
        itemList2.add(new ItemDetail("P12", "Purple", 1, 250, PrintType.SCREEN, true));
        itemList2.add(new ItemDetail("P12", "Gray", 1, 250, PrintType.SCREEN, true));

        // create and add a second job with its list of ItemDetail objects
        jobList.add(new Job(new Checkbox("Innovators Inc"), "246801", ShipDate.getDate("03/10/14"), itemList2));

        // we use a no root model
        NoRootTreeTableModel noRootTreeTableModel = new NoRootTreeTableModel(jobList);
        treeTable = new JXTreeTable(noRootTreeTableModel);
        treeTable.setEditable(false);
        treeTable.setRootVisible(false);
        treeTable.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
        treeTable.getTableHeader().setReorderingAllowed(false);
        treeTable.getColumn(0).setPreferredWidth(200);
        treeTable.getColumn(1).setPreferredWidth(75);
        treeTable.getColumn(2).setPreferredWidth(15);
            treeTable.getColumn(2).setMinWidth(15);
            treeTable.getColumn(2).setMaxWidth(15);
        treeTable.getColumn(3).setPreferredWidth(50);
            treeTable.getColumn(3).setMaxWidth(50);
        treeTable.getColumn(4).setPreferredWidth(25);
        JScrollPane scrollPane = new JScrollPane(treeTable);
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        contentPane.add(scrollPane, "cell 0 1 7 1,grow");

    }       
}

The Job class (Branches in the TreeTable):

package info.chrismcgee.sky.production;

import java.awt.Checkbox;
import java.util.List;

import org.joda.time.LocalDate;

public class Job {

    private Checkbox cbJob;
    private String idNumber;
    private LocalDate shipDate;
    private List<ItemDetail> itemList;

    public Job(Checkbox cbJob, String idNumber, LocalDate shipDate, List<ItemDetail> itemList)
    {
        this.cbJob = cbJob;
        this.idNumber = idNumber;
        this.shipDate = shipDate;
        this.itemList = itemList;
    }

    public List<ItemDetail> getItemList()
    {
        return itemList;
    }

    public void setItemList(List<ItemDetail> itemList)
    {
        this.itemList = itemList;
    }

    /**
     * @return the cbJob
     */
    public Checkbox getCbJob() {
        return cbJob;
    }

    /**
     * @param cbJob the cbJob to set
     */
    public void setCbJob(Checkbox cbJob) {
        this.cbJob = cbJob;
    }

    /**
     * @return the idNumber
     */
    public String getIdNumber() {
        return idNumber;
    }

    /**
     * @param idNumber the idNumber to set
     */
    public void setIdNumber(String idNumber) {
        this.idNumber = idNumber;
    }

    /**
     * @return the shipDate
     */
    public LocalDate getShipDate() {
        return shipDate;
    }

    /**
     * @param shipDate the shipDate to set
     */
    public void setShipDate(LocalDate shipDate) {
        this.shipDate = shipDate;
    }

}

The OrderDetail class (Leaves in the TreeTable):

package info.chrismcgee.sky.production;


public class ItemDetail {

    private String productId;
    private String detail;
    private long numColors;
    private long numQuantity;
    private PrintType printType;
    private boolean active;

    public ItemDetail(String productId, String detail, long numColors, long numQuantity, PrintType printType, boolean active)
    {
        this.productId = productId;
        this.detail = detail;
        this.numColors = numColors;
        this.numQuantity = numQuantity;
        this.printType = printType;
        this.active = active;
    }

    /**
     * @return the productId
     */
    public String getProductId() {
        return productId;
    }

    /**
     * @param productId the productId to set
     */
    public void setProductId(String productId) {
        this.productId = productId;
    }

    /**
     * @return the detail
     */
    public String getDetail() {
        return detail;
    }

    /**
     * @param detail the detail to set
     */
    public void setDetail(String detail) {
        this.detail = detail;
    }

    /**
     * @return the numColors
     */
    public long getNumColors() {
        return numColors;
    }

    /**
     * @param numColors the numColors to set
     */
    public void setNumColors(long numColors) {
        this.numColors = numColors;
    }

    /**
     * @return the numQuantity
     */
    public long getNumQuantity() {
        return numQuantity;
    }

    /**
     * @param numQuantity the numQuantity to set
     */
    public void setNumQuantity(long numQuantity) {
        this.numQuantity = numQuantity;
    }

    /**
     * @return the printType
     */
    public PrintType getPrintType() {
        return printType;
    }

    /**
     * @param printType the printType to set
     */
    public void setPrintType(PrintType printType) {
        this.printType = printType;
    }

    /**
     * @return the active
     */
    public boolean isActive() {
        return active;
    }

    /**
     * @param active the active to set
     */
    public void setActive(boolean active) {
        this.active = active;
    }

}

and, finally, the NoRootTreeTableModel class, which extends the AbstractTreeTableModel class, so that the root of the tree isn't shown:

package info.chrismcgee.sky.production.treetable;

import info.chrismcgee.sky.production.ItemDetail;
import info.chrismcgee.sky.production.Job;

import java.util.List;

import org.jdesktop.swingx.treetable.AbstractTreeTableModel;

public class NoRootTreeTableModel extends AbstractTreeTableModel {

    private final static String[] COLUMN_NAMES = {"Name/Product", "Job # / Detail", "T",
        "Colors", "Quantity", "Total"};

    private List<Job> jobList;

    public NoRootTreeTableModel(List<Job> jobList)
    {
        super(new Object());
        this.jobList = jobList;
    }

    @Override
    public int getColumnCount() {
        return COLUMN_NAMES.length;
    }

    @Override
    public String getColumnName(int column)
    {
        return COLUMN_NAMES[column];
    }

    @Override
    public boolean isCellEditable(Object node, int column)
    {
        return false;
    }

    @Override
    public boolean isLeaf(Object node)
    {
        return node instanceof ItemDetail;
    }

    @Override
    public int getChildCount(Object parent) {
        if (parent instanceof Job) {
            Job job = (Job) parent;
            return job.getItemList().size();
        }
        return jobList.size();
    }

    @Override
    public Object getChild(Object parent, int index) {
        if (parent instanceof Job) {
            Job job = (Job) parent;
            return job.getItemList().get(index);
        }
        return jobList.get(index);
    }

    @Override
    public int getIndexOfChild(Object parent, Object child) {
        Job job = (Job) parent;
        ItemDetail item = (ItemDetail) child;
        return job.getItemList().indexOf(item);
    }

    @Override
    public Object getValueAt(Object node, int column) {
        if (node instanceof Job) {
            Job job = (Job) node;
            switch (column) {
                case 0:
                    return job.getCbJob();
                case 1:
                    return job.getIdNumber();
            }
        } else if (node instanceof ItemDetail) {
            ItemDetail item = (ItemDetail) node;
            switch (column) {
                case 0:
                    return item.getProductId();
                case 1:
                    return item.getDetail();
                case 2:
                    return item.getPrintType();
                case 3:
                    return item.getNumColors();
                case 4:
                    return item.getNumQuantity();
                case 5:
                    return item.getNumColors() * item.getNumQuantity();
            }
        }
        return null;
    }

}

I know that is a lot of code, but I couldn't think of a quick way to cut it down and still have it work the way I need it to. And actually, it's cut down quite a bit already. (I left out a bunch of stuff that is normally also in that JFrame.)

When the code is run, I don't get a checkbox for the branches; instead, I get what appears to be a String representation of the checkbox code:

Huh? How did this happen and, more importantly, how can I get the checkbox to show up instead?


回答1:


You should never add components to a model, instead you should be providing a TreeCellRenderer to render the value of the node. The Node is probably using the toString value of the user object assigned to it...

Take a look at How to Use Trees and Customizing a Tree's Display for more details

Example Ripped Straight from Customizing a Tree's Display

I'll leave it up to you create the editor and determine how the check box should get selected

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

public class TreeExample {

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

                    //Create and set up the window.
                    JFrame frame = new JFrame("GenealogyExample");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                    //Create and set up the content pane.
                    GenealogyExample newContentPane = new GenealogyExample();
                    newContentPane.setOpaque(true); //content panes must be opaque
                    frame.setContentPane(newContentPane);

                    //Display the window.
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static class GenealogyExample extends JPanel
            implements ActionListener {

        private JTree tree;
        private static String SHOW_ANCESTOR_CMD = "showAncestor";

        public GenealogyExample() {
            super(new BorderLayout());

            //Construct the panel with the toggle buttons.
            JRadioButton showDescendant
                    = new JRadioButton("Show descendants", true);
            final JRadioButton showAncestor
                    = new JRadioButton("Show ancestors");
            ButtonGroup bGroup = new ButtonGroup();
            bGroup.add(showDescendant);
            bGroup.add(showAncestor);
            showDescendant.addActionListener(this);
            showAncestor.addActionListener(this);
            showAncestor.setActionCommand(SHOW_ANCESTOR_CMD);
            JPanel buttonPanel = new JPanel();
            buttonPanel.add(showDescendant);
            buttonPanel.add(showAncestor);

            //Construct the tree.
            tree = new JTree(new GenealogyModel(getGenealogyGraph()));
      tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
            tree.setCellRenderer(new CheckBoxTreeCellRenderer());

            JScrollPane scrollPane = new JScrollPane(tree);
            scrollPane.setPreferredSize(new Dimension(200, 200));

            //Add everything to this panel.
            add(buttonPanel, BorderLayout.PAGE_START);
            add(scrollPane, BorderLayout.CENTER);
        }

        /**
         * Required by the ActionListener interface. Handle events on the
         * showDescendant and showAncestore buttons.
         */
        public void actionPerformed(ActionEvent ae) {
            if (ae.getActionCommand() == SHOW_ANCESTOR_CMD) {
                showAncestor(true);
            } else {
                showAncestor(false);
            }
        }

        public void showAncestor(boolean b) {
        Object newRoot = null;
        TreePath path = tree.getSelectionModel().getSelectionPath();
        if (path != null) {
            newRoot = path.getLastPathComponent();
        }
        ((GenealogyModel)tree.getModel()).showAncestor(b, newRoot);
    }

        /**
         * Constructs the genealogy graph used by the model.
         */
        public Person getGenealogyGraph() {
            //the greatgrandparent generation
            Person a1 = new Person("Jack (great-granddaddy)");
            Person a2 = new Person("Jean (great-granny)");
            Person a3 = new Person("Albert (great-granddaddy)");
            Person a4 = new Person("Rae (great-granny)");
            Person a5 = new Person("Paul (great-granddaddy)");
            Person a6 = new Person("Josie (great-granny)");

            //the grandparent generation
            Person b1 = new Person("Peter (grandpa)");
            Person b2 = new Person("Zoe (grandma)");
            Person b3 = new Person("Simon (grandpa)");
            Person b4 = new Person("James (grandpa)");
            Person b5 = new Person("Bertha (grandma)");
            Person b6 = new Person("Veronica (grandma)");
            Person b7 = new Person("Anne (grandma)");
            Person b8 = new Person("Renee (grandma)");
            Person b9 = new Person("Joseph (grandpa)");

            //the parent generation
            Person c1 = new Person("Isabelle (mom)");
            Person c2 = new Person("Frank (dad)");
            Person c3 = new Person("Louis (dad)");
            Person c4 = new Person("Laurence (dad)");
            Person c5 = new Person("Valerie (mom)");
            Person c6 = new Person("Marie (mom)");
            Person c7 = new Person("Helen (mom)");
            Person c8 = new Person("Mark (dad)");
            Person c9 = new Person("Oliver (dad)");

            //the youngest generation
            Person d1 = new Person("Clement (boy)");
            Person d2 = new Person("Colin (boy)");

            Person.linkFamily(a1, a2, new Person[]{b1, b2, b3, b4});
            Person.linkFamily(a3, a4, new Person[]{b5, b6, b7});
            Person.linkFamily(a5, a6, new Person[]{b8, b9});
            Person.linkFamily(b3, b6, new Person[]{c1, c2, c3});
            Person.linkFamily(b4, b5, new Person[]{c4, c5, c6});
            Person.linkFamily(b8, b7, new Person[]{c7, c8, c9});
            Person.linkFamily(c4, c7, new Person[]{d1, d2});

            return a1;
        }

    }

    public static class CheckBoxTreeCellRenderer implements TreeCellRenderer {

        private JCheckBox cb = new JCheckBox();

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

            cb.setText(value.toString());
            cb.setOpaque(selected);
            if (selected) {
                cb.setBackground(UIManager.getColor("Tree.selectionBackground"));
                cb.setForeground(UIManager.getColor("Tree.selectionForeground"));
            } else {
                cb.setBackground(tree.getBackground());
                cb.setForeground(tree.getForeground());
            }

            return cb;

        }

    } 

    public static class GenealogyModel implements TreeModel {

        private boolean showAncestors;
        private Vector<TreeModelListener> treeModelListeners
                = new Vector<TreeModelListener>();
        private Person rootPerson;

        public GenealogyModel(Person root) {
            showAncestors = false;
            rootPerson = root;
        }

        /**
         * Used to toggle between show ancestors/show descendant and to change the
         * root of the tree.
         */
        public void showAncestor(boolean b, Object newRoot) {
            showAncestors = b;
            Person oldRoot = rootPerson;
            if (newRoot != null) {
                rootPerson = (Person) newRoot;
            }
            fireTreeStructureChanged(oldRoot);
        }

//////////////// Fire events //////////////////////////////////////////////
        /**
         * The only event raised by this model is TreeStructureChanged with the root
         * as path, i.e. the whole tree has changed.
         */
        protected void fireTreeStructureChanged(Person oldRoot) {
            int len = treeModelListeners.size();
            TreeModelEvent e = new TreeModelEvent(this,
                    new Object[]{oldRoot});
            for (TreeModelListener tml : treeModelListeners) {
                tml.treeStructureChanged(e);
            }
        }

//////////////// TreeModel interface implementation ///////////////////////
        /**
         * Adds a listener for the TreeModelEvent posted after the tree changes.
         */
        public void addTreeModelListener(TreeModelListener l) {
            treeModelListeners.addElement(l);
        }

        /**
         * Returns the child of parent at index index in the parent's child array.
         */
        public Object getChild(Object parent, int index) {
            Person p = (Person) parent;
            if (showAncestors) {
                if ((index > 0) && (p.getFather() != null)) {
                    return p.getMother();
                }
                return p.getFather();
            }
            return p.getChildAt(index);
        }

        /**
         * Returns the number of children of parent.
         */
        public int getChildCount(Object parent) {
            Person p = (Person) parent;
            if (showAncestors) {
                int count = 0;
                if (p.getFather() != null) {
                    count++;
                }
                if (p.getMother() != null) {
                    count++;
                }
                return count;
            }
            return p.getChildCount();
        }

        /**
         * Returns the index of child in parent.
         */
        public int getIndexOfChild(Object parent, Object child) {
            Person p = (Person) parent;
            if (showAncestors) {
                int count = 0;
                Person father = p.getFather();
                if (father != null) {
                    count++;
                    if (father == child) {
                        return 0;
                    }
                }
                if (p.getMother() != child) {
                    return count;
                }
                return -1;
            }
            return p.getIndexOfChild((Person) child);
        }

        /**
         * Returns the root of the tree.
         */
        public Object getRoot() {
            return rootPerson;
        }

        /**
         * Returns true if node is a leaf.
         */
        public boolean isLeaf(Object node) {
            Person p = (Person) node;
            if (showAncestors) {
                return ((p.getFather() == null)
                        && (p.getMother() == null));
            }
            return p.getChildCount() == 0;
        }

        /**
         * Removes a listener previously added with addTreeModelListener().
         */
        public void removeTreeModelListener(TreeModelListener l) {
            treeModelListeners.removeElement(l);
        }

        /**
         * Messaged when the user has altered the value for the item identified by
         * path to newValue. Not used by this model.
         */
        public void valueForPathChanged(TreePath path, Object newValue) {
            System.out.println("*** valueForPathChanged : "
                    + path + " --> " + newValue);
        }
    }

    public static class Person {

        Person father;
        Person mother;
        List<Person> children;
        private String name;

        public Person(String name) {
            this.name = name;
            mother = father = null;
            children = new ArrayList<Person>();
        }

        /**
         * Link together all members of a family.
         *
         * @param pa the father
         * @param ma the mother
         * @param kids the children
         */
        public static void linkFamily(Person pa,
                Person ma,
                Person[] kids) {
            for (Person kid : kids) {
                pa.children.add(kid);
                ma.children.add(kid);
                kid.father = pa;
                kid.mother = ma;
            }
        }

/// getter methods ///////////////////////////////////
        public String toString() {
            return name;
        }

        public String getName() {
            return name;
        }

        public Person getFather() {
            return father;
        }

        public Person getMother() {
            return mother;
        }

        public int getChildCount() {
            return children.size();
        }

        public Person getChildAt(int i) {
            return (Person) children.get(i);
        }

        public int getIndexOfChild(Person kid) {
            return children.indexOf(kid);
        }
    }
}



回答2:


Sturm gives most of the solution.

The missing piece of the puzzle is that you can get the JTree from the JXTreeTable like this:

JTree tree = (JTree) treeTable.getCellRenderer(0, treeTable.getHierarchicalColumn())

It's a bit of a hack, but I don't know if they have any better API to get at it.




回答3:


With a few minutes of googling, I found this CheckBox node tree sample and this blog post about JXTreeTable with CheckBoxes, as well as this very simple solution.

Is this not what you are looking for?



来源:https://stackoverflow.com/questions/21924846/checkbox-within-a-jxtreetable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!