использование пользовательского JComponent Swing в TableCellRenderer

StackOverflow https://stackoverflow.com/questions/864707

Вопрос

Хорошо, я знаю, как создать простой собственный JComponent.Я знаю, как переопределить TableCellRenderer.Кажется, я не могу совместить эти два понятия.

Вот образец JComponent Я создал:

public static class BarRenderer extends JComponent
{
    final private double xmin;
    final private double xmax;
    private double xval;
    public BarRenderer(double xmin, double xmax)
    {
        this.xmin=xmin;
        this.xmax=xmax;
    }

    @Override protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawRect(r.x, r.y,
                (int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
    }

    public void setXval(double x) { 
        this.xval = x;
        repaint();
    }
    public double getXval() { return xval; }
}

Он отлично работает как автономный JComponent.Я звоню setXval(something) и он обновляется нормально.(редактировать:У меня есть Swing Timer, который периодически обновляет данные)

Но если этот компонент я возвращаю в TableCellRenderer.getTableCellRendererComponent(), то он перерисовывается только тогда, когда я нажимаю на соответствующую ячейку.Что дает?Должно быть, я упускаю что-то очень простое.

Это было полезно?

Решение

По соображениям производительности JTable повторно использует компоненты средства рендеринга для рисования нескольких ячеек - поэтому, когда вы видите компонент в JTable, его на самом деле нет в традиционном смысле компонента в контейнере, который присутствует в определенном месте.Это означает, что вызов repaint() для компонента рендеринга ничего не дает.

Наиболее эффективным вариантом было бы сохранить целочисленное значение панели в вашей TableModel.Тогда ваш TableCellRenderer будет выглядеть примерно так:

public class BarTableCellRenderer implements TableCellRenderer {
    private final BarRenderer rendererComponent = new BarRenderer(0, 10);

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

Затем вы можете изменить Integer в своей TableModel, и это вызовет перерисовку панели (вам может понадобиться TableModel.fireTableCellUpdated в зависимости от используемой вами реализации TableModel).

Другие советы

Вы оба (Расс Хейворд и Эндрю) помогли, главное было сделать следующее:

  • сохраните состояние, которое должно быть видимым, в самой TableModel, а не в средстве рендеринга
  • убедитесь, что при изменении состояния TableModel fireTableCellUpdated() называется
  • есть только один Объект TableCellRenderer и один JComponent для моего пользовательского столбца (не по одному на ячейку)
    • в пределах TableCellRenderer.getTableCellRendererComponent() сохранить состояние ячейки для последующего рендеринга (долгосрочное хранение находится в TableModel)
    • предоставить это состояние JComponent
    • вернуть JComponent
    • переопределить JComponent.PaintComponent()
  • Одна из удобных возможностей — расширить JComponent и реализовать TableCellRenderer с помощью пользовательского средства визуализации, а затем TableCellRenderer.getTableCellRendererComponent() вы сохраняете состояние ячейки и return this;

Вот соответствующий отрывок из моего кода, который теперь работает:

class TraceControlTableModel extends AbstractTableModel {
    /* handle table state here */

    // convenience method for setting bar value (table model's column 2)
    public void setBarValue(int row, double x)
    {
        setValueAt(x, row, 2);
    }
}

// one instance of BarRenderer will be set as the
// TableCellRenderer for table column 2
public static class BarRenderer extends JComponent 
    implements TableCellRenderer 
{
    final private double xmin;
    final private double xmax;
    private double xval;
    public BarRenderer(double xmin, double xmax)
    {
        super();
        this.xmin=xmin;
        this.xmax=xmax;
    }

    @Override protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawRect(r.x, r.y,
                (int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
    }

    @Override
    public Component getTableCellRendererComponent(JTable arg0,
            Object value, 
            boolean isSelected, boolean hasFocus,
            int row, int col)
    {
        // save state here prior to returning this object as a component
        // to be painted
        this.xval = (Double)value;
        return this;
    }
}

Если вы создаете таблицу, скажем, из трех строк, каждая из которых имеет разные значения Xval, то правильно ли она изначально отображается, то есть каждая ячейка имеет разную панель?

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

Если данные изменились, но таблица не перерисовывается сразу, я бы сказал, что ваша TableModel работает неправильно.

базовые изменения данных -> изменения TableModel -> запускает TableModelEvent -> повторную визуализацию JTable

Посмотрите учебник TableModel: http://java.sun.com/docs/books/tutorial/uiswing/comComponents/table.html#data

чтобы убедиться, что вы все делаете правильно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top