user3600280
user3600280

Reputation: 1

Java: Repaint in Swing Not Working

I am learning java swing and am having trouble with the following program. It creates a small frame with a quit button at top. The objective is to display coordinates wherever the mouse is clicked. When I click the mouse 2 unwanted things are happening:

  1. the quit button is overridden by the mouse clicks and it no longer responds (instead of responding to event and quitting, it displays coordinates on top of the quit button).
  2. when I click at a new location, the coordinates from the old location persist.

I used removeAll() and revalidate() before doing repaint() based on this discussion but that has not helped. This code is taken from here and the code to says to research online documentation for why this is happening.

Any pointers?

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;

public class QuitCoordinateTest {
  public static void main(String[] args){
    GUI gui = new GUI();
  }
}

class MyFrame extends JFrame implements ActionListener{
  int clickX;
  int clickY;

  public void paint(Graphics g){
    g.drawString("" + clickX + ", " + clickY, clickX, clickY);
  }

  public void actionPerformed(ActionEvent e){
    System.exit(0);
  }
}
//=======================================================//

class GUI extends MyFrame {
  JButton quitButton = new JButton("Quit");

  public GUI(){

    MyFrame displayWindow = new MyFrame();
    displayWindow.setTitle("Title");

    /*
    JPanel buttonPanel = new JPanel();
    buttonPanel.add(quitButton);
    displayWindow.getContentPane().add(buttonPanel,BorderLayout.NORTH);
    JPanel textPanel = new JPanel();
    */

    displayWindow.getContentPane().add(quitButton,BorderLayout.NORTH);
    quitButton.addActionListener(displayWindow);
    displayWindow.setSize(201,201);
    displayWindow.setVisible(true); 
//    displayWindow.pack();

    displayWindow.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

    displayWindow.addMouseListener(new MouseProc(displayWindow));

  }//end constructor

}//end class GUI definition
//=======================================================//

//This listener class monitors for mouse presses and 
// displays the coordinates of the mouse pointer when the
// mouse is pressed on the source object. 

class MouseProc extends MouseAdapter{
  MyFrame refToWin;

  MouseProc(MyFrame inWin){
    refToWin = inWin;
  }

  //Override the mousePressed method to determine and 
  // display the coordinates when the mouse is pressed.
  public void mousePressed(MouseEvent e){

    refToWin.removeAll();
    refToWin.clickX = e.getX();
    refToWin.clickY = e.getY();

    //Force the JFrame object to be repainted in order to
    // display the coordinate information.

    refToWin.removeAll();
    refToWin.validate();
    refToWin.repaint();

  }
}

Upvotes: 0

Views: 854

Answers (3)

Niroshan Abeywickrama
Niroshan Abeywickrama

Reputation: 99

you don't have to use any of repaint(),invalidate() etc. i highly recommend to use

SwingUtilities.invokeLater(new Runnable() {

            public void run() {
             //TODO udpdate UI compontents, layouts etc.
            }
});

this guarantees that UI components update on real time. Because we don't know when the system update UI hierarchy so we can't force it. This allow system to determine by it's self.

Upvotes: 0

DaveB
DaveB

Reputation: 2143

Your event is getting consumed by the handler that prints the coordinates, you need to redispatch the event so that the button can see it. You can do it like this, inside the coordinate display event handler:

Component c = e.getComponent();  
c.getParent().dispatchEvent( e ); 

Also, I'd be tempted to use the glass pane of the frame, and put a JLabel on it with the co-ordinates rather than messing with the paint method.

Upvotes: 0

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

  1. repaint() is working fine.
  2. Avoid drawing directly on the JFrame.
  3. Instead draw in the protected void paintComponent(Graphics g) method override of a JPanel that is then displayed in your JFrame.
  4. Be sure to call the super's paintComponent(g) method inside of your paintComponent override -- this will erase the old images and is the reason for one of your problems.
  5. Use reasonable comments in your code. Too many comments and too much text distracts and makes understanding your code harder, not easier.
  6. Calling removeAll() on your JFrame will do just that -- remove all components including your button. Why are you calling this? Are you sure that you want to call this method?
  7. A minor nitpick -- you'll want to avoid directly setting the fields of another class, such as your clickX and clickY fields. Instead, make them private, and only allow outside classes to modify them through public methods. While it may not matter much for this small program, it will matter greatly when you start scaling up your programming and create large programs with complex interactions. The key to success here will be to limit and control all communication between classes to avoid hard to detect side effects.

For example, something like...

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      String str = String.format("[%d, %d]", clickX, clickY);
      g.drawString(str, clickX, clickY);
   }

   public int getClickX() {
      return clickX;
   }

   public void setClickX(int clickX) {
      this.clickX = clickX;
   }

   public int getClickY() {
      return clickY;
   }

   public void setClickY(int clickY) {
      this.clickY = clickY;
   }

For example

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

@SuppressWarnings("serial")
public class DetectClicks extends JPanel {
   private static final int PREF_W = 800;
   private static final int PREF_H = 650;
   private int clickX;
   private int clickY;

   public DetectClicks() {
      MyMouseListener mouseAdapter = new MyMouseListener(this);
      addMouseListener(mouseAdapter);
      addMouseMotionListener(mouseAdapter); // to allow dragging!
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      String str = String.format("[%d, %d]", clickX, clickY);
      g.drawString(str, clickX, clickY);
   }

   public int getClickX() {
      return clickX;
   }

   public void setClickX(int clickX) {
      this.clickX = clickX;
   }

   public int getClickY() {
      return clickY;
   }

   public void setClickY(int clickY) {
      this.clickY = clickY;
   }

   private static void createAndShowGui() {
      DetectClicks mainPanel = new DetectClicks();

      JFrame frame = new JFrame("DetectClicks");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class MyMouseListener extends MouseAdapter {
   private DetectClicks detectClicks;

   public MyMouseListener(DetectClicks detectClicks) {
      this.detectClicks = detectClicks;
   }

   @Override
   public void mousePressed(MouseEvent evt) {
      showPoint(evt);
   }

   @Override
   public void mouseDragged(MouseEvent evt) {
      showPoint(evt);
   }

   private void showPoint(MouseEvent evt) {
      detectClicks.setClickX(evt.getX());
      detectClicks.setClickY(evt.getY());
      detectClicks.repaint();
   }

}

Upvotes: 4

Related Questions