自定义JComponent拒绝绘制(如果添加到ArrayList)

时间:2013-03-26 19:37:47

标签: java swing drawing paint jcomponent

这里是应该绘制JComponents的表单中的相关代码 - 这段代码创建并添加到表单中的JComponent并将其添加到ArrayList,因为我需要以后能够销毁组件。

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;

public class frmMain extends javax.swing.JFrame implements ActionListener {
  ArrayList<Pocitadlo> poc;
  Random rnd;
  /**
   * Creates new form frmMain
   */
  public frmMain() {
    initComponents();

    poc = new ArrayList<Pocitadlo>();
    rnd = new Random();

//    Pocitadlo tmp = new Pocitadlo(100, 40, 40, getFont());
//    tmp.addActionListener(this);
//    this.add(tmp);
//    tmp.start();
  }

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
    int val, x, y;

    val = 20;
    x = (this.getWidth() / 2) - 10 + rnd.nextInt(20);
    y = (this.getHeight() / 2) - 10 + rnd.nextInt(20);

    try{
      val = Integer.parseInt(txtCounterValue.getText());

      Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
      tmp.addActionListener(this);
      poc.add(tmp);
      tmp.setVisible(true);
      tmp.start();
      this.add(tmp);
      this.setTitle("Počet počítadiel: " + this.getComponentCount());
      repaint();
      tmp.repaint();
    } catch(Exception e){
      JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť     celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
    }
  }

  @Override
  public void actionPerformed(ActionEvent e){
    if(e.getActionCommand().equals("counterFinished")){
      Pocitadlo tmp = (Pocitadlo)e.getSource();

      poc.remove(tmp);
      this.setTitle("Počet počítadiel: " + poc.size());
      this.remove(tmp);
    }
  }

这里是JComponents的代码我正在添加并且预期会被绘制:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.Timer;

public class Pocitadlo extends JComponent implements MouseListener, ActionListener {
  private int maxValue, value;
  private Boolean isRunning, isDisplayed;
  private Timer valueTimer, blinkTimer;
  private ActionListener actionListener;
  private Font font;

  public Pocitadlo(int timeout, int x, int y, Font f){
    isRunning = false;
    isDisplayed = true;
    maxValue = value = timeout;

    valueTimer = new Timer(1000, this);
    valueTimer.setActionCommand("valueTimer");

    blinkTimer = new Timer(200, this);
    blinkTimer.setActionCommand("blinkTimer");

    this.setBounds(x, y, 100, 50);
  }

  public void start(){
    isRunning = true;
    valueTimer.start();
  }

  public void stop(){
    isRunning = false;
    valueTimer.stop();
  }

  @Override
  public void actionPerformed(ActionEvent e){
    if(e.getActionCommand().equals("valueTimer")){
      value--;

      if(actionListener != null){
        repaint();
        actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
      }

      if(value == 0 && actionListener != null){
        isRunning = false;
        valueTimer.stop();
        actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
      }
      if(value < maxValue / 2 && !blinkTimer.isRunning()) blinkTimer.start();
    }

    if(e.getActionCommand().equals("blinkTimer")){
      isDisplayed = !isDisplayed;
      repaint();
    }
  }

  public void addActionListener(ActionListener listener){
    actionListener = listener;
  }

  @Override
  public void mouseClicked(MouseEvent e) {
    value += 5000;
    if(value > maxValue) value = maxValue;
    repaint();
  }

  @Override
  public void paintComponent(Graphics g){
    g.setColor(Color.red);
    g.fillRect(getX(), getY(), getWidth(), getHeight());
    if(isDisplayed){
      g.setColor(Color.green);
      g.fillRect(getX(), getY(), getWidth(), getHeight());
      g.setColor(Color.black);
      //g.setFont(font);
      g.drawString(value + "/" + maxValue, 0, 0);
    }

    //super.paintComponent(g);
  }
}
  1. 这没有任何结果 - JComponents被创建并正确地添加到ArrayList,并且它的paintComponent方法运行,以及计时器事件,组件也在倒计时时至少从ArrayList中被正确删除达到0,但没有画任何东西,我不明白为什么。

  2. 请注意frmMain构造函数中的注释代码有效,如果取消注释,它会绘制JComponent ......好吧...只是矩形,不会绘制任何文本,但我想这是另一个问题,但是,如果你也愿意帮助那个问题,那我很高兴。

  3. 另外,如果我错误地使用了动作事件和听众,请忽略它,我是Java的初学者,并且想知道如何正确地做到这一点,但是后来< / b>,现在我只需要让这段代码工作,所以请关注这个问题,谢谢。

  4. 是的,有一些死机,比如传递给组件的字体,这些都是我试图让drawString工作的原因。

1 个答案:

答案 0 :(得分:3)

你的项目遭受了许多关于如何在Swing中进行绘画的误解。

因为(在我测试的时候)你没有提供完整的代码,我不得不做出一些假设。

您的期望是JFrame的内容窗格使用null布局。我发现很难看到你如何管理它并在框架上有其他组件。在任何情况下,我都会隔离一个单独的容器,作为Pocitadlo的“主要”视图。这允许您继续使用布局管理器来管理其他组件。

我有点担心您将整个Pocitadlo生成代码捕获到try-catch只是为了捕获数字转换。由于您已经应用了默认值,因此将转换捕获到其自己的try-catch中是合理的,如果失败则忽略它(根据需要显示消息)。现在我从外面进来,所以你可能会看到需要这个,它只是引起了我的注意......

您的valueTimer actionPerformed代码略显错误,因为blinkTimer即使在valueTimer完成后也可以继续运行。我通过将两个if语句合并到一个if-else语句而不是......

来更正此问题
if (value == 0 && actionListener != null) {
    isRunning = false;
    valueTimer.stop();
    actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
    blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
    blinkTimer.start();
}

我个人会在JPanel上使用JComponentJPanel开头是不透明的。

主要问题在于paintComponent方法。

  1. 您应该始终致电super.paintComponent,而对于不透明的组件,应首先调用它。
  2. 您似乎认为在绘画时必须更正组件位置,例如g.fillRect(getX(), getY(), getWidth(), getHeight());。这实际上是错误的。图形上下文已经被翻译,因此组件/图形的x / y位置现在等于0x0。请仔细阅读Painting in AWT and Swing以获得更好的解释,但基本上,这意味着,您绘制区域的左上角现在是0x0。
  3. 您似乎认为文字是从第一个字符的顶部/左角绘制的。这实际上是错误的。文本沿着它的基线绘制。您需要通过Font s上升值调整y位置来补偿此情况。
  4. import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.GridBagConstraints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.util.ArrayList;
    import java.util.Random;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestRepaint01 {
    
        public static void main(String[] args) {
            new TestRepaint01();
        }
    
        public TestRepaint01() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException ex) {
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
    
                    MainFrame frame = new MainFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setSize(200, 200);
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
    
            });
        }
    
        public class MainFrame extends javax.swing.JFrame implements ActionListener {
    
            ArrayList<Pocitadlo> poc;
            Random rnd;
            private JPanel body;
    
            /**
             * Creates new form frmMain
             */
            public MainFrame() {
    
                setLayout(new BorderLayout());
    
                poc = new ArrayList<Pocitadlo>();
                rnd = new Random();
    
                body = new JPanel(null);
                add(body);
                JButton btn = new JButton("Add");
                btn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        int val, x, y;
    
                        val = 20;
                        x = (getWidth() / 2) - 10 + rnd.nextInt(20);
                        y = (getHeight() / 2) - 10 + rnd.nextInt(20);
    
    //                    try {
    //                        val = Integer.parseInt(txtCounterValue.getText());
    
                            Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
                            tmp.addActionListener(MainFrame.this);
                            GridBagConstraints gbc = new GridBagConstraints();
                            gbc.gridwidth = GridBagConstraints.REMAINDER;
                            gbc.weightx = 1;
                            gbc.fill = GridBagConstraints.HORIZONTAL;
                            poc.add(tmp);
                            tmp.start();
                            body.add(tmp);
                            body.repaint();
                            setTitle("Počet počítadiel: " + getComponentCount());
    //                    } catch (Exception e) {
    //                        JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť     celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
    //                    }
                    }
                });
                add(btn, BorderLayout.SOUTH);
    
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals("counterFinished")) {
                    System.out.println("Counter finished");
                    Pocitadlo tmp = (Pocitadlo) e.getSource();
    
                    poc.remove(tmp);
                    this.setTitle("Počet počítadiel: " + poc.size());
                    body.remove(tmp);
    //                body.revalidate();
                    body.repaint();
                }
            }
    
        }
    
        public class Pocitadlo extends JPanel implements MouseListener, ActionListener {
    
            private int maxValue, value;
            private Boolean isRunning, isDisplayed;
            private Timer valueTimer, blinkTimer;
            private ActionListener actionListener;
            private Font font;
    
            public Pocitadlo(int timeout, int x, int y, Font f) {
                isRunning = false;
                isDisplayed = true;
                maxValue = value = timeout;
    
                valueTimer = new Timer(1000, this);
                valueTimer.setActionCommand("valueTimer");
    
                blinkTimer = new Timer(200, this);
                blinkTimer.setActionCommand("blinkTimer");
    
                this.setBounds(x, y, 100, 50);
            }
    
            @Override
            public void removeNotify() {
                super.removeNotify();
                stop();
                blinkTimer.stop();
            }
    
            public void start() {
                isRunning = true;
                valueTimer.start();
            }
    
            public void stop() {
                isRunning = false;
                valueTimer.stop();
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals("valueTimer")) {
                    value--;
                    System.out.println("value = " + value);
    
                    if (actionListener != null) {
                        repaint();
                        actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
                    }
    
                    if (value == 0 && actionListener != null) {
                        isRunning = false;
                        valueTimer.stop();
                        actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
                        blinkTimer.stop();
                    } else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
                        blinkTimer.start();
                    }
                }
    
                if (e.getActionCommand().equals("blinkTimer")) {
                    System.out.println("Blink");
                    isDisplayed = !isDisplayed;
                    repaint();
                }
            }
    
            public void addActionListener(ActionListener listener) {
                actionListener = listener;
            }
    
            @Override
            public void mouseClicked(MouseEvent e) {
                value += 5000;
                if (value > maxValue) {
                    value = maxValue;
                }
                repaint();
            }
    
            @Override
            public void mouseExited(MouseEvent e) {
            }        
    
            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                g.setColor(Color.red);
                // location bad
                g.fillRect(0, 0, getWidth(), getHeight());
                if (isDisplayed) {
                    g.setColor(Color.green);
                    g.fillRect(0, 0, getWidth(), getHeight());
                    g.setColor(Color.black);
                    //g.setFont(font);
                    FontMetrics fm = g.getFontMetrics();
                    g.drawString(value + "/" + maxValue, 0, fm.getAscent());
                }
    
                g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
    
            }
    
            @Override
            public void mousePressed(MouseEvent e) {
            }
    
            @Override
            public void mouseReleased(MouseEvent e) {
            }
    
            @Override
            public void mouseEntered(MouseEvent e) {
            }
    
        }
    
    }
    

    您遇到的任何问题都不是超级关键(当然,您的程序不能达到您想要的效果),并建议您在代码能力方面达到顶峰。我这样说,因为我犯了同样的错误......;)

    仔细查看2D GraphicsCustom Painting

    我还会看一下Code Conventions for the Java Programming Language因为如果你不跟随他们就会惹恼别人;)