Java 油漆组件是如何工作的?
这可能是一个很难回答的问题。我刚刚开始学习Java 我不了解paintComponent方法的操作。我知道如果我想画一些东西,我必须重写paintComponent方法Java 油漆组件是如何工作的?,java,swing,paintcomponent,Java,Swing,Paintcomponent,这可能是一个很难回答的问题。我刚刚开始学习Java 我不了解paintComponent方法的操作。我知道如果我想画一些东西,我必须重写paintComponent方法 public void paintComponent(Graphics g) { ... } 但是什么时候叫它?我从未见过像object.PaintComponent这样的东西,但它仍然是在程序运行时绘制的 图形参数是什么?它是从哪里来的?调用方法时必须提供参数。但正如我之前所说的,这个方法似乎永远不会被显式调用。那么谁
public void paintComponent(Graphics g)
{
...
}
但是什么时候叫它?我从未见过像object.PaintComponent这样的东西,但它仍然是在程序运行时绘制的
图形参数是什么?它是从哪里来的?调用方法时必须提供参数。但正如我之前所说的,这个方法似乎永远不会被显式调用。那么谁提供这个参数呢?为什么我们要把它投给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();
}
}
如果您查看它,您将看到它使用javax.swing.JComponent.safelyGetGraphicsComponent间接地从JComponent本身检索图形,该组件本身最终从其第一个重量级父级剪辑到它自己从其相应的本机资源获取的组件边界
关于必须将图形强制转换为Graphics2D这一事实,在使用Windows Toolkit时,图形实际上扩展了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);
}
}
对您的问题的非常简短的回答是,paintComponent在需要时被调用。有时更容易将JavaSwingGUI系统看作一个黑匣子,在这个黑匣子中,许多内部构件都是在没有太多可见性的情况下处理的 有许多因素可以决定何时需要重新绘制组件,包括移动、重新调整大小、更改焦点、被其他帧隐藏等等。这些事件中有许多是自动神奇地检测到的,当确定需要该操作时,会在内部调用paintComponent 我使用Swing已经很多年了,我想我从来没有直接调用过paintComponent,甚至没有见过直接从其他东西调用它。我最接近的方法是使用repait方法以编程方式触发某些组件的重新绘制,我假设这些组件调用了正确的paintComponent方法 根据我的经验,paintComponent很少被直接覆盖。我承认有一些定制的渲染任务需要这样的粒度,但是JavaSwing确实提供了一组相当健壮的JComponent和布局,可以用来完成很多繁重的工作,而不必直接重写这个组件。我想我的观点是,在您开始尝试滚动自己的自定义渲染组件之前,确保您不能对本机JComponents和布局执行某些操作。调用object.paintcomponent是一个错误 而是在创建面板时自动调用此方法。组件类中定义的repaint方法也可以显式调用paintComponent方法
调用repaint的效果是Swing会自动清除面板上的图形,并执行paintComponent方法来重新绘制此面板上的图形。如果希望组件上的任何以前的图形都是永久性的,则可能必须重新定义void paintComponentGraphics g{}方法。您需要通过像super.painComponent;一样显式调用升序类的方法来实现这一点;。这样,java在任何时候都需要使用paintComponent方法来保持所做的更改
这可以通过这样一个事实来解释:如果您不这样做,超类将通过调用它自己的方法来撤销您所做的一切,完全忽略任何更改。从开始。@trashgod总是很乐意发布一个具体的链接,该链接也在:-中引用,或者换句话说:总是先查看wiki…@kleopatra:+1在下一个不可避免的链接消失时也更容易修复。@nubhi219无所谓:-你无法控制绘画的发生时间总之,内部是。。。好在应用程序开发中,没有什么值得担心的,除了在非常罕见的情况下,hanks@GuillaumePolet抓住了这一点。正如你所说,我的条件在技术上是不正确的。我编辑这篇文章是为了澄清。SeKa,既然你已经在swing工作多年,你介意我问一下,swing/javafx是否值得学生/求职者学习?swing/javafx是否经常使用?非常感谢您的建议。如果TestPaint没有扩展 JPanel类?
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);
}
}