Как работает paintComponent?
Это может быть очень пустой вопрос. Я только начинаю изучать Java
Я не понимаю работу метода paintComponent. Я знаю, хочу ли я что-то нарисовать, я должен переопределить метод paintComponent.
public void paintComponent(Graphics g)
{
...
}
Но когда он называется? Я никогда не вижу ничего подобного "object.paintComponent(g)", но все же он нарисован, когда программа запущена.
А что такое параметр Graphics? Откуда это? Параметр должен быть указан при вызове метода. Но, как я уже говорил, кажется, что этот метод никогда не будет явно вызван. Итак, кто предоставляет этот параметр? И почему мы должны передавать его в Graphics2D?
public void paintComponent(Graphics g)
{
...
Graphics2D g2= (Graphics2D) g;
...
}
Ответы
Ответ 1
(очень) короткий ответ на ваш вопрос заключается в том, что paintComponent
называется "когда это необходимо". Иногда легче подумать о GUI-системе Java Swing как о "черном ящике", где большая часть внутренних элементов обрабатывается без видимой видимости.
Существует ряд факторов, которые определяют, когда необходимо перекрасить компонент, начиная от перемещения, изменения размера, изменения фокуса, скрытия других кадров и т.д. и т.д. Многие из этих событий обнаруживаются автоматически, и paintComponent
вызывается внутренне, когда определено, что эта операция необходима.
Я работал с Swing в течение многих лет, и я не думаю, что я когда-либо назывался paintComponent
напрямую, или даже видел, что он вызван напрямую из чего-то другого. Самое близкое, что я пришел, это использовать методы repaint()
для программного запуска перерисовки определенных компонентов (которые, как я полагаю, называет правильные методы paintComponent
ниже по течению.
По моему опыту, paintComponent
редко переопределяется. Я признаю, что есть задачи персонализированного рендеринга, требующие такой детализации, но Java Swing предлагает (довольно) надежный набор JComponents и Layouts, который можно использовать для большей части тяжелой работы без прямого переопределения paintComponent
. Я предполагаю, что моя точка зрения состоит в том, чтобы убедиться, что вы не можете что-то сделать с родными JComponents и макетами перед тем, как уйти, пытаясь свернуть ваши собственные компоненты, создаваемые с помощью персонализированных компонентов.
Ответ 2
Две вещи, которые вы можете сделать здесь:
- Прочитайте Живопись в AWT и Swing
- Используйте отладчик и поставьте точку останова в методе 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
на 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);
}
}
Ответ 3
Внутренние системы GUI вызывают этот метод, и они передают параметр Graphics
в качестве графического контекста, на который вы можете рисовать.
Ответ 4
Вызов object.paintComponent(g)
- ошибка.
Вместо этого этот метод вызывается автоматически при создании панели. Метод paintComponent()
также может быть явно вызван методом repaint()
, определенным в классе Component
.
Эффект вызова repaint()
заключается в том, что Swing автоматически очищает графику на панели и выполняет метод paintComponent
для перерисовки графики на этой панели.