user12251537
user12251537

Reputation:

Moving an object within a specific grid space in java JFrame possibly using GridBagLayout?

I want to move a rectangle up and down over a 1x 10 vertical grid on a JFrame Window.

The grid should be numbered and I should be able to know what level on the grid the rectangle is.

I haven't got a huge amount of experience with java but as of right now my basic code that I understand from a tutorial I watched allows me to move the rectangle up and down on the screen but the rectangle goes off the screen and it isn't on a grid.

package JavaWork;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Lift extends JPanel implements ActionListener, KeyListener {
    Timer tm = new Timer(5, this);
    int x = 0, y = 0, velX = 0, velY = 0;
    /**
     * 
     */
    public Lift() {
        // TODO Auto-generated constructor stub
        tm.start();
        addKeyListener(this);
        setFocusable(true);
        setFocusTraversalKeysEnabled(false);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.RED);
        g.fillRect(x, y, 50, 30);

    }

    public void actionPerformed(ActionEvent e) {
        x = x + velX;
        y = y + velY;
        repaint();
    }

    public void keyPressed(KeyEvent e) {
        int c = e.getKeyCode();

        if (c == KeyEvent.VK_UP) {
            velX = 0;
            velY = -1;

        }
        if (c == KeyEvent.VK_DOWN) {
            velX = 0;
            velY = 1;

        }
    }

    public void keyTyped(KeyEvent e) {}
    public void keyReleased(KeyEvent e) {}


    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO code application logic here
        Lift t = new Lift();
        JFrame jf = new JFrame();

        jf.setTitle("Lift");
        jf.setSize(600, 400);
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.add(t);
    }
}

Any advice, piece of code, a website or video link to a tutorial would be greatly appreciated :)

Upvotes: 0

Views: 980

Answers (2)

c0der
c0der

Reputation: 18812

You can do it by using GridLayout. Have a transparent lift at each floor, and an opaque (red in this example) in one floor.
Moving up or down is achieved by simply changing the right lift (represented by a JLabel in the following mre) from transparent to opaque:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Lift extends JPanel implements ActionListener, KeyListener {

    private static final int NUMBER_OF_FLOORS = 5, LIFT_W =100, LIFT_H =100, PAUSE = 2000;
    private final JLabel[] lifts = new JLabel[NUMBER_OF_FLOORS];
    private int liftPosition = 0;
    private boolean isMovingUp = true;
    private final Timer tm = new Timer(PAUSE, this);

    public Lift() {
        setLayout(new GridLayout(0, 1));
        for(int floor = NUMBER_OF_FLOORS -1; floor >=0 ; floor--){
            JLabel lift = getLift(floor);
            lifts[floor] = lift;
            add(lift);
        }

        addKeyListener(this);
        tm.start();
    }

    private JLabel getLift(int floor) {
        JLabel lift = new JLabel();
        lift.setPreferredSize(new Dimension(LIFT_W, LIFT_H));
        lift.setBackground(Color.RED);
        if(liftPosition == floor) {
            lift.setOpaque(true);
        }
        return lift;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        autoMove();
    }

    private void autoMove(){
        if(liftPosition == 0) {
            isMovingUp = true;
        } else if(liftPosition >= NUMBER_OF_FLOORS -1) {
            isMovingUp = false;
        }
        if(isMovingUp) {
            moveUp();
        } else {
            moveDown();
        }
    }

    private void moveUp(){
        if(liftPosition >= NUMBER_OF_FLOORS -1) return;
        lifts[liftPosition].setOpaque(false);
        liftPosition++;
        lifts[liftPosition].setOpaque(true);
        repaint();
    }

    private void moveDown(){
        if(liftPosition <= 0) return;
        lifts[liftPosition].setOpaque(false);
        liftPosition--;
        lifts[liftPosition].setOpaque(true);
        repaint();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int c = e.getKeyCode();
        if (c == KeyEvent.VK_UP) {
            moveUp();
        }
        if (c == KeyEvent.VK_DOWN) {
            moveDown();
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {}
    @Override
    public void keyReleased(KeyEvent e) {}

    public static void main(String[] args) {
        // TODO code application logic here
        Lift t = new Lift();
        JFrame jf = new JFrame();
        jf.setTitle("Lift");
        jf.setSize(300, 400);
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.add(t, BorderLayout.WEST);
    }
}

enter image description here

Upvotes: 0

VGR
VGR

Reputation: 44414

You can accomplish the equivalent of a grid yourself, by only drawing with a y-coordinate which is a multiple of 40. I suggest 40 since you set your window’s height to 400, and you want to have 10 “floors” in your grid and 400 ÷ 10 = 40.

So the last line in your paintComponent method should look like this:

g.fillRect(x, y * 40, 50, 30);

Technically, the window’s drawing area will be a little smaller than 400, since the JFrame size includes the window’s title bar and borders. You can fix that by replacing this:

jf.setSize(600, 400);

with this:

jf.add(t);
jf.pack();

and removing the other jf.add(t); line. You will also need to add a getPreferredSize override in your class:

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

(You will also need to an import java.awt.Dimension; line at the top of your program.)

The pack() method tells a window to do its best to accommodate the preferred size of every component it contains. That is why it is necessary for your panel to report its own preferred size, and that is why it is necessary for the panel to be added before the JFrame is packed and is set visible.

As for why the rectangle moves off the screen so quickly, it’s due to the number you pass to your Timer:

Timer tm = new Timer(5, this);

That 5 means the actionPerformed method is called by Swing every 5 milliseconds—that’s 200 times per second! You probably want a more reasonable value, like twice per second:

Timer tm = new Timer(500, this);

Finally, you will want to make sure that your rectangle never leaves the screen, by checking for a minimum and maximum y value in your actionPerformed method:

y = y + velY;
y = Math.max(0, y);     // don’t let y go off the top
y = Math.min(9, y);     // don’t let y go off the bottom

Upvotes: 1

Related Questions