Search code examples
javamultithreadingshapesgraphics2d

How to move Shape in Java?


I draw shapes on JPanel in a separate thread. I want to move these shapes via calling method move() but the figure does not change its location.

This my CustomShape

public class CustomShape {
    private static final int Y_STEP = 5;
    private static final int X_STEP = 5;
    public String name;
    public Shape shape;
    public Color color;
    private Point newLocation;

    public void move() {
        newLocation.x += X_STEP;
        newLocation.y += Y_STEP;
        //How set new location ?
        //It doesn't work
        this.shape.getBounds().setLocation(newLocation);        
        System.out.println(String.format("New location is [%d,%d]",newLocation.x, newLocation.y));
        System.out.println(String.format("Move to [%d,%d]", this.shape.getBounds().getLocation().x, this.shape.getBounds().getLocation().y));       
    }

    public CustomShape(Shape shape, Color color, String name) {
        this.shape = shape;
        this.color = color;
        this.name = name;
        newLocation = this.shape.getBounds().getLocation();
    }

Sample output on the console

New location is [15,15]
Move to [10,10]
New location is [20,20]
Move to [10,10]
New location is [25,25]
Move to [10,10]

My JPanel

public class ViewPanel extends JPanel {
    private static final long serialVersionUID = 5252479726227082794L;
    private List<CustomShape> shapeList = new ArrayList<CustomShape>();
    private Map<String, Thread> threads = new HashMap<String, Thread>();
    private Timer timer;
    private static final int TIMER_SPEED = 1000;

    public ViewPanel() {
        super();
        this.setDoubleBuffered(true);
        timer = new Timer(TIMER_SPEED, null);

        timer.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                testMove();
            }
        });
    }
    private void testMove() {
        for (CustomShape shape : shapeList) {
            shape.move();
        }
    }

    public void startMove() {
        timer.start();
    }

    public void stopMove() {
        timer.stop();
    }

    public void addShape(CustomShape shape) {
        shapeList.add(shape);
        if (!threads.containsKey(shape.getName())) {
            Thread t = new Thread(new DrawThread(shape, this.getGraphics()),
                    shape.getName());
            threads.put(shape.getName(), t);
            t.start();
        }
        this.repaint();
    }

    public void removeShape(CustomShape shape) {
        if (threads.containsKey(shape.getName())) {
            Thread t = threads.remove(shape.getName());
            t.interrupt();
            shapeList.remove(shape);
        }
        this.repaint();
    }
}

Thread for draw shape

public class DrawThread implements Runnable {
    private static final int THREAD_SLEEP = 100;
    private CustomShape shape;
    private Graphics2D g2d;
    private boolean interrupted = false;

    public DrawThread(CustomShape shape, Graphics g) {
        this.shape = shape;
        this.g2d = (Graphics2D)g;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(THREAD_SLEEP);
                g2d.setColor(this.shape.getColor());
                g2d.draw(this.shape.getShape());
            } catch (InterruptedException e) {
                System.out.println(String.format("interrupt %s", Thread
                        .currentThread().getName()));
                interrupted = true;
            } finally {
                if (interrupted)
                    break;
                }
        }
    }
}

Solution

  • Its a lot of code to read,difficult to find the bug,so its better to post an SSCCE for this.However, here is a shot EG to show how you can move shapes through an arrow key, the JApplet is focused on a mouse click.If you don't want to use an arrow key, you can implement mouse motion. See comments in the program for better understanding.

    move rect

    enter image description here

    Here is the code:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    
    public class EgToMove extends JApplet
                  implements KeyListener, FocusListener, MouseListener {
    
    static final int SQUARE_SIZE = 40;
    int squareTop, squareLeft;
    
    boolean focussed = false; 
    DisplayPanel canvas;
    
    public void init() {
    
    
      squareTop = getSize().height / 2 - SQUARE_SIZE / 2;
      squareLeft = getSize().width / 2 - SQUARE_SIZE / 2;
    
    
      canvas = new DisplayPanel();  
      setContentPane(canvas);     
    
      canvas.setBackground(Color.BLACK);
      canvas.addFocusListener(this);   // Set up the applet to listen for events
      canvas.addKeyListener(this);   
      canvas.addMouseListener(this);
    
      } 
    
     public void keyTyped(KeyEvent e) {
      //do nothing, if u want something
     }
    
    class DisplayPanel extends JPanel {
    
      public void paintComponent(Graphics g) {
    
         super.paintComponent(g);  
    
         if (focussed)
            g.setColor(Color.cyan);
         else
            g.setColor(Color.lightGray);
    
         int width = getSize().width;  
         int height = getSize().height; 
         g.drawRect(0,0,width-1,height-1);
         g.drawRect(1,1,width-3,height-3);
         g.drawRect(2,2,width-5,height-5);
         g.fillRect(squareLeft, squareTop, SQUARE_SIZE, SQUARE_SIZE);
    
    
    
         if (!focussed) {
            g.setColor(Color.magenta);
            g.drawString("Click to activate",7,20);
         }
      }  
      }
    
      // --------- Event handling methods
    
      public void focusGained(FocusEvent evt) {
    
      focussed = true;
      canvas.repaint();  // redraw with cyan border ///may b u had problem here
      }
    
      public void focusLost(FocusEvent evt) {
    
      focussed = false;
      canvas.repaint();  // redraw without cyan border///may b u had problem here
      }
    
    
    
      public void keyPressed(KeyEvent evt) {
    
    
      int key = evt.getKeyCode();  // keyboard code for the key that was pressed
    
      if (key == KeyEvent.VK_LEFT) {
         squareLeft -= 8;
         if (squareLeft < 3)
            squareLeft = 3;
         canvas.repaint();
      }
      else if (key == KeyEvent.VK_RIGHT) {
         squareLeft += 8;
         if (squareLeft > getSize().width - 3 - SQUARE_SIZE)
            squareLeft = getSize().width - 3 - SQUARE_SIZE;
         canvas.repaint();
      }
      else if (key == KeyEvent.VK_UP) {
         squareTop -= 8;
         if (squareTop < 3)
            squareTop = 3;
         canvas.repaint();
      }
      else if (key == KeyEvent.VK_DOWN) {
         squareTop += 8;
         if (squareTop > getSize().height - 3 - SQUARE_SIZE)
            squareTop = getSize().height - 3 - SQUARE_SIZE;
         canvas.repaint();
      }
    
      } 
    
    
     public void keyReleased(KeyEvent evt) {
      // empty method, required by the KeyListener Interface
     }
    
    
     public void mousePressed(MouseEvent evt) {
        // Request that the input focus be given to the
        // canvas 
      canvas.requestFocus();
     }
    
    
     public void mouseEntered(MouseEvent evt) { }
     public void mouseExited(MouseEvent evt) { }
     public void mouseReleased(MouseEvent evt) { }
     public void mouseClicked(MouseEvent evt) { }
    
    
     }