java - llamar - ¿Cómo funciona PaintComponent?
paintcomponent java ejemplo (4)
Dos cosas que puedes hacer aquí:
- Leer pintura en AWT y Swing
- Use un depurador y ponga un punto de interrupción en el método paintComponent. Luego, recorra la stacktrace y vea cómo proporciona el parámetro Graphics.
Solo por información, esta es la pila de fichas que obtuve del ejemplo del código que publiqué al final:
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
El parámetro Graphics viene de aquí:
RepaintManager.paintDirtyRegions(Map) line: 781
El fragmento involucrado es el siguiente:
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();
}
}
Si le echas un vistazo, verás que recupera los gráficos del JComponent mismo (indirectamente con javax.swing.JComponent.safelyGetGraphics(Component, Component)
) que a su vez lo toma de su primer "padre de peso pesado" (recortado a los límites del componente) que él mismo toma de su recurso nativo correspondiente.
En cuanto al hecho de que tiene que transmitir los Graphics
a un Graphics2D
, sucede que al trabajar con el Window Toolkit, Graphics
realmente amplía Graphics2D
, sin embargo, podría usar otros Graphics
que no "tienen que" se extienden Graphics2D
(no ocurren muy a menudo pero AWT / Swing te permite hacer eso).
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);
}
}
Esta podría ser una pregunta muy novato. Estoy empezando a aprender Java
No entiendo el funcionamiento del método paintComponent. Sé que si quiero dibujar algo, debo anular el método paintComponent.
public void paintComponent(Graphics g)
{
...
}
Pero cuando se llama? Nunca veo algo como "object.paintComponent (g)" pero aún se dibuja cuando el programa se está ejecutando.
¿Y cuál es el parámetro Graphics? ¿De dónde es? El parámetro debe ser proporcionado cuando se llama al método. Pero como dije antes, parece que este método nunca se llama explícitamente. Entonces, ¿quién proporciona este parámetro? ¿Y por qué tenemos que lanzarlo a Graphics2D?
public void paintComponent(Graphics g)
{
...
Graphics2D g2= (Graphics2D) g;
...
}
La (muy) corta respuesta a su pregunta es que paintComponent
se llama "cuando debe ser". A veces es más fácil pensar en el sistema Java Swing GUI como una "caja negra", donde la mayoría de los internos se manejan sin demasiada visibilidad.
Hay una serie de factores que determinan cuándo se debe volver a pintar un componente, desde moverse, cambiar el tamaño, cambiar el enfoque, estar oculto por otros marcos, y así sucesivamente. Muchos de estos eventos se detectan de forma auto-mágica, y se llama internamente a paintComponent
cuando se determina que esa operación es necesaria.
He trabajado con Swing durante muchos años, y no creo haber llamado alguna paintComponent
directamente a paintComponent
, o incluso haberlo visto directamente desde otra cosa. Lo más cerca que he llegado es usando los métodos de repaint()
para activar mediante programación un repintado de ciertos componentes (que supongo que llama a los métodos paintComponent
correctos en paintComponent
descendente).
En mi experiencia, paintComponent
rara vez se reemplaza directamente. Admito que hay tareas de renderizado personalizadas que requieren dicha granularidad, pero Java Swing ofrece un conjunto (bastante) robusto de JComponents y Layouts que se pueden usar para hacer gran parte del trabajo pesado sin tener que anular directamente paintComponent
. Supongo que mi punto aquí es asegurarme de que no se puede hacer algo con JComponents y Layouts nativos antes de que intentes rodar tus propios componentes renderizados a medida.
Las partes internas del sistema GUI llaman a ese método, y pasan el parámetro Graphics
como un contexto gráfico sobre el que puede dibujar.
Llamar a object.paintComponent(g)
es un error.
En cambio, este método se llama automáticamente cuando se crea el panel. El método paintComponent()
también se puede llamar explícitamente mediante el método paintComponent()
definido en la clase Component
.
El efecto de llamar a repaint()
es que Swing borra automáticamente el gráfico en el panel y ejecuta el método paintComponent
para volver a dibujar los gráficos en este panel.