有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java根据JTable的列坐标定位组件

场景:用户选择一个表格文件进行分析,然后使用JTable将其作为预览加载到GUI中。我希望用户在分析之前对列进行注释,但我不能替换列标题,因为那样会让人困惑


我现有的解决方案可以工作,但非常粗糙,正如您在下面的屏幕截图enter image description here中看到的,组合框的定位不是特别好,当列数增加到20-30或更多时,这会变得非常混乱

当前选项卡窗格有三个子面板,顶部面板包括标签和按钮,中间面板包括组合框和表格,底部面板包含分析按钮

private void dataPreview(final String[][] data, String[] headers, final JTabbedPane comp) {
    // Take care of column headers
    if (headers.length == 0) {
        headers = new String[data[1].length];
        for (int i = 0; i < headers.length; i++)
            headers[i] = "C" + i;
    }

    // Column annotations
    final Dataset.ANNOT_TYPE[] annots = new Dataset.ANNOT_TYPE[headers.length];
    final JComboBox<?>[] combos = new JComboBox[annots.length];

    // the upper part of the panel
    final PreviewPanel descPanel = new PreviewPanel(frame);
    final ParamPanel paramPanel = new ParamPanel();
    final JPanel upperContainer = new JPanel(new BorderLayout());
    paramPanel.setVisible(false);

    descPanel.setParamButtonAction(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            boolean b = paramPanel.isVisible();
            paramPanel.setVisible(!b);
        }
    });

    upperContainer.add(descPanel, BorderLayout.NORTH);
    upperContainer.add(paramPanel, BorderLayout.SOUTH);

    // Define table model
    DataPreviewTableModel model = new DataPreviewTableModel(data, headers);
    final JTable table = new JTable(model);
    table.getColumnModel().getColumn(0).setPreferredWidth(25);
    table.setTableHeader(new JTableHeader(table.getColumnModel()){
        //Implement table header tool tips.
            private static final long serialVersionUID = -7015589028339208609L;
            public String getToolTipText(MouseEvent e) {
                java.awt.Point p = e.getPoint();
                int index = columnModel.getColumnIndexAtX(p.x);
                return table.getColumnName(index);              
            }
        });

    for(int i=0; i<headers.length; i++)
        table.getColumnModel().getColumn(i).setMinWidth(60);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);


    // create the combo boxes for column annotation
    final JPanel comboPanel = new JPanel();
    comboPanel.setBorder(new EmptyBorder(3, 0, 3, 0));
    comboPanel.add(new JLabel("Columns:"));
    for (int i = 0; i < combos.length; i++) {
        final JComboBox<?> box = new JComboBox<Object>(Dataset.ANNOT_TYPE.values());
        final int colIndex = i;
        box.setMinimumSize(new Dimension(60, box.getMinimumSize().height));
        box.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                int colType = box.getSelectedIndex();
                table.getColumnModel().getColumn(colIndex+1)
                        .setCellRenderer(new CellColorRenderer(colType));
                table.repaint();
            }
        });

        comboPanel.add(box);
        combos[i] = box;
    }

    final JPanel middlePanel = new JPanel(new BorderLayout());
    middlePanel.add(comboPanel, BorderLayout.NORTH);
    middlePanel.add(new JScrollPane(table), BorderLayout.CENTER);

    JPanel lowerPanel = new JPanel(new BorderLayout());
    final JButton analyzeButton = new JButton("Analyze Dataset!");
    lowerPanel.add(analyzeButton, BorderLayout.LINE_END);
    final JPanel container = new JPanel(new BorderLayout());
    container.add(upperContainer, BorderLayout.NORTH);
    container.add(new JScrollPane(middlePanel), BorderLayout.CENTER);
    container.add(lowerPanel, BorderLayout.SOUTH);

    comp.addTab("Preview", container);

问题:

  1. 这是一个合理的设置,如果是这样,我如何才能更好地定位我的组合框
  2. 如果现有设计是次优设计,我如何改进它而不必重新进行整个设计

我按照建议查看了[JTableHeader.getHeaderRect()][2]here,但我不确定如何根据标题矩形的x,y坐标放置组合,因为它们位于不同的面板中


共 (2) 个答案

  1. # 1 楼答案

    我基于JTableTableColumnModel创建了一个简单的布局。只需将组件添加到面板中,并指定组件应位于上方的列(如果将面板添加到表下方,则位于下方):

    import java.awt.*;
    import java.util.HashMap;
    import java.util.Map;
    import javax.swing.event.ListSelectionEvent;
    import javax.swing.event.ChangeEvent;
    import javax.swing.table.TableColumnModel;
    import javax.swing.event.TableColumnModelEvent;
    import javax.swing.event.TableColumnModelListener;
    import javax.swing.table.TableColumn;
    import javax.swing.*;
    
    /**
     */
    public class TableColumnModelLayout
        implements LayoutManager2, java.io.Serializable, TableColumnModelListener
    {
        //  Track a constraint added to a component
        private HashMap<Component, Integer> constraints = new HashMap<Component, Integer>();
    
        private TableColumnModel model;
        private JPanel container;
    
        /**
         *  Convenience constructor to provide for "stacking" of components. Each
         *  component will be stacked above the previous component and sized to
         *  fill the space of the parent container.
         */
        public TableColumnModelLayout(TableColumnModel model, JPanel container)
        {
            this.model = model;
            this.container = container;
            model.addColumnModelListener( this );
        }
    
        /**
         *  Gets the constraints for the specified component.
         *
         *  @param   component the component to be queried
         *  @return  the constraint for the specified component, or null
         *           if component is null or is not present in this layout
         */
        public Integer getConstraints(Component component)
        {
            return (Integer)constraints.get(component);
        }
    
        /**
         * Adds the specified component with the specified name to the layout.
         * @param name the name of the component
         * @param comp the component to be added
         */
        public void addLayoutComponent(String name, Component comp) {}
    
        /*
         *  Keep track of any specified constraint for the component.
         */
        public void addLayoutComponent(Component component, Object constraint)
        {
            if (constraint == null)
            {
                constraints.remove(component);
            }
            else if (constraint instanceof Integer)
            {
                Integer column = (Integer)constraint;
    
                if (column >= 0 && column < model.getColumnCount())
                {
                    constraints.put(component, (Integer)constraint);
                }
                else
                {
                    String message = "Invalid column specified: " + column;
                    throw new IllegalArgumentException( message );
                }
            }
            else
            {
                String message = "Constraint parameter must be of type Integer";
                throw new IllegalArgumentException( message );
            }
        }
    
        /**
         * Removes the specified component from the layout.
         *
         * @param comp the component to be removed
         */
        public void removeLayoutComponent(Component component)
        {
            constraints.remove( component );
        }
    
        /**
         *  Determine the minimum size on the Container
         *
         *  @param   target   the container in which to do the layout
         *  @return  the minimum dimensions needed to lay out the
         *           subcomponents of the specified container
         */
        public Dimension minimumLayoutSize(Container parent)
        {
            return preferredLayoutSize(parent);
        }
    
        /**
         *  Determine the preferred size on the Container
         *
         *  @param   parent   the container in which to do the layout
         *  @return  the preferred dimensions to lay out the
         *           subcomponents of the specified container
         */
        public Dimension preferredLayoutSize(Container parent)
        {
        synchronized (parent.getTreeLock())
        {
            int width = 0;
    
            for (int i = 0; i < model.getColumnCount(); i++)
            {
                TableColumn tc = model.getColumn(i);
                width += tc.getWidth();
            }
    
            int height = 0;
    
            for (Component component: parent.getComponents())
            {
                height = Math.max(height, component.getPreferredSize().height);
            }
    
            Insets insets = parent.getInsets();
            width += insets.left + insets.right;
            height += insets.top + insets.bottom;
    
            return new Dimension(width, height);
        }
        }
    
        /**
         * Lays out the specified container using this layout.
         * <p>
         *
         * @param     target   the container in which to do the layout
         */
        public void layoutContainer(Container parent)
        {
        synchronized (parent.getTreeLock())
        {
            Insets insets = parent.getInsets();
            int offset = insets.left;
            int[] offsets = new int[model.getColumnCount()];
    
            for (int i = 0; i < model.getColumnCount(); i++)
            {
                offsets[i] = offset;
                TableColumn tc = model.getColumn(i);
                offset += tc.getWidth();
            }
    
            for (Component component: parent.getComponents())
            {
                Dimension preferred = component.getPreferredSize();
                Integer column = constraints.get(component);
                int width = model.getColumn(column).getWidth();
    
                component.setBounds(offsets[column], insets.top, width, preferred.height);
            }
        }}
    
        /**
         * There is no maximum.
         */
        public Dimension maximumLayoutSize(Container target)
        {
            return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
        }
    
        /**
         * Returns the alignment along the x axis.  Use center alignment.
         */
        public float getLayoutAlignmentX(Container parent)
        {
            return 0.0f;
        }
    
        /**
         * Returns the alignment along the y axis.  Use center alignment.
         */
        public float getLayoutAlignmentY(Container parent)
        {
            return 0.5f;
        }
    
        /**
         * Invalidates the layout, indicating that if the layout manager
         * has cached information it should be discarded.
         */
        public void invalidateLayout(Container target)
        {
            // remove constraints here?
        }
    
        /**
         * Returns the string representation of this column layout's values.
         * @return   a string representation of this grid layout
         */
        public String toString()
        {
            return getClass().getName();
        }
    
        //  Implement TableColumnModelListener
    
        public void columnMarginChanged(ChangeEvent e)
        {
    /*
            for (int i = 0; i < tcm.getColumnCount(); i++)
            {
                TableColumn tc = tcm.getColumn(i);
                Component c = header.getComponent(i);
                rl.addLayoutComponent(c, new Float(tc.getWidth()));
            }
    
            header.revalidate();
    */
            container.revalidate();
        }
    
        public void columnAdded(TableColumnModelEvent e) {}
        public void columnMoved(TableColumnModelEvent e) {}
        public void columnRemoved(TableColumnModelEvent e) {}
        public void columnSelectionChanged(ListSelectionEvent e) {}
    
        private static void createAndShowGUI()
        {
            JTable table = new JTable(5, 5);
            JScrollPane scrollPane1 = new JScrollPane( table );
            table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
    
            JPanel header = new JPanel();
            TableColumnModelLayout layout = new TableColumnModelLayout(table.getColumnModel(), header);
            header.setLayout( layout );
            header.add(new JLabel("Column 0"), new Integer(0));
            JLabel label2 = new JLabel("Column 2");
            label2.setHorizontalAlignment(JLabel.RIGHT);
            header.add(label2, new Integer(2));
            header.add(new JLabel("Column 4"), new Integer(4));
            JScrollPane scrollPane2 = new JScrollPane( header );
            scrollPane2.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
            scrollPane2.getHorizontalScrollBar().setModel( scrollPane1.getHorizontalScrollBar().getModel() );
    
    
            JFrame frame = new JFrame("Table Column Model Layout");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(scrollPane2, BorderLayout.PAGE_START);
            frame.add(scrollPane1, BorderLayout.CENTER);
            frame.setLocationByPlatform( true );
            frame.pack();
            frame.setVisible( true );
        }
    
        public static void main(String[] args)
        {
            EventQueue.invokeLater( () -> createAndShowGUI() );
    /*
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    createAndShowGUI();
                }
            });
    */
        }
    }
    

    组件将随着TableColumn的大小而增长/收缩

    构件将显示在列的左边缘。您可能希望修改该选项,使构件相对于柱居中

  2. # 2 楼答案

    注意comboPanel是一个具有默认JPanelFlowLayout,它根据组合框的首选大小将组件居中。结果,他们在中间聚集在一起。一些备选方案:

    • 指定具有额外列的GridLayout,并为check列使用空组件。初始表格将对齐,尽管随后对列宽的更改将改变这一点

      JPanel comboPanel = new JPanel(new GridLayout(0, annots.length + 1));
      
    • 使用here所示的方法向每个相关的头添加一个组合框,注意here中引用的注意事项