簡體   English   中英

可擴展 JTable 單元格編輯器

[英]Expandable JTable Cell Editor

我有一行數據,我可以在其中對遠程服務器進行操作。

我在JTable中顯示這些數據,因為它避免了為每一行創建一個組件的成本,而且有很多。

在此處輸入圖像描述

我已經設法顯示組件並能夠與單元格交互,(但我仍然是第一次點擊)。

在此處輸入圖像描述

但我正在苦苦掙扎的是我希望我的編輯器組件是可擴展的,當然還要更新當前行高。 當然,在折疊時恢復到正常的行高。 我想我需要向單元格編輯器注冊一些聽眾,但我目前無法正確地做到這一點。

  • ComponentListner::componentResized使整個表不斷重繪。
  • editorComponent.addPropertyChangeListener("preferredSize", propertyChangeListener) ,並不總是重新繪制表格
  • editorComponent.addPropertyChangeListener("preferredSize", propertyChangeListener) ,並不總是重新繪制表格

因此這個問題。

在此處輸入圖像描述 在此處輸入圖像描述

有點超出 scope,我目前正在爭論組件是否應該在轉到另一個單元格時保持擴展(但這可以是模型的一部分的expanded屬性)。

這是一個最小的示例,我沒有嘗試在編輯器組件(在getTableCellEditorComponent中)上注冊偵聽器。

在此先感謝您的指點。

package io.github.bric3.fireplace.ui;

import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.awt.*;

public class DynamicCellRow {
    @NotNull
    private static JTable makeTable() {
        var jTable = new JTable(
                new Object[][]{
                        {"a", "charly"},
                        {"b", "tango"}
                },
                new Object[]{"id", "control"}
        );
        {
            var richColumn = jTable.getColumnModel().getColumn(1);
            richColumn.setCellRenderer(new TableCellRenderer() {
                private final ExpandablePanel expandablePanelRenderComponent = new ExpandablePanel();
                @Override
                public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                    expandablePanelRenderComponent.setValue(value);
                    return updatePreferredRowHeight(table, expandablePanelRenderComponent, row, column);
                }
            });

            richColumn.setCellEditor(new DynamicCellEditor());
        }
        return jTable;
    }

    public static <T extends JComponent> T updatePreferredRowHeight(JTable table, T cellComponent, int row, int column) {
        // Adjust cell height per component
        int originalPreferredHeight = cellComponent.getPreferredSize().height;
        cellComponent.setSize(
                table.getColumnModel().getColumn(column).getWidth(),
                originalPreferredHeight
        );
        int newPreferredHeight = cellComponent.getPreferredSize().height;
        if (table.getRowHeight(row) < newPreferredHeight) {
            table.setRowHeight(row, newPreferredHeight);
        }
        return cellComponent;
    }

    static class ExpandablePanel extends JPanel {

        private final JLabel comp;

        ExpandablePanel() {
            setBorder(BorderFactory.createLineBorder(Color.black));
            setLayout(new GridBagLayout());

            GridBagConstraints gbc = new GridBagConstraints();
            // horizontal left-to-right layout
            gbc.gridx = 0;
            gbc.gridy = 0;

            // resizing behavior
            gbc.weightx = 1;
            gbc.weighty = 1;

            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.fill = GridBagConstraints.BOTH;

            JPanel advanced = new JPanel();
            {
                advanced.setLayout(new BoxLayout(advanced, BoxLayout.Y_AXIS));
                advanced.setBorder(new TitledBorder("Advance Settings"));
                advanced.add(new JCheckBox("Live"));
                advanced.add(new JCheckBox("Condition"));
                advanced.add(new JCheckBox("Disable"));
            }
            advanced.setVisible(false);

            var standard = new JPanel();
            {
                standard.setLayout(new BoxLayout(standard, BoxLayout.X_AXIS));
                comp = new JLabel("Label 1");
                standard.add(comp);
                standard.add(new JButton("Button 1"));
                var expandButton = new JButton("+");
                expandButton.addActionListener(e -> {
                    if (advanced.isVisible()) {
                        advanced.setVisible(false);
                        expandButton.setText("+");
                    } else {
                        advanced.setVisible(true);
                        expandButton.setText("-");
                    }
                });
                standard.add(expandButton);
            }
            add(standard, gbc);


            gbc.gridy++;
            gbc.weighty = 0;
            add(advanced, gbc);
        }

        public void setValue(Object value) {
            comp.setText(value.toString());
        }
    }

    private static class DynamicCellEditor extends AbstractCellEditor implements TableCellEditor {
        Object value;
        @Override
        public Object getCellEditorValue() {
            return value; // not changing
        }

        private final ExpandablePanel expandablePanel = new ExpandablePanel();

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            this.value = value;
            expandablePanel.setValue(value);
            return updatePreferredRowHeight(table, expandablePanel, row, column);
        }
    }


    public static void main(String[] args) {

        var contentPane = new JPanel(new BorderLayout());
        contentPane.add(new JScrollPane(makeTable()));

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("DynamicCellRow");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(contentPane);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

@kleopatra 謝謝。 你的提示讓我走上了正確的軌道。 我編寫了以下代碼來在布局操作期間預先計算行高。

當一個單元格被編輯時,編輯器被添加到表中,然后重新驗證,並相應地計算高度。

我之前有一個問題,因為在渲染器中設置高度實際上阻止了單元格組件的正確顯示。

在下面的代碼中,請注意doLayout如何根據單元格組件的首選大小(無論是渲染還是編輯單元格)調整行高。

請注意,我選擇在編輯停止后折疊組件,因此DynamicExpndablePanelCellEditor中的偵聽器。

import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.awt.*;

public class DynamicCellRow {
    public static void main(String[] args) {

        var contentPane = new JPanel(new BorderLayout());
        contentPane.add(new JScrollPane(makeTable()));

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("DynamicCellRow");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(contentPane);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

    @NotNull
    private static JTable makeTable() {
        var jTable = new JTable(
                new Object[][]{
                        {"a", "charly"},
                        {"b", "tango"}
                },
                new Object[]{"id", "control"}
        ) {
            @Override
            public void doLayout() {
                super.doLayout();
                adjustRowHeights();
            }

            private void adjustRowHeights() {
                for (int row = 0; row < getRowCount(); row++) {
                    int rowHeight = getRowHeight();

                    for (int column = 0; column < getColumnCount(); column++) {
                        var editorComponent = getEditorComponent();
                        if (getEditingRow() == row && getEditingColumn() == column && editorComponent != null) {
                            editorComponent.setSize(getColumnModel().getColumn(column).getWidth(), 0);
                            rowHeight = Math.max(rowHeight, editorComponent.getPreferredSize().height);
                        } else {
                            var comp = prepareRenderer(getCellRenderer(row, column), row, column);
                            rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
                        }
                    }

                    setRowHeight(row, rowHeight);
                }
            }
        };
        {
            var richColumn = jTable.getColumnModel().getColumn(1);
            richColumn.setCellRenderer(new ExpandablePanelCellRenderer());
            richColumn.setCellEditor(new DynamicExpndablePanelCellEditor());
        }
        return jTable;
    }


    static class ExpandablePanel extends JPanel {

        private final JLabel comp;
        private final JPanel advanced;

        ExpandablePanel() {
            setBorder(BorderFactory.createLineBorder(Color.black));
            setLayout(new GridBagLayout());

            GridBagConstraints gbc = new GridBagConstraints();
            // horizontal left-to-right layout
            gbc.gridx = 0;
            gbc.gridy = 0;

            // resizing behavior
            gbc.weightx = 1;
            gbc.weighty = 1;

            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.fill = GridBagConstraints.BOTH;

            advanced = new JPanel();
            {
                advanced.setLayout(new BoxLayout(advanced, BoxLayout.Y_AXIS));
                advanced.setBorder(new TitledBorder("Advance Settings"));
                advanced.add(new JCheckBox("Live"));
                advanced.add(new JCheckBox("Condition"));
                advanced.add(new JCheckBox("Disable"));
            }
            advanced.setVisible(false);

            var standard = new JPanel();
            {
                standard.setLayout(new BoxLayout(standard, BoxLayout.X_AXIS));
                comp = new JLabel("Label 1");
                standard.add(comp);
                standard.add(new JButton("Button 1"));
                var expandButton = new JButton("+");
                expandButton.addActionListener(e -> {
                    if (advanced.isVisible()) {
                        advanced.setVisible(false);
                        expandButton.setText("+");
                    } else {
                        advanced.setVisible(true);
                        expandButton.setText("-");
                    }
                });
                standard.add(expandButton);
            }
            add(standard, gbc);


            gbc.gridy++;
            gbc.weighty = 0;
            add(advanced, gbc);
        }

        public void setValue(Object value) {
            comp.setText(value.toString());
        }

        public void setAdvancedVisibility(boolean visible) {
            advanced.setVisible(visible);
        }
    }

    private static class DynamicExpndablePanelCellEditor extends AbstractCellEditor implements TableCellEditor {
        Object value;
        @Override
        public Object getCellEditorValue() {
            return value; // not changing
        }

        private final ExpandablePanel expandablePanel = new ExpandablePanel();
        {
            addCellEditorListener(new CellEditorListener() {
                @Override
                public void editingStopped(ChangeEvent e) {
                    expandablePanel.setAdvancedVisibility(false);
                }

                @Override
                public void editingCanceled(ChangeEvent e) {
                    expandablePanel.setAdvancedVisibility(false);
                }
            });
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            this.value = value;
            expandablePanel.setValue(value);

            return expandablePanel;
        }
    }

    private static class ExpandablePanelCellRenderer implements TableCellRenderer {
        private final ExpandablePanel expandablePanelRenderComponent = new ExpandablePanel();

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

暫無
暫無

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

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