Применить TableCellRenderer к отдельной ячейке

Я пытаюсь иметь возможность раскрашивать отдельные ячейки в JTable, но до сих пор мне удалось применить TableCellRenderer ко всему столбцу, что, очевидно, дает сбой. У меня есть собственный JTable:

public class JColorTable extends JTable{
  (...)
  public void setCellColor(int col, int row, Color newColor) {
    getColumnModel().getColumn(col).setCellRenderer(new ColorField(col, row, newColor, background));
    repaint();
  }
}

ColorField выглядит так:

class ColorField extends DefaultTableCellRenderer {

    (...))

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {

        JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        if (row == newRow && column == newCol) {
            l.setBackground(Color.red);
        } else {
            l.setBackground(defaultColor);
        }

        return l;
    }
}

Это работает как шарм, когда у меня есть одна цветная ячейка в столбце, но когда я пытаюсь раскрасить другую ячейку в этом столбце, предыдущая удаляется (из-за того, что условие в ColorField не применяется к предыдущему столбцу).

Есть ли способ применить ColorField только к одной ячейке, а не ко всему столбцу? Если да, то как? Боюсь, ничего подходящего не нашел.


person Martin Melka    schedule 06.05.2013    source источник


Ответы (4)


JTable имеет метод getCellRenderer(), который вы можете переопределить. Он вызывается, когда ячейке требуется отрисовка, и возвращает ее на основе строки и столбца.

Ваш JTable должен будет вести некоторую запись о том, какое средство визуализации использовать для каждой ячейки (по строке и столбцу). 2D-массив сделает это или Map с ключом, являющимся значением X, Y.

Добавьте метод, чтобы установить средство визуализации для определенной ячейки (по строке и столбцу), и готово.

class MyTable extends JTable {

    // all the other JTable stuff goes here too ...

    public TableCellRenderer getCellRenderer(int row, int column) {
        TableCellRenderer myRenderer = getCustomRenderer(row, column);
        if (myRenderer != null) {
            return myRenderer;
        }
        // else...
        return super.getCellRenderer(row, column);
    }

    private Map<Integer, Map<Integer, TableCellRenderer>> rendererMap = new ...;

    public void setCustomRenderer(int row, int column, TableCellRenderer renderer) {
        Map<Integer, TableCellRenderer> m1 = rendererMap.get(row);
        if (m1 == null) {
            m1 = new ...;
            rendererMap.put(row, m1);
        }
        m1.put(column, renderer);
    }

    public TableCellRenderer getCustomRenderer(int row, int column) {
        Map<Integer, TableCellRenderer> m1 = rendererMap.get(row);
        if (m1 == null) {
            return null;
        }
        return m1.get(column);
    }
}

Версия по умолчанию getTableCellRenderer использует средство визуализации, установленное для столбца, если оно есть, в противном случае оно использует средство визуализации на основе класса содержимого ячейки. Содержимое ячеек по умолчанию во многих случаях Object. Это зависит от используемого TableModel.

person Lee Meador    schedule 06.05.2013
comment
Метод getCustomRenderer должен иметь тип TableCellRenderer, а не быть недействительным. - person George W; 01.10.2013
comment
@JorgeBlancoSegura Спасибо. Починил это. Возможно, StackOverflow нужна встроенная Java IDE, чтобы убедиться, что набираемый мной код хорош. :) - person Lee Meador; 01.10.2013
comment
См. Эту более чистую реализацию. - person Andrew Thompson; 19.05.2014
comment
Вы можете указать, какой TableCellRender следует использовать для рендеринга данного типа класса, или даже назначить TableCellRender напрямую TableColumn через ColumnModel. Нет необходимости расширять JTable, это очень ограничивает, особенно если вы имеете дело с существующим источником и не можете изменить реализацию таблицы. - person MadProgrammer; 20.05.2014

Лично я бы избегал любого решения, которое требовало бы от вас расширения JTable. Основная причина в том, что это делает ваш код менее переносимым и тесно связывает ваш рендерер с конкретной реализацией JTable. Это может удовлетворить ваши потребности, но лично я всегда пытаюсь увидеть более широкую картину / потенциал / повторное использование.

Помните, что средство визуализации ячейки привязано к столбцу таблицы. Он не предназначен для предоставления настраиваемой визуализации для каждой ячейки, per se. Что вы можете сделать, так это логика рендеринга для каждой ячейки на основе ряда условий в рендерере.

Я бы, вероятно, собрал какой-то механизм условий / формата, который вы могли бы использовать для передачи значения ячейки и координат, чтобы затем принимать решения на основе потребностей конкретной реализации механизма.

class ColorField extends DefaultTableCellRenderer {

    (...))

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {

        super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        // CellFormat is an object you create to hold the various aspects
        // of the cell format
        // You could also pass in the selected and focused flags if you
        // needed them...
        // getFormatEngine is a method you would write to provide
        // to gain access to the format engine
        CellFormat format = getFormatEngine().getFormatFor(value, row, column);
        setBackground(format.getBackground());
        setForeground(format.getForeground());
        setFont(format.getFont());
        // Any other properties you may wish to effect...

        return this;
    }
}

FormatEngine будет основой для принятия решений о том, как лучше всего отформатировать данную ячейку. Он также должен поддерживать формат "по умолчанию".

Это означает, что вы можете разработать средство визуализации "по умолчанию" для определенных типов ячеек, но при этом иметь возможность предоставлять настраиваемую визуализацию в соответствии с вашими потребностями без необходимости использования нескольких операторов if или расширения базовой визуализации.

Это также основа для механизма условного форматирования, где вы можете применить ряд условий к заданному значению и вернуть соответствующий формат на основе результатов.

person MadProgrammer    schedule 06.05.2013
comment
Это кажется более чистым подходом, чем принятый ответ. - person Andrew Thompson; 19.05.2014

Вам нужна информация о цвете для каждой ячейки в столбце. Эта информация может находиться в значениях:

class CellValue {
    Color getBackgroundColor() { ... }
    String getCellContent() { ... }
}

Тогда ваш getTableCellRendererComponent() метод может выглядеть так:

CellValue cellValue = (CellValue) value;
Color bgColor = cellValue.getBackgroundColor();
String text = cellValue.getCellContent();

JLabel l = (JLabel) super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column);

l.setBackground(bgColor);

...

Конечно, вам нужно адаптировать TableModel для хранения экземпляров CellValue.

person fjf2002    schedule 06.05.2013