Swing repaint() ruft paintComponent(g1d) nicht auf

Diskutiere repaint() ruft paintComponent(g1d) nicht auf im AWT, Swing, JavaFX & SWT Bereich.
L

liann

Hallo, ich habe gerade vermutlich einen blackout, aber nach 40 minuten im code und internet suchen , finde ich echt nichts ­čĄ».
repaint() wird bei dem Label augerufen, jedoch paintComponent(g1d) nicht:
Java:
// JLabel object draw
draw = new JLabel() {
            @Override
            protected void paintComponent(Graphics g1d) {
                super.paintComponent(g1d);
                draw((SunGraphics2D) g1d);
                System.out.println("painted!");
                // wird nicht aufgerufen
            }

            @Override
            public void repaint() {
                super.repaint();
                System.out.println("repainted draw!"); // wird immer aufegrufen
            }
        };

private void draw(SunGraphics2D g2) {
        g2 = (SunGraphics2D) g2.create();
        drawBackground(g2);
        drawGames(g2);
        g2.dispose();
// auch ohne create() und somit auch ohne dispose() klappt es nicht
    }

private void drawGames(Graphics2D g2) {
        for (Drawable drawable : games)
            if (drawable != null)
                drawable.draw(g2);
        for (Drawable drawable : games)
            if (drawable != null)
                drawable.drawInfoText(g2);
    // hier m├╝ssten keine Probleme sein: die gleichen methoden werden in anderen Klassen auch aufgerufen
    }

    private void drawBackground(SunGraphics2D g2) {
        g2.setColor(Color.darkGray);
        g2.fillRect(0, 0, getWidth(), getHeight());
    }
Wo ist mein Fehler??
 
U

user30

1. die super-Aufrufe sollten immer die letzte Anweisung sein,
2. repaint() solltest du nicht ├╝berschreiben, sondern nur aufrufen,
3. dispose() muss imho nicht aufgerufen werden,
 
L

liann

repaint() solltest du nicht ├╝berschreiben, sondern nur aufrufen,
gilt das f├╝r alle oder nur f├╝r das label zum zeichnen? Denn in meinem JFrame wird repaint() ├╝berschrieben mit:
Java:
@Override
public void repaint() {
    super.repaint();
    draw.repaint();
}
Das ├╝berschreiben im Label war nur da, um mir selber sicher zu gehen, dass die methode aufgerufen wird, was sie wird.
Die anderen Sachen habe ich kurz ge├Ąndert, jedoch klappt es immer noch nicht
 
L

liann

Okay also ich habe jetzt ein m├Âgliches Problem gefunden, wenn ich n├Ąmlich repaint() per knopfdruck aufrufe, klappt alles. Dann m├╝sste es an folgender Methode liegen. Ich wei├č aber nicht, was daran falsch sein sollte. Ich will n├Ąmlich einen ├ťbergang zwischen zwei JFrames erzeugen, der fl├╝ssig ist.
Java:
 public void transformTo(JFrame jframe) {
        while (jframe.getWidth() != getWidth() || jframe.getHeight() != getHeight()) {
            int x = jframe.getWidth() - getWidth();
            int y = jframe.getHeight() - getHeight();
            if (x < 0) {
                x++;
            } else if (x > 0) {
                x--;
            }
            if (y < 0) {
                y++;
            } else if (y > 0) {
                y--;
            }
            setSize(jframe.getWidth() - x, jframe.getHeight() - y);
            setLocationRelativeTo(null);
            repaint(); // ruft nicht warum auch immer paintComponent(g1d)
        }
}
     private void addBtn(){
         JButton b = new JButton();
         add(b);
         b.addActionListener(e -> repaint()); // ruft paintComponent(g1d) auf
     }
 
mihe7

mihe7

Ich wei├č aber nicht, was daran falsch sein sollte. Ich will n├Ąmlich einen ├ťbergang zwischen zwei JFrames erzeugen, der fl├╝ssig ist.
Das Problem ist, dass Deine transferTo-Methode im Event Dispatch Thread (EDT, aka UI-Thread) ausgef├╝hrt wird. So lange Deine Schleife l├Ąuft, wird sich Dein UI nicht aktualisieren. Du muss daf├╝r sorgen, dass die Schleife in einem anderen Thread ausgef├╝hrt wird, w├Ąhrend die Methoden, die das UI ver├Ąndern, im EDT laufen. Das repaint() am Ende der Schleife d├╝rfte ├╝berfl├╝ssig sein, da das Fenster bei einer Gr├Â├čen├Ąnderung sowieso neu gezeichnet werden muss.

Je nach Implementierung musst Du dann ber├╝cksichtigen, dass nun zwei Threads im Spiel sind und sich die Gr├Â├čen├Ąnderung nicht sofort auswirkt. D. h. die Pr├╝fung zu Beginn der Schleife w├╝rde ohne weitere Ma├čnahmen ggf. auf die alten Werte zugreifen.

Die einfachste Variante scheint mir hier ein Swing-Timer zu sein, auch wenn dieser nicht besonders pr├Ązise ist.
 
J

JustNobody

Jetzt m├Âchte ich auch einfach mal ein paar Punkte kommentieren.

1. die super-Aufrufe sollten immer die letzte Anweisung sein,
2. repaint() solltest du nicht ├╝berschreiben, sondern nur aufrufen,
3. dispose() muss imho nicht aufgerufen werden,
1. Also an welcher Stelle die super Aufrufe erfolgen ist nicht vorgegeben. Die Frage ist halt, ob und an welcher Stelle die Funktionalit├Ąt gew├╝nscht wird. Das ist also nicht so pauschal ausdr├╝ckbar.

2. In der Regel reicht es, repaint nur zu nutzen. Aber wenn die Funktionalit├Ąt erweitert werden soll, dann spricht nat├╝rlich auch nichts dagegen, repaint zu ├╝berschreiben. Die Frage ist also: was ist dieses draw? Die Logik scheint mir ein wenig verr├╝ckt zu sein, aber generell spricht nichts dagegen, auch an der Stelle etwas einzuf├╝gen. Aber darauf gehe ich sp├Ąter noch einmal ausf├╝hrlicher ein.

3. dispose() ist richtig, denn er hat ein create(), sprich er erzeugt die Graphics Instanz und muss daher diese auch entsorgen.
Aber wozu dient der create() Aufruf? Der ist aus meiner Sicht unn├Âtig. Die Instanz, die ├╝bergeben wird, kann direkt verwendet werden.

Generell ist wichtig, die Grundlagen zu verstehen. Was passiert da denn, wenn ein invalidate, repaint oder ├Ąhnliches gemacht wird - sei es aktiv im code oder indirekt durch das System?

Erst einmal: Was passiert da in so einem Programm? Man hat, wenn man es einfach betrachtet, einfach eine Schleife in einem Thread (EDT Thread), die Events abarbeitet. Events k├Ânnen sein: Tastendr├╝cke, Mausclicks, Mausbewegungen, Anforderungen, etwas zu tun, ... Das ist also wichtig, denn ein repaint ist eigentlich nichts anderes als: Wie packen ein Event an das Fenster in die Eventqueue, die sagt: Mal dich neu. Das findet somit erst statt, wenn das Event bearbeitet werden kann. So lange die Applikation also nicht zu dem Event kommt, passiert nichts. (Siehe Antwort von @mihe7)

Wenn ein Betriebssystem Fenster darstellt, dann interessiert es sich nicht f├╝r den Inhalt. F├╝r die Darstellung des Inhalts ist das Programm zust├Ąndig. Was aber das Betriebssystem interessiert ist: Ist das Fenster korrekt dargestellt. Das ist etwas, das sich z.B. ver├Ąndert, wenn ein anderes Fenster das Fenster Deiner Applikation ├╝berdeckt. Dann ist klar: Dann ist der Inhalt des Fensters in dem ├╝berlappten Bereich nicht mehr g├╝ltig. Das kann man auch erreichen, indem man invalidate aufruft und den Bereich angibt, der nicht mehr g├╝ltig sein soll.
Wenn das Betriebssystem nun so einen Bereich erkennt, dann folgt daraus, dass das Betriebssystem das Fenster auffordert, diesen Bereich neu zu malen.

Wenn man nun ein Programm schreibt, dann kann man das nutzen: immer, wenn sich Daten ├Ąndern, dann m├╝ssen die dargestellt werden. Also macht man z.B. ein repaint Aufruf auf einem Fenster.
Das sorgt dann daf├╝r, dass das ganze Fenster incl. aller Controls und so, neu gemalt wird. Um die Childs wird sich automatisch gek├╝mmert. Wenn das "draw" also sowas wie ein Child Element ist, ist dieses ├ťberschreiben unn├Âtig.
Und wenn eine Applikation mehrere Fenster hat, dann ist auch denkbar: Wenn ich beim Hauptfenster repaint aufrufe, dann soll auch auf einem / allen anderen Fenster repaint aufgerufen werden. Das w├Ąre eine valide Anforderung, die dann z.B. in einer so ├╝berschriebenen repaint Methode resultieren k├Ânnten.
 
L

liann

Erst einmal danke f├╝r di ganzen Antworten!
Hei├čt das jetzt, dass repaint() einfach ignoriert wird, da gerade eine andere Aufgabe ausgef├╝hrt wird? Und ich mache den ├ťbergang so, dass ich 2 Klassen habe, die beide von JFrame erben. Das eine ist sichtbar, das andere nicht. Wenn ich das Fenster "wechseln" will, zum Beispiel nach einem Mausklick von dem User, rufe ich beim Hauptfenster transformTo(secondWindow) auf. Allerdings wird nur ein kleines rechteck gezeichnet, was der gr├Â├če von vorher entspricht. Wenn ich jetzt das Fenster wirklich ├╝bermalen will, muss ich also was machen? Oder soll ich eine ganz andere Methode f├╝r den Zweck schreiben, die irgendwas anders macht?
 
U

user30

In der Regel reicht es, repaint nur zu nutzen. Aber wenn die Funktionalit├Ąt erweitert werden soll, dann spricht nat├╝rlich auch nichts dagegen, repaint zu ├╝berschreiben.
Was ist das denn f├╝r eine dumme Antwort? Da hat jemand das Prinzip von AWT noch nicht verstanden.... traurig.
 
mihe7

mihe7

Hei├čt das jetzt, dass repaint() einfach ignoriert wird, da gerade eine andere Aufgabe ausgef├╝hrt wird?
Ja, so lange die Aufgabe l├Ąuft. Stell Dir einen Stapel mit Aufgaben vor, der abgearbeitet werden muss. Swing (eigentlich AWT) arbeitet diesen Stapel ab. Eine dieser Aufgaben ist Deine Methode, die irgendwann mal drankommt. In dieser Methode erzeugst Du nun neue Aufgaben (repaint() -> mal mir das Fenster neu), die landen ganz unten im Stapel. Wenn Deine Methode (Aufgabe) erledigt ist, kommt die n├Ąchste dran, bis irgendwann dann auch das repaint() an der Reihe ist. Hinzu kommt, dass der Spa├č intern bzgl. der Anzeige optimiert wird (das aber nur nebenbei).

Hier mal was zusammengeschustert:
Java:
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Point;

import javax.swing.*;

public class Test {
    static class FrameTransform {
        public static final int DEFAULT_DELAY = 20;
        private final Timer timer;
        private final JFrame frame;
        private final Dimension from;
        private final Dimension to;
        private final int duration;

        private long startTime;

        public FrameTransform(JFrame frame, Dimension from, Dimension to, 
                              int duration) {
            this(frame, from, to, duration, DEFAULT_DELAY);
        }

        public FrameTransform(JFrame frame, Dimension from, Dimension to, 
                              int duration, int delay) {
            this.frame = frame;
            this.from = from;
            this.to = to;
            this.duration = duration;

            timer = new Timer(delay, e -> animate());
            timer.setRepeats(true);
        }

        public void start() {
            this.startTime = System.currentTimeMillis();
            frame.setVisible(true);
            frame.toFront();
            timer.start();
        }

        private void animate() {
            long elapsed = System.currentTimeMillis() - startTime; 
            if (elapsed >= duration) {
                resize(to);
                timer.stop();
            }

            int widthDiff = to.width - from.width;
            int heightDiff = to.height - from.height;
            int relevantDiff = Math.abs(widthDiff) > Math.abs(heightDiff) ?
                   widthDiff : heightDiff;

            double fraction = ((double) relevantDiff) / duration;

            int width = from.width + bound(elapsed * fraction, widthDiff);
            int height = from.height + bound(elapsed * fraction, heightDiff);

            resize(new Dimension(width, height));
        }

        private int bound(double value, int boundary) {
            return (int) (boundary < 0 
                    ? Math.max(boundary, value)
                    : Math.min(boundary, value));
        }

        private void resize(Dimension size) {
            Point center = GraphicsEnvironment
                     .getLocalGraphicsEnvironment()
                     .getCenterPoint();
                  
            int x = center.x - size.width / 2;
            int y = center.y - size.height / 2;
            frame.setBounds(x, y, size.width, size.height);
        }
    }

    private final Dimension defaultSize = new Dimension(800, 600);
    private final Dimension resizedSize = new Dimension(200, 200);


    public void run() {
        final JFrame frame = createFrame("The Frame");

        JButton button = new JButton("Animate");
        button.addActionListener(e -> {
            Dimension size = frame.getSize();
            Dimension from = size.equals(defaultSize) ? defaultSize : resizedSize;
            Dimension to = size.equals(defaultSize) ? resizedSize : defaultSize;

            new FrameTransform(frame, from, to, 5000).start();
        });

        frame.add(button);
        frame.setVisible(true);
    }

    private JFrame createFrame(String title) {
        JFrame frame = new JFrame(title);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(defaultSize);
        frame.setLocationRelativeTo(null);
        return frame;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
J

JustNobody

Was ist das denn f├╝r eine dumme Antwort? Da hat jemand das Prinzip von AWT noch nicht verstanden.... traurig.
Wenn Du Deine Aussage etwas ausf├╝hren k├Ânntest anstatt nur so rum zu poltern.

Wenn die Ersteller der Meinung gewesen w├Ąren, dass repaint generell nicht ├╝berschrieben werden sollte, dann w├Ąre die Methode wohl final.

Wenn an der Funktionalit├Ąt von Repaint eine ├änderung notwendig w├Ąre: Warum ist es aus Deiner Sicht nicht legitim, dies zu ├╝berschreiben?
(Au├čer nat├╝rlich, dass Du dieser Meinung bist und Du nat├╝rlich unfehlbar bist .... Aber ich denke mal eher, dass Du irgendwelche Texte einfach nicht richtig verstanden hast wie so oft... repaint wird nicht ├╝berschrieben, um irgendwelche Controls zu malen. Evtl. willst Du schlicht auf sowas hinaus.... aber das ist auch nicht gemacht worden, sondern es ging um reine repaint Funktionalit├Ąten ...)
 
mihe7

mihe7

Danke an alle, ich merke mir: Timer > Thread.sleep(long), danke
Achtung: es gibt zwei Timer-Klassen (java.util.Timer und javax.swing.Timer). Der hier verwendete Swing-Timer ist halt eine M├Âglichkeit, um in mehr oder weniger regelm├Ą├čigen Abst├Ąnden Code im EDT auszuf├╝hren. Der Code selbst l├Ąuft also nicht nebenher, so dass das UI blockiert, so lange animate() l├Ąuft. Wenn Du wirklich Aufgaben von langer Dauer ausf├╝hren musst, kannst Du z. B. SwingWorker verwenden oder selbst einen Thread erzeugen.
 
Thema: 

repaint() ruft paintComponent(g1d) nicht auf

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben