Question

I have implemented a JProgressBar in a JTable. I used renderer for the ProgressBar NOT EDITOR.

Now I tried to implement a ProgressBar set value but due to EDT its not working so I used SwingUtilties but it did not work as well.

EXPECTED BEHAVIOUR - The JProgressBar must be setting value to 80 , currently it is showing only 0%

public class SkillSetTableProgressBarRenderer extends JProgressBar implements
        TableCellRenderer {

    public SkillSetTableProgressBarRenderer() {
        super(0, 100);
        super.setPreferredSize(new Dimension(100, 80));
    }

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

        final JProgressBar bar = (JProgressBar) value;

        if (bar.getString().equals("JAVA") || bar.getString().equals("SWING"))
            super.setString("Mellow");
        else
            super.setString("GOOD");

        setOpaque(true);
        table.setOpaque(true);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                System.err.println("MAIN ANDER");
                setValue(80);
                bar.setValue(80);
            }
        });

        super.setStringPainted(true);
        super.setIndeterminate(true);
        super.setPreferredSize(new Dimension(140, 16));
        if (isSelected) {
            super.setBackground(table.getSelectionBackground());
            super.setForeground(table.getSelectionForeground());
            // this.setBackground(table.getSelectionBackground());
        } else {
            super.setForeground(table.getForeground());
            super.setBackground(Color.WHITE);
        }

        return this;
    }

}
Was it helpful?

Solution

1) even is possible, don't create JComponents inside TableCellRenderer, nor re_create Object, Renderer is only for formatting cell contents

2) use SwingWorker for moving with progress in JProgressBar (if you have real important reasons, then use Runnable#Tread)

example about Runnable#Tread

enter image description here

import java.awt.Component;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class TableWithProgressBars {

    public static class ProgressRenderer extends JProgressBar implements TableCellRenderer {

        private static final long serialVersionUID = 1L;

        public ProgressRenderer(int min, int max) {
            super(min, max);
            this.setStringPainted(true);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            this.setValue((Integer) value);
            return this;
        }
    }
    private static final int maximum = 100;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TableWithProgressBars().createGUI();
            }
        });

    }

    public void createGUI() {
        final JFrame frame = new JFrame("Progressing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Integer[] oneRow = {0, 0, 0, 0};
        String[] headers = {"One", "Two", "Three", "Four"};
        Integer[][] data = {oneRow, oneRow, oneRow, oneRow, oneRow,};
        final DefaultTableModel model = new DefaultTableModel(data, headers);
        final JTable table = new JTable(model);
        table.setDefaultRenderer(Object.class, new ProgressRenderer(0, maximum));
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        frame.add(new JScrollPane(table));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        new Thread(new Runnable() {

            @Override
            public void run() {
                Object waiter = new Object();
                synchronized (waiter) {
                    int rows = model.getRowCount();
                    int columns = model.getColumnCount();
                    Random random = new Random(System.currentTimeMillis());
                    boolean done = false;
                    while (!done) {
                        int row = random.nextInt(rows);
                        int column = random.nextInt(columns);
                        Integer value = (Integer) model.getValueAt(row, column);
                        value++;
                        if (value <= maximum) {
                            model.setValueAt(value, row, column);
                            try {
                                waiter.wait(15);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        done = true;
                        for (row = 0; row < rows; row++) {
                            for (column = 0; column < columns; column++) {
                                if (!model.getValueAt(row, column).equals(maximum)) {
                                    done = false;
                                    break;
                                }
                            }
                            if (!done) {
                                break;
                            }
                        }
                    }
                    frame.setTitle("All work done");
                }
            }
        }).start();
    }
}

OTHER TIPS

Looks like you're trying to use a ProgressBar as a CellRenderer. One thing you should know, is that CellRenders are only called when a cell is being drawn, and setting values at other moments has no effect: this is because Swing uses a flyweight pattern for renderers, and thus reuses the renderer.

To get the effect you want, you should

  • notify the table that the cell is to be updated, for instance by updating its underlying model (that will fire the necessary events), and
  • set all values you need before the getTableCellRendererComponent return, so, remove the invokeLater (the getter is called on the EDT, so you don't need to worry about threading there).

Take a look at the Swing table tutorial: renderers and editors. The renderer uses the component to make a sort of 'stamp'. Changing the component afterwards has no effect since the component is not actually contained in your UI. That is one of the reasons you can return the same component over and over again, which would not be possible if the component was actually contained in the table. This is all explained in the linked tutorial.

To solve your problem, just remove the SwingUtilities.invokeLater method call, and return a JProgressBar where the correct progress is set.

And if I see this correctly, you have a TableModel containing a JProgressBar. I hope there is a good reason for this, since this is a UI component which should normally not be included on the model side. This is based on your

final JProgressBar bar = (JProgressBar) value;

call in the renderer. I would strongly suggest to take a look at your TableModel to see whether this can be avoided

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top