6

I created my own CellRenderer which include some several strings and a JProgressBar in a JList Item ... But the JProgressBar and so the whole JList Item will painted one time and I'm looking for a way to repaint the Items ... I tried to start a thread, that will permanently repaint ... But I don't know what I have to repaint to get the result ...

JList repaint ... no result CellRenderer repaint ... no result JFrame repaint ... no result

Does anyone understand my Problem and know a way out?

Thank you very much!

UPDATE: [Update deleted]

NEXT UPDATE:

import java.awt.Component;
import java.awt.Dimension;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.ListCellRenderer;


public class Main extends JFrame{

public DefaultListModel contentListModel = new DefaultListModel();
public MyCellRenderer MCR = new MyCellRenderer();
public JList contentList = new JList(contentListModel);

public Main(){
    super("Example");
    setMinimumSize(new Dimension(300,300));
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    contentList.setCellRenderer(MCR);
    contentListModel.addElement("");
    contentListModel.addElement("");
    add(contentList);
}

public static void main(String[] args){
    new Main().setVisible(true);
}

class MyCellRenderer extends JPanel implements ListCellRenderer{

    public MyCellRenderer(){
        JProgressBar jpb = new JProgressBar();
        jpb.setIndeterminate(true);
        add(jpb);
    }

    @Override
    public Component getListCellRendererComponent(JList arg0, Object arg1,
            int arg2, boolean arg3, boolean arg4) {
        // TODO Auto-generated method stub
        return this;
    }



}

}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • [SSCCE](http://sscce.org/) first, answer second ;) – MadProgrammer Apr 17 '13 at 06:29
  • for better help sooner post an [SSCCE](http://sscce.org/), short, runnable, compilable, just about a.m. issue – mKorbel Apr 17 '13 at 06:30
  • To turn 2 source files into an SSCCE, have only one declared as `public`. Add the source for the 2nd class, into the end of the source for the first. – Andrew Thompson Apr 17 '13 at 07:02
  • *"Does anyone understand my Problem.."* I sure don't. What do you expect to see in the GUI? What do you see instead? – Andrew Thompson Apr 17 '13 at 07:06
  • questions 1. are you sure that status of `JProgressBar` is `setIndeterminate(true);`, did you tried put `JProgressBar` to the `JPanel` or `JFrame` with this properties, 2. `public class MyCellRenderer extends JPanel implements ListCellRenderer{` why there is `JPanel`, – mKorbel Apr 17 '13 at 07:07
  • Yes we understand your program, you don't understand how to use CellRenderers – MadProgrammer Apr 17 '13 at 07:08
  • Yes, I tried to add the JProgressBar for example on the JFrame and ist moving ... I used JPanel because this is a very small example of my Project where i add some strings to the item – Tobias Raphael Dieckmann Apr 17 '13 at 07:13
  • @TobiasRaphaelDieckmann Can highlight what it is you are trying to do?? – MadProgrammer Apr 17 '13 at 07:24

5 Answers5

4

As already mentioned, renderers are not live components, that is not part of component hierarchy. Consequently, "natural" animation effects (like f.i. the movement of the indeterminate progressbar) are lost. A trick that might work - but beware: that's highly LAF dependent! - is to lie to the system and report the bar as being dispayable always. That combined with a timer faking a new value every x ms might show the animation again:

public static class ProgressBarRenderer implements TableCellRenderer {

    /** The bar. */
    private JProgressBar indeterminate = new JProgressBar() {
        // fake displayable to trigger ui animation
        @Override
        public boolean isDisplayable() {
            return true;
        };
    };

    /** The bar. */
    private JProgressBar determinate = new JProgressBar() ;

    public ProgressBarRenderer() {
        indeterminate.setStringPainted(true);
        indeterminate.setIndeterminate(true);
        indeterminate.setString(null);
    }    

    @Override
    public Component getTableCellRendererComponent(JTable table,
            Object value, boolean isSelected, boolean hasFocus, int row,
            int column) {
        int pbi = (Integer) value;
        if (pbi < 0) {
            return indeterminate;
        }
        determinate.setValue(pbi);
        return determinate;
    }
}

// a timer driving the animation
Action update = new AbstractAction() {

    int count;
    @Override
    public void actionPerformed(ActionEvent e) {
        table.setValueAt(-1, 0, AncientSwingTeam.INTEGER_COLUMN);
    }

};
new Timer(100, update).start();
kleopatra
  • 51,061
  • 28
  • 99
  • 211
3

Cell renderers are static/rubber stamps of components, they are not "real", active components. They are simply "painted" onto the surface of the view that uses them.

This means that it's not (really) possible to repaint them as such. You can encourage the list to update by changing the value of the row you want to change. This will cause the model to trigger an update that will cause the list to repaint.

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestProgressListCellRenderer {

    public static void main(String[] args) {
        new TestProgressListCellRenderer();
    }

    public TestProgressListCellRenderer() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final DefaultListModel<Float> model = new DefaultListModel<>();
                model.addElement(0f);

                JList<Float> list = new JList<>(model);
                list.setCellRenderer(new ProgressListCellRenderer());

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(list));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Timer timer = new Timer(125, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        float value = model.getElementAt(0);
                        value += 0.01;
                        if (value >= 1f) {
                            value = 1f;
                            ((Timer)e.getSource()).stop();
                        }
                        model.setElementAt(value, 0);
                    }
                });
                timer.setRepeats(true);
                timer.setCoalesce(true);
                timer.start();

            }
        });
    }

    public class ProgressListCellRenderer extends JProgressBar implements ListCellRenderer<Float> {

        @Override
        public Component getListCellRendererComponent(JList<? extends Float> list, Float value, int index, boolean isSelected, boolean cellHasFocus) {
            setValue(Math.round(value * 100));
            return this;
        }            
    }        
}

This is a simple example and I tried using setIndeterminate, but I couldn't get it to update (very annoying), but this might trigger some ideas

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • So maybe it's better to work with a JPanel without JList, ListModel and CellRenderer and develop only the behavior ... – Tobias Raphael Dieckmann Apr 17 '13 at 07:18
  • 1
    The problem with setIndeterminate is that the ProgressBarUI actually uses a Timer to pace the animation. However this is done only if the JProgressBar is "Displayable" which is never the case because of the whole JList rendering mechanism. I am not sure that it can be easily performed with JList. I believe that it would be easier to use "Live" components instead (but I am waiting to be proven wrong) – Guillaume Polet Apr 17 '13 at 07:18
  • @TobiasRaphaelDieckmann I'd "suggest" adding the `JProgressBar` directly to the `JList`, but Kleo would hunt me down and do nasty things to me... – MadProgrammer Apr 17 '13 at 07:23
  • That was my question in the comment ... So I guess I try to use "Live" components ... Should be the only way ... Or I kick the JProgressBar out of my concept ... – Tobias Raphael Dieckmann Apr 17 '13 at 07:30
3
  • +1 for interesting issue,

  • but Renderer in Swing isn't designated to showing animations, this is only static painting, illusion, snapshot,

  • you have to animate this Object in your code, but not easy job for JProgressBar.setIndeterminate(true); if is possible without dirty hacks,

    1. required own JProgressBar

    2. required own animation for ListCellRenderer, and the result could be so far in compare with JProgressBar placed in JPanel e.g.


  • I think JList couldn't be proper JComponents, use JTable with one Column and /or without JTableHeader

  • simple workaround to use JPanel (layed by GridLayout) with JProgressBars for JProgressBar.setIndeterminate(true);, then output to the Swing GUI will be very similair to the JTable (without JTableHeader or JList)

    1. put this JPanel to JScrollPane

    2. override getPreferredSize for JScrollPane, then you'll simulating JList.setVisibleRowCount properly

    3. change JScrollBar.setUnitIncrement for natural scrolling for JPanel with JComponents and with JList, JTable, JTextArea placed in JScrollPane

mKorbel
  • 109,525
  • 20
  • 134
  • 319
3

You may be able to drive the animation with your own javax.swing.Timer in a custom ProgressBarUI, seen here; incrementAnimationIndex() looks promising, but I've not tried it.

image

Absent a better solution, you can use a ProgressIcon in a DefaultListCellRenderer; the approach is illustrated here in a JTabbedPane.

image

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
0

It's easy.

public void updateListData(final JList myList) {
  SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      ListModel lm = myList.getModel();
      DefaultListModel dlm = new DefaultListModel();
      for (int i = 0; i < lm.getSize(); i++) {
        dlm.addElement(lm.getElementAt(i));
      }
      myList.setModel(dlm);
    }
  });
}

The code above can be called from EDT and from another thread. But you also should read how to deal with Swing components and understand models (ListModel, TableModel, etc). To ask an element in JList to repaint you should modify its object in model.

Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • This won't change anything. Besides, it's an overkill to recreate the entire ListModel with the exact same value. You could simply call `myList.repaint()` to have the same effect (which would still not solve the problem). – Guillaume Polet Apr 17 '13 at 07:21
  • @Sergiy Medvynskyy not and answer to ListCellRenderer, please to delete this answer – mKorbel Apr 17 '13 at 07:45