JTable с кнопкой «закрыть» в заголовке столбца

Я пытаюсь создать таблицу с настраиваемыми заголовками столбцов. Я хочу, чтобы заголовки столбцов включали кнопку, на которую пользователи могут нажимать. Функция кнопки будет заключаться в удалении столбца из таблицы. По сути, я пытаюсь создать что-то вроде это .

Вот мой код:

public class CustomColumnHeadersTable {

    private static String[] columnNames = {
        "Column 1", "Column 2", "Column 3"
    };
    private static String[][] data = {
        {"A", "B", "C"},
        {"D", "E", "F"},
        {"G", "H", "I"}
    };

    public CustomColumnHeadersTable() {
        DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames);
        JTable table = new JTable(model);
        JScrollPane scrollPane = new JScrollPane(table);

        //set Header Renderer of each column to use the Custom renderer
        Enumeration enumeration = table.getColumnModel().getColumns();
        while (enumeration.hasMoreElements()) {
            TableColumn aColumn = (TableColumn) enumeration.nextElement();
            aColumn.setHeaderRenderer(new CustomColumnCellRenderer());
        }

        JFrame frame = new JFrame();
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        frame.setPreferredSize(new Dimension(300, 150));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        CustomColumnHeadersTable ccht = new CustomColumnHeadersTable();
    }
}

class CustomColumnCellRenderer implements TableCellRenderer {

    private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif";
        //using a URL for the icon, so I don't have to upload the icon with the question
    private static Dimension buttonSize = new Dimension(16, 16);
    private static Dimension buttonBoxSize = new Dimension(16, 16);
    private static Border panelBorder = BorderFactory.createRaisedBevelBorder();

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        JPanel panel = new JPanel();
        JLabel label = new JLabel();
        JButton button = new JButton();
        Box buttonBox = Box.createHorizontalBox();
        BorderLayout layout = new BorderLayout();

        label.setText(table.getColumnName(column));

        try { button.setIcon(new ImageIcon(new URL(iconURL))); }
        catch (MalformedURLException ex) {
            Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex);
        }

        //set size of the button and it's box
        button.setMaximumSize(buttonSize);
        button.setSize(buttonSize);
        button.setPreferredSize(buttonSize);
        buttonBox.setMaximumSize(buttonBoxSize);
        buttonBox.setSize(buttonBoxSize);
        buttonBox.setPreferredSize(buttonBoxSize);

        button.addMouseListener(new CustomMouseListener()); //doesn't work...

        buttonBox.add(button);
        panel.add(label, BorderLayout.CENTER);
        panel.add(buttonBox, BorderLayout.EAST);

        panel.setBorder(panelBorder);

        return panel;
    }
}

class CustomMouseListener implements MouseListener
{
    public void mouseClicked(MouseEvent e) { System.out.println("Mouse Clicked."); }
    public void mousePressed(MouseEvent e) { System.out.println("Mouse Pressed."); }
    public void mouseReleased(MouseEvent e) { System.out.println("Mouse Released."); }
    public void mouseEntered(MouseEvent e) { System.out.println("Mouse Entered."); }
    public void mouseExited(MouseEvent e) { System.out.println("Mouse Exited."); }
}

По умолчанию, если я правильно понимаю, JTable использует JLabel для визуализации заголовков столбцов. Моя идея - использовать кастомную реализацию TableCellRenderer, и построить свой собственный заголовок столбца из нескольких компонентов, а именно из JPanel, который содержит JLabel и JButton. Я создаю и возвращаю это в функции getTableCellRendererComponent (...).

Визуально это работает. Проблема в том, что я не могу обнаружить щелчки мыши по кнопке (или, если на то пошло, по панели, которая ее удерживает). Простое добавление MouseListener к кнопке не работает. Событие никогда не достигает его.

Я нашел несколько похожих вещей в Интернете, но они не обеспечивают нужной мне функциональности.

Во-первых, вот пример того, как поместить JCheckBox в заголовок:

http://java-swing-tips.blogspot.com/2009/02/jtableheader-checkbox.html

Проблема в том, что весь заголовок является флажком. Щелчок по флажку или соответствующей метке дает тот же эффект. Таким образом, невозможно отсортировать столбец. Я' Я бы хотел сделать так, чтобы при нажатии на метку столбец сортируется, а при нажатии кнопки закрытия столбец удаляется из таблицы. Другими словами, заголовок должен иметь две отдельные области с отдельными обработчиками событий мыши.

Я нашел здесь другой пример:

http://www.devx.com/getHelpOn/10MinuteSolution/20425/1954?pf=true

Это включает в себя размещение кнопок JButton в ячейках таблицы, а затем обнаружение щелчков мыши по самой таблице, вычисление столбца и строки, в которых произошел щелчок, и отправка события соответствующей кнопке.

С этим тоже есть несколько проблем. Во-первых, кнопки находятся в ячейках, а не в заголовках. Во-вторых, это снова всего лишь один компонент, а не несколько компонентов внутри JPanel. Хотя я получил идею диспетчеризации событий из этого примера, я не могу заставить ее работать для составного компонента.

Я попробовал другой подход. Я рассудил, что если я могу получить координаты кнопок закрытия, то, зная координаты щелчка мышью, я могу вычислить, какая кнопка была нажата, и соответствующим образом отправить событие. Я провел несколько тестов и обнаружил, что компоненты внутри заголовка таблицы на самом деле не расположены на экране.

Я добавил статическую переменную JButton в свой основной (общедоступный) класс и сделал класс, реализующий TableCellRenderer, внутренним классом класса основной класс. В getTableCellRendererComponent (...) перед возвратом я назначаю только что созданный JButton этой статической переменной. Таким образом, я могу справиться с этим, так сказать. Затем, в основном, я попытался использовать getX (), getY (), getWidth (), getHeight () и getLocationOnScreen (), используя эту статическую переменную. X, Y, Ширина и Высота возвращают 0. GetLocationOnScreen () вызывает сбой программы, говоря, что компонент должен присутствовать на экране, чтобы эта функция работала.

Код для этого выглядит примерно так:

    public class CustomColumnHeadersTable {

        private static JButton static_button;
        ///the rest as before....

«класс CustomColumnCellRenderer реализует TableCellRenderer» становится внутренним классом CustomColumnHeadersTable. Для этого мне нужно отказаться от статических переменных в CustomColumnCellRenderer, поэтому я не стал беспокоиться о значках, URL-адресах и т. Д. Вместо кнопки со значком я просто использовал простую кнопку с надписью «BUTTON» ...

Затем внутри getTableCellRendererComponent (...), непосредственно перед оператором return, я сделал

static_button = button;

И, наконец, внутри main () я попытался сделать это:

    System.out.println("X: " + static_button.getX());
    System.out.println("Y: " + static_button.getY());
    System.out.println("W: " + static_button.getWidth());
    System.out.println("H: " + static_button.getHeight());
    System.out.println("LOC: " + static_button.getLocation());
    System.out.println("LOC on screen: " + static_button.getLocationOnScreen());

Результат выглядит следующим образом:

X: 0
Y: 0
W: 0
H: 0
LOC: java.awt.Point[x=0,y=0]
Exception in thread "main" java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1943)
at java.awt.Component.getLocationOnScreen(Component.java:1917)
...

Другими словами, кнопка имеет все размеры 0, и, согласно Java, на самом деле она не расположена на экране (хотя я это видно ...). Вызов getLocationOnScreen () приводит к сбою программы.

Так что, пожалуйста, помогите, если сможете. Может кто знает как это сделать. Может быть, вы могли бы просто предложить какой-нибудь другой подход, чтобы попробовать. Или, может быть, вы знаете, что это вообще невозможно ...

Спасибо за вашу помощь.

8
задан Nate W. 10 February 2011 в 19:51
поделиться