如何在JTable中使JButton可单击

时间:2019-04-22 20:55:54

标签: java swing jtable jbutton

我有一个程序,其中有一个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;
    }

}

我需要更改渲染器/编辑器类中的某些内容吗?创建按钮时,我也尝试在我的StartingPoint类中添加actionlistener。

1 个答案:

答案 0 :(得分:1)

“从概念上讲”,这个想法很简单,并且在How to Use Tables

中有详细介绍。

您需要先定义编辑器(和渲染器)。为了简单起见,我选择将两者包装在一起,因为两者都重复了许多功能。

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;
    }

}

现在,这里的重要部分是isCellEditablegetColumnClass方法。这些帮助确定可以编辑哪些单元格,并为查找JTable的渲染器/编辑器提供一个入口,这使我们进入下一步,您需要配置JTable以支持自定义编辑器/ renderer

您可以通过多种方式来执行此操作,但为简单起见,setDefaultRenderersetDefaultEditor应该可以正常工作...

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参考

可运行示例...

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();
            }

        }
    }
}