4

NOTE: The problem with my code was simply that I created the method to clear the rects and everything but the only thing I was doing wrong was instantiating DrawPanel class's myDraw object inside of the go() method. And so I had to instantiate DrawPanel again with Stop and that created a whole new object. So I ended up calling the clearRects method on a different DrawPanel object than the one rects were being added to. Anyway, I decided to go with code suggestions by MadProgrammer because his code was exactly how Java: A Beginner's Guide teaches it and was much cleaner.

Well, I have been running around StackOverflow since this morning and have been able to fix a lot of problems with my code but I am still stuck with this problem with ArrayLists.

I have the following piece of code that does not seem to do what I intend for it to do. Now I am aware that I am the one making a mistake somewhere but not really sure how to correct it.

The way it is set up is that when I hit the stop button, the ArrayList should clear so I have a blank JPanel so to speak, here's the code snippets. I can post the whole program if you want me to though but I am only pasting the snippet here because I'm assuming I'm making a pretty simple and dumb mistake on my part:

class DrawPanel extends JPanel {
    ArrayList<MyRectangle> rects = new ArrayList<>();
    Random rand = new Random();

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);

        addRect();

        for(MyRectangle r : rects) {
        g.setColor(r.getColor());
        g.fillRect(r.x, r.y, r.width, r.height);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(500,500);
    }

    public ArrayList<MyRectangle> addRect() {
        int ht = rand.nextInt(getHeight());
    int wd = rand.nextInt(getWidth());

    int x = rand.nextInt(getWidth() - wd);
    int y = rand.nextInt(getHeight() - ht);

    int r = rand.nextInt(256);
    int g = rand.nextInt(256);
    int b = rand.nextInt(256);

    rects.add(new MyRectangle(x, y, wd, ht, new Color(r, b, g)));
    System.out.println(rects.size());
    return rects;
}

    public void clearEvent(ActionEvent e) {
        System.out.println(rects.size());
        rects.clear();
        frame.repaint();
        System.out.println("I was called");
    }
}

And here's the part where the button calls it in its actionPerformed method:

class StopListener implements ActionListener {
    DrawPanel draw = new DrawPanel();
    public void actionPerformed(ActionEvent e) {
        timer.stop();
        draw.clearEvent(e);
    }
    }

EDIT: I understand that the arraylist object that my clearEvent method refers to is not the same one that addRect()'s adding stuff to. What I am asking, I guess, is how to make it "connect" so I can wipe things clean using the JButton.

EDIT: Here's the full program:

import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import java.awt.*;

public class TwoButtonsRandomRec {

    JFrame frame;
    Timer timer;

    public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            TwoButtonsRandomRec test = new TwoButtonsRandomRec();
            test.go();
        }
        });
    }

    public void go() {
    frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JButton startButton = new JButton("Start");
    startButton.addActionListener(new StartListener());
    JButton stopButton = new JButton("Stop");
    stopButton.addActionListener(new StopListener());

    final DrawPanel myDraw = new DrawPanel();

    timer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            myDraw.repaint();
        }
        });

    frame.add(startButton, BorderLayout.NORTH);
    frame.add(stopButton, BorderLayout.SOUTH);
    frame.add(myDraw, BorderLayout.CENTER);
    frame.pack();
    frame.setVisible(true);
    }

    class StartListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        timer.start();
    }
    }

    class StopListener implements ActionListener {
    DrawPanel draw = new DrawPanel();
    public void actionPerformed(ActionEvent e) {
        timer.stop();
        draw.clearEvent(e);
    }
    }

    class DrawPanel extends JPanel {
    ArrayList<MyRectangle> rects = new ArrayList<>();
    Random rand = new Random();

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);

        addRect();

        for(MyRectangle r : rects) {
        g.setColor(r.getColor());
        g.fillRect(r.x, r.y, r.width, r.height);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(500,500);
    }

    public ArrayList<MyRectangle> addRect() {
        int ht = rand.nextInt(getHeight());
        int wd = rand.nextInt(getWidth());

        int x = rand.nextInt(getWidth() - wd);
        int y = rand.nextInt(getHeight() - ht);

        int r = rand.nextInt(256);
        int g = rand.nextInt(256);
        int b = rand.nextInt(256);

        rects.add(new MyRectangle(x, y, wd, ht, new Color(r, b, g)));
        System.out.println(rects.size());
        return rects;
    }

    public void clearEvent(ActionEvent e) {
        System.out.println(rects.size());
        rects.clear();
        repaint();
        System.out.println("I was called");
    }
    }
}

class MyRectangle extends Rectangle {
    Color color;
    public MyRectangle(int x, int y, int w, int h, Color c) {
    super(x, y, w, h);
    this.color = c;
    }

    public Color getColor() {
    return color;
    }
}

Here's the previous relevant question I asked here in case anyone's interested.

Strange JFrame Behavior

Community
  • 1
  • 1
Nico
  • 3,471
  • 2
  • 29
  • 40
  • 1
    I don't see any code involving arraylists in your button listener. – DrinkJavaCodeJava Dec 11 '12 at 23:57
  • @redelman431 the actionPerformed method calls the clearEvent method from DrawPanel class. clearEvent is the method that I thought should clear the ArrayList but obviously I don't know what I'm doing...so any suggestions would be greatly appreciated. – Nico Dec 11 '12 at 23:59
  • 1
    I would also call `repaint` on the panel and not the frame – MadProgrammer Dec 12 '12 at 00:00
  • For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Dec 12 '12 at 00:02
  • 1
    Does the repaint() method end up invoking the paintComponent() method? If so, paintComponent() ends up refilling the array by calling the addRect() method. – schtever Dec 12 '12 at 00:02
  • @AndrewThompson I just pasted the whole program. Hope that makes it easier to help. – Nico Dec 12 '12 at 00:04
  • @schtever Yes it does call paintComponent() but when I hit stop, the Timer stops as well. So nothing should continue being called until I hit start again, right? The rectangles are stored within an ArrayList and I simply want that ArrayList to be empty when I hit stop. – Nico Dec 12 '12 at 00:05
  • 1
    BTW - `int r = rand.nextInt(256);` should be `int r = rand.nextInt(255);` – Andrew Thompson Dec 12 '12 at 00:06
  • 1
    Unless I'm reading this code wrong, every time paintComponent is being called, the array is being refilled. To get the effect you want, you have to separate the code that fills the array from the code that paints it. I would suggest moving the call to addRect() method somewhere else, maybe into the StartListener? – schtever Dec 12 '12 at 00:09
  • 1
    @nickecarlo schtever is correct, even though you've stopped the timer, Swing may call `paintComponent` of it's own accord (such as in response to a repaint request or external event - such as the window being resized). Adding rectangles to the list within the `paintComponent` is you major problem. You timer should be calling `addRect` and `repaint`, not just calling `repaint` on the draw panel – MadProgrammer Dec 12 '12 at 00:13
  • @schtever and MadProgrammer, I have taken your advice and removed addRect() from paintComponent()...it still doesn't fix my original problem though. – Nico Dec 12 '12 at 00:31
  • @MadProgrammer Nevermind, just saw your answer in its entirety. I apologize for my previous comment. Lemme look at all your suggestions in detail and I will get back if I still don't get it. – Nico Dec 12 '12 at 00:35
  • 1
    @nickecarlo I've updated the example to include our suggestions and it works fine for me – MadProgrammer Dec 12 '12 at 00:38
  • Thank you everyone who helped and put up with my ignorance. – Nico Dec 12 '12 at 01:02

1 Answers1

3

I see two immediate issues.

The first is, you're calling addRect within the paintComponent method, which means, even after you clear the List, on the next repaint, a new rectangle will be added to it.

Secondly, I would call repaint in the DrawPanel instead of using frame.repaint(), as you really only want to update the draw panel, not the entire frame

public class BadPaint05 {

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

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

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new MasterPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MasterPane extends JPanel {

        private DrawPanel drawPane;
        private Timer timer;

        public MasterPane() {
            setLayout(new BorderLayout());
            drawPane = new DrawPanel();

            add(drawPane);

            JButton stop = new JButton("Stop");
            stop.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    drawPane.clearEvent(e);
                    timer.stop();
                }
            });

            timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    drawPane.addRect();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

            add(stop, BorderLayout.SOUTH);

        }

    }

    class DrawPanel extends JPanel {

        ArrayList<MyRectangle> rects = new ArrayList<>();
        Random rand = new Random();

        @Override
        public void paintComponent(Graphics g) {

            super.paintComponent(g);

//            addRect();

            for (MyRectangle r : rects) {
                g.setColor(r.getColor());
                g.fillRect(r.x, r.y, r.width, r.height);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(500, 500);
        }

        public ArrayList<MyRectangle> addRect() {
            int ht = rand.nextInt(getHeight());
            int wd = rand.nextInt(getWidth());

            int x = rand.nextInt(getWidth() - wd);
            int y = rand.nextInt(getHeight() - ht);

            int r = rand.nextInt(256);
            int g = rand.nextInt(256);
            int b = rand.nextInt(256);

            rects.add(new MyRectangle(x, y, wd, ht, new Color(r, b, g)));
            System.out.println(rects.size());
            repaint();
            return rects;
        }

        public void clearEvent(ActionEvent e) {
            System.out.println(rects.size());
            rects.clear();
//            frame.repaint();
            repaint();
            System.out.println("I was called");
        }
    }

    public class MyRectangle {

        private int x, y, width, height;
        private Color color;

        private MyRectangle(int x, int y, int wd, int ht, Color color) {
            this.x = x;
            this.y = y;
            this.width = wd;
            this.height = ht;
            this.color = color;
        }

        public Color getColor() {
            return color;
        }

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • thank you so much. I would up vote your answer again if I could. I noticed that you named your program BadPaint05...is that just as a joke or is the program you made not really a good example? I'm asking because I'm in the learning process and wondering whether I could repeat these practices suggested by you in your code or you just put this program together to solve my particular problem? – Nico Dec 12 '12 at 01:02
  • @nickecarlo `BadPaint05` is just a quick and nasty project name. I have several that I keep on hand as many people seem to have similar problems, so rather then re-coding a whole new solution, I can draw on previous examples - it basically allows me to catalog the examples ;) - no offense intended – MadProgrammer Dec 12 '12 at 01:04
  • Haha none taken. It actually cracked me up. And I am also aware of my crappy programming skills. Thanks again. – Nico Dec 12 '12 at 01:05