简体   繁体   English

如何使 JButton 在 JTable 中可点击

[英]How to make JButton clickable in JTable

I have a program where I have a JTable with text in most cells, however the last cell in each row I need to have a JButton.我有一个程序,其中大多数单元格中有一个带有文本的 JTable,但是每行中的最后一个单元格我需要有一个 JButton。 I am using a custom button renderer as well as editor ( i don't want users editing the content of anything in the table).我正在使用自定义按钮渲染器和编辑器(我不希望用户编辑表中任何内容的内容)。 I'm unsure as how to get my buttons clickable though.我不确定如何让我的按钮可点击。 My main code is:我的主要代码是:

StartingPoint.java起点.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 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 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;
    }

}

Do I need to change something in the renderer / editor classes?我需要更改渲染器/编辑器类中的某些内容吗? I also tried adding actionlistener in my StartingPoint class when I create the button.我还尝试在创建按钮时在我的开始点类中添加动作侦听器。

"Conceptually", the idea is pretty simple and is covered in some detail in How to Use Tables “概念上”,这个想法非常简单,在如何使用表格中有详细介绍

You need to start by defining your editor (and renderer).您需要从定义编辑器(和渲染器)开始。 I've chosen to wrap both together for simplicity, as much of the functionality is repeated for both.为简单起见,我选择将两者包装在一起,因为两者都重复了大部分功能。

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

    }
}

Since it's likely that you'll only have one active editor, when ever getTableCellEditorComponent is called, you need to grab a reference to the underlying data (ie the File reference).由于您可能只有一个活动编辑器,因此在调用getTableCellEditorComponent时,您需要获取对基础数据的引用(即File引用)。

Typically, a editor will return a value back to the model, in this case, I'm not sure that makes sense.通常,编辑器会将一个值返回给模型,在这种情况下,我不确定这是否有意义。 Not saying you can't do it, but I'd be questioning the purpose.不是说你做不到,但我会质疑目的。

For my example, I only needed the reference to the File itself, so, technically, I only needed a single column.对于我的示例,我只需要对File本身的引用,因此,从技术上讲,我只需要一列。 Instead, I devised a model which required two columns, but used the reference of the File to populate both.相反,我设计了一个需要两列的模型,但使用File的引用来填充这两列。 This is a neat example which demonstrates the ability for a "simple" object to be expanded into multiple parts and represented by the model in a different way...这是一个简洁的示例,它演示了将“简单”对象扩展为多个部分并以不同方式由模型表示的能力......

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

}

Now, the important parts here are the isCellEditable and getColumnClass methods.现在,这里的重要部分是isCellEditablegetColumnClass方法。 These help determine which cells can be edited and provide a entry point for looking up renderers/editors for the JTable , which brings us to the next step, you need to configure the JTable to support your custom editor/renderer这些有助于确定可以编辑哪些单元格并提供一个入口点来查找JTable渲染器/编辑器,这将我们带到下一步,您需要配置JTable以支持您的自定义编辑器/渲染器

There's a few ways that you can do this, but for simplicity, setDefaultRenderer and setDefaultEditor should work just fine...有几种方法可以做到这一点,但为了简单起见, 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: You can use a single instance of TableDeleteButtonEditor , I'm just been lazy with my copy and pasting注意:您可以使用TableDeleteButtonEditor的单个实例,我只是懒得复制和粘贴

And from there, you should now be able to represent a list of File s in a JTable , where the last column is a button (with the file name) and when clicked, in this example, will print the File reference从那里,您现在应该能够在JTable表示File的列表,其中最后一列是一个按钮(带有文件名),在此示例中,单击时将打印File引用

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

        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM