Panel方法中的重绘不会更新

时间:2014-03-28 12:18:37

标签: java swing timer jpanel actionlistener

我正在尝试制作一个像这样工作的程序: 在Window类中,每次单击按钮时,都会调用Panel的方法panel2:首先是绘制第一个圆,然后是第二个圆(在计时器中定义的时间之后)。然后,我再次点击按钮,它绘制一个拳头圈,然后是第二个圈,然后是第三个圈。等等 问题是,当我点击获得一个接一个出现的3个圆圈时,在前一步骤绘制的两个第一个圆圈(在我第二次按下按钮之前)停留在屏幕上,而当我只有第三个圆圈被绘制时按下按钮(而不是:绘制第一个圆,绘制第二个圆,绘制第三个圆)。我希望我很清楚。 这是一个简单的代码:

窗口

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Window extends JFrame implements ActionListener{
    int h = 2;

    Panel b = new Panel();
    JPanel container = new JPanel();

    JButton btn = new JButton("Start");
    JButton bouton = new JButton();


    Panel boutonPane = new Panel();

    public Window(){

        this.setTitle("Animation");
        this.setSize(300, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        JPanel top = new JPanel();

        btn.addActionListener(this);
        top.add(btn);
        container.add(top);
        this.setContentPane(container);
        this.setVisible(true);
    }

    public void window2(){
        this.setTitle("ADHD");
        this.setSize(1000,700);
        this.setLocationRelativeTo(null);


        if (h < 11){

            boutonPane.panel2(h);
            bouton.addActionListener(this);
            boutonPane.add(bouton);
            this.add(boutonPane);
            this.setContentPane(boutonPane);
            updateWindow2();
        }
        this.setVisible(true);
    }

    public void updateWindow2(){
        boutonPane.panel2(h);
        this.revalidate();
        this.repaint();
    }

    public void actionPerformed(ActionEvent e){
        if ((JButton) e.getSource() == btn){
            System.out.println("pressed0");
            window2();
        }
        if ((JButton) e.getSource() == bouton){
            h++;
            System.out.println("pressed" + h);
            updateWindow2();
        }
    }

    public static void main(String[] args){
        Window w = new Window();
    }
}

面板

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;


public class Panel extends JPanel implements ActionListener{

    int m;
    int u=0;
    int lgi, lrgi;
    int [] ta;
    Timer timer1 = new Timer(300, this);

    Panel(){

    }

    public void panel2(int n){
        m=n;
        ta = new int [n];

        for(int it=0; it<m;it++){
            ta[it]=100*it;
        }

        timer1.start();
    }


    public void paintComponent(Graphics gr){
        super.paintComponent(gr);

        gr.setColor(Color.red);
        for(int i=0;i<m;i++){
            gr.fillOval(ta[i],ta[i], 150, 150);
        }
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        if(u<m){
            u++;
            revalidate();
            repaint();
        }
    }

}

1 个答案:

答案 0 :(得分:1)

您的代码需要使用两个int值来决定要绘制的圆圈数和时间:

  • 第一个int应该是要绘制的当前圆圈的数量,比如称为currentCirclesToDraw
  • 第二个int将是绘制总数的圆圈数。
  • 如果您使用我建议的List<Ellipse2D>,则此数字将是列表的大小。因此,如果列表被调用ellipseList,那么第二个数字将是ellipseList.size()
  • 第一个变量将在计时器中递增到列表的大小,但不会更大,并且paintComponent方法将使用它来决定要绘制的圆圈数。
  • 此处的关键点:按下按钮时,第一个数字currentCirclesToDraw必须重新设置为0。这样你的paintComponent方法将开始绘制0个圆,然后是1,然后是2,...

例如,paintComponent方法可能如下所示:

@Override
protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
  g2.setColor(CIRCLE_COLOR);
  for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
     g2.fill(ellipseList.get(i));
  }
}

我使用for循环条件语句中的第二个术语i < currentCirclesToDraw && i < ellipseList.size()作为额外的故障保护,以确保我们不会尝试在列表中绘制更多的圆圈。

我的计时器的ActionListener将增加currentCirclesToDraw变量并调用重绘。一旦currentCirclesToDraw达到ellipseList:

的大小,它就会停止Timer
private class TimerListener implements ActionListener {
  @Override
  public void actionPerformed(ActionEvent e) {
     if (currentCirclesToDraw < ellipseList.size()) {
        currentCirclesToDraw++;
        repaint();
     } else {
        // stop the Timer
        ((Timer)e.getSource()).stop();
     }
  }
}

我的按钮的actionPerformed方法会将currentCirclesToDraw重置为0,将新的Ellipse2D添加到我的ellipseList(如果我们还没有达到MAX_CIRCLE_INDEX),将调用repaint()来清除JPanel,并构建和启动计时器:

  public void actionPerformed(java.awt.event.ActionEvent arg0) {
     currentCirclesToDraw = 0;  // this is key -- reset the index used to control how many circles to draw
     if (ellipseList.size() < MAX_CIRCLE_INDEX) {
        double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
        double y = x;
        double w = CIRCLE_WIDTH;
        double h = CIRCLE_WIDTH;
        ellipseList.add(new Ellipse2D.Double(x, y, w, h));
     }
     repaint(); // clear image
     new Timer(TIMER_DELAY, new TimerListener()).start();
  };

修改 2014年3月30日 请注意,所有这些都可以像这样放在一起:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

/**
 * http://stackoverflow.com/a/22714405/522444
 * http://stackoverflow.com/questions/22712655/repaint-in-panel-method-not-updated
 * @author Pete
 *
 */
@SuppressWarnings("serial")
public class TimerCircles extends JPanel {
   private static final int PREF_W = 1000;
   private static final int PREF_H = 700;
   private static final Color CIRCLE_COLOR = Color.RED;
   public static final int MAX_CIRCLE_INDEX = 11;
   public static final int TIMER_DELAY = 300;
   public static final int CIRCLE_WIDTH = 100;
   private final List<Ellipse2D> ellipseList = new ArrayList<>();
   private int currentCirclesToDraw = 0;

   public TimerCircles() {
      add(new JButton(new ButtonAction("New Circle", KeyEvent.VK_C)));
   }

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

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setColor(CIRCLE_COLOR);
      for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
         g2.fill(ellipseList.get(i));
      }
   }

   private class ButtonAction extends AbstractAction {
      public ButtonAction(String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      public void actionPerformed(java.awt.event.ActionEvent arg0) {
         currentCirclesToDraw = 0;  // this is key -- reset the index used to control how many circles to draw
         if (ellipseList.size() < MAX_CIRCLE_INDEX) {
            double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
            double y = x;
            double w = CIRCLE_WIDTH;
            double h = CIRCLE_WIDTH;
            ellipseList.add(new Ellipse2D.Double(x, y, w, h));
         }
         repaint(); // clear image
         new Timer(TIMER_DELAY, new TimerListener()).start();
      };
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         if (currentCirclesToDraw < ellipseList.size()) {
            currentCirclesToDraw++;
            repaint();
         } else {
            // stop the Timer
            ((Timer)e.getSource()).stop();
         }
      }
   }

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

      JFrame frame = new JFrame("TimerCircles");
      frame.setDefaultCloseOperation(JFrame.EXIT_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();
         }
      });
   }
}