-2

I have searched this website and cannot find any posts that ask a question pertaining to bouncing a ball off of a JSlider. I have modified the code to get the ball to bounce off the sides of the window opened as well as to speed up via the JSlider panel, but the ball (circle) goes down behind the panel that is the JSlider. While it bounces around the window, it goes through the JSlider, and I want the ball to be able to consider the JSlider as the bottom part of the window. The code shown below is my constructor.

import java.awt.*;
import java.awt.event.*;
import javax. swing. *;
import javax.swing.event.*;
public class SpeedControlPanel extends JPanel
{

  private final int WIDTH = 600;
  private final int HEIGHT = 400;
  private final int BALL_SIZE = 50;
  private Circle bouncingBall; // the object that moves
  private Timer timer;
  private int moveX, moveY; // increment to move each time
  private JPanel pSpeeder;
  private JSlider sSpeeder;
  private JLabel lSpeeder;
  Dimension height;
  // --------------------------------------------
  // Sets up the panel, including the timer
  // for the animation
  // --------------------------------------------


      public SpeedControlPanel ()
      {
        timer = new Timer(30, new ReboundListener());
        this.setLayout (new BorderLayout());
        bouncingBall = new Circle(BALL_SIZE);
        moveX = moveY = 5;
        // Set up a slider object here
        setPreferredSize (new Dimension (WIDTH, HEIGHT));
        setBackground(Color.black);
        lSpeeder = new JLabel("Timer Delay");
        lSpeeder.setAlignmentX(Component.LEFT_ALIGNMENT);

        sSpeeder = new JSlider(JSlider.HORIZONTAL, 0, 200, 30);
        sSpeeder.setMajorTickSpacing(40);
        sSpeeder.setMinorTickSpacing(10);
        sSpeeder.setPaintTicks(true);
        sSpeeder.setPaintLabels(true);
        sSpeeder.setAlignmentX(Component.LEFT_ALIGNMENT);

        sSpeeder.addChangeListener(new SlideListener());

        pSpeeder = new JPanel();
        pSpeeder.add(lSpeeder);
        pSpeeder.add(sSpeeder);

        add(pSpeeder, BorderLayout.SOUTH);
        timer.start();
      }

  // ---------------------
  // Draw the ball
  // ---------------------
      public void paintComponent (Graphics page)
      {
        super.paintComponent (page);
        bouncingBall.draw(page);
      }
  // ***************************************************
  // An action listener for the timer
  // ***************************************************
  public class ReboundListener implements ActionListener
  {
  // ----------------------------------------------------
  // actionPerformed is called by the timer -- it updates
  // the position of the bouncing ball
  // ----------------------------------------------------
      public void actionPerformed(ActionEvent action)
          {

          bouncingBall.move(moveX, moveY);
          // change direction if ball hits a side
          int x = bouncingBall.getX();
          int y = bouncingBall.getY();


          int slidePanelHt = pSpeeder.getSize().height; 

          if (x < 0 || x >= WIDTH - BALL_SIZE)
          moveX = moveX * -1;
          if (y <= 0 || y >= HEIGHT - BALL_SIZE)
          moveY = moveY * -1;
          repaint();
          }
  }
  // ***************************************************
  // A change listener for the slider.
  // ***************************************************
      private class SlideListener implements ChangeListener
          {
          // ------------------------------------------------
          // Called when the state of the slider has changed;
          // resets the delay on the timer.
          // ------------------------------------------------
              public void stateChanged (ChangeEvent event)
              {
                timer.setDelay(sSpeeder.getValue());
              }
          }
}

Is there a way to modify the width/height of the JSlider to get the ball to bounce off of it?

c0der
  • 18,467
  • 6
  • 33
  • 65
Pkd
  • 29
  • 8

2 Answers2

1
  1. Create a wrapper JPanel that holds everything, and give it a BorderLayout
  2. Put your drawing/animation JPanel into the wrapper in the BorderLayout.CENTER position
  3. Put your JSlider into the wrapper JPanel in the BorderLayout.PAGE_END position
  4. Add the wrapper to the GUI instead of the drawing/animation JPanel

Done

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
0

I would recommend separating the gui from its control by implementing the MVC Pattern.
Have a model that holds all the information that the view (gui) needs:

/*
 * The model contains the information for the view and information from the view
 * The model is independent of the user interface.
 */
class Model{

    private final int WIDTH = 600;
    private final int HEIGHT = 400;

    private int x,y, delay;
    private final int radius;

    Model(int radius) {
        this.radius = radius;
        x= radius;  y= radius; delay = 30; //default values
    }

    void move(int moveX, int moveY) {
        x += moveX; y+= moveY;
    }

    int getX() { return x;  }
    void setX(int x) { this.x = x; }

    int getY() {return y;}
    void setY(int y) { this.y = y; }

    int getRaduis() {return radius;}

    int getDelay() {return delay;}

    void setDelay(int delay) {this.delay = delay;}

    int getWidth() {return WIDTH;}

    int getHeiht() {return HEIGHT;  }
}

Have a view that uses the model to displays gui. Note that the slider and the animation are on two separate JPanels as proposed by Hovercraft Full Of Eels:

/*
 * View is just that: a dumb as possible display 
 */
class View extends JPanel {

    View(Model model) {
        setLayout (new BorderLayout());
        add(new BallPane(model));                          //as explained in Hovercraft Full Of Eels
        add(new SliderPane(model), BorderLayout.PAGE_END); //answer
    }

    class BallPane extends JPanel {

        private final Model model;

        BallPane(Model model) {
            this.model = model;
            setPreferredSize (new Dimension (model.getWidth(), model.getHeiht()));
            setBackground(Color.black);
        }

        // ---------------------
        // Draw the ball
        // ---------------------
        @Override
        public void paintComponent (Graphics page)  {
            super.paintComponent (page);
            page.setColor(Color.CYAN);
            page.fillOval(model.getX(), model.getY(), model.getRaduis()*2, model.getRaduis()*2);
        }
    }

    class SliderPane extends JPanel {

        private final Model model;
        private final JSlider sSpeeder;
        SliderPane(Model model) {
            this.model = model;
            sSpeeder = new JSlider(JSlider.HORIZONTAL, 0, 200, 30);
            sSpeeder.setMajorTickSpacing(40);
            sSpeeder.setMinorTickSpacing(10);
            sSpeeder.setPaintTicks(true);
            sSpeeder.setPaintLabels(true);
            sSpeeder.setAlignmentX(Component.LEFT_ALIGNMENT);
            sSpeeder.addChangeListener(new SlideListener());
            add(sSpeeder);
        }

        // ***************************************************
        // A change listener for the slider.
        // ***************************************************
        private class SlideListener implements ChangeListener
        {
            // ------------------------------------------------
            // Called when the state of the slider has changed;
            // resets the delay on the timer.
            // ------------------------------------------------
            @Override
            public void stateChanged (ChangeEvent event)
            {
                model.setDelay(sSpeeder.getValue());
            }
        }
    }
}

Putting it all together: see the following mvce : it adds a controller that controls the model and view.
For convenience and simplicity, the following code can be copy-pasted into one file called BouncingBall.java, and run.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;   
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/*
 * The controller wires the view and model, and does the processing.
 */
public class BouncingBall {

    private final int BALL_SIZE = 25;
    private int moveX =5, moveY =5; // increment to move each time
    private final Timer timer;
    private final Model model;
    private JPanel view;

    BouncingBall() {

        model = new Model(BALL_SIZE);
        timer = new Timer(model.getDelay(), new ReboundListener());
        makeAndShowGui();
        timer.start();
    }

    private void makeAndShowGui() {
        JFrame window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        view = new View(model);
        window.add(view);
        window.pack();
        window.setResizable(false);
        window.setVisible(true);
    }

    private void updateGui() {
        // change direction if ball hits a side
        int x = model.getX();
        int y = model.getY();

        if (x < 0 || x >= model.getWidth() - model.getRaduis()*2) {
            moveX = moveX * -1;
        }

        if (y <= 0 || y >= model.getHeiht() - model.getRaduis()*2) {
            moveY = moveY * -1;
        }

        model.setX(x+moveX);
        model.setY(y+moveY);
        timer.setDelay(model.getDelay()); //update timer
        view.repaint();
    }

    // ***************************************************
    // An action listener for the timer
    // ***************************************************
    public class ReboundListener implements ActionListener  {

        // ----------------------------------------------------
        // actionPerformed is called by the timer -- it updates
        // the position of the bouncing ball
        // ----------------------------------------------------
        @Override
        public void actionPerformed(ActionEvent action) {
            updateGui();
        }
    }

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

/*
 * The model contains the information for the view and information from the view
 * The model is independent of the user interface.
 */
class Model{

    private final int WIDTH = 600;
    private final int HEIGHT = 400;

    private int x,y, delay;
    private final int radius;

    Model(int radius) {
        this.radius = radius;
        x= radius;  y= radius; delay = 30; //default values
    }

    void move(int moveX, int moveY) {
        x += moveX; y+= moveY;
    }

    int getX() { return x;  }
    void setX(int x) { this.x = x; }

    int getY() {return y;}
    void setY(int y) { this.y = y; }

    int getRaduis() {return radius;}

    int getDelay() {return delay;}

    void setDelay(int delay) {this.delay = delay;}

    int getWidth() {return WIDTH;}

    int getHeiht() {return HEIGHT;  }
}

/*
 * View is just that: a dumb as possible display 
 */
class View extends JPanel {

    View(Model model) {
        setLayout (new BorderLayout());
        add(new BallPane(model));                          //as explained in Hovercraft Full Of Eels
        add(new SliderPane(model), BorderLayout.PAGE_END); //answer
    }

    class BallPane extends JPanel {

        private final Model model;

        BallPane(Model model) {
            this.model = model;
            setPreferredSize (new Dimension (model.getWidth(), model.getHeiht()));
            setBackground(Color.black);
        }

        // ---------------------
        // Draw the ball
        // ---------------------
        @Override
        public void paintComponent (Graphics page)  {
            super.paintComponent (page);
            page.setColor(Color.CYAN);
            page.fillOval(model.getX(), model.getY(), model.getRaduis()*2, model.getRaduis()*2);
        }
    }

    class SliderPane extends JPanel {

        private final Model model;
        private final JSlider sSpeeder;
        SliderPane(Model model) {
            this.model = model;
            sSpeeder = new JSlider(JSlider.HORIZONTAL, 0, 200, 30);
            sSpeeder.setMajorTickSpacing(40);
            sSpeeder.setMinorTickSpacing(10);
            sSpeeder.setPaintTicks(true);
            sSpeeder.setPaintLabels(true);
            sSpeeder.setAlignmentX(Component.LEFT_ALIGNMENT);
            sSpeeder.addChangeListener(new SlideListener());
            add(sSpeeder);
        }

        // ***************************************************
        // A change listener for the slider.
        // ***************************************************
        private class SlideListener implements ChangeListener
        {
            // ------------------------------------------------
            // Called when the state of the slider has changed;
            // resets the delay on the timer.
            // ------------------------------------------------
            @Override
            public void stateChanged (ChangeEvent event)
            {
                model.setDelay(sSpeeder.getValue());
            }
        }
    }
}

enter image description here

c0der
  • 18,467
  • 6
  • 33
  • 65