【问题标题】:Repaint in Panel method not updated面板方法中的重绘未更新
【发布时间】:2014-03-28 12:18:36
【问题描述】:

我正在尝试制作一个像这样工作的程序: 在 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();
        }
    }

}

【问题讨论】:

  • 如果您的程序产生错误,您应该发布该错误消息以供所有人查看。
  • 另外:请学习并遵循 Java 命名约定。方法和变量名称应以小写字母开头。还要给出有意义的变量名称。很难知道ta,或u,或lg代表什么变量。
  • 另外,当使用 for 循环遍历数组时,在条件语句中使用数组的长度字段来决定循环多少次。您当前的代码使用与 ta 数组的长度无关的 int 变量 u,导致您的代码从 for 循环中抛出 ArrayIndexOutOfBoundsException。
  • @peeskillet -- 你的答案去哪儿了?我对你的回答投的赞成票到哪里去了?
  • @Hovercraft -- 我更正了你告诉我的一切。对于变量u,我放了这个,因为它使paintComponent用actionPerformed中的当前变量重新绘制(因为我只在paintComponent中绘制了一个)。

标签: java swing timer jpanel actionlistener


【解决方案1】:

您的代码需要使用两个 int 值来决定要绘制多少个圆圈以及何时绘制:

  • 第一个 int 应该是当前要绘制的圆的计数,比如称为 currentCirclesToDraw
  • 第二个 int 是要绘制的圆圈总数。
  • 如果你像我建议的那样使用List&lt;Ellipse2D&gt;,那么这个数字就是列表的大小。所以如果列表被称为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 &lt; currentCirclesToDraw &amp;&amp; i &lt; ellipseList.size() 作为额外的故障保护,以确保我们不会尝试绘制比列表中更多的圆圈。

我的计时器的 ActionListener 会增加 currentCirclesToDraw 变量并调用 repaint。一旦 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();
         }
      });
   }
}

【讨论】:

    猜你喜欢
    • 2012-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多