标签移动时不调用paintComponent

时间:2013-08-13 13:38:51

标签: java swing paintcomponent

我有一个简单的 JPanel (otherJPanel),下面有一个标签,都位于另一个 JPanel (mainJPanel)中。 mainJPanel会覆盖paintComponent,并在其他地方手动调用repaint(),并动态设置标签文本和otherJPanel的背景。

但是,当我在otherJPanel内移动标签时,永远不会调用paintComponent。知道为什么吗?

为了测试这个,我创建了下面的测试项目,两个位置都有标签。但是,如果在处理循环中没有调用paintComponent,我做错了什么?

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;
    }
}

1 个答案:

答案 0 :(得分:5)

你在Swing事件线程上调用了Thread.sleep(...),这导致你的整个应用程序都处于睡眠状态:

        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();
        }

不要这样做;改为使用Swing Timer。

即,

     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 结尾处的 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方法仅用于绘画和绘画。它不应该用于设置程序或组件的状态有几个原因,包括你没有完全控制何时或甚至调用此方法(因为你发现),所以你的状态代码不应该依赖于它的调用。

  

每当调用重绘时,我应该如何更新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);
   }

}