当前位置: 首页 > 知识库问答 >
问题:

使用JTable作为ListCellRenderer组件的JComboBox

申屠昆
2023-03-14

我目前正在开发一个JComboBox组件,我希望在其中的组合框中有一个JTable用于下拉选择。我扩展了ListCellRenderer,并且在弹出窗口中有一个表。

我想用两种不同的方式来呈现。第一个作为所选行的绑定列的标签,当弹出窗口不可见时。第二种是在弹出窗口可见时用JScrollPane显示表。

不幸的是,当我这样做时,弹出窗口被缩小到列表的行高,这只为表的列留下了空间。

如果我只是使用滚动窗格,我可以看到完整的表,但是当弹出窗口不可见时,我会看到组合框中的表,这不是我想要的。

下面是一个编译和运行的最小示例:

import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

public class TableComboBoxMain {
    public static void main(String[] args) {
        JTableComboBox<Employee> combo = new JTableComboBox<>();
        combo.addItem(new Employee("April",3));
        //combo.setMaximumRowCount(10);
        combo.setRenderer(new TableListCellRenderer(combo));
        JFrame frame = new JFrame("Test Table Combo Box");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(combo);
        frame.pack();
        frame.setVisible(true);
    }

    public static class Employee{
        String name;
        int nr;
        public Employee(String name, int number ){
            this.name =name;
            this.nr = number;
        }
    }

    public static class JTableComboBox<E> extends JComboBox<E> implements ListSelectionListener{
        @Override
        public void valueChanged(ListSelectionEvent e) {System.out.println("Row selected"+e.getFirstIndex());
            this.setSelectedIndex(e.getFirstIndex());
        }
    }

    public static class TableListCellRenderer extends JScrollPane implements ListCellRenderer{
    JTable table;
    JTableComboBox combo;
    boolean mouseListenerAdded;
    public TableListCellRenderer(JTableComboBox combo){
        this.combo=combo;
        DefaultTableModel model = new DefaultTableModel();
        String[] cols1 = new String[]{"1","2","3","4"};
        String[] cols2 = new String[]{"Mark","John","April","Mary"};
        model.addColumn("Nr", cols1);model.addColumn("Name",cols2);
        table = new JTable(model);table.setShowGrid(true);table.setGridColor(Color.gray);
        this.getViewport().add(table);
    }
    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        if(!mouseListenerAdded){
            list.addMouseListener(this.getListener());
            mouseListenerAdded = true;
        }//If this is uncommented then the BasicComboPopup size is always no more than 1 row?!!
        if(!combo.isPopupVisible()){
            Employee emp = (Employee) value;
            return new JLabel(emp.name);

        }else
           return this;
    }

    public MouseAdapter getListener(){
        MouseAdapter adapter = new MouseAdapter(){
        @Override
        public void mousePressed(MouseEvent e) {
                if(combo.isPopupVisible()){
                    System.out.println("MouseClicked over list");
                    int row = table.rowAtPoint(e.getPoint());
                    if(row>0){
                        table.setRowSelectionInterval(row-1, row-1);
                        ListDataEvent event = new ListDataEvent(combo,ListDataEvent.CONTENTS_CHANGED,row-1,row-1);
                        combo.contentsChanged(event);
                    }
                }
            }
        };
        return adapter;
    }
}
}

共有1个答案

澹台志诚
2023-03-14

所以我找到了解决问题的办法。我还没说完,因为我还想有几个互动:

  1. 我希望能够移动列
  2. 我希望能够为列提供弹出菜单
  3. 我希望能够用鼠标垂直滚动

我想张贴的解决方案,以防任何人想要一个起点的例子。当我解决这些附加问题时,我会更新我的答案。

下面是测试类:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.table.DefaultTableModel;

public class TableComboBoxMain {
    public static void main(String[] args) {
        JTableComboBox<String> combo = new JTableComboBox<>();
        combo.addItem("");
        BasicComboBoxUI ui = new BasicComboBoxUI(){
            @Override
            protected ComboPopup createPopup() {
                return new BasicComboPopup( comboBox ){
                    @Override
                    protected int getPopupHeightForRowCount(int maxRowCount) {
                        return 100;
                    }
                    @Override
                    protected JScrollPane createScroller() {
                        JScrollPane sp = new JScrollPane( list,
                            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED );
                        sp.getHorizontalScrollBar().setFocusable( false );
                        return sp;
                    }
                };
            }
        };
        combo.setUI(ui);
        combo.setRenderer(new TableListCellRenderer(combo));
        JFrame frame = new JFrame("Test Table Combo Box");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(combo);
        frame.pack();
        frame.setVisible(true);
    }

    public static class Employee{
        String name;
        int nr;
        public Employee(String name, int number ){
            this.name =name;
            this.nr = number;
        }
    }

    public static class JTableComboBox<E> extends JComboBox<E> implements ListSelectionListener{
        @Override
        public void valueChanged(ListSelectionEvent e) {
            System.out.println("Row selected"+e.getFirstIndex());
            this.setSelectedIndex(e.getFirstIndex());
        }
    }

    public static class TableListCellRenderer extends DefaultListCellRenderer{
        JTable table;
        JTableComboBox combo;
        JPanel renderer = new JPanel();
        JPanel colHeader = new JPanel();
        JScrollPane scroll = new JScrollPane();
        boolean mouseListenerAdded;
        public TableListCellRenderer(JTableComboBox combo){
            this.combo=combo;
            DefaultTableModel model = new DefaultTableModel();
            String[] cols1 = new String[]{"1","2","3","4","5","6"};
            String[] cols2 = new String[]{"Mark","John","April","Mary","Joe","Jack"};
            model.addColumn("Nr", cols1);model.addColumn("Name",cols2);
            table = new JTable(model);
            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            colHeader.add(table.getTableHeader());
            renderer.setLayout(new BorderLayout());
            renderer.add(colHeader, BorderLayout.NORTH);
            renderer.add(table,BorderLayout.CENTER);
            scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
            scroll.getViewport().add(table);

        }
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            list.setFixedCellHeight(table.getRowHeight()*(table.getRowCount()+1)+10);
            list.setFixedCellWidth(table.getPreferredSize().width);
            JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if(!mouseListenerAdded){
                list.addMouseListener(this.getListener());
                mouseListenerAdded = true;
            }
            if(!combo.isPopupVisible()){
                label.setText("Select...");
                if(table.getSelectedRowCount()>0)
                    label.setText(""+table.getModel().getValueAt(table.getSelectedRow(),1));
                return label;
            }
               return scroll;
        }

        public MouseAdapter getListener(){
            MouseAdapter adapter = new MouseAdapter(){
            @Override
            public void mousePressed(MouseEvent e) {
                    if(combo.isPopupVisible()){
                        System.out.println("MouseClicked over list");
                        int row = table.rowAtPoint(e.getPoint());
                        if(row>0){
                            table.setRowSelectionInterval(row-1, row-1);
                            ListDataEvent event = new ListDataEvent(combo,ListDataEvent.CONTENTS_CHANGED,row-1,row-1);
                            combo.contentsChanged(event);
                        }
                    }
                }
            };
            return adapter;
        }
    }
}

该解决方案的关键部分是,为了在弹出窗口中拥有您想要的表和表的特性,您需要重写UI。具体而言,您需要重写BasicComboBoxui上的CreatePopupGetPopupHeightForRowCountCreateScroller上的BasicComboPopup。最后,在实现GetListCellRenderingComponent时,您设置了与表的首选高度和宽度匹配的固定高度和宽度。这将允许弹出窗口的主滚动器充当表格的滚动窗格。

 类似资料:
  • 我正在尝试制作一个JTable,它在一个单元格中有一个JComboBox。我知道我可以使用celleditor,但诀窍是我希望在每一行的组合框中有不同的信息。表中的每一行都代表一个对象,在该对象上有一个arraylist和它的内容,我想在组合框中。到目前为止,这是我的思考过程。 现在,当我稍后调用该方法时(通过按一个按钮),我想插入一个新的行,其中包含Coloum5中唯一的组合框,但我不知道如何做

  • 问题内容: JTable的默认行为是将焦点更改为下一个单元格,我想在按TAB键时强制其将焦点移至下一个组件(例如JTextField)。 我重写的方法始终返回false: 但这仍然不会改变对下一个组件的关注! 我应该如何将重点放在下一个组件而不是下一个单元格上? 问题答案: 如果确实要这样做,则需要更改表操作映射的默认行为。 然后,您需要执行一些操作来处理遍历 另一种方法是禁用底层… (尚未测试)

  • 问题内容: 说我有一个功能组件: 直接将其作为函数调用有什么区别: 与将其称为组件相比: 我对性能影响最感兴趣,React如何在内部对它们进行区别对待,也许对React Fiber中的情况可能有所不同,我听说功能组件的性能得到了提升。 问题答案: 调用它作为函数要快得多,事实上,几个月前就已经有讨论了。在这一点上,功能性反应组件不能是PureComponents,因此没有真正适用于它们的额外优化。

  • 我正在尝试模拟react bootstrap

  • 那行代码给了我一个错误,它给了我... 线程中的异常"AWT-EventQuue-0"java.lang.ClassCast异常:javax.swing.JTable1美元不能转换为javax.swing.table.默认表模型 我该怎么解决?我需要从表中删除一行。 这是我实例化tabla_proveedor的地方