使用自定义单元格编辑器显示备用 toString() 方法

Display alternate toString() Method with custom cell editor

我正在创建基本上是 excel 的副本。用户将数据输入到 JTable 中,然后该数据被解析、处理,并显示相应类型的 Cell 的 toString() 方法,我已经为该接口创建了多个 subclasses。

当用户开始编辑 FormulaCell 时,我希望单元格显示通过 getFormula() 方法检索到的公式,而不是通过 toString() 方法检索到的评估公式。

我探索了自定义单元格编辑器,但我无法弄清楚要覆盖哪种方法。我认为代码在自定义单元格编辑器或我需要创建的任何内容中都是这样的:

if (cell instanceof FormulaCell) {
  startingText = (FormulaCell) cell).getFormula();
}

JTable 中的每个单元格都有不同的 class,因此我无法真正覆盖 DefaultTableModel 的 getColumnClass() 方法并为公式单元格设置自定义单元格编辑器 class。

作为参考,这是我程序的主要部分:

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.table.*;

import cell.*;

public class Program extends JPanel {

    private DefaultTableModel model;
    private JTable table;

    public static final int ASCII_SHIFT = 64, HEIGHT = 10, WIDTH = 7, ROW_HEIGHT = 40;
    public static final Dimension BUTTON_SIZE = new Dimension(70,30),
            TABLE_SIZE = new Dimension(780, 400);

    //makes program runnable
    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI() {

        //sets title and does everything necessary to
        //create and show the GUI
        JFrame frame = new JFrame("TextExcel");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Program contentPane = new Program();
        contentPane.setOpaque(true);
        frame.setContentPane(contentPane);
        frame.setResizable(false);
        frame.pack();
        frame.setVisible(true);
    }

    public Program() {

        //sets the layout
        super(new BorderLayout());

        //creates a String[] for column headers
        String[] letter = new String[WIDTH];
        for (int i = 0; i < letter.length; i++) {
            byte[] character = {(byte) (i + ASCII_SHIFT + 1)};
            letter[i] = new String(character);
        }

        //creates the table
        model = new DefaultTableModel(letter, HEIGHT);
        table = new JTable(model);

        //makes a cell parse the input when enter is pressed
        Action action = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                TableCellListener tcl = (TableCellListener)e.getSource();
                int row = tcl.getRow();
                int column = tcl.getColumn();
                String input = (String) model.getValueAt(row, column);
                parse(input, row, column);
            }
        };
        @SuppressWarnings("unused")
        TableCellListener tcl = new TableCellListener(table, action);

        //centers the headers
        JTableHeader header = table.getTableHeader();
        header.setDefaultRenderer(new HeaderRenderer(table));
        header.setReorderingAllowed(false);

        //centers text in cells
        DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
        centerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
        table.setDefaultRenderer(Object.class, centerRenderer);

        //sets the height of rows
        for (int i = 0; i < HEIGHT; i++) {
            table.setRowHeight(i, ROW_HEIGHT);
        }

        //creates a scroll-pane for the table and numbers the rows
        JScrollPane scrollPane = new JScrollPane(table);
        JTable rowTable = new RowNumberTable(table);
        scrollPane.setRowHeaderView(rowTable);
        scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader());
        add(scrollPane);

        //sizes the table
        table.setFillsViewportHeight(true);
        table.setPreferredScrollableViewportSize(TABLE_SIZE);

        //creates a panel to place buttons
        JPanel buttonPanel = new JPanel(new FlowLayout());

        //creates a button to clear the table
        JButton clearButton = new JButton("clear");
        clearButton.setPreferredSize(BUTTON_SIZE);
        clearButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e) 
            {
                clearTable();
            }
        });
        buttonPanel.add(clearButton);

        //message displayed when help button is pressed
        String helpMessage = "To construct a Cell, double click a cell, "
                + "enter the argument, and press enter.\n"
                + "To clear a Cell, press the \"clear\" button.\n\n"
                + "There are 5 subclasses of Cell:\n"
                + "StringCell, DateCell, NumberCell, "
                + "FormulaCell, and SumAvgCell.\n"
                + "All numbers are displayed as Fractions and all Cells displaying numbers extend\n"
                + "NumberCell. Any double entered will be converted to a Fraction.\n\n"
                + "* StringCells display text and are simply typed into the cell. If an input is\n"
                + "invalid for all other types of Cell, it will become a StringCell.\n"
                + "* DateCells display dates in the form (Month Day, Year). They are constructed\n"
                + "in the form (m/d/yy) or (m/d/yyyy). Extra zeroes in m and d are not necessary.\n"
                + "* NumberCells simply display Fractions and can be constructed from a double,\n"
                + "int, standard fraction, or mixed fraction.\n"
                + "* FormulaCells are constructed from any combination of operations(+-*/%),\n"
                + "values, parantheses, and references. Order of operations is supported. An\n"
                + "example of a valid input would be \"(A1 + 4.4 * b3) % C2 - 3_6/8\".\n"
                + "* SumAvgCells can be used to find the sum or average of a rectangular area of\n"
                + "cells. They are constructed in the form (command reference - reference).\n"
                + "The first reference is the top-left corner and the second reference is the\n"
                + "bottom-right corner. An example of a valid input would be \"sum A1 - B2\".\n"
                + "Another valid input would be \"avg C1 - C8\".";

        //creates a help button to display a helpful message
        JButton helpButton = new JButton("help");
        helpButton.setPreferredSize(BUTTON_SIZE);
        helpButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e) 
            {
                JOptionPane.showMessageDialog(getComponent(0), helpMessage, "HelpBox", 1);
            }
        });
        buttonPanel.add(helpButton);

        //creates a panel to place the table
        JPanel tablePanel = new JPanel(new BorderLayout());
        tablePanel.add(scrollPane, BorderLayout.CENTER);

        //adds the button and table panels
        add(tablePanel, BorderLayout.NORTH);
        add(buttonPanel, BorderLayout.SOUTH);
    }

    //parses user input to set a cell value
    public void parse(String input, int row, int column) {

        //initializes cell to be set
        Cell cell = null;

        //helpful variables
        String ref = "([a-g]\d*)";
        String dub = "([-+]?\d*\.?\d*)";
        String frac = "((\d+_\d+/\d+)|(\d+/\d+))";

        //parses the input according to regular expressions
        input = input.toLowerCase();
        if (input.matches("(" + ref + "|" + dub + "|" + frac + ")"
                + "( [-+*/%] (" + ref + "|" + dub + "|" + frac + "))+")) {
            cell = new FormulaCell(input, model);
        }
        else if (input.matches("((sum)|(avg)) " + ref + " - " + ref)) {
            cell = new SumAvgCell(input, model);
        }
        else if (input.matches(dub + "|" + frac)) {
            cell = new NumberCell(input);
        }
        else if (input.matches("(\d{1}|\d{2})/(\d{1}|\d{2})/(\d{2}|\d{4})")) {
            cell = new DateCell(input);
        }
        else {
            cell = new StringCell(input);
        }

        //sets the cell value
        model.setValueAt(cell, row, column);
    }

    //sets all cell values to null
    public void clearTable() {
        for (int i = 0; i < HEIGHT; i++) {
            for(int j = 0; j < WIDTH; j++) {
                model.setValueAt(null, i, j);
            }
        }
    }
}

任何关于如何创建所需行为的指导将不胜感激。

定义一个方法,例如getDetailedText(),在Cell界面中returns 将在单元格编辑器中使用的文本。然后 return 来自此方法的公式 FormulaCell。对于其他单元格,您可以 return toString().

单元格界面:

public interface Cell {

     public String getDetailedText();
}

公式单元格:

public class FormulaCell implements Cell {

    public String getDetailedText() {
         return formula;
    }
}

其他单元格(如果您想为每个单元格显示另一个文本,只需 return 此处):

public class OtherCell implements Cell {

    public String getDetailedText() {
         return toString();
    }
}

然后创建一个单元格编辑器并将其设置为 table 的默认编辑器。

默认单元格编辑器:

public class MyDefaultCellEditor extends DefaultCellEditor {
    public MyDefaultCellEditor() {
        super(new JTextField());
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
            int column) {
        JTextField textField = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);
        Cell cell = (Cell) value;

        textField.setText(cell.getEditorText());
        return textField;
    }
} 

设为table:

table.setDefaultEditor(Object.class, new MyDefaultCellEditor());