簡體   English   中英

僅將JButton添加到JTable中的某些行和列

[英]Add JButtons only to certain rows and columns in a JTable

我想將一些JButton添加到JTable,但僅添加到特定的單元格。 到目前為止,我已經編寫了一個類,可以將Buttons僅添加到一行。 我找到了很多有關如何將Button添加到整個列的教程,但是我不知道如何僅將按鈕添加到某些行。

這就是我希望表格看起來像的樣子:

這就是我想要的樣子

理想的解決方案是,如果我可以僅基於tabledata的值添加Buttons。 例如,如果數據包含空字符串,則不顯示按鈕,並且它具有正確的值,則字符串將是按鈕的文本。

源代碼

類ConnectionPanel

public class ConnectionPanel extends JFrame{

public ConnectionPanel(){

    Object[][] licData = {{"License 1", "0.0.0.0", "connect", "disconnect", ""},{"License 2", "123.123.123", "", "", ""},{"License 3", "42.23.4", "connect", "disconnect", "delete"}};

    ConnTableModel licConnModel = new ConnTableModel(licData);

    this.setLayout(new MigLayout("", "[grow]", "[][grow][][][][][][][grow][][][][][]"));
    this.setSize(new Dimension(500, 300));
    JLabel lblLicenses = new JLabel("Licenses");
    this.add(lblLicenses, "cell 0 0,growx");

    JTable licenseTable = new JTable(licConnModel);
    licenseTable.setTableHeader(null);

    new ButtonColumn(licenseTable, 2, 0);
    new ButtonColumn(licenseTable, 3, 0);
    new ButtonColumn(licenseTable, 2, 2);
    new ButtonColumn(licenseTable, 3, 2);
    new ButtonColumn(licenseTable, 4, 2);

    JScrollPane scrollPaneLic = new JScrollPane();
    scrollPaneLic.setViewportView(licenseTable);
    this.add(scrollPaneLic, "cell 0 1 1 6,grow");
}

內部靜態類ConnTableModel

public static class ConnTableModel extends AbstractTableModel {
    Object[][] data;

    public ConnTableModel(Object[][] data){
        this.data = data;
    }

    @Override
    public int getRowCount() {
        return data.length;
    }

    @Override
    public int getColumnCount() {
        return data[0].length;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        return data[rowIndex][columnIndex];
    }

    public boolean isCellEditable(int rowIndex, int columnIndex) { 
        if(columnIndex == 2 || columnIndex == 3 || columnIndex == 4) {
            return true;
        } else {
            return false;
        }
    }   
}

內部類ButtonColumn

class ButtonColumn extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor, ActionListener
{
    JTable table;
    JButton editButton;
    JButton renderButton;
    String text;
    int showRow;

    public ButtonColumn(JTable table, int column, int showRow) {
        super();
        this.table = table;
        this.showRow = showRow;
        renderButton = new JButton();

        editButton = new JButton();
        editButton.setFocusPainted( false );
        editButton.addActionListener( this );

        TableColumnModel columnModel = table.getColumnModel();
        columnModel.getColumn(column).setCellRenderer( this );
        columnModel.getColumn(column).setCellEditor( this );
    }

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

    @Override
    public void actionPerformed(ActionEvent e) {
        fireEditingStopped();

        if(text.equals("connect")){
            System.out.println("conn");
        }else if(text.equals("disconnect")){
            System.out.println("disc");
        }
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean selected, int row,
            int column) {
        if(row == showRow){
         text = (value == null) ? "" : value.toString();
            editButton.setText( text );
            return editButton;
        }else{
            return null;
        }
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean hasFocus,
            int row, int column) {
        if (hasFocus) {
            renderButton.setForeground(table.getForeground());
            renderButton.setBackground(UIManager.getColor("Button.background"));
        } else if (selected) {
            renderButton.setForeground(table.getSelectionForeground());
             renderButton.setBackground(table.getSelectionBackground());
        } else {
            renderButton.setForeground(table.getForeground());
            renderButton.setBackground(UIManager.getColor("Button.background"));
        }

        renderButton.setText((value == null) ? "" : value.toString());
        if(row == showRow) {
            return renderButton;
        } else {
            return null;
        }
    }

}

主要方法

    public static void main(String[] args) {
        ConnectionPanel con = new ConnectionPanel();
        con.setVisible(true);
    }
}

當我使用“ new ButtonColumn(myTable,column,row)”創建新的Button時,如果我對多個行進行了此操作,它將僅顯示我在表中為每一列創建的最后一個Button。 我真的不知道為什么會這樣。 我猜“ ButtonColumn”類有問題嗎? 有沒有辦法只為某些行創建Button,還是直接從TableModel創建它們?

您以完全錯誤的方式完成了此操作。 無論如何,引起您提到的問題的根本原因是:創建ButtonColumn實例時,它將是該單元格的單元格渲染器和編輯器。 當你執行時

 new ButtonColumn(licenseTable, 2, 2);
 new ButtonColumn(licenseTable, 2, 3);

現在第2列單元格渲染器為ButtonColumn(licenseTable, 2, 3) ,它將為除3之外的任何行返回null,因此您將僅在第2列的第3行中看到按鈕。

其他問題:

  1. 不要為編輯器和渲染器使用相同的實例,這可能會導致一些繪畫問題

  2. 單元格渲染器旨在提供基於行,列,對象的組件。 因此渲染器可以評估這些值,然后可以決定是返回按鈕還是返回空標簽。

這是一個簡單的mcve

public class ButtonTableTest {

    public static void main(String[] args) {
        final Random random = new Random();
        DefaultTableModel tableModel = new DefaultTableModel(20, 7) {
            @Override
            public Class<?> getColumnClass(int arg0) {
              // provide the default renderer and editor of String for empty cells
                return String.class; 
            }

            @Override
            public boolean isCellEditable(int row, int column) {
               // do not request the editor for empty cells
                return !"".equals(getValueAt(row, column));
            }

            @Override
            public Object getValueAt(int row, int column) {
                // some random table content
                if (null == super.getValueAt(row, column)) {
                    int nextInt = random.nextInt(10);
                    if (nextInt > 5)
                        super.setValueAt(String.format("cell %dx%d", row, column), row, column);
                    else
                        super.setValueAt("", row, column);
                }
                return super.getValueAt(row, column);
            }

            @Override
            public void setValueAt(Object arg0, int arg1, int arg2) {
                // prevent update to NULL
            }

        };

        JTable jTable = new JTable(tableModel);
        jTable.setPreferredSize(new Dimension(800, 350));
        final JButton jButton = new JButton();

        jTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                    boolean hasFocus, int row, int column) {
                Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected,
                        hasFocus, row, column);
                if ("".equals(value)) {
                    // default renderer for empty cells
                    return tableCellRendererComponent;
                } else {
                    jButton.setAction(createSameActionForEditorAndRenderer(table, value));
                    return jButton;
                }
            }
        });
        jTable.setDefaultEditor(String.class, new DefaultCellEditor(new JCheckBox()) { // JCheckBox is closest to a button...

            @Override
            public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
                    int column) {
                Component tableCellEditorComponent = super.getTableCellEditorComponent(table, value, isSelected, row,
                        column);
                jButton.setAction(createSameActionForEditorAndRenderer(jTable, value));
                return jButton;
            }

        });
        JOptionPane.showMessageDialog(null, jTable);
    }

    private static AbstractAction createSameActionForEditorAndRenderer(JTable table, Object value) {
        return new AbstractAction((String) value) {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                SwingUtilities.invokeLater(() -> {
                    JOptionPane.showMessageDialog(table, String.format("clicked on %s",value));
                });
                table.getCellEditor().stopCellEditing();
                table.repaint();
            }
        };
    }
}

當我單擊一個單元格時,它經常顯示錯誤的值。 有時,它是同一行或先前單擊的單元格中另一個單元格的值。 我不知道為什么會這樣嗎?

這是因為我為渲染器和編輯器使用了相同的JButton實例。

這是修復的更改:

        //final JButton jButton = new JButton();

        jTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
           private final JButton jButton = new JButton();
        // rest of renderer

        jTable.setDefaultEditor(String.class, new DefaultCellEditor(new JCheckBox()) { // JCheckBox is closest to a button...
           private final JButton jButton = new JButton();
        // rest of editor

你打電話時

TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer(this);
columnModel.getColumn(column).setCellEditor(this);

ButtonColumn的構造函數中,為該列設置CellRendererCellEditor 最后一次調用將贏得並覆蓋該列的CellRendererCellEditor舊設置。

問題不是五分鍾的問題。 我將創建一個容器,該容器可以容納ButtonColumn ,然后可以決定要渲染/編輯哪個ButtonColumn ,但這非常棘手。 在下面可以找到一種快速解決渲染問題的快速嘗試:

新的內部類RowColumn

用於將行和列組合以將其與Map關聯的類。

static class RowColumn {
    final int row;
    final int column;

    RowColumn(int theColumn, int theRow) {
        this.column = theColumn;
        this.row = theRow;
    }

    public int getRow() {
        return row;
    }

    public int getColumn() {
        return column;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + column;
        result = prime * result + row;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        RowColumn other = (RowColumn) obj;
        if (column != other.column)
            return false;
        if (row != other.row)
            return false;
        return true;
    }


}

新的內部類ButtonColumnContainer

容納多個ButtonColumn的容器。

static class ButtonColumnContainer implements TableCellRenderer {
    Map<RowColumn, ButtonColumn> mapIntToColumn = new HashMap<>();

    ButtonColumn createButtonColumn(JTable table, int column, int row) {

        RowColumn rc = new RowColumn(column, row);
        ButtonColumn buttonColumn = new ButtonColumn(column, row);
        mapIntToColumn.put(rc, buttonColumn);

        TableColumnModel columnModel = table.getColumnModel();
        columnModel.getColumn(column).setCellRenderer(this);
        // columnModel.getColumn(column).setCellEditor(this);

        return buttonColumn;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean hasFocus,
            int row, int column) {
        ButtonColumn buttonColumn = getButtonColumn(column, row);

        if (buttonColumn != null) {
            JButton renderButton = buttonColumn.getRenderButton();
            if (hasFocus) {
                renderButton.setForeground(table.getForeground());
                renderButton.setBackground(UIManager.getColor("Button.background"));
            } else if (selected) {
                renderButton.setForeground(table.getSelectionForeground());
                renderButton.setBackground(table.getSelectionBackground());
            } else {
                renderButton.setForeground(table.getForeground());
                renderButton.setBackground(UIManager.getColor("Button.background"));
            }

            renderButton.setText((value == null) ? "" : value.toString());
            return renderButton;
        } else {
            return null;
        }
    }

    public Component getTableCellEditorComponent(JTable table, Object value, boolean selected, int row,
            int column) {
        ButtonColumn buttonColumn = getButtonColumn(column, row);

        if (buttonColumn != null) {
            JButton editButton = buttonColumn.getEditButton();
            String text = (value == null) ? "" : value.toString();
            editButton.setText(text);
            return editButton;
        } else {
            return null;
        }

    }

    private ButtonColumn getButtonColumn(int column, int row) {
        RowColumn rowColumn = new RowColumn(column, row);
        return mapIntToColumn.get(rowColumn);
    }

}

改編的內部類ButtonColumn

原始類已添加了JButtoneditButtonrenderButton的getter和setter方法。

static class ButtonColumn extends AbstractCellEditor implements ActionListener {

    final JButton editButton;
    final JButton renderButton;
    String text;
    int showRow;

    public ButtonColumn(int column, int showRow) {
        super();
        this.showRow = showRow;
        renderButton = new JButton();

        editButton = new JButton();
        editButton.setFocusPainted(false);
        editButton.addActionListener(this);

    }

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

    @Override
    public void actionPerformed(ActionEvent e) {
        fireEditingStopped();

        if (text.equals("connect")) {
            System.out.println("conn");
        } else if (text.equals("disconnect")) {
            System.out.println("disc");
        }
    }

    public JButton getEditButton() {
        return editButton;
    }

    public JButton getRenderButton() {
        return renderButton;
    }



}

而不是最初對ButtonColumn構造函數的調用

ButtonColumnContainer container = new ButtonColumnContainer();
container.createButtonColumn(licenseTable, 2, 0);
container.createButtonColumn(licenseTable, 3, 0);
container.createButtonColumn(licenseTable, 2, 2);
container.createButtonColumn(licenseTable, 3, 2);
container.createButtonColumn(licenseTable, 4, 2);

請注意,此代碼沒有編輯任何內容的功能,但至少應解決渲染問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM