1

I am new to Java. I started to learn by creating a bouncing ball GUI.

I have managed to do all i wanted to do with it except one thing. I can't get the ball to change its color whenever it bounces off the Frame Borders.

I know i can't use getGraphics for drawing but in this case i can also not use the paint Method as i already use it for the initial settings at the beginning of the Program. If i call the paint method from within the loop, it does change the color but all the JButtons in the North Side of the Layout mysteriously vanish.

The one Thing that makes the whole thing even more complicated for me is the fact that i am looping the ball bounce in a void run method of the Runnable Interface.

I couldn't figure out how to solve it elsewise.

You Guys can copy my code in your IDE. The one thing that doesn't work is that the ball remains black and doesn't changes it'S color. Everything else is running fine.

So Please help me figure out how i can change the ball's color whenever it bounces.

Thanks

Code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Ball extends JFrame implements Runnable, ActionListener {

    private JButton start;
    private JButton stop;
    private JButton fast;
    private JButton slow;
    private JButton reset;
    private int ball_x;
    private int ball_y;
    private int dx = 10;
    private int dy = 20;
    private int size = 50;
    private boolean active;
    private int i = 20;
    private int R = 255;
    private int G = 0;
    private int B = 255;
    private Color rgb;


    private MyPanel screen;

    private class MyPanel extends JPanel {
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            ball_x = screen.getWidth() / 2 - 25;
            ball_y = screen.getHeight() / 2 - 25;
            i = 20;
            g.setColor(rgb);
            g.fillOval(ball_x, ball_y, size, size);
        }

        public void clr() {

            if (R <= 10) {
                R = 255;
            }
            R = R - 10;


            if (G >= 255) {
                G = 0;
            }

            G = G + 15;


            if (B <= 20) {
                B = 255;
            }

            B = B - 20;

        }
    }


    public void start() {
        active = true;
        start.setText("Start");
        start.setEnabled(false);
        start.setText("Jumping...");
        Thread th = new Thread(this); // Thread anlegen
        th.start(); // Thread starten
    }

    public void stop() {
        if (active) {
            start.setText("Continue");
        }
        active = false;
        start.setEnabled(true);
    }

    public void reset() {
        active = false;
        size = 50;
        screen.repaint();
        start.setText("Start");
        start.setEnabled(true);
    }

    public void slow() {
        i = i + 2;
        if (i >= 60) {
            i = 60;
        }
    }

    public void fast() {
        i = i - 2;
        if (i <= 5) {
            i = 5;
        }
    }

    public void actionPerformed(ActionEvent ereignis) {

        if (ereignis.getSource() == start) {
            start();
        }

        if (ereignis.getActionCommand() == "G+") {
            fast();
        }

        if (ereignis.getActionCommand() == "G-") {
            slow();
        }

        if (ereignis.getActionCommand() == "Reset") {
            reset();
        }

        if (ereignis.getActionCommand() == "Pause") {
            stop();
        }

    }

    public Ball() {

        this.setTitle("Jumping Ball \u00a9 The One");
        Container panel = new Container();
        panel.setLayout(new BorderLayout());
        JPanel subpanel = new JPanel();
        //subpanel.setLayout(new GridLayout());
        screen = new MyPanel();
        this.setVisible(true);
        this.setSize(1366, 768);
        this.setContentPane(panel);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        start = new JButton("Start");
        fast = new JButton("G+");
        slow = new JButton("G-");
        reset = new JButton("Reset");
        stop = new JButton("Pause");
        subpanel.add(start);
        subpanel.add(fast);
        subpanel.add(slow);
        subpanel.add(reset);
        subpanel.add(stop);
        panel.add(subpanel, "North");
        panel.add(screen, "Center");
        start.addActionListener(this);
        stop.addActionListener(this);
        reset.addActionListener(this);
        slow.addActionListener(this);
        fast.addActionListener(this);

    }

    public void run() {

        while (active) {
            screen.getGraphics().clearRect(ball_x, ball_y, size, size);

            if (ball_x > (screen.getWidth() - 30) || ball_x < 25) {
                dx = -dx;
                size = size + 3;
                screen.clr();

            }

            if (ball_y > (screen.getHeight() - 30) || ball_y < 25) {
                dy = -dy;
                size = size + 3;
                screen.clr();

            }

            ball_x = ball_x + dx;
            ball_y = ball_y + dy;

            rgb = new Color(R, G, B);

            screen.getGraphics().setColor(rgb);
            screen.getGraphics().fillOval(ball_x, ball_y, size, size);
            try {
                Thread.sleep(i);
            } catch (InterruptedException x) {
                x.printStackTrace();
            }
        }

    }


    public static void main(String[] args) {
        Ball bouncer = new Ball();

    } // End of Method Main


}

Thank You

Ashot Karakhanyan
  • 2,804
  • 3
  • 23
  • 28
Zombievirus
  • 177
  • 1
  • 2
  • 12
  • Why does it take over 200 LOC to show how you tried to change a color? For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). – Andrew Thompson Apr 23 '14 at 14:26
  • I would make a method that changes the color. Then you can call the method in a loop, which will change the color – Marshall Tigerus Apr 23 '14 at 14:29
  • This is the whole program. It's not just the part where i tried to change the color. To be precise: `screen.getGraphics().setColor(rgb);` is not working and i can't use the paint method because it is already doing something else in the beginning. – Zombievirus Apr 23 '14 at 14:33
  • @MarshallTigerus For that i would need the paintComponent Method but i can't use it as it is already being used. I just started Java like 10 days ago. No idea how to cope with this problem. – Zombievirus Apr 23 '14 at 14:35
  • You should probably do some debugging. See if your code even reaches that point. I also don't see where you call run() – Marshall Tigerus Apr 23 '14 at 14:40
  • I rewrote the method to change the color. But still not working: `public void clr() { if (R <= 10) { R = 255; } R = R - 10; if (G >= 255) { G = 0; } G = G + 15; if (B <= 20) { B = 255; } B = B - 20; super.paintComponent(getGraphics()); screen.getGraphics().setColor(rgb); } }` Right after this method is called it returns back to the loop and does `screen.getGraphics().fillOval(ball_x, ball_y, size, size);` – Zombievirus Apr 23 '14 at 14:44
  • i call run in the start() method with th.start(); – Zombievirus Apr 23 '14 at 14:47
  • Every painting that happens via `getGraphics()` will fail sooner or later in one or the other form. You mentioned that ~"you can't use paint/paintComponent", but with no convincing reason. Have a look at http://docs.oracle.com/javase/tutorial/uiswing/painting/ – Marco13 Apr 23 '14 at 16:11

2 Answers2

3

While complete, your example has several critical problems:

  • It is incorrectly synchronized.

  • It calls setVisible() prematurely.

  • It relies on the frame's geometry, rather than an enclosed panel.

Instead, start with the example cited here using a Swing Timer. Note that the color changes with each repaint(), and a sound is played with each bounce. Add a member variable, bounced.

private boolean bounced;

When the ball bounces, set bounced = true in the actionPerformed() method of the Timer.

if (ballX - RADIUS < 0) {
    …
    bounced = true;
} else if (ballX + RADIUS > BOX_WIDTH) {
    …
    bounced = true;
}

In paintComponent(), change the color accordingly.

g.setColor(clut.peek());
if (bounced) {
    clut.add(clut.remove());
    bounced = false;
}

It may be easier to see the change with a smaller clut.

private static final float N = 16;
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thank You very much! I will have a look at all what you said. I need some time for that. clut for example is something i have never heard before. i just googled it. – Zombievirus Apr 24 '14 at 04:15
  • Take some time; a related example is examined [here](http://stackoverflow.com/q/9849950/230513); a [color look-up table](http://en.wikipedia.org/wiki/Colour_look-up_table) is a convenient way to have a flexible palette of colors. – trashgod Apr 24 '14 at 12:39
  • Thanx for the example. It is really interesting. But the one thing i don't get is: why can't i call the `this.setVisible(true)` in the constructor? Why do i HAVE to do this in the main method? – Zombievirus Apr 24 '14 at 18:05
  • 1
    You don't; just call it _after_ adding your components and invoking `pack()`. – trashgod Apr 24 '14 at 19:15
1

Well i got it to work but i don't think this is the best way to do it! I had to ditch Threads and go for Timer.

I have been reading everywhere that one shouldn't mix AWT and Swing but i don't see any other possibility than panel.setLayout(new BorderLayout()); or public void paintComponent(Graphics g)

This is obviously mixing AWT and Swing Components but i didn't find any pure Swing way to do it.

Anyways this is my entire Code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Ball extends JFrame implements ActionListener {

    private JButton start;
    private JButton stop;
    private JButton fast;
    private JButton slow;
    private JButton reset;
    private int ball_x;
    private int ball_y;
    private int dx = 10;
    private int dy = 20;
    private boolean active;
    private int i = 30;
    private int R = 255;
    private int G = 255;
    private int B = 0;
    private Color rgb;
    private Timer trigger = new Timer(i, this);

    private MyPanel screen;

    private class MyPanel extends JPanel {

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (start.getText() == "Start") {
                ball_x = screen.getWidth() / 2 - 50;
                ball_y = screen.getHeight() / 2 - 50;
            }

            if (start.getText() == "Jumping...") {
                g.clearRect(ball_x, ball_y, 100, 100);
            }

            ball_x = ball_x + dx;
            ball_y = ball_y + dy;

            rgb = new Color(R, G, B);
            g.setColor(rgb);

            g.fillOval(ball_x, ball_y, 100, 100);

            if (ball_x > (screen.getWidth() - 100) || ball_x < 10) {
                dx = -dx;
                screen.clr();
            }

            if (ball_y > (screen.getHeight() - 100) || ball_y < 10) {
                dy = -dy;
                screen.clr();
            }

        }

        public void clr() {

            if (R <= 10) {
                R = 255;
            }
            R = R - 10;

            if (G <= 20) {
                G = 255;
            }

            G = G - 20;

            if (B >= 255) {
                B = 0;

            }

            B = B + 15;

        }
    }

    public void start() {

        active = true;
        start.setText("Start");
        start.setEnabled(false);
        start.setText("Jumping...");
        trigger.start();
        screen.repaint();

    }

    public void stop() {
        if (active) {
            start.setText("Continue");
        }
        active = false;
        start.setEnabled(true);
        trigger.stop();

    }

    public void reset() {
        active = false;
        start.setText("Start");
        start.setEnabled(true);
        i = 100;
        trigger.stop();
        screen.repaint();
    }

    public void slow() {
        i = i + 10;
        if (i >= 100) {
            i = 100;
        }

        trigger.setDelay(i);
    }

    public void fast() {
        i = i - 10;
        if (i <= 10) {
            i = 10;
        }
        trigger.setDelay(i);
    }

    public void actionPerformed(ActionEvent ereignis) {

        if ((ereignis.getSource() == start) || (trigger.isRunning() == true)) {
            start();
        }

        if (ereignis.getActionCommand() == "G+") {
            fast();
        }

        if (ereignis.getActionCommand() == "G-") {
            slow();
        }

        if (ereignis.getActionCommand() == "Reset") {
            reset();
        }

        if (ereignis.getActionCommand() == "Pause") {
            stop();
        }

    }

    public Ball() {

        this.setTitle("Jumping Ball \u00a9 The One");
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        this.setContentPane(panel);
        JPanel subpanel = new JPanel();
        panel.add(subpanel, "North");
        screen = new MyPanel();
        screen.setBackground(Color.WHITE);
        panel.add(screen, "Center");
        start = new JButton("Start");
        fast = new JButton("G+");
        slow = new JButton("G-");
        reset = new JButton("Reset");
        stop = new JButton("Pause");
        subpanel.add(start);
        subpanel.add(fast);
        subpanel.add(slow);
        subpanel.add(reset);
        subpanel.add(stop);
        start.addActionListener(this);
        stop.addActionListener(this);
        reset.addActionListener(this);
        slow.addActionListener(this);
        fast.addActionListener(this);
        this.setSize(1280, 720);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        Ball fenster = new Ball();

    } // Ende der Methode Main
}

I know this is not the professional way to do it but this is the best i could do and it does what i wanted it to do.

I will be more than happy if someone could point out some absolute no-go's in my Code.

Thanx

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Zombievirus
  • 177
  • 1
  • 2
  • 12
  • 1
    +1 for much improved code; see also [*Initial Threads*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html); the dictum is to avoid mixing AWT and Swing _components_; using AWT events and graphics is fine.. – trashgod Apr 26 '14 at 00:33
  • Thank You very much trashgod =) Threads seem to be more difficult than i expected. Right now i am working on a "make the ball bounce or you lose" game. basically its just a drawRect that moves with the mouse x position (no y movement) and the ball must be bounced with it. not really a big deal but seems like a good practice. – Zombievirus Apr 26 '14 at 02:16
  • Next Problem encountered [http://stackoverflow.com/questions/23320073/mousemotionlistener-in-a-thread] – Zombievirus Apr 27 '14 at 06:20