Chapter 9: Threads and Swing

Chapter 9 - Threads and Swing

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Scrolling Text in a Custom Component
ScrollText (see Listing 9.8) is a custom JComponent that takes the text passed to its constructor and scrolls it from left to right across the face of the component. At the time of construction, an off-screen image is prepared with the specified text and an internal thread is started to scroll this image. ScrollText is a self-running object and uses some of the techniques from Chapter 11, Self-Running Objects, to manage its internal thread.
Listing 9.8  ScrollText.javaScroll Text Across the Face of the Component
  1: import java.awt.*;
  2: import java.awt.image.*;
  3: import java.awt.font.*;
  4: import java.awt.geom.*;
  5: import javax.swing.*;
  6:
  7: public class ScrollText extends JComponent {
  8:     private BufferedImage image;
  9:     private Dimension imageSize;
10:     private volatile int currOffset;
11:
12:     private Thread internalThread;
13:     private volatile boolean noStopRequested;
14:
15:     public ScrollText(String text) {
16:         currOffset = 0;
17:         buildImage(text);
18:
19:         setMinimumSize(imageSize);
20:         setPreferredSize(imageSize);
21:         setMaximumSize(imageSize);
22:         setSize(imageSize);
23:
24:         noStopRequested = true;
25:         Runnable r = new Runnable() {
26:                 public void run() {
27:                     try {
28:                         runWork();
29:                     } catch (Exception x) {
30:                         x.printStackTrace();
31:                     }
32:                 }
33:             };
34:
35:         internalThread = new Thread(r, ScrollText);
36:         internalThread.start();
37:     }
38:
39:     private void buildImage(String text) {
40:         // Request that the drawing be done with anti-aliasing
41:         // turned on and the quality high.
42:         RenderingHints renderHints = new RenderingHints(
43:             RenderingHints.KEY_ANTIALIASING,
44:             RenderingHints.VALUE_ANTIALIAS_ON);
45:
46:         renderHints.put(
47:             RenderingHints.KEY_RENDERING,
48:             RenderingHints.VALUE_RENDER_QUALITY);
49:
50:         // Create a scratch image for use in determining
51:         // the text dimensions.
52:         BufferedImage scratchImage = new BufferedImage(
53:                 1, 1, BufferedImage.TYPE_INT_RGB);
54:
55:         Graphics2D scratchG2 = scratchImage.createGraphics();
56:         scratchG2.setRenderingHints(renderHints);
57:
58:         Font font =
59:             new Font(Serif, Font.BOLD Font.ITALIC, 24);
60:
61:         FontRenderContext frc = scratchG2.getFontRenderContext();
62:         TextLayout tl = new TextLayout(text, font, frc);
63:         Rectangle2D textBounds = tl.getBounds();
64:         int textWidth = (int) Math.ceil(textBounds.getWidth());
65:         int textHeight = (int) Math.ceil(textBounds.getHeight());
66:
67:         int horizontalPad = 10;
68:         int verticalPad = 6;
69:
70:         imageSize = new Dimension(
71:                 textWidth + horizontalPad,
72:                 textHeight + verticalPad
73:            );
74:
75:         // Create the properly- sized image
76:         image = new BufferedImage(
77:                 imageSize.width,
78:                 imageSize.height,
79:                 BufferedImage.TYPE_INT_RGB);
80:
81:         Graphics2D g2 = image.createGraphics();
82:         g2.setRenderingHints(renderHints);
83:
84:         int baselineOffset =
85:             (verticalPad / 2) - ((int) textBounds.getY());
86:
87:         g2.setColor( Color .white);
88:         g2.fillRect(0, 0, imageSize.width, imageSize.height);
89:
90:         g2.setColor(Color.blue);
91:         tl.draw(g2, 0, baselineOffset);
92:
93:         // Free-up resources right away, but keep image for
94:         // animation.
95:         scratchG2.dispose();
96:         scratchImage.flush();
97:         g2.dispose();
98:     }
99:
100:     public void paint(Graphics g) {
101:         // Make sure to clip the edges, regardless of curr size
102:         g.setClip(0, 0, imageSize.width, imageSize.height);
103:
104:         int localOffset = currOffset ; // in case it changes
105:         g.drawImage(image, -localOffset, 0, this);
106:         g.drawImage(
107:             image, imageSize.width - localOffset, 0, this);
108:
109:         // draw outline
110:         g.setColor(Color.black);
111:         g.drawRect(
112:             0, 0, imageSize.width - 1, imageSize.height - 1);
113:     }
114:
115:     private void runWork() {
116:         while (noStopRequested) {
117:             try {
118:                 Thread.sleep(100);  // 10 frames per second
119:
120:                 // adjust the scroll position
121:                 currOffset =
122:                     (currOffset + 1) % imageSize.width;
123:
124:                 // signal the event thread to call paint()
125:                 repaint();
126:             } catch (InterruptedException x) {
127:                 Thread.currentThread().interrupt();
128:             }
129:         }
130:     }
131:
132:     public void stopRequest() {
133:         noStopRequested = false;
134:         internalThread.interrupt();
135:     }
136:
137:     public boolean isAlive() {
138:         return internalThread.isAlive();
139:     }
140:
141:     public static void main(String[] args) {
142:         ScrollText st =
143:             new ScrollText(Java can do animation!);
144:
145:         JPanel p = new JPanel(new FlowLayout());
146:         p.add(st);
147:
148:         JFrame f = new JFrame(ScrollText Demo);
149:         f.setContentPane(p);
150:         f.setSize(400, 100);
151:         f.setVisible(true);
152:     }
153: }
In main() (lines 141152), a new ScrollText instance is constructed (lines 142143) and put into a JPanel with a FlowLayout to let the instance of ScrollText take on its preferred size (lines 145146). This JPanel is put into a JFrame and set visible.
Inside the constructor (lines 1537), currOffset is set to initially be pixels. currOffset is the x-position of the image relative to the components coordinate system. Because currOffset is set by the internal thread and read by paint() , it is volatile (line 10). The buildImage() method is called to create the off-screen image that scrolls (line 17). The rest of the constructor sets the dimensions of the component and starts up the internal thread.
The buildImage() method (lines 3998) is used to prepare the off-screen image with the desired text. Because the text will be drawn to the image only once, the rendering hints are set for quality and anti-alias (lines 4248). A scratch image is created first and used in determining the exact pixel dimensions needed for the specified text (lines 5265). A little bit of horizontal and vertical padding is added and the real off-screen image is created (lines 6779). A graphics context is created from the image and used for drawing the text (lines 8191).
Whenever the event-handling thread calls paint() (lines 100113), the off-screen image is redrawn onto the component. The value of currOffset is captured in the local variable localOffset in case currOffset is changed while paint() is in progress (line 104). Actually, the image is drawn twice. It is drawn once off to the left of the component by the localOffset (line 105). And it is drawn a second time by the same offset from the right side of the component (lines 106107). Parts of the images will be automatically clipped because they extend off the sides of the component (line 102). After the image is in place, a black outline is drawn around the edge (lines 110112).
The runWork() method (lines 115130) is invoked by the internal thread that was started in the constructor. It loops continuously until another thread invokes the stopRequest() method. In the while loop, the internal thread sleeps for 0.1 seconds (line 118), increments currOffset (lines 121122), and puts a request onto the event queue for the paint() method to be called (line 125). The value of currOffset is kept between and the width of the off-screen image (line 122). currOffset is volatile so that the event thread sees the changes in value being made by the internal thread.
Figure 9.8 shows a snapshot of ScrollText in action. Figure 9.9 shows the same component a few seconds later. The text Java can do animation! is scrolling from right to left across the component. The main() method of ScrollText is simply used for demonstration purposes. ScrollText can be used as a component anywhere . You might want to enhance ScrollText so that the colors, font, scroll rate, and size of the scroll window can be specified for a more real-world application. You can speed up the scrolling by moving more than one pixel at a time, or by moving more than 10 times per second. Keep in mind that increasing the number of advances per second will use more processor resources.
Figure 9.8: ScrollTextsnapshot of text scrolling in progress.
Figure 9.9: ScrollTextanother snapshot a few seconds later.
  Note Beginning with JDK 1.2, there is a class javax.swing.Timer that can be used to simplify animation. After being started, Timer calls the actionPerformed() method on the registered object at regular intervals. The event-handling thread is used to invoke actionPerformed() , so direct modification of visible components from within actionPerformed() is safe. If the action is nonblocking and brief, the use of Timer might be an appropriate substitute for the techniques Ive shown you here.

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net