Каково правильное разделение обязанностей между TableCellRenderer и TableModel при использовании JTables?

Я работаю над частью приложения, которое отображает таблицы со статистическими данными о видеофайлах, представленных классом FrameInfo. Теперь, после того, как у меня изначально была модель таблицы, которая делала бы все, включая некоторое форматирование, я реорганизовал ее до другой крайности, и модель таблицы возвращала только экземпляр FrameInfo для каждой строки, а затем позволяла CellRenderer решать, какое поле отображать и как для каждой. столбец. Это было здорово, так как я мог делать приятные вещи, например, переключать отображение, например. значения тайм-кода между тиками, секундами или тайм-кодами ("00:01:02:03") только путем перерисовки. Я был счастлив, пока не скопировал и не вставил содержимое таблицы в электронную таблицу gdocs и не заметил, что получил только вывод toString() объектов моей модели во всех ячейках (что было логично, когда я начал думать об этом, но, очевидно, не то, что я хочу) .

Мои варианты, насколько я их вижу сейчас:

1) Поместите все обратно в модель

Плюсы: мне бы в буфере обмена было все так, как отображается, когда я копирую

Минусы: - означает запуск событий модели при переключении режима отображения тайм-кодов - написание маркеров (кстати, я использую JXTables) снова станет беспорядочным, так как мне придется выполнять сопоставление строк, где теперь я могу использовать свои объекты модели

2) Оставьте все как есть и создайте пользовательское действие копирования, которое использует средство визуализации, а затем извлекает текст из отображаемой метки.

Плюсы: - Код таблицы остается чистым

Минусы: - Объем работы (?) - Для таких вещей, как округление числа, я потерял бы точность

3) Поместите в модель все, кроме динамических материалов (тайм-коды), и сделайте тайм-коды в средстве визуализации, и смиритесь с тем фактом, что я не получаю WYSIWYG для копирования и вставки для этих столбцов.

Плюсы и минусы: - Более или менее половинчатый компромисс

Любой совет или, может быть, даже какой-нибудь код выхода, который я мог бы использовать, кто-нибудь?

Спасибо за ваше время!


person user1573546    schedule 21.07.2013    source источник
comment
для JXTable путь состоит в том, чтобы позволить пользовательскому transferHandler использовать xTable.getStringValueAt, чтобы получить точно такое же строковое представление, которое используется для рендеринга (и поиска, и фильтрации и сортировки регулярных выражений, и... :-)   -  person kleopatra    schedule 21.07.2013
comment
@kleopatra: Пожалуйста, подумайте над тем, чтобы сделать это ответом.   -  person trashgod    schedule 21.07.2013


Ответы (2)


Расширение ответа @trashgod: вариант 1 совершенно неверен :-) TableModel должен содержать данные и ничего больше. Представление данных в таблице (фактически, в любом из представлений коллекции Swing) является исключительной задачей рендерера. И работа TransferHandler заключается в том, чтобы экспортировать данные в разумной форме, желательно с тем же строковым представлением, что и средство визуализации.

JXTable упрощает совместное использование строкового представления между соавторами: небольшая монета для создания текстового контента называется StringValue, с которой настроены все внутренние средства визуализации. После настройки эта строка используется во всех расширенных функциях, связанных со строками, таких как поиск, сортировка, фильтрация на основе регулярных выражений и API таблицы:

String text = table.getStringAt(row, column);

Что позволяет пользовательскому TransferHandler основывать построение строк на:

/**
 * A TableTransferable that uses JXTable string api to build
 * the exported data.
 * 
 * C&p from BasicTableUI, replaced toString with 
 * table.getStringAt(row, col)
 */
public static class XTableTransferHandler extends TransferHandler {

    /**
     * Create a Transferable to use as the source for a data transfer.
     * 
     * @param c The component holding the data to be transfered. This
     *        argument is provided to enable sharing of TransferHandlers by
     *        multiple components.
     * @return The representation of the data to be transfered.
     * 
     */
    @Override
    protected Transferable createTransferable(JComponent c) {
        if (!(c instanceof JXTable))
            return null;
        JXTable table = (JXTable) c;
        int[] rows;
        int[] cols;

        if (!table.getRowSelectionAllowed()
                && !table.getColumnSelectionAllowed()) {
            return null;
        }

        if (!table.getRowSelectionAllowed()) {
            int rowCount = table.getRowCount();

            rows = new int[rowCount];
            for (int counter = 0; counter < rowCount; counter++) {
                rows[counter] = counter;
            }
        } else {
            rows = table.getSelectedRows();
        }

        if (!table.getColumnSelectionAllowed()) {
            int colCount = table.getColumnCount();

            cols = new int[colCount];
            for (int counter = 0; counter < colCount; counter++) {
                cols[counter] = counter;
            }
        } else {
            cols = table.getSelectedColumns();
        }

        if (rows == null || cols == null || rows.length == 0
                || cols.length == 0) {
            return null;
        }

        StringBuffer plainBuf = new StringBuffer();
        StringBuffer htmlBuf = new StringBuffer();

        htmlBuf.append("<html>\n<body>\n<table>\n");

        for (int row = 0; row < rows.length; row++) {
            htmlBuf.append("<tr>\n");
            for (int col = 0; col < cols.length; col++) {
                // original:
                // Object obj = table.getValueAt(rows[row], cols[col]);
                // String val = ((obj == null) ? "" : obj.toString());
                // replaced by JXTable api:
                String val = table.getStringAt(row, col);
                plainBuf.append(val + "\t");
                htmlBuf.append("  <td>" + val + "</td>\n");
            }
            // we want a newline at the end of each line and not a tab
            plainBuf.deleteCharAt(plainBuf.length() - 1).append("\n");
            htmlBuf.append("</tr>\n");
        }

        // remove the last newline
        plainBuf.deleteCharAt(plainBuf.length() - 1);
        htmlBuf.append("</table>\n</body>\n</html>");

        return new BasicTransferable(plainBuf.toString(),
                htmlBuf.toString());
    }

    @Override
    public int getSourceActions(JComponent c) {
        return COPY;
    }

}

Пример использования:

DefaultTableModel model = new DefaultTableModel(
        new String[]{"Action"}, 0);
JXTable table = new JXTable(model);
Object[] keys = table.getActionMap().allKeys();
for (Object key : keys) {
    model.addRow(new Object[]{table.getActionMap().get(key)});
}
StringValue sv = new StringValue() {

    @Override
    public String getString(Object value) {
        if (value instanceof Action) {
            return (String) ((Action) value).getValue(Action.NAME);
        }
        return StringValues.TO_STRING.getString(value);
    }

};
table.getColumn(0).setCellRenderer(new DefaultTableRenderer(sv));
table.setDragEnabled(true);
table.setTransferHandler(new XTableTransferHandler());
person kleopatra    schedule 22.07.2013
comment
Спасибо, это именно то, что я искал! - person user1573546; 22.07.2013

Ваш TableModel должен содержать данные и выбранный рендерер должен форматировать содержимое ячейки. Как показано здесь, вы можете применить желаемую визуализацию для конкретного домена в своем пользовательском файле Transferable. Используйте java.text.MessageFormat, чтобы ваши TableCellRenderer и Transferable применяли одинаковое форматирование к данным, полученным из модели.

person trashgod    schedule 21.07.2013
comment
+1 - но это SwingX, где вы могли бы использовать API JXTable для передачи (например, getStringValueAt (...)) - person kleopatra; 21.07.2013
comment
Спасибо, что нашли время, трэшбог! Вы абсолютно правы, хотя предложение kleopatra, относящееся к свингу, немного лучше подходит для моей ситуации. - person user1573546; 22.07.2013