Problems with moving an object in Java Swing

43 views Asked by At

I'm having trouble displaying the movement of a custom Jbutton object in Jpanel that has been added to the Jframe. The chart is displayed but when the startMovement method is called the chart disappears under the Jpanel.

I've been fighting it for days but I can't resolve it.

public class MyPanel extends JPanel {
    private BufferedImage backgroundImage;

    public MyPanel(String imagePath) {
        try {
            backgroundImage = ImageIO.read(new File(imagePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (backgroundImage != null) {
            g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
        }
    }
}


import view.MyPanel;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class MovingButton extends JButton implements ActionListener {
    private String cartaVisualizzataPath;
    private String retroCartaVisualizzataPath;
    private int x;
    private int y;
    private int deltaX;
    private int deltaY;
    private Timer timerMovimento;
    private Timer timerRotazione;
    private int finalx;
    private int finaly;
    private BufferedImage frontCardImage;
    private BufferedImage backCardImage;
    private boolean revealing = false;
    private double scaleX = 1.0;

    static JFrame frame = new JFrame();
    static MyPanel myPanel = new MyPanel("/Users/andrea/Il mio Drive/Università/- Metodologie di programmazione/BackGround_Resized.png");


    public MovingButton(int x, int y, String cartaVisualizzataPath, String retroCartaVisualizzataPath) {
        this.x = x;
        this.y = y;
        this.cartaVisualizzataPath = cartaVisualizzataPath;
        this.retroCartaVisualizzataPath = retroCartaVisualizzataPath;
        timerMovimento = new Timer(10, this);
        timerRotazione = new Timer(10, this);

        // Caricamento dell'immagine dal percorso specificato
        try {
            frontCardImage = ImageIO.read(new File(this.cartaVisualizzataPath));
            backCardImage = ImageIO.read(new File(this.retroCartaVisualizzataPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        //Effetto movimento
        if (e.getSource().equals(timerMovimento)) {
            System.out.println("Sono qui");
            System.out.println("x: " + x);
            System.out.println("y: " + x);

            if (x == finalx && y == finaly) {
                timerMovimento.stop();
                System.out.println("Terminato movimento");
            }

            if (x == finalx)
                deltaX = 0;

            if (y == finaly)
                deltaY = 0;

            x += deltaX;
            y += deltaY;

            myPanel.repaint();
        }

        //Effetto rotazione
        if (e.getSource().equals(timerRotazione)) {
            // Aggiorna l'effetto di girata della carta
            scaleX -= 0.01; // Modifica questo valore per regolare la velocità di girata

            if (scaleX <= 0 && !revealing) {
                scaleX = 0;
                revealing = true; // Inizia a rivelare la carta sottostante
                timerRotazione.setDelay(20); // Riduci la velocità di rivelazione
            } else if (scaleX <= -1.0 && revealing) {
                scaleX = -1.0;
                timerRotazione.stop(); // Ferma il timer quando la carta è completamente rivelata
            }

            // Ridisegna il pannello
            myPanel.repaint();
        }

        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        int centerX = x + frontCardImage.getWidth() / 2;
        int centerY = y + frontCardImage.getHeight() / 2;

        // Disegna la carta frontale (superiore)
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.translate(centerX, centerY);
        g2d.scale(scaleX, 1.0); // Scala sull'asse X

        if (!revealing)
            g2d.drawImage(frontCardImage, -frontCardImage.getWidth(null) / 2, -frontCardImage.getHeight(null) / 2, null);
        else
            g2d.drawImage(backCardImage, frontCardImage.getWidth(null) / 2, -backCardImage.getHeight(null) / 2, -backCardImage.getWidth(null), backCardImage.getHeight(null), null);

        g2d.dispose();
    }

    public void avviaMovimento(int finalx, int finaly, int deltaX, int deltaY) {

        this.finalx = finalx;
        this.finaly = finaly;
        this.deltaX = deltaX;
        this.deltaY = deltaY;

        timerMovimento.start();

    }

    public void avviaRotazione() {
        this.timerRotazione.start();

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            String frontCardPath = "/Users/andrea/Il mio Drive/Università/- Metodologie di programmazione/iloveimg-resized/CartaCoperta.png";
            String backCardPath = "/Users/andrea/Il mio Drive/Università/- Metodologie di programmazione/iloveimg-resized/CuoriAsso.png";

//            frame = new JFrame();
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.setSize(new Dimension(1440, 900)); // Imposta le dimensioni della finestra
            frame.setLayout(new BorderLayout());

//            myPanel = new MyPanel("/Users/andrea/Il mio Drive/Università/- Metodologie di programmazione/BackGround_Resized.png");
            myPanel.setSize(new Dimension(800,800));
            myPanel.setLayout(null);

            MovingButton button = new MovingButton(0,0, backCardPath, backCardPath);
            button.setBounds(0,0,77, 88);

            myPanel.add(button);

            frame.add(new JButton("Ciao"), BorderLayout.NORTH);
            frame.add(myPanel, BorderLayout.CENTER);
            frame.add(new JButton("Ciao"), BorderLayout.SOUTH);
            frame.add(new JButton("Ciao"), BorderLayout.LINE_START);
            frame.add(new JButton("Ciao"), BorderLayout.LINE_END);

            frame.setVisible(true); // Rendi la finestra visibile

            button.avviaMovimento(400,400,1,1);

        });
    }

}


I would like the paper movement to be displayed

1

There are 1 answers

0
Hovercraft Full Of Eels On

Your problems are here:

You first override a JButton

public class MovingButton extends JButton implements ActionListener {

and give it an x and y fields. Note that this could itself can be dangerous if later you give the class public int getX() and getY() methods, since these would override key Swing component methods for placement of a component. I've been burned on this.

private int x;
private int y;

In your Swing Timer's ActionListener, you advance the x and y values and call repaint() all to help animate the movement of the image that this component paints:

@Override
public void actionPerformed(ActionEvent e) {

    //Effetto movimento
    if (e.getSource().equals(timerMovimento)) {
        //...

        x += deltaX;
        y += deltaY;

        myPanel.repaint();
    }
    repaint();
}

And in the paintComponent method you draw the image, translated by the values held by x and y:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    int centerX = x + frontCardImage.getWidth() / 2;
    int centerY = y + frontCardImage.getHeight() / 2;

    // Disegna la carta frontale (superiore)
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.translate(centerX, centerY);
    g2d.scale(scaleX, 1.0); // Scala sull'asse X

    if (!revealing)
        g2d.drawImage(frontCardImage, -frontCardImage.getWidth(null) / 2, -frontCardImage.getHeight(null) / 2, null);
    else
        g2d.drawImage(backCardImage, frontCardImage.getWidth(null) / 2, -backCardImage.getHeight(null) / 2, -backCardImage.getWidth(null), backCardImage.getHeight(null), null);

    g2d.dispose();
}

But note that x and y are relative to this component itself, this JButton. And so when you draw the image 10 pixels by 10 pixels, then no matter where this component is located, it will show the image moved relative to itself by 10 x 10 pixels, and this is not what you want.

So, while the button may originally look like this:
enter image description here

as it animates, the image will start moving off of the button, enter image description here

and then...
enter image description here

Instead, you want to draw the image at 0, 0 relative to the component itself, and translate the location of the component in the container, the location of the button.

Myself, if I were doing this sort of animation however, I'd just move an image and make it not extend a component.

For example, using this image:
enter image description here

located in a imgs directory that is a subdirectory of where this class file is located, I could do something like:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

class MyPanel02 extends JPanel {
    private static final int TIMER_DELAY = 20;
    private static final int DELTA = 3;
    private static final int PANEL_WIDTH = 1200;
    private static final int PANEL_HEIGHT = 800;
    private BufferedImage backgroundImage;
    private BufferedImage frontCardImage;
    private BufferedImage pressedCardImage;
    private int cardX = 0;
    private int cardY = 0;
    private Timer animationTimer = new Timer(TIMER_DELAY, e -> timerAxnPerformed());
    private Shape shape;
    private boolean isPressed = false;

    public MyPanel02(String imagePath) {

        addComponentListener(new MyComponentAdapter());

        try {
            URL cardUrl = getClass().getResource(imagePath);
            frontCardImage = ImageIO.read(cardUrl);
            int newW = frontCardImage.getWidth();
            int newH = frontCardImage.getHeight();

            // create pressed card image for when the mouse button is pressed over the card
            pressedCardImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = pressedCardImage.createGraphics();
            g2d.drawImage(frontCardImage, 0, 0, null);
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
            g2d.setColor(Color.BLACK);
            g2d.fillRect(0, 0, newW, newH);
            g2d.dispose();

            shape = new Rectangle(0, 0, newW, newH);
        } catch (IOException e) {
            e.printStackTrace();
        }
        animationTimer.start();
        addMouseListener(new MyMouseAdapter());
    }

    private class MyComponentAdapter extends ComponentAdapter {
        @Override
        public void componentResized(ComponentEvent e) {
            backgroundImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = backgroundImage.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setPaint(new GradientPaint(0, 0, Color.RED, getWidth(), getHeight(), Color.YELLOW));
            g2.fillRect(0, 0, getWidth(), getHeight());
            g2.dispose();
        }
    }

    @Override // get preferred size of the panel
    public Dimension getPreferredSize() {
        return new Dimension(PANEL_WIDTH, PANEL_HEIGHT);
    }

    private void timerAxnPerformed() {
        cardX += DELTA;
        cardY += DELTA;
        if (cardX > getWidth() || cardY > getHeight()) {
            cardX = 0;
            cardY = 0;
        }
        shape = new Rectangle(cardX, cardY, shape.getBounds().width, shape.getBounds().height);
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (backgroundImage != null) {
            g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
        }
        if (frontCardImage != null && !isPressed) {
            g.drawImage(frontCardImage, cardX, cardY, this);
        }
        if (pressedCardImage != null && isPressed) {
            g.drawImage(pressedCardImage, cardX, cardY, this);
        }
    }

    private class MyMouseAdapter extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            if (shape.contains(e.getPoint())) {
                if (animationTimer.isRunning()) {
                    animationTimer.stop();
                } 
                isPressed = true;
                repaint();
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (!isPressed) {
                return;
            }
            isPressed = false;
            repaint();
            if (!animationTimer.isRunning()) {
                animationTimer.start();
            }
        }
    }
}

public class MovingBtnMain {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame();
                MyPanel02 myPanel = new MyPanel02("imgs/PlayingCard.jpg");
                frame.add(myPanel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);

            }
        });
    }
}

Which could look like this:

enter image description here