paintComponent是如何工作的?

2022-08-31 12:42:01

这可能是一个非常新手的问题。我刚刚开始学习Java

我不明白paintComponent方法的操作。我知道如果我想画一些东西,我必须覆盖paintComponent方法。

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

但是它什么时候被叫到呢?我从来没有看到过像“object.paintComponent(g)”这样的东西,但是当程序运行时,它仍然被绘制出来。

什么是图形参数?它来自哪里?调用方法时必须提供参数。但正如我之前所说,似乎这种方法从未被明确调用。那么谁提供这个参数呢?为什么我们必须将其转换为Graphics2D?

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

答案 1

对你的问题的(非常)简短的回答是,这被称为“当它需要的时候”。有时,将Java Swing GUI系统视为“黑匣子”更容易,其中许多内部组件在处理时没有太多的可见性。paintComponent

有许多因素决定了何时需要重新绘制组件,包括移动、调整大小、更改焦点、被其他帧隐藏等。其中许多事件都是自动神奇地检测到的,并在确定该操作是必需的时在内部调用。paintComponent

我已经和Swing合作了很多年,我不认为我曾经直接打电话,甚至没有见过它直接从别的东西打电话。我最近最接近的是使用这些方法以编程方式触发某些组件的重绘(我假设这些组件在下游调用正确的方法)。paintComponentrepaint()paintComponent

根据我的经验,很少被直接覆盖。我承认有些自定义渲染任务需要这样的粒度,但是Java Swing确实提供了一组(相当)健壮的JComponents和布局,可用于完成大部分繁重的工作,而无需直接覆盖 。我想我在这里的观点是为了确保在你尝试推出自己的自定义渲染组件之前,你不能用原生的JComponents和布局做一些事情。paintComponentpaintComponent


答案 2

您可以在此处执行两项操作:

  1. 阅读AWT和秋千中的绘画
  2. 使用调试器并在 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  

图形参数来自此处:

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)

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

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

推荐