Dann wird das JPanel "bemalt", was relativ viel Zeit in Anspruch nimmt,
da es sich um eine große Farbmatrix handelt.
Wenn ich jetzt scrolle, wird die
1
adjustmentValueChanged()
Methode des AdjustmentListeners der aufrufenden JScrollBar aufgerufen,
die ein
1
repaint()
des JPanel zur Folge hat.
Zum Problem:
Lasse ich bei jedem repaint() des JPanel die gesamte Farbmatrix neu
malen, zeigt er beim scrollen alles schön ABER es dauert zu lang, bis
das gesamte JPanel neu gemalt ist.
Lasse ich das JPanel nicht neu malen beim scrollen, so wird dort, wohin
ich srolle nur weißer Hintergrund angezeigt.
Wie schaffe ich es auf einfachem Wege, dass das die Farbmatrix des
JPanel beim Scrollen überall zu sehen ist, ohne bei jedem Scrollen das
gesamte JPanel neu zeichnen zu müssen?
tom schrieb:> Wie schaffe ich es auf einfachem Wege, dass das die Farbmatrix des> JPanel beim Scrollen überall zu sehen ist, ohne bei jedem Scrollen das> gesamte JPanel neu zeichnen zu müssen?
Am einfachsten indem man (nicht nur beim scrollen) das Bild auf ein
BufferedImage zeichnet und immer wenn sich etwas geändert hat dieses neu
malt und (im AWT Thread) repaint() aufruft.
Dieses Bild kann man dann in der Paintmethode gemütlich ausgeben, und
muss auch nicht ständig neuzeichnen. Während das Bild gemalt wird,
könnte man sogar einen Loadingindicator zeigen...
Hallo Markus,
danke für die schnelle Antwort!
Zuerst mal klappt deine Lösung ziemlich gut.
Es gibt nur noch ein Problem:
Scrolle ich vertikal, so klappt alles super.
Scrolle ich aber horizontal, wird nur der letzte "Block" der Farbmatrix
in horizontaler Richtung hinten dran gesetzt.
Scrolle ich DANACH wieder vertikal, passiert das gleiche in vertikaler
Richtung.
Konstruktor JFrame:
1
...
2
CanvasPanel canvasPanel = new CanvasPanel();
3
ScrollPaneListener spListener = new ScrollPaneListener();
4
scrollPane = new JScrollPane(canvasPanel,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.getHorizontalScrollBar().addAdjustmentListener(spListener); scrollPane.getVerticalScrollBar().addAdjustmentListener(spListener);
5
...
6
[\code]
7
8
ScrollPaneListener:
9
[code]
10
class ScrollPaneListener implements AdjustmentListener{
11
public void adjustmentValueChanged(AdjustmentEvent e){
Hallo Läubi,
danke für den Vorschlag.
Hab leider noch nie mit BufferedImage gearbeitet.
Hast du ein Codeschnipsel, das mir deine Idee verdeutlicht?
Danke
Hier jetzt alle Zeichenaufrufe auf dem Objekt gImage!
18
z.B. gImage.drawLine(0,0,10,10);
19
****/
20
buffImage.set(img);
21
}
Die Signatur von setData ggf. an deine Bedürfnisse anpassen. Wenn du
dann in einem Swingworker in der execute Methode setData aufrufst und in
done dann repaint bleibt deine GUI reaktiv und blockiert auch nicht beim
ersten Aufruf, ebenso nicht beim scrollen.
Vllt noch mal eine Frage zu dem SwingWorker.
Wie benutze ich den? Die Oracle API hat mir nicht wirklich
weitergeholfen.
Mein Programm soll folgendes machen:
Es gibt ein Haupt-JPanel, darin befindet sich eine JScrollPane und ein
JButton. Die JScrollPane hält das CanvasPanel.
- Wird jetzt der Button gedrückt, werden die Daten geändert und das Bild
im CanvasPanel mit diesen neuen Daten neu gemalt.
- Zusätzlich gibt es im Haupt-JPanel einen per JButton aktivierbaren
Timer, der die neuen Daten aller x ms automatisch berechnet und das
CanvasPanel automatisch aktualisiert.
Hab halt bisher immer in der actionPerformed() des Timers repaint()
aufgerufen aber das ist mit meinem oben aufgeführten Code doch recht
langsam.
MainPanel:
1
...
2
button1.addActionListener(new ActionListener(){
3
4
public void actionPerformed(ActionEvent e){
5
daten = berechneNeueDaten();
6
canvasPanel.setDaten(daten)
7
canvasPanel.repaint();
8
}
9
});
10
11
button2.addActionListener(new ActionListener(){
12
13
Timer timer;
14
15
public void actionPerformed(ActionEvent e){
16
timer = new Timer(stepTime, new ActionListener(){
17
18
public void actionPerformed(ActionEvent e){
19
daten = berechneNeueDaten();
20
canvasPanel.setDaten(daten);
21
canvasPanel.repaint();
22
}
23
});
24
25
timer.start();
26
27
// blockiere AWT-Thread bis OK gedrückt wird
28
JOptionPane.showMessageDialog(parentFrame, "Timer mit OK stoppen", "",0);
29
30
timer.stop();
31
}
32
});
33
...
Wie könnte ich das ganze in einem SwingWorker realisieren, um deinen
obigen Code für CanvasPanel nutzen zu können?
Ich hab dir doch schon oben geschrieben wie man das (asyncron) machen
könnte.
Du must einfach mal deine Gedanken/den Code etwas Strukturieren.
Es gibt hier doch mehrere Einzelaufgaben, die man sehr schön trennen:
- Das Zeichnen an sich
- Das Darstellen eines bereits gezeichneten Bildes
- Eine Regelmäßige Änderung der Daten (Timerbasiert)
- Die Steuerung von Parametern über das UI
Aktuell mantscht du einfach alles zusammen und das gibt Probleme. Der
Swingworker ist z.B. eher für einmalige Tasks, nicht für wiederkehrende.
Für Wiederkehrende gibt es den Timer, der ist aber eben nicht dafür um
wiederkehrend lange Berechnungen zu machen. Dafür gibt es die
SceduledExecutors und zusammen mit meinem obigen Beispiel sollte das
dann auch gut zusammen spielen.
Danke für deine Hilfe.
Habe für mein Projekt leider nicht mehr die Zeit, um mich in
ScheduledExecutor einzuarbeiten. Werd daher wohl bei meiner
Timer-Variante bleiben müssen, auch wenn sie langsam ist.
Also nochmal vielen Dank.