Как сделать JButton кликабельным в JTable - PullRequest
1 голос
/ 22 апреля 2019

У меня есть программа, в которой у меня есть JTable с текстом в большинстве ячеек, однако в последней ячейке в каждой строке мне нужен JButton.Я использую пользовательский рендерер кнопок, а также редактор (я не хочу, чтобы пользователи редактировали содержимое чего-либо в таблице).Я не уверен, как сделать мои кнопки кликабельными, хотя.Мой основной код:

StartingPoint.java

import java.util.Vector;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class StartingPoint {
    public static void main(String[] args) {
        String column_names[] = {"Text", "Button"};
        DefaultTableModel dtm = new DefaultTableModel(column_names, 0);

        JButton button1 = new JButton();
        button1.setText("Button 1");
        button1.setToolTipText("Button");

        JButton button2 = new JButton();
        button2.setText("Button 2");
        button2.setToolTipText("Buttonnn");

        Vector<Object> row1 = new Vector<Object>();
        row1.add("Testing");
        row1.add(button1);

        Vector<Object> row2 = new Vector<Object>();
        row2.add("More Testing");
        row2.add(button2);

        dtm.addRow(row1);
        dtm.addRow(row2);

        JTable table = new JTable(dtm);
        JScrollPane scrolly = new JScrollPane(table);
        table.setFillsViewportHeight(true);

        JTableButtonRenderer buttonRenderer = new JTableButtonRenderer();
        JTableButtonEditor buttonEditor = new JTableButtonEditor();
        table.getColumn("Button").setCellRenderer(buttonRenderer);
        table.getColumn("Button").setCellEditor(buttonEditor);

        JFrame frame = new JFrame("Testing");
        frame.getContentPane().add(scrolly);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

JTableButtonRenderer

import java.awt.Component;

import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class JTableButtonRenderer implements TableCellRenderer {
    JTableButtonRenderer() {}
    @Override
    public Component getTableCellRendererComponent(JTable table, Object 
value, boolean isSelected, boolean hasFocus, int rows, int columns) {
        JButton button = (JButton)value;
        return button;
    }
}

JTableButtonEditor.java

import java.awt.Component;
import java.util.EventObject;

import javax.swing.JTable;
import javax.swing.event.CellEditorListener;
import javax.swing.table.TableCellEditor;

public class JTableButtonEditor implements TableCellEditor {

    @Override
    public void addCellEditorListener(CellEditorListener arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void cancelCellEditing() {
        // TODO Auto-generated method stub

    }

    @Override
    public Object getCellEditorValue() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isCellEditable(EventObject arg0) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void removeCellEditorListener(CellEditorListener arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean shouldSelectCell(EventObject arg0) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean stopCellEditing() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Component getTableCellEditorComponent(JTable arg0, Object arg1, 
boolean arg2, int arg3, int arg4) {
        // TODO Auto-generated method stub
        return null;
    }

}

Нужно ли что-то менять в классах рендерера / редактора?Я также попытался добавить actionlistener в мой класс StartingPoint при создании кнопки.

1 Ответ

1 голос
/ 23 апреля 2019

«Концептуально», идея довольно проста и подробно описана в Как использовать таблицы

Вам нужно начать с определения вашего редактора (и средства визуализации).Я решил обернуть оба вместе для простоты, так как большая часть функциональности повторяется для обоих.

public class TableDeleteButtonEditor extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {

    private File source;
    private JButton button;

    public TableDeleteButtonEditor() {
        button = new JButton();
        button.addActionListener(new LoadActionListener());
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        return true;
    }

    protected JButton prepare(JTable table, Object value, boolean isSelected, int row, int column) {
        if (!(value instanceof File)) {
            source = null;
            button.setEnabled(false);
            return null;
        }
        source = (File) value;
        button.setEnabled(true);
        button.setText(source.getName());
        button.setToolTipText(source.getPath());
        return button;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        return prepare(table, value, isSelected, row, column);
    }

    @Override
    public Object getCellEditorValue() {
        return source;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table,
                                  Object value,
                                  boolean isSelected,
                                  boolean hasFocus,
                                  int row,
                                  int column) {
        return prepare(table, value, isSelected, row, column);
    }

    public class LoadActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent evt) {
            // Here, you need to make some decisions about what to do...
            // You have a reference to the File instance
            System.out.println("You clicked " + source);
            stopCellEditing();
        }

    }
}

Поскольку, вероятно, у вас будет только один активный редактор, когда когда-либо вызывается getTableCellEditorComponentвам нужно получить ссылку на базовые данные (т.е. ссылку File).

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

Для моего примера мне нужна была только ссылка на сам File, поэтому, технически, мне нужен был только один столбец,Вместо этого я разработал модель, в которой требовалось два столбца, но использовал ссылку File для заполнения обоих.Это хороший пример, который демонстрирует способность «простого» объекта быть разложенным на несколько частей и представляться моделью по-другому ...

public class FileTableModel extends AbstractTableModel {

    private List<File> files;

    public FileTableModel(List<File> files) {
        this.files = files;
    }

    @Override
    public int getRowCount() {
        return files.size();
    }

    @Override
    public int getColumnCount() {
        return 2;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex == 1;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        switch (columnIndex) {
            case 1: return File.class;
            default: return String.class;
        }
    }

    @Override
    public Object getValueAt(int row, int col) {
        File file = files.get(row);
        switch (col) {
            case 0: return file.getName();
            case 1: return file;
        }
        return null;
    }

}

Теперь важными частями здесь являютсяisCellEditable и getColumnClass методы.Они помогают определить, какие ячейки можно редактировать, и предоставляют точку входа для поиска средств визуализации / редакторов для JTable, что подводит нас к следующему шагу: вам необходимо настроить JTable для поддержки вашего пользовательского редактора / средства визуализации

Есть несколько способов сделать это, но для простоты setDefaultRenderer и setDefaultEditor должны работать очень хорошо ...

List<File> files = Arrays.asList(new File(".").listFiles());
FileTableModel model = new FileTableModel(files);
JTable table = new JTable(model);
table.setDefaultEditor(File.class, new TableDeleteButtonEditor());
table.setDefaultRenderer(File.class, new TableDeleteButtonEditor());

nb: Вы можете использоватьодин экземпляр TableDeleteButtonEditor, мне просто лень с копией и вставкой

И оттуда вы теперь сможете представлять список File с в JTableгде последний столбец является кнопкой (с именем файла) и при щелчке в этом примере будет напечатан File reference

Runnable Example ...

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;
import java.util.EventObject;
import java.util.List;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());

            List<File> files = Arrays.asList(new File(".").listFiles());
            FileTableModel model = new FileTableModel(files);
            JTable table = new JTable(model);
            table.setDefaultEditor(File.class, new TableDeleteButtonEditor());
            table.setDefaultRenderer(File.class, new TableDeleteButtonEditor());

            add(new JScrollPane(table));
        }

    }

    public class FileTableModel extends AbstractTableModel {

        private List<File> files;

        public FileTableModel(List<File> files) {
            this.files = files;
        }

        @Override
        public int getRowCount() {
            return files.size();
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex == 1;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            switch (columnIndex) {
                case 1:
                    return File.class;
                default:
                    return String.class;
            }
        }

        @Override
        public Object getValueAt(int row, int col) {
            File file = files.get(row);
            switch (col) {
                case 0:
                    return file.getName();
                case 1:
                    return file;
            }
            return null;
        }

    }

    public class TableDeleteButtonEditor extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {

        private File source;
        private JButton button;

        public TableDeleteButtonEditor() {
            button = new JButton();
            button.addActionListener(new LoadActionListener());
        }

        @Override
        public boolean shouldSelectCell(EventObject anEvent) {
            return true;
        }

        protected JButton prepare(JTable table, Object value, boolean isSelected, int row, int column) {
            if (!(value instanceof File)) {
                source = null;
                button.setEnabled(false);
                return null;
            }
            source = (File) value;
            button.setEnabled(true);
            button.setText(source.getName());
            button.setToolTipText(source.getPath());
            return button;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            return prepare(table, value, isSelected, row, column);
        }

        @Override
        public Object getCellEditorValue() {
            return source;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table,
                Object value,
                boolean isSelected,
                boolean hasFocus,
                int row,
                int column) {
            return prepare(table, value, isSelected, row, column);
        }

        public class LoadActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent evt) {
                // Here, you need to make some decisions about what to do...
                // You have a reference to the File instance
                System.out.println("You clicked " + source);
                stopCellEditing();
            }

        }
    }
}
...