Skip to Main Content

Application Development Software

Announcement

For appeals, questions and feedback, please email oracle-forums_moderators_us@oracle.com

Problem rendering MultiColumn ComboBox in java

In code below, I have two combo boxes where I am trying to:

  1. Make the first one looks just like the second one (without ugly borders and separate right button);
  2. Remove the ComboPopup black line border;
  3. And if possible highlight the mouse-over popup table row.

MainClass class:

import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;

import com.eps.agir.libraries.DetailedComboBox;

@SuppressWarnings("serial")
public class MainClass extends JFrame {

    private JPanel cp_root;
    
    public MainClass() {

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        cp_root = new JPanel(null);
        setContentPane(cp_root);
        
        List<List<?>> data = new ArrayList<List<?>>();
        data.add(new ArrayList<String>(Arrays.asList("1", "1st Element", "1st Parameter")));
        data.add(new ArrayList<String>(Arrays.asList("2", "2nd Element", "2nd Parameter")));
        data.add(new ArrayList<String>(Arrays.asList("3", "3rd Element", "3rd Parameter")));
        
        DetailedComboBox multiColumn = new DetailedComboBox(new String[]{"ID", "Element", "Parameter"}, new int[]{0, 200, 150}, 1, new int[]{0});
        multiColumn.setTableData(data);
        multiColumn.setBounds(20, 30, 400, 30);
        cp_root.add(multiColumn);

        JComboBox<String> simple = new JComboBox<String>(new DefaultComboBoxModel<String>(new String[] {"1st Element", "2nd Element", "3rd Element"}));
        simple.setBounds(20, 70, 400, 30);
        cp_root.add(simple);
        
    }
    
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    
                    MainClass frame = new MainClass();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    
}

DetailedComboBox class:

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

@SuppressWarnings("serial")
public class DetailedComboBox extends JComboBox<Object> {
  
    private List<List<? extends Object>> tableData;
    private String[] columnNames;
    private int[] columnWidths;
    private int displayColumn;
    private int[] removedColumns;
    
    public DetailedComboBox(String[] colNames, int[] colWidths, int displayColumnIndex, int removedIndexes[]){
   
        super();
  
        this.columnNames = colNames;
        this.columnWidths = colWidths;
        this.displayColumn = displayColumnIndex;
        this.removedColumns = removedIndexes;

        setUI(new TableComboBoxUI());
        setEditable(false);
        
    }

    /**
     * Populate the combobox and drop-down table with the supplied data.
     * If the supplied List is non-null and non-empty, it is assumed that
     * the data is a List of Lists to be used for the drop-down table.
     * The combobox is also populated with the column data from the
     * column defined by <code>displayColumn</code>.
     */
    public void setTableData(List<List<? extends Object>> tableData){
    
        this.tableData = (tableData == null ? new ArrayList<List<? extends Object>>() : tableData);

        // even though the incoming data is for the table, we must also
        // populate the combobox's data, so first clear the previous list.
        removeAllItems();

        // then load the combobox with data from the appropriate column
        Iterator<List<? extends Object>> iter = this.tableData.iterator();
        while (iter.hasNext()){
            List<? extends Object> rowData = iter.next();
            addItem(rowData.get(displayColumn));
        }
    }

    public List<? extends Object> getSelectedRow(){
        return tableData.get(getSelectedIndex());
    }

    /**
     * The handler for the combobox's components
     */
    private class TableComboBoxUI extends MetalComboBoxUI {
    
        /**
         * Create a popup component for the ComboBox
         */
        protected ComboPopup createPopup(){
            return new TableComboPopup(comboBox, this);
        }

        /**
         * Return the JList component
         */
        @SuppressWarnings("rawtypes")
        public JList getList(){
            return listBox;
        }
    }

    /**
     * The drop-down of the combobox, which is a JTable instead of a JList.
     */
    private class TableComboPopup extends BasicComboPopup implements ListSelectionListener, ItemListener{
    
        private final JTable table;

        private TableComboBoxUI comboBoxUI;
        private PopupTableModel tableModel;
        private JScrollPane scroll;
        
        /**
         * Construct a popup component that's a table
         */
        @SuppressWarnings("rawtypes")
        public TableComboPopup(JComboBox combo, TableComboBoxUI ui){
    
            super(combo);
            this.comboBoxUI = ui;

            tableModel = new PopupTableModel();
            table = new JTable(tableModel);
            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            table.getTableHeader().setReorderingAllowed(false);

            TableColumnModel tableColumnModel = table.getColumnModel();
            tableColumnModel.setColumnSelectionAllowed(false);

            for (int index = 0; index < table.getColumnCount(); index++){
                TableColumn tableColumn = tableColumnModel.getColumn(index);
                tableColumn.setPreferredWidth(columnWidths[index]);
            }
            
            for (int index = removedColumns.length-1; index >= 0 ; index--) {
                table.removeColumn(tableColumnModel.getColumn(removedColumns[index]));
            }
            
            scroll = new JScrollPane(table);
            scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
            scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

            ListSelectionModel selectionModel = table.getSelectionModel();
            selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            selectionModel.addListSelectionListener(this);
            combo.addItemListener(this);

            table.addMouseListener(new MouseAdapter(){
                public void mousePressed(MouseEvent event){
                    Point p = event.getPoint();
                    int row = table.rowAtPoint(p);

                    comboBox.setSelectedIndex(row);
                    hide();
                }
            });

            table.setBackground(UIManager.getColor("ComboBox.listBackground"));
            table.setForeground(UIManager.getColor("ComboBox.listForeground"));
            
        }

        /**
         * This method is overridden from BasicComboPopup
         */
        public void show(){
            
            if (isEnabled()){
                super.removeAll();

                int scrollWidth = table.getPreferredSize().width + 20;
                int scrollHeight = comboBoxUI.getList().getPreferredScrollableViewportSize().height;
                scroll.setPreferredSize(new Dimension(scrollWidth, scrollHeight));

                super.add(scroll);

                ListSelectionModel selectionModel = table.getSelectionModel();
                selectionModel.removeListSelectionListener(this);
                selectRow();
                selectionModel.addListSelectionListener(this);

                int scrollX = 0;
                int scrollY = comboBox.getBounds().height;

                show(comboBox, scrollX, scrollY);
            }
        }

        /**
         * Implemention of ListSelectionListener
         */
        public void valueChanged(ListSelectionEvent event){
            comboBox.setSelectedIndex(table.getSelectedRow());
        }

        /**
         * Implemention of ItemListener
         */
        public void itemStateChanged(ItemEvent event){
            if (event.getStateChange() != ItemEvent.DESELECTED){
                ListSelectionModel selectionModel = table.getSelectionModel();
                selectionModel.removeListSelectionListener(this);
                selectRow();
                selectionModel.addListSelectionListener(this);
            }
        }

        /**
         * Sync the selected row of the table with the selected row of the combo.
         */
        private void selectRow(){
            int index = comboBox.getSelectedIndex();
            
            if (index != -1){
                table.setRowSelectionInterval(index, index);
                table.scrollRectToVisible(table.getCellRect(index, 0, true));
            }
        }
        
    }

    /**
     * A model for the popup table's data
     */
    private class PopupTableModel extends AbstractTableModel{
        /**
         * Return the # of columns in the drop-down table
         */
        public int getColumnCount(){
            return columnNames.length;
        }

        /**
         * Return the # of rows in the drop-down table
       */
        public int getRowCount(){
            return tableData == null ? 0 : tableData.size();
        }

        /**
         * Determine the value for a given cell
       */
        public Object getValueAt(int row, int col){
            if (tableData == null || tableData.size() == 0){
                return "";
            }
            
            return tableData.get(row).get(col);
        }

        /**
         * All cells in the drop-down table are uneditable
         */
        public boolean isCellEditable(int row, int col){
            return false;
       }
    
       /**
        * Pull the column names out of the tableInfo object for the header
        */
        public String getColumnName(int column){
            String columnName = null;
            
            if (column >= 0 && column < columnNames.length){
                columnName = columnNames[column].toString();
            }
            
            return (columnName == null) ? super.getColumnName(column) : columnName;
        }
    }
    
}

I think it's because use of MetalComboBoxUI in DetailedComboBox class.

I will appreciate any alternative solution.

Thanks in advance.

Comments
Post Details
Added on Mar 9 2024
0 comments
39 views