Prima di tutto sono necessarie delle considerazioni.
1) Tutto quello che riguarda i componenti grafici (finestre, pulsanti, etichette, ecc) e gli eventi che questi intercettano/rilanciano sono gestiti da un unico thread: EDT (Event Dispatch Thread). Lui si occupa di tutto: dal disegno dei componenti al dispacciamento degli eventi.
2) L'EDT interagisce con gli altri thread dell'applicazione in modo molto particolare: fa una sola cosa per volta e non deve essere tenuto occupato troppo a lungo per operazioni onerose.
Da queste due cose deriva che il meccanismo che sta sotto alla "grafica" di Swing va trattato con i guanti e vanno seguite determinate regole ben precise quando si ha a che fare con cose come "le animazioni". Per realizzare un'animazione serve un thread che si occupi di fare i dovuti calcoli su coordinate, dimenensione e logica operativa e che "interagisca" in qualche modo con l'EDT.
Per far interagire un thread con l'EDT si deve usare uno dei due metodi che il framework Swing mette a disposizione:
- SwingUtilities.invokeLater()
- SwingUtilities.invokeAndWait()
La differenza fra i due la dice il nome: il primo mette in coda un lavoro da far fare all'EDT e prosegue, il secondo mette in coda un lavoro da far fare all'EDT ed attende che questo venga fatto.
Quindi la tua applicazione dovrà avere almeno un thread che si occupa di spostare la carta secondo la tua logica e questo thread dovrà, ad ogni operazione di spostamento, dire all'EDT di spostare effettivamente la carta.
Questo che ti posto è un esempio piuttosto rozzo, ma funzionante, di quello che stai cercando di fare tu (la carta l'ho realizzata con una semplice JLabel bordata, tu puoi tranquillamente usare l'immagine):
codice:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class PannelloCarta extends JFrame {
private class Animator extends Thread {
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
private final PannelloCarta win;
private int x;
private int y;
private int width;
private int height;
private int maxX;
private int maxY;
private int direction;
private boolean incDirection;
private volatile boolean attivo;
public Animator(PannelloCarta win, int x, int y, int width, int height, int maxX, int maxY, int direction) {
this.win = win;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.maxX = maxX;
this.maxY = maxY;
this.direction = direction;
incDirection = true;
}
@Override
public void run() {
attivo = true;
while( attivo ) {
switch( direction ) {
case HORIZONTAL:
if ( incDirection ) {
if (x + width < maxX) {
++x;
} else {
invertDirection();
}
} else {
if (x > 0) {
--x;
} else {
invertDirection();
}
}
break;
case VERTICAL:
if ( incDirection ) {
if (y + height < maxY) {
++y;
} else {
invertDirection();
}
} else {
if (y > 0) {
--y;
} else {
invertDirection();
}
}
break;
}
redraw();
try {
sleep( 5 );
} catch (InterruptedException ie) { /* ignore */ }
}
}
public void ferma() {
attivo = false;
}
private void invertDirection() { incDirection = !incDirection; }
private void redraw() {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
win.spostaCarta(x, y);
}
});
}
}
private JLabel carta;
private JPanel jpCentrale;
private Animator animator;
public PannelloCarta() {
initComponents();
setTitle("Movimento carta");
setSize(1000, 1000);
setDefaultCloseOperation( EXIT_ON_CLOSE );
setLocationRelativeTo( null );
}
private void initComponents() {
Container c = getContentPane();
c.setLayout( new BorderLayout() );
carta = new JLabel("Carta");
carta.setBounds(10, 10, 80, 120);
carta.setSize( new Dimension(80, 120) );
carta.setBorder( BorderFactory.createLineBorder(Color.black) );
jpCentrale = new JPanel();
jpCentrale.setLayout( null );
jpCentrale.add( carta );
JButton cmdOriz = new JButton("Muovi Orizzontale");
cmdOriz.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
muoviOrizzontale();
}
});
JButton cmdVertic = new JButton("Muovi Verticale");
cmdVertic.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
muoviVerticale();
}
});
JPanel jpComandi = new JPanel( new FlowLayout(FlowLayout.CENTER, 40, 20) );
jpComandi.add( cmdOriz );
jpComandi.add( cmdVertic );
c.add(jpComandi, BorderLayout.NORTH);
c.add(jpCentrale, BorderLayout.CENTER);
}
private void spostaCarta(int newX, int newY) {
carta.setLocation(newX, newY);
}
private void muoviOrizzontale() {
if (animator != null) {
animator.ferma();
animator = null;
}
animator = new Animator(this,
carta.getLocation().x,
carta.getLocation().y,
carta.getSize().width,
carta.getSize().height,
jpCentrale.getSize().width,
jpCentrale.getSize().height,
Animator.HORIZONTAL);
animator.start();
}
private void muoviVerticale() {
if (animator != null) {
animator.ferma();
animator = null;
}
animator = new Animator(this,
carta.getLocation().x,
carta.getLocation().y,
carta.getSize().width,
carta.getSize().height,
jpCentrale.getSize().width,
jpCentrale.getSize().height,
Animator.VERTICAL);
animator.start();
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
} catch (Exception e) { /* L&F */ }
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
new PannelloCarta().setVisible( true );
}
});
}
}
Focalizza soprattutto la tua attenzione sui punti del codice in cui viene invocato SwingUtilities.invokeLater(). Anche all'inizio, nel main. Quello è il punto in cui i thread (prima di tutto il Main-Thread, poi il thread Animatore) si interfacciano con l'EDT per fargli fare qualcosa.
Ciao.