Java:在Swing中重新绘制不工作

Java:在Swing中重新绘制不工作,java,swing,repaint,Java,Swing,Repaint,我正在学习JavaSwing,在下面的程序中遇到了问题。它创建了一个小框架,顶部有一个退出按钮。目标是在单击鼠标的任何位置显示坐标。当我单击鼠标时,发生了两件不需要的事情: “退出”按钮被鼠标单击覆盖,不再响应(它不会响应事件和退出,而是在“退出”按钮顶部显示坐标) 当我在新位置单击时,旧位置的坐标将保持不变 我使用了removeAll()和revalidate(),然后根据进行了repaint(),但这没有帮助。此代码取自,代码表示要研究在线文档以了解发生这种情况的原因 有什么建议吗 impo

我正在学习JavaSwing,在下面的程序中遇到了问题。它创建了一个小框架,顶部有一个退出按钮。目标是在单击鼠标的任何位置显示坐标。当我单击鼠标时,发生了两件不需要的事情:

  • “退出”按钮被鼠标单击覆盖,不再响应(它不会响应事件和退出,而是在“退出”按钮顶部显示坐标)
  • 当我在新位置单击时,旧位置的坐标将保持不变 我使用了
    removeAll()
    revalidate()
    ,然后根据进行了
    repaint()
    ,但这没有帮助。此代码取自,代码表示要研究在线文档以了解发生这种情况的原因

    有什么建议吗

    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();
    
      }
    }
    
  • repaint()
    工作正常
  • 避免直接在JFrame上绘图
  • 而是在JFrame中显示的JPanel的
    受保护的void paintComponent(图形g)
    方法覆盖中绘制
  • 一定要在paintComponent覆盖中调用super的
    paintComponent(g)
    方法——这将擦除旧图像,这是您出现问题的原因之一
  • 在代码中使用合理的注释。太多的注释和太多的文本会分散注意力,使理解代码变得更困难,而不是更容易
  • 在JFrame上调用
    removeAll()
    就可以做到这一点——删除包括按钮在内的所有组件。你为什么叫这个?是否确实要调用此方法
  • 一个次要的吹毛求疵——您可能希望避免直接设置另一个类的字段,例如clickX和clickY字段。相反,将它们设置为私有,并且只允许外部类通过公共方法修改它们。虽然这对这个小程序可能没有多大影响,但当您开始扩展您的编程并创建具有复杂交互的大型程序时,这将非常重要。这里成功的关键是限制和控制类之间的所有通信,以避免难以察觉的副作用

  • 例如,像

       @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;
       }
    

    比如说

    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();
       }
    
    }
    
  • repaint()
    工作正常
  • 避免直接在JFrame上绘图
  • 而是在JFrame中显示的JPanel的
    受保护的void paintComponent(图形g)
    方法覆盖中绘制
  • 一定要在paintComponent覆盖中调用super的
    paintComponent(g)
    方法——这将擦除旧图像,这是您出现问题的原因之一
  • 在代码中使用合理的注释。太多的注释和太多的文本会分散注意力,使理解代码变得更困难,而不是更容易
  • 在JFrame上调用
    removeAll()
    就可以做到这一点——删除包括按钮在内的所有组件。你为什么叫这个?是否确实要调用此方法
  • 一个次要的吹毛求疵——您可能希望避免直接设置另一个类的字段,例如clickX和clickY字段。相反,将它们设置为私有,并且只允许外部类通过公共方法修改它们。虽然这对这个小程序可能没有多大影响,但当您开始扩展您的编程并创建具有复杂交互的大型程序时,这将非常重要。这里成功的关键是限制和控制类之间的所有通信,以避免难以察觉的副作用

  • 例如,像

       @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;
       }
    

    比如说

    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();
       }
    
    }
    

    您的事件正在被打印坐标的处理程序使用,您需要重新修补事件,以便按钮可以看到它。您可以在坐标显示事件处理程序中这样做:

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

    此外,我会尝试使用框架的玻璃窗格,并用坐标在其上放置一个JLabel,而不是混乱的绘画方法

    打印坐标的处理程序正在使用事件,您需要重新修补事件,以便按钮可以看到它。您可以在坐标显示事件处理程序中这样做:

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

    此外,我会尝试使用框架的玻璃窗格,并用坐标在其上放置一个JLabel,而不是混乱的绘画方法

    您不必使用任何repaint()、invalidate()等。 我强烈建议使用

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

    这保证了UI组件的实时更新。因为我们不知道系统何时更新UI层次结构,所以无法强制更新。这允许系统自行决定。

    您不必使用任何repaint()、invalidate()等。 我强烈建议使用

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

    这保证了UI组件的实时更新。因为我们不知道系统何时更新UI层次结构,所以无法强制更新。这允许系统自行决定。

    谢谢!我更新了代码以显示添加的quitButton。对不起,新的秋千。我假设您的意思是为(a)按钮和(b)坐标区域创建JPanel,并在其中使用绘画。我尝试创建JPanel以避免坐标重叠在quit按钮上。它会更改外观和窗口大小。我必须提供一个201x201窗口,其退出按钮跨越顶部窗口的宽度。我将致力于确定JPanel的大小。我使用removeAll删除基于链接讨论的旧坐标。看来super能解决这个问题。谢谢!我更新了代码以显示添加的quitButton。对不起,新的秋千。我假设您的意思是为(a)按钮和(b)坐标区域创建JPanel,并在其中使用绘画。我尝试创建JPanel以避免坐标重叠在quit按钮上。它会更改外观和窗口大小。我必须提供一个201x201窗口,其退出按钮跨越顶部窗口的宽度。我将致力于确定JPanel的大小。我使用removeAll删除基于链接讨论的旧坐标。看来super会解决这个问题。