java swing multi column autocomplete combobox

时光毁灭记忆、已成空白 提交于 2019-11-28 00:37:38

Here's a JComboBox that uses a JTable for it's drop-down instead of a JList. You need to give it table data (a List of Lists) instead of the usual. Sorry, there's no auto-complete.

I've been using this for years. It works, but I make no promises. Heck, there may even be some dead code in there leftover from experimentation. It's just a mish-mosh from different ideas I grabbed from different code samples over the years. I'm sure there's room for improvement.

DetailedComboBox:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
import javax.swing.table.*;

/**
 * A JComboBox that has a JTable as a drop-down instead of a JList
 */
public class DetailedComboBox extends JComboBox
{
  public static enum Alignment {LEFT, RIGHT}

  private List<List<? extends Object>> tableData;
  private String[] columnNames;
  private int[] columnWidths;
  private int displayColumn;
  private Alignment popupAlignment = Alignment.LEFT;

  /**
   * Construct a TableComboBox object
   */
  public DetailedComboBox(String[] colNames, int[] colWidths,
                          int displayColumnIndex)
  {
    super();
    this.columnNames = colNames;
    this.columnWidths = colWidths;
    this.displayColumn = displayColumnIndex;

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

  /**
   * Set the type of alignment for the popup table
   */
  public void setPopupAlignment(Alignment alignment)
  {
    popupAlignment = alignment;
  }

  /**
   * 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
     */
    @Override
    protected ComboPopup createPopup()
    {
      return new TableComboPopup(comboBox, this);
    }

    /**
     * Return the JList component
     */
    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;
//    private JList list = new JList();
//    private ListSelectionListener selectionListener;
//    private ItemListener itemListener;

    /**
     * Construct a popup component that's a table
     */
    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]);
      }

      scroll = new JScrollPane(table);
      scroll.setHorizontalScrollBarPolicy(
          JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
      scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

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

      table.addMouseListener(new MouseAdapter()
      {
        @Override
        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
     */
    @Override
    public void show()
    {
      if (isEnabled())
      {
        super.removeAll();

        int scrollWidth = table.getPreferredSize().width +
            ((Integer) UIManager.get("ScrollBar.width")).intValue() + 1;
        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;

        if (popupAlignment == Alignment.RIGHT)
        {
          scrollX = comboBox.getBounds().width - scrollWidth;
        }

        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
     */
    @Override
    public boolean isCellEditable(int row, int col)
    {
      return false;
    }

    /**
     * Pull the column names out of the tableInfo object for the header
     */
    @Override
    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;
    }
  }
}

Driver:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

import javax.swing.*;

public class DetailedComboBoxDemo implements Runnable
{
  private DetailedComboBox combo;
  private JTextField name;
  private JTextField capital;

  public void run()
  {
    List<List<?>> tableData = new ArrayList<List<?>>();
    tableData.add(new ArrayList<String>(
                  Arrays.asList("MD", "Maryland", "Annapolis")));
    tableData.add(new ArrayList<String>(
                  Arrays.asList("NH", "New Hampshire", "Concord")));
    tableData.add(new ArrayList<String>(
                  Arrays.asList("NJ", "New Jersey", "Trenton")));
    tableData.add(new ArrayList<String>(
                  Arrays.asList("NM", "New Mexico", "Santa Fe")));
    tableData.add(new ArrayList<String>(
                  Arrays.asList("ND", "North Dakota", "Bismark")));

    String[] columns = new String[]{"State", "Name", "Capital"};
    int[] widths = new int[]{50, 100, 100};

    combo = new DetailedComboBox(columns, widths, 0);
    combo.setTableData(tableData);
    combo.setSelectedIndex(-1);
    combo.setPopupAlignment(DetailedComboBox.Alignment.LEFT);
    combo.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        showDetails();
      }
    });

    name = new JTextField(10);
    capital = new JTextField(10);
    name.setEditable(false);
    capital.setEditable(false);

    JPanel p = new JPanel(new FlowLayout());
    p.add(new JLabel("State"));
    p.add(combo);
    p.add(new JLabel("Name"));
    p.add(name);
    p.add(new JLabel("Capital"));    
    p.add(capital);

    JFrame frame = new JFrame("DetailedComboBox Demo");
    frame.getContentPane().add(p, BorderLayout.CENTER);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  private void showDetails()
  {
    List<? extends Object> rowData = combo.getSelectedRow();
    name.setText(rowData.get(1).toString());
    capital.setText(rowData.get(2).toString());
  }

  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new DetailedComboBoxDemo());
  }
}

To achieve this you need to use customized sub class of BasicComboxUI and BasicComboPopup. Use the JTable in the Popup returned by BasicComboPopup instead of JList.

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