Java 油漆组件是如何工作的?

Java 油漆组件是如何工作的?,java,swing,paintcomponent,Java,Swing,Paintcomponent,这可能是一个很难回答的问题。我刚刚开始学习Java 我不了解paintComponent方法的操作。我知道如果我想画一些东西,我必须重写paintComponent方法 public void paintComponent(Graphics g) { ... } 但是什么时候叫它?我从未见过像“object.paintComponent(g)”这样的东西,但它仍然是在程序运行时绘制的 图形参数是什么?它是从哪里来的?调用方法时必须提供参数。但正如我之前所说的,这个方法似乎永远不会被显式调

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

我不了解paintComponent方法的操作。我知道如果我想画一些东西,我必须重写paintComponent方法

public void paintComponent(Graphics g)
{
   ...
}
但是什么时候叫它?我从未见过像“object.paintComponent(g)”这样的东西,但它仍然是在程序运行时绘制的

图形参数是什么?它是从哪里来的?调用方法时必须提供参数。但正如我之前所说的,这个方法似乎永远不会被显式调用。那么谁提供这个参数呢?为什么我们要把它投给Graphics2D

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

GUI系统的内部调用该方法,并将
Graphics
参数作为图形上下文传递给您,您可以在其上进行绘制。

这里可以做两件事:

  • 阅读
  • 使用调试器并在paintComponent方法中放置断点。然后沿stacktrace向上移动,查看如何提供图形参数
  • 仅供参考,下面是我从最后发布的代码示例中获得的stacktrace:

    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 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)
    ),最终从其第一个“重量级父级”(剪裁到组件边界)获取图形它自己从相应的本地资源中获取

    考虑到您必须将
    图形
    转换为
    图形2d
    ,在使用窗口工具包时,
    图形
    实际上扩展了
    图形2d
    ,但您可以使用其他
    图形
    ,它们“不必”扩展
    图形2d
    (这种情况并不经常发生,但AWT/Swing允许您这样做)

    对您的问题的(非常)简短的回答是,
    paintComponent
    被称为“需要时”。有时更容易将Java Swing GUI系统视为一个“黑匣子”,在这个黑匣子中,许多内部构件都是在没有太多可见性的情况下处理的

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

    我使用Swing已经很多年了,我想我从来没有直接调用过
    paintComponent
    ,甚至没有见过直接从其他东西调用它。最近的一次是使用
    repaint()
    方法以编程方式触发某些组件的重新绘制(我假设它调用了下游正确的
    paintComponent
    方法


    根据我的经验,
    paintComponent
    很少被直接覆盖。我承认有一些定制的渲染任务需要这样的粒度,但Java Swing确实提供了一个一组强大的JComponents和布局,可以用来完成许多繁重的工作,而不必直接覆盖
    paintComponent
    。我想我在这里的目的是确保在您开始尝试滚动自己的自定义渲染组件之前,您不能对本机JComponents和布局做任何事情。

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

    相反,在创建面板时会自动调用此方法。
    paintComponent()
    方法也可以由
    Component
    类中定义的
    repaint()
    方法显式调用


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

    您可能需要重新定义方法
    void paintComponent(graphics g){
    如果您希望任何以前的绘图在组件上是永久性的。您需要通过像
    super.painComponent();
    这样显式调用升序类的方法来实现这一点。这样,java在任何时候都需要使用您正在保留更改的paintComponent方法


    这是因为,如果您不这样做,超类将通过调用自己的方法撤消您所做的一切,完全忽略任何更改。

    从开始。@trashgod总是很乐意发布一个具体的链接,该链接也在:-)中引用或者换句话说:总是先查看维基…@kleopatra:+1也更容易在下一个(不可避免的)链接消失时修复。@nubhi219无所谓:-)无论如何,你无法控制绘画的发生时间,内部是……嗯……内部,在应用程序开发中没有什么值得担心的,除了在非常罕见的情况下Hanks@GuillaumePolet抓住了这一点。正如你所说,我的术语在技术上是不正确的。我编辑了这篇文章以澄清。SeKa,因为你有与swing打了多年交道,你介意我问一下,swing/javafx是否值得未来的学生/求职者学习?swing/javafx是否经常使用?非常感谢你的建议。如果TestPaint没有扩展JPanel类,你会如何绘制JPanel?
    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();
                        }
                    }
    
    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);
        }
    }