paintComponent如何工作?

这可能是一个很小的问题。 我刚刚开始学习Java

我不明白paintComponent方法的操作。 我知道如果我想绘制的东西,我必须重写paintComponent方法。

public void paintComponent(Graphics g) { ... } 

但是什么时候叫? 我从来没有看到任何像“object.paintComponent(g)”,但它仍然是在程序运行时绘制的。

什么是graphics参数? 这个从哪里来? 调用方法时必须提供参数。 但正如我之前所说,似乎这种方法从来没有被明确地称为。 那么谁提供这个参数? 为什么我们必须将其投射到Graphics2D?

 public void paintComponent(Graphics g) { ... Graphics2D g2= (Graphics2D) g; ... } 

你的问题的(非常)简短的答案是, paintComponent被称为“当它需要”。 有时候把Java Swing GUI系统看作是一个“黑匣子”,在这个“黑匣子”中,很多内部处理都没有太多的可见性。

确定组件何时需要重新绘制的因素有很多,从移动,重新resize,改变焦点,被其他框架隐藏等等等等。 这些事件中的很多事件都是自动检测到的,并且在确定需要执行该操作时,会在内部调用paintComponent

我和Swing一起工作了很多年,我不认为我曾经直接调用paintComponent ,甚至直接从别的东西中直接调用paintComponent 。 最接近的是使用repaint()方法以编程方式触发重新绘制某些组件(我假设调用下游正确的paintComponent方法。

根据我的经验, paintComponent很less被直接覆盖。 我承认有自定义渲染任务需要这样的粒度,但是Java Swing确实提供了(相当)强大的一组JComponents和Layouts,可以用来完成许多繁重的工作,而无需直接覆盖paintComponent 。 我想这里的重点是确保你不能在使用本地JComponents和Layouts的时候尝试使用自定义渲染的组件。

你可以在这里做两件事:

  1. 在AWT和Swing中阅读绘画
  2. 使用debugging器并在paintComponent方法中放置一个断点。 然后沿着堆栈跟踪,看看如何提供Graphics参数。

仅供参考,下面是我在最后发布的代码示例中获得的堆栈跟踪:

 Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 15 in TestPaint)) TestPaint.paintComponent(Graphics) line: 15 TestPaint(JComponent).paint(Graphics) line: 1054 JPanel(JComponent).paintChildren(Graphics) line: 887 JPanel(JComponent).paint(Graphics) line: 1063 JLayeredPane(JComponent).paintChildren(Graphics) line: 887 JLayeredPane(JComponent).paint(Graphics) line: 1063 JLayeredPane.paint(Graphics) line: 585 JRootPane(JComponent).paintChildren(Graphics) line: 887 JRootPane(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5228 RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482 RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413 RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206 JRootPane(JComponent).paint(Graphics) line: 1040 GraphicsCallback$PaintCallback.run(Component, Graphics) line: 39 GraphicsCallback$PaintCallback(SunGraphicsCallback).runOneComponent(Component, Rectangle, Graphics, Shape, int) line: 78 GraphicsCallback$PaintCallback(SunGraphicsCallback).runComponents(Component[], Graphics, int) line: 115 JFrame(Container).paint(Graphics) line: 1967 JFrame(Window).paint(Graphics) line: 3867 RepaintManager.paintDirtyRegions(Map<Component,Rectangle>) line: 781 RepaintManager.paintDirtyRegions() line: 728 RepaintManager.prePaintDirtyRegions() line: 677 RepaintManager.access$700(RepaintManager) line: 59 RepaintManager$ProcessingRunnable.run() line: 1621 InvocationEvent.dispatch() line: 251 EventQueue.dispatchEventImpl(AWTEvent, Object) line: 705 EventQueue.access$000(EventQueue, AWTEvent, Object) line: 101 EventQueue$3.run() line: 666 EventQueue$3.run() line: 664 AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method] ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 76 EventQueue.dispatchEvent(AWTEvent) line: 675 EventDispatchThread.pumpOneEventForFilters(int) line: 211 EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 128 EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 117 EventDispatchThread.pumpEvents(int, Conditional) line: 113 EventDispatchThread.pumpEvents(Conditional) line: 105 EventDispatchThread.run() line: 90 

Graphics参数来自这里:

 RepaintManager.paintDirtyRegions(Map) line: 781 

涉及的片段如下:

 Graphics g = JComponent.safelyGetGraphics( dirtyComponent, dirtyComponent); // If the Graphics goes away, it means someone disposed of // the window, don't do anything. if (g != null) { g.setClip(rect.x, rect.y, rect.width, rect.height); try { dirtyComponent.paint(g); // This will eventually call paintComponent() } finally { g.dispose(); } } 

如果你看看它,你会看到它从JComponent本身(间接使用javax.swing.JComponent.safelyGetGraphics(Component, Component) )获取graphics,它本身最终从它的第一个“重量级父级”到组件边界),它自己从相应的本地资源中获取它。

关于你必须将Graphics转换为Graphics2D的事实,只是在使用Window Toolkit时, Graphics实际上扩展了Graphics2D ,但是你可以使用其他的“不必”扩展Graphics2D (它不会经常发生,但AWT / Swing允许你这样做)。

 import java.awt.Color; import java.awt.Graphics; import javax.swing.JFrame; import javax.swing.JPanel; class TestPaint extends JPanel { public TestPaint() { setBackground(Color.WHITE); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(0, 0, getWidth(), getHeight()); } public static void main(String[] args) { JFrame jFrame = new JFrame(); jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setSize(300, 300); jFrame.add(new TestPaint()); jFrame.setVisible(true); } } 

GUI系统的内部调用该方法,并将Graphics参数作为graphics上下文传入,以便绘制该graphics上下文。

调用object.paintComponent(g)是一个错误。

相反,这个方法是在面板创build时自动调用的。 paintComponent()方法也可以通过Component类中定义的repaint()方法显式调用。

调用repaint()的效果是,Swing自动清除面板上的graphics,并执行paintComponent方法重绘此面板上的graphics。