2

I have a simple JPanel (otherJPanel) with a label below, both inside another JPanel (mainJPanel). mainJPanel overrides paintComponent, and repaint() is called manually elsewhere and the label text and the background of otherJPanel are set dynamically.

However, when I move the label inside otherJPanel then paintComponent is never called. Any idea why?

To test this I have created the test project below with labels in both positions. However, with this paintComponent is not called within the processing loop, what am I doing wrong?

package uk.co.moons.gui.components.testapps;

import java.util.logging.Level;
import java.util.logging.Logger;

public class DisplayJFrame extends javax.swing.JFrame {

/** Creates new form DisplayJFrame */
public DisplayJFrame() {
    initComponents();
}

@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    displayJPanel1 = new uk.co.moons.gui.components.DisplayJPanel();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setName("Form"); // NOI18N

    displayJPanel1.setName("displayJPanel1"); // NOI18N
    getContentPane().add(displayJPanel1, java.awt.BorderLayout.CENTER);

    pack();
}// </editor-fold>                        


public void draw() {       
    displayJPanel1.revalidate();
    displayJPanel1.repaint();
}

public void setTO(TestObject to) {
    displayJPanel1.setTo(to);        
}

public static void main(String args[]) {
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(DisplayJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(DisplayJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(DisplayJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(DisplayJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {

        public void run() {
            DisplayJFrame df = new DisplayJFrame();
            df.setVisible(true);
            TestObject to = new TestObject();
            df.setTO(to);
            for (int i = 10; i < 20; i++) {
                to.setValue(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(DisplayJFrame.class.getName()).log(Level.SEVERE, null, ex);
                }

                df.draw();//df.repaint();
            }
        }
    });
}



// Variables declaration - do not modify                     
private uk.co.moons.gui.components.DisplayJPanel displayJPanel1;
// End of variables declaration                   
}







package uk.co.moons.gui.components;

import java.awt.Graphics;
import uk.co.moons.gui.components.testapps.TestObject;

public class DisplayJPanel extends javax.swing.JPanel {

    private TestObject to = null;

/** Creates new form DisplayJPanel */
public DisplayJPanel() {
    initComponents();
}

public TestObject getTo() {
    return to;
}

public void setTo(TestObject to) {
    this.to = to;
}

@Override
public void paintComponent(Graphics g) {
    System.out.println("paintComponent");
    super.paintComponents(g);
    if (to != null) {
        int i = to.getValue();
        System.out.println("paintComponent " + i);
        jLabel1.setText(String.valueOf(i));
        jLabel2.setText(String.valueOf(i + 1));
    }
}

@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

    jPanel1 = new javax.swing.JPanel();
    jLabel2 = new javax.swing.JLabel();
    jLabel1 = new javax.swing.JLabel();

    setName("Form"); // NOI18N
    setLayout(new java.awt.BorderLayout());

    jPanel1.setName("jPanel1"); // NOI18N
    jPanel1.setLayout(new java.awt.BorderLayout());

    jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
    org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(uk.co.moons.gui.controlpanel.ControlPanelApp.class).getContext().getResourceMap(DisplayJPanel.class);
    jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
    jLabel2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
    jLabel2.setName("jLabel2"); // NOI18N
    jPanel1.add(jLabel2, java.awt.BorderLayout.CENTER);

    add(jPanel1, java.awt.BorderLayout.CENTER);

    jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
    jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N
    jLabel1.setBorder(javax.swing.BorderFactory.createEtchedBorder());
    jLabel1.setName("jLabel1"); // NOI18N
    add(jLabel1, java.awt.BorderLayout.PAGE_END);
}// </editor-fold>
// Variables declaration - do not modify
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JPanel jPanel1;
// End of variables declaration
}



package uk.co.moons.gui.components.testapps;

public class TestObject {
    private int value=0;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}
4

1 回答 1

5

您正在调用Thread.sleep(...)导致整个应用程序休眠的 Swing 事件线程:

        for (int i = 10; i < 20; i++) {
           to.setValue(i);
           try {
              Thread.sleep(100);
           } catch (InterruptedException ex) {
              Logger.getLogger(DisplayJFrame.class.getName()).log(
                    Level.SEVERE, null, ex);
           }

           df.draw();
        }

不要这样做;改用摆动计时器。

IE,

     public void run() {
        final DisplayJFrame df = new DisplayJFrame();
        df.setVisible(true);
        final TestObject to = new TestObject();
        df.setTO(to);

        int timerDelay = 100;
        final int maxCount = 20;
        new Timer(timerDelay, new ActionListener() {
           private int count = 0;

           @Override
           public void actionPerformed(ActionEvent evt) {
              if (count < maxCount) {
                 df.repaint();
                 to.setValue(count);
                 count++;
              } else {
                 ((Timer) evt.getSource()).stop();
              }
           }
        }).start();
        // for (int i = 10; i < 20; i++) {
        // to.setValue(i);
        // try {
        // Thread.sleep(100);
        // } catch (InterruptedException ex) {
        // Logger.getLogger(DisplayJFrame.class.getName()).log(
        // Level.SEVERE, null, ex);
        // }
        //
        // df.draw();
        // }
     }

也是无关的问题,但super.paintComponents(g);不是该paintComponent(g)方法的超级。那在 paintComponent s 的末尾会让大吃一惊。

// TODO: delete this method
public void paintComponent(Graphics g) {
  System.out.println("paintComponent");
  // super.paintComponents(g); // *** !! No -- this is not the super method ***
  super.paintComponent(g);
  if (to != null) {
     int i = to.getValue();
     System.out.println("paintComponent " + i);

     // of course you'll never set a JLabel's text from within this method
     jLabel1.setText(String.valueOf(i));
     jLabel2.setText(String.valueOf(i + 1));
  }
}

编辑
您在评论中声明:

好的,为什么我不想在paintComponent 中设置JLabel 的文本?

paintComponent方法仅用于绘画和绘画。它不应该用于设置程序或组件的状态,原因有多种,包括您无法完全控制何时甚至是否调用此方法(正如您所发现的那样),因此您的状态代码不应该依赖于它被调用。

每当调用 repaint 时,我应该如何让 JLabel 更新?

同样,您不应使用重绘来触发此操作。JLabel 应该显示什么?为什么你觉得它必须在重绘时改变?


编辑 2

我认为paintComponent 是这样做的地方。我的显示包含许多嵌套组件的层次结构,其标签和背景不断变化。我应该从主处理循环中检索组件并在那里设置值,而不是使用paintComponent?

不,不要让外部类像这样直接操纵您的视图。而是将您的模型和视图分开,并使用诸如 PropertyChangeListener 之类的侦听器以及公共方法来为您执行此操作。

例如,您可以通过将 TestObject 的值字段设置为“绑定属性”来轻松更新 JLabel 文本。这可以通过给类一个 PropertyChangeSupport 对象(实际上因为这是 Swing,一个 SwingPropertyChangeSupport 对象)来轻松完成,并允许外部类监听值的变化,如下所示:

class DisplayJPanel extends javax.swing.JPanel {

   private TestObject to = null;

   public DisplayJPanel() {
      initComponents();
   }

   public TestObject getTo() {
      return to;
   }

   public void setTo(final TestObject to) {
      this.to = to;

      // add listener to listen and react to changes in to value's state
      to.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if (TestObject.VALUE.equals(evt.getPropertyName())) {
               setLabel2Text(String.valueOf(to.getValue()));
            }
         }
      });
   }

   private void initComponents() {

      // etc...

   }

   public void setLabel2Text(String text) {
      jLabel2.setText(text);
   }

   private javax.swing.JLabel jLabel2;
   private javax.swing.JPanel jPanel1;
}

class TestObject {
   public static final String VALUE = "value";
   private int value = 0;
   private SwingPropertyChangeSupport propChangeSupport = new SwingPropertyChangeSupport(this);

   public int getValue() {
      return value;
   }

   public void setValue(int value) {
      int oldValue = this.value;
      int newValue = value;
      this.value = newValue;

      propChangeSupport.firePropertyChange(VALUE, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      propChangeSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      propChangeSupport.removePropertyChangeListener(listener);
   }

}
于 2013-08-13T13:50:50.193 回答