【问题标题】:Java repaint method does not work when called from another class从另一个类调用时,Java 重绘方法不起作用
【发布时间】:2010-11-26 02:31:22
【问题描述】:

我已经使用 Netbeans 编写 Java 代码大约一年了,并且已经编写了很多数据操作代码,这些代码可以在屏幕上绘制图形。我一般在我的主窗口中植入一个JPanel对象,编写自定义绘画代码,根据需要调用repaint()方法。

但是今天,我第一次尝试从包含面板的类(对象)调用面板上的重绘。虽然编译器并没有发现任何问题,并且在调试模式下,它正确地单步执行了对repaint的外部调用,实际上并没有发生repaint,代码也没有真正进入repaint方法。

我写了一个极简程序来演示这个问题,如下所示(Main 被省略,因为它只包含设置两个屏幕面板的代码。)

---类的描述,首先包含绘图表面,其他重绘调用---

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Panel1 extends JComponent
{
   GraphPnl graphPnl;
   boolean colorFlag;

   public Panel1()
   {
     setLayout(null);
     colorFlag = true;

     graphPnl = new GraphPnl();
     graphPnl.setBounds(10, 10, 110, 110);
     graphPnl.setBackground(Color.black);
     add(graphPnl);

}//Panel1()

public class GraphPnl extends JPanel
{
  //just draws a line segment, toggling color

  @Override
  public void paint(Graphics g)
  {
      super.paint(g);
      Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);

      if (colorFlag) {g2.setColor(Color.red);} else {g2.setColor(Color.green);}
      g2.drawLine(10, 10, 50, 50);
   }//paint
 }//GraphPnl
}//Panel1

import javax.swing.*;
import java.awt.event.*;

public class Panel2 extends JComponent
{
   JButton testBtn;
   TestAction testAction;
   Panel1 p1;

   public Panel2()
   {
      p1 = new Panel1();
      testBtn = new JButton("Click");
      testBtn.setBounds(10, 10, 80, 30);
      add(testBtn);
      testAction = new TestAction();
      testBtn.addActionListener(testAction);
   }//Panel2()


   public class TestAction implements ActionListener
   {
     public void actionPerformed(ActionEvent evt)
     {
       p1.colorFlag = ! p1.colorFlag;
       p1.graphPnl.repaint();
     }
   }//TestAction
}//Panel2

如果有人对此有任何见解,或者知道解决方法,我会很高兴听到 从你那里。

提前感谢您提供任何见解。

约翰·多纳

【问题讨论】:

  • 当然,窗口 1 的 repaint() 方法将在窗口 2 获得焦点时起作用。多线程与此无关,实际上与您想要的完全相反。所有 Swing 更新都应在 EDT 上完成。您接受的答案中的 cmets 毫无意义。提供的其他答案为您提供了问题的真正解决方案(基于您提供的有限代码,因为您尚未发布 SSCCE)。祝你好运。

标签: java swing methods repaint


【解决方案1】:

Main 被省略,因为它只包含设置两个屏幕面板的代码。)

好吧,根据定义,当您遇到问题时,在问题解决之前您不知道什么代码是相对的或不相关的。所以应该发布一个完整的SSCCE

作为一个疯狂的猜测,我会说你的组件的大小为 0,所以没有什么可以绘制的。

我一般在我的主窗口中植入一个JPanel对象,编写自定义绘画代码,根据需要调用repaint()方法

您可能很幸运,因为您将面板添加到 BorderLayout 的中心,这会自动为面板提供框架可用的所有空间。

trashgod 的示例展示了一种设置自定义组件首选大小的方法。另一种方法是重写 getPreferredSize() 方法以返回正确的值。

你真的应该学习如何使用布局管理器而不是使用空布局,你将在未来避免类似的问题。除非您有拖放类型的应用程序,否则无需使用空布局。

【讨论】:

  • 我碰巧喜欢空布局过程。我创建了很多非常自定义的 GUI,并尝试了一段时间的各种布局,但现在我可以在一个小时左右的时间内生成一个非常复杂的界面,如果我想改变它,我可以完全按照我的方式挤压移动东西觉得合适。在我看来,Swing 布局是 Java 中最薄弱的部分。
  • 这是所有新手的想法,因为他们不了解布局管理器。创建一个复杂的界面只需要几分钟,而不是几个小时。除了编程不是关于创建代码需要多长时间,而是关于维护代码和布局管理器使下一个人更容易。例如,如果您有一排 10 个组件,并且您需要在开始时添加一个组件。如果您使用 FlowLayout 它是一行代码。使用空布局,您需要调整原始 10 个组件的边界,因为它们现在向右移动。
【解决方案2】:

“Swing 程序应该覆盖paintComponent(),而不是覆盖paint()。”—Painting in AWT and Swing: The Paint Methods

main 被省略,因为它只包含设置两个屏幕面板的代码。

验证您是否在 EDT 上构建 GUI,如文章 Initial Threads 中所示。

附录:这是一个展示这两个原则的示例:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

/** @see http://stackoverflow.com/questions/4282159 */
public class GraphPanel extends JPanel {

    private boolean colorFlag;

    public GraphPanel() {
        this.setPreferredSize(new Dimension(640, 480));
    }

    public void toggle() {
        colorFlag = !colorFlag;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        if (colorFlag) {
            g2.setColor(Color.red);
        } else {
            g2.setColor(Color.blue);
        }
        g2.drawLine(0, 0, getWidth(), getHeight());
    }

    private void display() {
        JFrame f = new JFrame("GraphPanel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this, BorderLayout.CENTER);
        f.add(new ControlPanel(this), BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new GraphPanel().display();
            }
        });
    }
}

class ControlPanel extends JPanel {

    public ControlPanel(final GraphPanel gp) {
        this.add(new JButton(new AbstractAction("Click") {

            @Override
            public void actionPerformed(ActionEvent e) {
                gp.toggle();
                gp.repaint();
            }
        }));
    }
}

附录:正如@camickr 的评论中所述,A Visual Guide to Layout Managers 可能有助于指导您的布局选择。

【讨论】:

  • +1,获取示例和教程链接。现在海报也可以阅读使用布局管理器,我不必发布链接:-)
【解决方案3】:

我相信,当您绘制 JComponent 时,剪辑区域会设置为该 JComponent。因此,如果其他组件尝试绘制(或者如果您调用它们的重绘),它们将不会,因为剪辑。

【讨论】:

  • 我想你可能是对的。我尝试根据上述答案修改简单示例,并得到相同的结果。因此,如果您在屏幕上创建两个单独的窗口,则在窗口 2 具有焦点时调用窗口 1 的重绘方法将不起作用。也许有办法通过使用多个线程来解决这个问题,我会继续研究。
  • 我建议不要采用多线程方法,因为 Swing 在很大程度上不是多线程的。相反,您可能应该研究脏区的概念。每个窗口都应注意其窗口的某些部分何时变脏。然后你应该让重绘机制被自动调用。详情请见java.sun.com/products/jfc/tsc/articles/painting
猜你喜欢
  • 1970-01-01
  • 2016-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-01
  • 2021-09-25
  • 1970-01-01
相关资源
最近更新 更多