Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threading a paint method

I was wondering how I would thread the following code, or just a method in general:

public void run (){

    public void paint(Graphics g) {
        g.fillRect(20, 20, 20, 20);
        for (int i = 20; i < 1000; i++) {
            g.fillRect(20, i, 20, 20);
            Thread.sleep(10);
        }
    }
}

I find that I am unable to make a thread of this code because I get an illegal start of expression error, which is fair but I do not see a way around it.

like image 381
Nick Haughton Avatar asked Dec 12 '22 19:12

Nick Haughton


1 Answers

Its hard to tell what you are doing,

but seems like you are trying to override paint() of a Runnable from within its run() method.

This can surely not be done.

The logic is

  • Take a component
  • Override its paint method to draw what we need
  • Call method to update co-ordinates of rectangle (or in this case timer will do that)
  • Than call repaint() on the component so paint method may be called again and redraw the rectangle with its new co-ordinates (Timer would also take care of repainting after changing co-ordinates of Rectangle)
  • repeat last 2 steps as many times as needed/wanted

(when I say component I actually mean JPanel, paint method refers to overridden paintComponent(..) of JPanel as this is best practice.)

Some suggestions:

1) Dont override paint rather use JPanel and override paintComponent.

2) Dont forget to honor the paint chain and call super.XXX implementation of overridden paintComponent(Graphics g) (or any overridden method for that fact) unless purposefully leaving it out. i.e

class MyPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

         //do drawings here
    }
}

3) If drawing in paintComponent it is usually needed to override getPreferredSize() and return Dimensions which fit the contents/drawings of JPanel, i.e:

class MyPanel extends JPanel {
    @Override
    public Dimension getPreferredSize() {
         return new Dimension(300,300);
    }
}

3) Look at Swing Timer instead of Thread.sleep(..) as sleep will block GUI thread and make it seem to be frozen. i.e

Timer t = new Timer(10, new AbstractAction() {
    int count = 20;
    @Override
    public void actionPerformed(ActionEvent ae) {
        if (count < 1000) {
            //increment rectangles y position
            //now repaint container so we can see changes in co-ordinates (unless you have a timer which repaints for you too)
            count++;
        } else {//counter is at 1000 stop the timer
            ((Timer) ae.getSource()).stop();
        }
    }
});
t.start();

4) An alternative (because I see for now you are only moving a Rectangle which is not a Swing component) to Swing timer is TimerTask, and this can be used as long as no Swing components will be created/manipulated from within its run() method (as TimerTask does not run on EDT like Swing Timer). Note revalidate() and repaint() are Thread-safe so it can be used within TimerTask.

The advantage of the above is unnecessary code is kept of EDT (i.e moving AWT rectangle by changing co-ords) i.e

    final TimerTask tt = new TimerTask() {
        @Override
        public void run() {
            if (count < 1000) {
               //increment rectangles y position
                //now repaint container so we can see changes in co-ordinates (unless you have a timer which repaints for you too)
            count++;
            } else {//counter is at 1000 stop the timer
                cancel();
            }
        }
    };

    new Timer().scheduleAtFixedRate(tt, 0, 10);//start in 0milis and call run every 10 milis
like image 101
David Kroukamp Avatar answered Dec 30 '22 06:12

David Kroukamp