Ritprocedur och trådaspekter

Det finns några artiklar hos Sun som behandlar detta område.
Här kan du läsa om ritprocedurer http://java.sun.com/products/jfc/tsc/articles/painting/.
Här är tre artiklar om trådar:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads3.html

Trådar och Swing

Det är absolut nödvändigt att följa riktlinjerna i detta avsnitt, annars riskerar man att användargränssnittet blir långsamt och beter sig konstigt.

Swing klarar inte mer än en tråd

Swing är, av prestandamotiv, inte skrivet för flertrådade system.

Efter att någon av metoderna pack(), show() eller setVisible(true) anropats får ingen annan tråd än "event-dispatch thread" påverka utseendet hos en komponent eller något av dess barn.

Vad är event-dispatch thread? Det är den tråd som skickar till exempel mus- och tangentbordshändelser samt hanterar omritningar begärda med repaint() och revalidate(). Alla dessa begärda arbeten ligger köade i "event queue" och utförs i tur och ordning av ovan nämnda event-dispatch thread.

Det finns några undantag till regeln ovan, det viktigaste är att metoderna repaint(), revalidate() och invalidate() kan anropas från vilken tråd som helst.

Hur lägga något i händelsekön?

Oftast behöver vi inte uppdatera en komponent på något annat sätt än med ovan nämnda godkända metoder. Problem uppstår dock om vi till exempel vill uppdatera en komponents utseende på grund av något annat än en mus- eller tangenttryckning eller om vi vill utföra någon uppgift som tar så lång tid att det är olämpligt att blockera event-dispatch thread med den.

Ett jobb kan placeras i event queue med de statiska metoderna SwingUtilities.invokeLater() och SwingUtilities.invokeAndWait(). Båda tar ett objekt av Runnable som parameter och lägger objektets run() i event queue. Därifrån kommer den sedan att hämtas och köras av event-dispath thread, alltså inte i en ny tråd. Runnable används bara för att få en run()-metod, inte för att skapa en tråd. InvokeLater() returnerar omedelbart och vi har ingen aning om när koden verkligen körs, invokeAndWait() returnerar när koden körts.

Om en tidsödande uppgift ska utföras som respons på en inmatning från användaren måste vi dra igång en ny tråd som utför uppgiften eftersom vi annars skulle blockera event-dispatch thread och därmed hela grafikhanteringen. Här är ett exempel på ett program som gör just det LongOperation.java.

Ritproceduren

I mycket korta drag ritas en komponent på följande vis.

Fall 1 Ritandet börjar med att en tungviktskomponent (till exempel JFrame, JDialog eller JApplet) ska ritas om.

  1. Event-dispatch thread anropar paint() på tungviktaren.
  2. Tungviktarens paint(), dvs Container.paint(), anropar paint() på alla sina barn.
  3. Om barnet är en JComponent utförs följande av JComponent.paint()
    1. Anropa paintComponent(). Den ritar om aktuell komponent genom att anropa update() i komponentens UI-delegate, vilken ritar komponentens bakgrund (om den inte är genomskinlig) och därefter i sin tur anropar paint() i UI-delegate som slutligen ritar komponenten.
    2. Anropa paintBorder() för att rita komponentens kanter.
    3. Anropa paintChildren(), vilken anropar paint() i alla komponentens barn som behöver ritas om.
Fall 2 Ritandet börjar med att repaint() anropas på en JComponent.
  1. JComponent.repaint() lägger in en begäran i RepaintManagers kö för omritningar. RepaintManager kommer i sinom tid att skicka begäran till event queue.
  2. När begäran i sinom tid exekveras av event-dispatch thread anropas paintImmediately() i komponenten, vilken utför följande:
    1. Med hjälp av den yta som angavs för omritning vid anropet av repaint() och egenskaperna opaque och optimizedDrawingEnabled avgörs vid vilken av aktuell komponents förfäder omritningen måste börja.
    2. Anropa paint() i den komponent där omritningen ska börja, varvid paintComponent(), paintBorder() och paintChildren() anropas enligt ovan.

Regler för ritning

För att inte skärmen ska se konstig ut måste ritandet ske enligt vissa regler, här är de viktigaste. Om ditt program beter sig konstigt kan det vara bra att kolla att du uppfyller dessa regler. Här följer två exempel på grafik. Det är primitiva tillämpningar av drag-and-drop och rubberbanding.
I det här exemplet kan du klicka i rektangeln och flytta den, MoveGraphics.java. Här kan du klicka och dra med musen nedtryckt, ett streck ritas då från den punkt du tryckte ned musen till aktuell position, RubberBand.java. Eftersom vi inte ännu avhandlat de inblandade fönstren kanske det kan vara idé att återvända till dessa exempel senare.

Valideringsproceduren

JComponent innehåller metoderna invalidate(), validate() och revalidate(), vilka hanterar omritning av en komponent tillföljd av att dess layout behöver uppdateras.

En komponent har en property valid. Är den false är dess layout ogiltig och den behöver ritas om. Klassen RepaintManager har en kö med komponenter som har ogiltig layout. Den placerar en i taget av dessa på event queue för omritning.

Vill du tvinga en komponent att rita om sig ska du anropa revalidate(). Den kommer först att anropa invalidate() på komponenten och dess förfäder som ligger i samma container som den på vilken du anropade revalidate(), och därmed sätta valid till false och göra layouten ogiltig. Därefter placeras hela containern i kön för ogiltiga layouter och i sinom tid blir den omritad, varvid validate() anropas och layouten återigen blir giltig.