Frame.repaint()不起作用



这是我直接从"Head First Java"获得的源代码,但无论我做什么,我似乎都无法使其工作,我不知道我可能错过了什么

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGui3C implements ActionListener {
    JFrame frame;
    public static void main(String[] args) {
        SimpleGui3C gui = new SimpleGui3C();
        gui.go();
    }
public void go() {
    MyDrawPanel drawPanel = new MyDrawPanel();
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JButton button = new JButton("Change colors");
    button.addActionListener(this);
    frame.getContentPane().add(BorderLayout.SOUTH, button);
    frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
    frame.setSize(300, 300);
    frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {

    frame.repaint();
    }
}
class MyDrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        g.setColor(Color.white);
        g.fillRect(0, 0, 300, 300);
    int red = (int) (Math.random() * 255);
    int green = (int) (Math.random() * 255);
    int blue = (int) (Math.random() * 255);
    Color randomColor = new Color(red, green, blue);
    g.setColor(randomColor);
    g.fillOval(70, 70, 100, 100);
}
}

我试图找到另一种方法来做到这一点,不涉及重新绘制,而是每当事件发生时创建MyDrawPanel的新实例,但它仍然不起作用,因为我没有找到正确清除面板的方法,迄今为止我发现的唯一hack就是这样做,但这不是我想要实现的…

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGui3C implements ActionListener {
    JFrame frame;
    public static void main(String[] args) {
    SimpleGui3C gui = new SimpleGui3C();
    gui.go();
}
public void go() {
    MyDrawPanel drawPanel = new MyDrawPanel();
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JButton button = new JButton("Change colors");
    button.addActionListener(this);
    frame.getContentPane().add(BorderLayout.SOUTH, button);
    frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
    frame.setSize(300, 300);
    frame.setVisible(true);
}
    public void actionPerformed(ActionEvent event) {
        go();
    }
}

class MyDrawPanel extends JPanel {
    int red;
    int green;
    int blue;
    public MyDrawPanel() {
    this.red = (int) (Math.random() * 255);
    this.green = (int) (Math.random() * 255);
    this.blue = (int) (Math.random() * 255);
}
public void paintComponent(Graphics g) {
    g.setColor(Color.white);
    g.fillRect(0, 0, 300, 300);
    Color randomColor = new Color(this.red, this.green, this.blue);
    g.setColor(randomColor);
    g.fillOval(70, 70, 100, 100);
}
}

在处理Swing时,应该记住一些事情。当我看到你使用的代码时,我想给你指出来,以使你走上正确的道路。

    基于
  1. Swing的应用程序从各自的应用程序启动线程,称为EventDispatcherThread ( EDT ),而不是直接从主。更多关于这个话题的信息可以在Swing中的并发性
  2. 尽量不要在paintComponent ( ... )方法中执行任何计算,而是在其他地方执行这些计算,并且只需调用repaint ()
  3. paintComponent ( ... )的访问指定符为protected和不是public,因此当重写超类的方法时,尽量不要尽可能多地更改方法访问,直到不必要的。
  4. 当您在JPanel上绘图时,只需调用repaint ()JPanel的实例,而不是顶层容器的
  5. 当一个人扩展任何JComponenet/JPanel时,总是尝试重写,上述JComponent/JPanelgetPreferredSize (),作为许多布局将返回0,如果没有指定,因此没有油漆将完成。
  6. 记得调用super.paintComponent ( g ),作为第一行inside paintComponent ( ... )
  7. 不要在JFrame上设置大小,而是尝试调用pack (),如在Java文档中说明了附带的好处。包装法调整框架的大小,使其所有内容都位于或高于它们首选大小。另一种包装方法是确定框架尺寸通过调用setSize或setBounds(它们也会设置帧位置)。一般来说,使用pack优于调用setSize,因为pack让框架布局管理器负责框架大小和布局管理器擅长调整平台依赖关系和其他影响组件大小的因素。

下面是修改后的代码(只是在DrawPanel中添加了一个名为setValues ()的方法,在这里完成计算并调用repaint ()),基于以上几点:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SimpleGui implements ActionListener {
    private MyDrawPanel drawPanel;
    public static void main(String[] args) {
        Runnable r = new Runnable () {
            @Override
            public void run () {
                new SimpleGui ().go ();
            }
        };
        EventQueue.invokeLater ( r );
    }
    public void go() {
        drawPanel = new MyDrawPanel();
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
        JButton button = new JButton( "Change colors" );
        button.addActionListener( this );
        frame.add( drawPanel, BorderLayout.CENTER );
        frame.add( button, BorderLayout.PAGE_END );
        frame.pack ();
        frame.setLocationByPlatform ( true );
        frame.setVisible(true);
    }
    public void actionPerformed(ActionEvent event) {
        drawPanel.setValues ();
    }
}
class MyDrawPanel extends JPanel {
    private int width = 300;
    private int height = 300;
    private int red;
    private int green;
    private int blue;
    private Color randomColor;
    /*
     * Make this one customary habbit,
     * of overriding this method, when
     * you extends a JPanel/JComponent,
     * to define it's Preferred Size.
     * Now in this case we want it to be 
     * as big as the Image itself.
     */
    @Override
    public Dimension getPreferredSize () {
        return new Dimension ( width, height );
    }
    public void setValues () {
        red = ( int ) ( Math.random() * 255 );
        green = ( int) ( Math.random() * 255 );
        blue = ( int ) ( Math.random() * 255 );
        randomColor = new Color( red, green, blue );
        repaint ();
    }
    /*
     * This is where the actual Painting
     * Code for the JPanel/JComponent goes.
     * Here the first line super.paintComponent(...),
     * means we want the JPanel to be drawn the usual 
     * Java way first (this usually depends on the opaque
     * property of the said JComponent, if it's true, then
     * it becomes the responsibility on the part of the
     * programmer to fill the content area with a fully
     * opaque color. If it is false, then the programmer
     * is free to leave it untouched. So in order to 
     * overcome the hassle assoicated with this contract,
     * super.paintComponent(g) is used, since it adheres
     * to the rules, and performs the same task, depending
     * upon whether the opaque property is true or false),
     * then later on we will add our image to it, by 
     * writing the other line, g.drawImage(...).
     */
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent ( g );
        g.setColor(randomColor);
        g.fillOval(70, 70, 100, 100);
    }
}

这就是你想要的....

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGui3C implements ActionListener {
    static JFrame frame = null;  // changed here...
    public static void main(String[] args) {
        frame = new JFrame();  // changed here....
        SimpleGui3C gui = new SimpleGui3C();
        gui.go();
    }
    public void go() {
        MyDrawPanel drawPanel = new MyDrawPanel();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton button = new JButton("Change colors");
        button.addActionListener(this);
        frame.getContentPane().add(BorderLayout.SOUTH, button);
        frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
        frame.setSize(300, 300);
        frame.setVisible(true);
    }
    public void actionPerformed(ActionEvent event) {
        frame.repaint();
    }
}
class MyDrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        g.setColor(Color.white);
        g.fillRect(0, 0, 300, 300);
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        Color randomColor = new Color(red, green, blue);
        g.setColor(randomColor);
        g.fillOval(70, 70, 100, 100);
    }
}

在你的go()方法中…每次你通过JFrame frame = new JFrame();创建一个新对象,因此在你的每次点击一个新的框架出现在屏幕上…

通过在main()方法中创建对象,我们每次只在同一个jframe对象上调用repaint()方法,而不是创建新对象....

最新更新