paintComponent如何工作?


69

这可能是一个非常菜鸟的问题。我才刚开始学习Java

我不了解paintComponent方法的操作。我知道是否要绘制东西,必须重写paintComponent方法。

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

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

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

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


2
@trashgod总是很友善地发布具体链接-摇摆标签Wiki中也引用了该链接:-)换句话说,总是先查看Wiki ...
kleopatra 2013年

@kleopatra:+1在下一个(不可避免的)链接消失中也更容易修复。
垃圾桶

Answers:


45

您的问题的(非常)简短的答案paintComponent是“何时需要”。有时,将Java Swing GUI系统视为“黑匣子”会更容易,在黑匣子中,许多内部处理都没有太多可见性。

有许多因素决定何时需要重新绘制组件,包括移动,重新调整大小,更改焦点,被其他框架隐藏等等。这些事件中有许多是自动魔术检测的,并paintComponent在确定需要进行此操作时在内部调用。

我与Swing合作多年,我不认为我曾经paintComponent直接,甚至可以看到它直接从别的东西叫。我最接近的repaint()方法是使用这些方法以编程方式触发某些组件的重绘(我认为这会在paintComponent下游调用正确的方法。

以我的经验,paintComponent很少被直接覆盖。我承认有些定制渲染任务需要这种粒度,但是Java Swing确实提供了(相当)健壮的JComponents和Layouts集,可用于执行许多繁重的工作而不必直接重写paintComponent。我想我的意思是要确保在尝试滚动自己的自定义呈现的组件之前,不能对本机JComponents和Layout做任何事情。


感谢@GuillaumePolet赶上了。正如您所说,我的条款在技术上不正确。我已编辑帖子以明确说明。
SeKa 2013年

2
SeKa,由于您已经从事Swing多年,您是否介意我问Swing / javafx是否值得向学生/求职者学习的东西?swing / javafx使用很多吗?非常感谢您的任何建议。
2016年

13

您可以在这里做两件事:

  1. 阅读AWT和Swing中的绘画
  2. 使用调试器,并在paintComponent方法中放置一个断点。然后沿stacktrace前进,并查看如何提供Graphics参数。

只是为了提供信息,这是我从最后发布的代码示例中获得的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参数来自此处:

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为a的事实,Graphics2D恰好是在使用Window Toolkit时,它Graphics实际上是可扩展的Graphics2D,但是您可以使用其他Graphics“不必”扩展的文件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);
    }
}

如果TestPaint不扩展JPanel类,您将如何绘制到JPanel?
Doug Hauf 2014年

3

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


1
@ nubhihi219没关系:-)无论如何,您都无法控制绘画发生的时间,内部是......好吧...内部,在应用程序开发中无需担心,除非在极少数情况下
kleopatra

2

呼叫object.paintComponent(g)是一个错误。

而是在创建面板时自动调用此方法。该paintComponent()方法也可以通过显式调用repaint()中定义的方法Component类。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.